spring-cassandra compiling

This commit is contained in:
Matthew Adams
2013-11-25 18:32:25 -06:00
parent b8aa70509b
commit af0273b864
177 changed files with 17593 additions and 0 deletions

View File

@@ -0,0 +1,664 @@
# Cassandra storage config YAML
# NOTE:
# See http://wiki.apache.org/cassandra/StorageConfiguration for
# full explanations of configuration directives
# /NOTE
# The name of the cluster. This is mainly used to prevent machines in
# one logical cluster from joining another.
cluster_name: 'Test Cluster'
# This defines the number of tokens randomly assigned to this node on the ring
# The more tokens, relative to other nodes, the larger the proportion of data
# that this node will store. You probably want all nodes to have the same number
# of tokens assuming they have equal hardware capability.
#
# If you leave this unspecified, Cassandra will use the default of 1 token for legacy compatibility,
# and will use the initial_token as described below.
#
# Specifying initial_token will override this setting.
#
# If you already have a cluster with 1 token per node, and wish to migrate to
# multiple tokens per node, see http://wiki.apache.org/cassandra/Operations
num_tokens: 256
# initial_token allows you to specify tokens manually. While you can use # it with
# vnodes (num_tokens > 1, above) -- in which case you should provide a
# comma-separated list -- it's primarily used when adding nodes # to legacy clusters
# that do not have vnodes enabled.
# initial_token:
# See http://wiki.apache.org/cassandra/HintedHandoff
hinted_handoff_enabled: true
# this defines the maximum amount of time a dead host will have hints
# generated. After it has been dead this long, new hints for it will not be
# created until it has been seen alive and gone down again.
max_hint_window_in_ms: 10800000 # 3 hours
# Maximum throttle in KBs per second, per delivery thread. This will be
# reduced proportionally to the number of nodes in the cluster. (If there
# are two nodes in the cluster, each delivery thread will use the maximum
# rate; if there are three, each will throttle to half of the maximum,
# since we expect two nodes to be delivering hints simultaneously.)
hinted_handoff_throttle_in_kb: 1024
# Number of threads with which to deliver hints;
# Consider increasing this number when you have multi-dc deployments, since
# cross-dc handoff tends to be slower
max_hints_delivery_threads: 2
# The following setting populates the page cache on memtable flush and compaction
# WARNING: Enable this setting only when the whole node's data fits in memory.
# Defaults to: false
# populate_io_cache_on_flush: false
# Authentication backend, implementing IAuthenticator; used to identify users
# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthenticator,
# PasswordAuthenticator}.
#
# - AllowAllAuthenticator performs no checks - set it to disable authentication.
# - PasswordAuthenticator relies on username/password pairs to authenticate
# users. It keeps usernames and hashed passwords in system_auth.credentials table.
# Please increase system_auth keyspace replication factor if you use this authenticator.
authenticator: AllowAllAuthenticator
# Authorization backend, implementing IAuthorizer; used to limit access/provide permissions
# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthorizer,
# CassandraAuthorizer}.
#
# - AllowAllAuthorizer allows any action to any user - set it to disable authorization.
# - CassandraAuthorizer stores permissions in system_auth.permissions table. Please
# increase system_auth keyspace replication factor if you use this authorizer.
authorizer: AllowAllAuthorizer
# Validity period for permissions cache (fetching permissions can be an
# expensive operation depending on the authorizer, CassandraAuthorizer is
# one example). Defaults to 2000, set to 0 to disable.
# Will be disabled automatically for AllowAllAuthorizer.
permissions_validity_in_ms: 2000
# The partitioner is responsible for distributing rows (by key) across
# nodes in the cluster. Any IPartitioner may be used, including your
# own as long as it is on the classpath. Out of the box, Cassandra
# provides org.apache.cassandra.dht.{Murmur3Partitioner, RandomPartitioner
# ByteOrderedPartitioner, OrderPreservingPartitioner (deprecated)}.
#
# - RandomPartitioner distributes rows across the cluster evenly by md5.
# This is the default prior to 1.2 and is retained for compatibility.
# - Murmur3Partitioner is similar to RandomPartioner but uses Murmur3_128
# Hash Function instead of md5. When in doubt, this is the best option.
# - ByteOrderedPartitioner orders rows lexically by key bytes. BOP allows
# scanning rows in key order, but the ordering can generate hot spots
# for sequential insertion workloads.
# - OrderPreservingPartitioner is an obsolete form of BOP, that stores
# - keys in a less-efficient format and only works with keys that are
# UTF8-encoded Strings.
# - CollatingOPP collates according to EN,US rules rather than lexical byte
# ordering. Use this as an example if you need custom collation.
#
# See http://wiki.apache.org/cassandra/Operations for more on
# partitioners and token selection.
partitioner: org.apache.cassandra.dht.Murmur3Partitioner
# Directories where Cassandra should store data on disk. Cassandra
# will spread data evenly across them, subject to the granularity of
# the configured compaction strategy.
data_file_directories:
- .cassandra/var/lib/cassandra/data
# commit log
commitlog_directory: .cassandra/var/lib/cassandra/commitlog
# policy for data disk failures:
# stop: shut down gossip and Thrift, leaving the node effectively dead, but
# can still be inspected via JMX.
# best_effort: stop using the failed disk and respond to requests based on
# remaining available sstables. This means you WILL see obsolete
# data at CL.ONE!
# ignore: ignore fatal errors and let requests fail, as in pre-1.2 Cassandra
disk_failure_policy: stop
# Maximum size of the key cache in memory.
#
# Each key cache hit saves 1 seek and each row cache hit saves 2 seeks at the
# minimum, sometimes more. The key cache is fairly tiny for the amount of
# time it saves, so it's worthwhile to use it at large numbers.
# The row cache saves even more time, but must contain the entire row,
# so it is extremely space-intensive. It's best to only use the
# row cache if you have hot rows or static rows.
#
# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
#
# Default value is empty to make it "auto" (min(5% of Heap (in MB), 100MB)). Set to 0 to disable key cache.
key_cache_size_in_mb:
# Duration in seconds after which Cassandra should
# save the key cache. Caches are saved to saved_caches_directory as
# specified in this configuration file.
#
# Saved caches greatly improve cold-start speeds, and is relatively cheap in
# terms of I/O for the key cache. Row cache saving is much more expensive and
# has limited use.
#
# Default is 14400 or 4 hours.
key_cache_save_period: 14400
# Number of keys from the key cache to save
# Disabled by default, meaning all keys are going to be saved
# key_cache_keys_to_save: 100
# Maximum size of the row cache in memory.
# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
#
# Default value is 0, to disable row caching.
row_cache_size_in_mb: 0
# Duration in seconds after which Cassandra should
# safe the row cache. Caches are saved to saved_caches_directory as specified
# in this configuration file.
#
# Saved caches greatly improve cold-start speeds, and is relatively cheap in
# terms of I/O for the key cache. Row cache saving is much more expensive and
# has limited use.
#
# Default is 0 to disable saving the row cache.
row_cache_save_period: 0
# Number of keys from the row cache to save
# Disabled by default, meaning all keys are going to be saved
# row_cache_keys_to_save: 100
# The off-heap memory allocator. Affects storage engine metadata as
# well as caches. Experiments show that JEMAlloc saves some memory
# than the native GCC allocator (i.e., JEMalloc is more
# fragmentation-resistant).
#
# Supported values are: NativeAllocator, JEMallocAllocator
#
# If you intend to use JEMallocAllocator you have to install JEMalloc as library and
# modify cassandra-env.sh as directed in the file.
#
# Defaults to NativeAllocator
# memory_allocator: NativeAllocator
# saved caches
saved_caches_directory: .cassandra/var/lib/cassandra/saved_caches
# commitlog_sync may be either "periodic" or "batch."
# When in batch mode, Cassandra won't ack writes until the commit log
# has been fsynced to disk. It will wait up to
# commitlog_sync_batch_window_in_ms milliseconds for other writes, before
# performing the sync.
#
# commitlog_sync: batch
# commitlog_sync_batch_window_in_ms: 50
#
# the other option is "periodic" where writes may be acked immediately
# and the CommitLog is simply synced every commitlog_sync_period_in_ms
# milliseconds. By default this allows 1024*(CPU cores) pending
# entries on the commitlog queue. If you are writing very large blobs,
# you should reduce that; 16*cores works reasonably well for 1MB blobs.
# It should be at least as large as the concurrent_writes setting.
commitlog_sync: periodic
commitlog_sync_period_in_ms: 10000
# commitlog_periodic_queue_size:
# The size of the individual commitlog file segments. A commitlog
# segment may be archived, deleted, or recycled once all the data
# in it (potentially from each columnfamily in the system) has been
# flushed to sstables.
#
# The default size is 32, which is almost always fine, but if you are
# archiving commitlog segments (see commitlog_archiving.properties),
# then you probably want a finer granularity of archiving; 8 or 16 MB
# is reasonable.
commitlog_segment_size_in_mb: 32
# any class that implements the SeedProvider interface and has a
# constructor that takes a Map<String, String> of parameters will do.
seed_provider:
# Addresses of hosts that are deemed contact points.
# Cassandra nodes use this list of hosts to find each other and learn
# the topology of the ring. You must change this if you are running
# multiple nodes!
- class_name: org.apache.cassandra.locator.SimpleSeedProvider
parameters:
# seeds is actually a comma-delimited list of addresses.
# Ex: "<ip1>,<ip2>,<ip3>"
- seeds: "127.0.0.1"
# For workloads with more data than can fit in memory, Cassandra's
# bottleneck will be reads that need to fetch data from
# disk. "concurrent_reads" should be set to (16 * number_of_drives) in
# order to allow the operations to enqueue low enough in the stack
# that the OS and drives can reorder them.
#
# On the other hand, since writes are almost never IO bound, the ideal
# number of "concurrent_writes" is dependent on the number of cores in
# your system; (8 * number_of_cores) is a good rule of thumb.
concurrent_reads: 32
concurrent_writes: 32
# Total memory to use for sstable-reading buffers. Defaults to
# the smaller of 1/4 of heap or 512MB.
# file_cache_size_in_mb: 512
# Total memory to use for memtables. Cassandra will flush the largest
# memtable when this much memory is used.
# If omitted, Cassandra will set it to 1/3 of the heap.
# memtable_total_space_in_mb: 2048
# Total space to use for commitlogs. Since commitlog segments are
# mmapped, and hence use up address space, the default size is 32
# on 32-bit JVMs, and 1024 on 64-bit JVMs.
#
# If space gets above this value (it will round up to the next nearest
# segment multiple), Cassandra will flush every dirty CF in the oldest
# segment and remove it. So a small total commitlog space will tend
# to cause more flush activity on less-active columnfamilies.
# commitlog_total_space_in_mb: 4096
# This sets the amount of memtable flush writer threads. These will
# be blocked by disk io, and each one will hold a memtable in memory
# while blocked. If you have a large heap and many data directories,
# you can increase this value for better flush performance.
# By default this will be set to the amount of data directories defined.
#memtable_flush_writers: 1
# the number of full memtables to allow pending flush, that is,
# waiting for a writer thread. At a minimum, this should be set to
# the maximum number of secondary indexes created on a single CF.
memtable_flush_queue_size: 4
# Whether to, when doing sequential writing, fsync() at intervals in
# order to force the operating system to flush the dirty
# buffers. Enable this to avoid sudden dirty buffer flushing from
# impacting read latencies. Almost always a good idea on SSDs; not
# necessarily on platters.
trickle_fsync: false
trickle_fsync_interval_in_kb: 10240
# TCP port, for commands and data
storage_port: 7000
# SSL port, for encrypted communication. Unused unless enabled in
# encryption_options
ssl_storage_port: 7001
# Address to bind to and tell other Cassandra nodes to connect to. You
# _must_ change this if you want multiple nodes to be able to
# communicate!
#
# Leaving it blank leaves it up to InetAddress.getLocalHost(). This
# will always do the Right Thing _if_ the node is properly configured
# (hostname, name resolution, etc), and the Right Thing is to use the
# address associated with the hostname (it might not be).
#
# Setting this to 0.0.0.0 is always wrong.
listen_address: localhost
# Address to broadcast to other Cassandra nodes
# Leaving this blank will set it to the same value as listen_address
# broadcast_address: 1.2.3.4
# Internode authentication backend, implementing IInternodeAuthenticator;
# used to allow/disallow connections from peer nodes.
# internode_authenticator: org.apache.cassandra.auth.AllowAllInternodeAuthenticator
# Whether to start the native transport server.
# Please note that the address on which the native transport is bound is the
# same as the rpc_address. The port however is different and specified below.
start_native_transport: true
# port for the CQL native transport to listen for clients on
native_transport_port: 9042
# The maximum threads for handling requests when the native transport is used.
# This is similar to rpc_max_threads though the default differs slightly (and
# there is no native_transport_min_threads, idle threads will always be stopped
# after 30 seconds).
# native_transport_max_threads: 128
# Whether to start the thrift rpc server.
start_rpc: true
# The address to bind the Thrift RPC service and native transport
# server -- clients connect here.
#
# Leaving this blank has the same effect it does for ListenAddress,
# (i.e. it will be based on the configured hostname of the node).
#
# Note that unlike ListenAddress above, it is allowed to specify 0.0.0.0
# here if you want to listen on all interfaces but is not best practice
# as it is known to confuse the node auto-discovery features of some
# client drivers.
rpc_address: localhost
# port for Thrift to listen for clients on
rpc_port: 9160
# enable or disable keepalive on rpc connections
rpc_keepalive: true
# Cassandra provides three out-of-the-box options for the RPC Server:
#
# sync -> One thread per thrift connection. For a very large number of clients, memory
# will be your limiting factor. On a 64 bit JVM, 180KB is the minimum stack size
# per thread, and that will correspond to your use of virtual memory (but physical memory
# may be limited depending on use of stack space).
#
# hsha -> Stands for "half synchronous, half asynchronous." All thrift clients are handled
# asynchronously using a small number of threads that does not vary with the amount
# of thrift clients (and thus scales well to many clients). The rpc requests are still
# synchronous (one thread per active request).
#
# The default is sync because on Windows hsha is about 30% slower. On Linux,
# sync/hsha performance is about the same, with hsha of course using less memory.
#
# Alternatively, can provide your own RPC server by providing the fully-qualified class name
# of an o.a.c.t.TServerFactory that can create an instance of it.
rpc_server_type: sync
# Uncomment rpc_min|max_thread to set request pool size limits.
#
# Regardless of your choice of RPC server (see above), the number of maximum requests in the
# RPC thread pool dictates how many concurrent requests are possible (but if you are using the sync
# RPC server, it also dictates the number of clients that can be connected at all).
#
# The default is unlimited and thus provides no protection against clients overwhelming the server. You are
# encouraged to set a maximum that makes sense for you in production, but do keep in mind that
# rpc_max_threads represents the maximum number of client requests this server may execute concurrently.
#
# rpc_min_threads: 16
# rpc_max_threads: 2048
# uncomment to set socket buffer sizes on rpc connections
# rpc_send_buff_size_in_bytes:
# rpc_recv_buff_size_in_bytes:
# Uncomment to set socket buffer size for internode communication
# Note that when setting this, the buffer size is limited by net.core.wmem_max
# and when not setting it it is defined by net.ipv4.tcp_wmem
# See:
# /proc/sys/net/core/wmem_max
# /proc/sys/net/core/rmem_max
# /proc/sys/net/ipv4/tcp_wmem
# /proc/sys/net/ipv4/tcp_wmem
# and: man tcp
# internode_send_buff_size_in_bytes:
# internode_recv_buff_size_in_bytes:
# Frame size for thrift (maximum message length).
thrift_framed_transport_size_in_mb: 15
# Set to true to have Cassandra create a hard link to each sstable
# flushed or streamed locally in a backups/ subdirectory of the
# keyspace data. Removing these links is the operator's
# responsibility.
incremental_backups: false
# Whether or not to take a snapshot before each compaction. Be
# careful using this option, since Cassandra won't clean up the
# snapshots for you. Mostly useful if you're paranoid when there
# is a data format change.
snapshot_before_compaction: false
# Whether or not a snapshot is taken of the data before keyspace truncation
# or dropping of column families. The STRONGLY advised default of true
# should be used to provide data safety. If you set this flag to false, you will
# lose data on truncation or drop.
auto_snapshot: true
# Add column indexes to a row after its contents reach this size.
# Increase if your column values are large, or if you have a very large
# number of columns. The competing causes are, Cassandra has to
# deserialize this much of the row to read a single column, so you want
# it to be small - at least if you do many partial-row reads - but all
# the index data is read for each access, so you don't want to generate
# that wastefully either.
column_index_size_in_kb: 64
# Size limit for rows being compacted in memory. Larger rows will spill
# over to disk and use a slower two-pass compaction process. A message
# will be logged specifying the row key.
in_memory_compaction_limit_in_mb: 64
# Number of simultaneous compactions to allow, NOT including
# validation "compactions" for anti-entropy repair. Simultaneous
# compactions can help preserve read performance in a mixed read/write
# workload, by mitigating the tendency of small sstables to accumulate
# during a single long running compactions. The default is usually
# fine and if you experience problems with compaction running too
# slowly or too fast, you should look at
# compaction_throughput_mb_per_sec first.
#
# concurrent_compactors defaults to the number of cores.
# Uncomment to make compaction mono-threaded, the pre-0.8 default.
#concurrent_compactors: 1
# Multi-threaded compaction. When enabled, each compaction will use
# up to one thread per core, plus one thread per sstable being merged.
# This is usually only useful for SSD-based hardware: otherwise,
# your concern is usually to get compaction to do LESS i/o (see:
# compaction_throughput_mb_per_sec), not more.
multithreaded_compaction: false
# Throttles compaction to the given total throughput across the entire
# system. The faster you insert data, the faster you need to compact in
# order to keep the sstable count down, but in general, setting this to
# 16 to 32 times the rate you are inserting data is more than sufficient.
# Setting this to 0 disables throttling. Note that this account for all types
# of compaction, including validation compaction.
compaction_throughput_mb_per_sec: 16
# Track cached row keys during compaction, and re-cache their new
# positions in the compacted sstable. Disable if you use really large
# key caches.
compaction_preheat_key_cache: true
# Throttles all outbound streaming file transfers on this node to the
# given total throughput in Mbps. This is necessary because Cassandra does
# mostly sequential IO when streaming data during bootstrap or repair, which
# can lead to saturating the network connection and degrading rpc performance.
# When unset, the default is 200 Mbps or 25 MB/s.
# stream_throughput_outbound_megabits_per_sec: 200
# How long the coordinator should wait for read operations to complete
read_request_timeout_in_ms: 5000
# How long the coordinator should wait for seq or index scans to complete
range_request_timeout_in_ms: 10000
# How long the coordinator should wait for writes to complete
write_request_timeout_in_ms: 2000
# How long a coordinator should continue to retry a CAS operation
# that contends with other proposals for the same row
cas_contention_timeout_in_ms: 1000
# How long the coordinator should wait for truncates to complete
# (This can be much longer, because unless auto_snapshot is disabled
# we need to flush first so we can snapshot before removing the data.)
truncate_request_timeout_in_ms: 60000
# The default timeout for other, miscellaneous operations
request_timeout_in_ms: 10000
# Enable operation timeout information exchange between nodes to accurately
# measure request timeouts. If disabled, replicas will assume that requests
# were forwarded to them instantly by the coordinator, which means that
# under overload conditions we will waste that much extra time processing
# already-timed-out requests.
#
# Warning: before enabling this property make sure to ntp is installed
# and the times are synchronized between the nodes.
cross_node_timeout: false
# Enable socket timeout for streaming operation.
# When a timeout occurs during streaming, streaming is retried from the start
# of the current file. This _can_ involve re-streaming an important amount of
# data, so you should avoid setting the value too low.
# Default value is 0, which never timeout streams.
# streaming_socket_timeout_in_ms: 0
# phi value that must be reached for a host to be marked down.
# most users should never need to adjust this.
# phi_convict_threshold: 8
# endpoint_snitch -- Set this to a class that implements
# IEndpointSnitch. The snitch has two functions:
# - it teaches Cassandra enough about your network topology to route
# requests efficiently
# - it allows Cassandra to spread replicas around your cluster to avoid
# correlated failures. It does this by grouping machines into
# "datacenters" and "racks." Cassandra will do its best not to have
# more than one replica on the same "rack" (which may not actually
# be a physical location)
#
# IF YOU CHANGE THE SNITCH AFTER DATA IS INSERTED INTO THE CLUSTER,
# YOU MUST RUN A FULL REPAIR, SINCE THE SNITCH AFFECTS WHERE REPLICAS
# ARE PLACED.
#
# Out of the box, Cassandra provides
# - SimpleSnitch:
# Treats Strategy order as proximity. This improves cache locality
# when disabling read repair, which can further improve throughput.
# Only appropriate for single-datacenter deployments.
# - PropertyFileSnitch:
# Proximity is determined by rack and data center, which are
# explicitly configured in cassandra-topology.properties.
# - GossipingPropertyFileSnitch
# The rack and datacenter for the local node are defined in
# cassandra-rackdc.properties and propagated to other nodes via gossip. If
# cassandra-topology.properties exists, it is used as a fallback, allowing
# migration from the PropertyFileSnitch.
# - RackInferringSnitch:
# Proximity is determined by rack and data center, which are
# assumed to correspond to the 3rd and 2nd octet of each node's
# IP address, respectively. Unless this happens to match your
# deployment conventions (as it did Facebook's), this is best used
# as an example of writing a custom Snitch class.
# - Ec2Snitch:
# Appropriate for EC2 deployments in a single Region. Loads Region
# and Availability Zone information from the EC2 API. The Region is
# treated as the datacenter, and the Availability Zone as the rack.
# Only private IPs are used, so this will not work across multiple
# Regions.
# - Ec2MultiRegionSnitch:
# Uses public IPs as broadcast_address to allow cross-region
# connectivity. (Thus, you should set seed addresses to the public
# IP as well.) You will need to open the storage_port or
# ssl_storage_port on the public IP firewall. (For intra-Region
# traffic, Cassandra will switch to the private IP after
# establishing a connection.)
#
# You can use a custom Snitch by setting this to the full class name
# of the snitch, which will be assumed to be on your classpath.
endpoint_snitch: SimpleSnitch
# controls how often to perform the more expensive part of host score
# calculation
dynamic_snitch_update_interval_in_ms: 100
# controls how often to reset all host scores, allowing a bad host to
# possibly recover
dynamic_snitch_reset_interval_in_ms: 600000
# if set greater than zero and read_repair_chance is < 1.0, this will allow
# 'pinning' of replicas to hosts in order to increase cache capacity.
# The badness threshold will control how much worse the pinned host has to be
# before the dynamic snitch will prefer other replicas over it. This is
# expressed as a double which represents a percentage. Thus, a value of
# 0.2 means Cassandra would continue to prefer the static snitch values
# until the pinned host was 20% worse than the fastest.
dynamic_snitch_badness_threshold: 0.1
# request_scheduler -- Set this to a class that implements
# RequestScheduler, which will schedule incoming client requests
# according to the specific policy. This is useful for multi-tenancy
# with a single Cassandra cluster.
# NOTE: This is specifically for requests from the client and does
# not affect inter node communication.
# org.apache.cassandra.scheduler.NoScheduler - No scheduling takes place
# org.apache.cassandra.scheduler.RoundRobinScheduler - Round robin of
# client requests to a node with a separate queue for each
# request_scheduler_id. The scheduler is further customized by
# request_scheduler_options as described below.
request_scheduler: org.apache.cassandra.scheduler.NoScheduler
# Scheduler Options vary based on the type of scheduler
# NoScheduler - Has no options
# RoundRobin
# - throttle_limit -- The throttle_limit is the number of in-flight
# requests per client. Requests beyond
# that limit are queued up until
# running requests can complete.
# The value of 80 here is twice the number of
# concurrent_reads + concurrent_writes.
# - default_weight -- default_weight is optional and allows for
# overriding the default which is 1.
# - weights -- Weights are optional and will default to 1 or the
# overridden default_weight. The weight translates into how
# many requests are handled during each turn of the
# RoundRobin, based on the scheduler id.
#
# request_scheduler_options:
# throttle_limit: 80
# default_weight: 5
# weights:
# Keyspace1: 1
# Keyspace2: 5
# request_scheduler_id -- An identifier based on which to perform
# the request scheduling. Currently the only valid option is keyspace.
# request_scheduler_id: keyspace
# Enable or disable inter-node encryption
# Default settings are TLS v1, RSA 1024-bit keys (it is imperative that
# users generate their own keys) TLS_RSA_WITH_AES_128_CBC_SHA as the cipher
# suite for authentication, key exchange and encryption of the actual data transfers.
# NOTE: No custom encryption options are enabled at the moment
# The available internode options are : all, none, dc, rack
#
# If set to dc cassandra will encrypt the traffic between the DCs
# If set to rack cassandra will encrypt the traffic between the racks
#
# The passwords used in these options must match the passwords used when generating
# the keystore and truststore. For instructions on generating these files, see:
# http://download.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#CreateKeystore
#
server_encryption_options:
internode_encryption: none
keystore: conf/.keystore
keystore_password: cassandra
truststore: conf/.truststore
truststore_password: cassandra
# More advanced defaults below:
# protocol: TLS
# algorithm: SunX509
# store_type: JKS
# cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA]
# require_client_auth: false
# enable or disable client/server encryption.
client_encryption_options:
enabled: false
keystore: conf/.keystore
keystore_password: cassandra
# require_client_auth: false
# Set trustore and truststore_password if require_client_auth is true
# truststore: conf/.truststore
# truststore_password: cassandra
# More advanced defaults below:
# protocol: TLS
# algorithm: SunX509
# store_type: JKS
# cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA]
# internode_compression controls whether traffic between nodes is
# compressed.
# can be: all - all traffic is compressed
# dc - traffic between different datacenters is compressed
# none - nothing is compressed.
internode_compression: all
# Enable or disable tcp_nodelay for inter-dc communication.
# Disabling it will result in larger (but fewer) network packets being sent,
# reducing overhead from the TCP protocol itself, at the cost of increasing
# latency if you block for cross-datacenter responses.
inter_dc_tcp_nodelay: false
# Enable or disable kernel page cache preheating from contents of the key cache after compaction.
# When enabled it would preheat only first "page" (4KB) of each row to optimize
# for sequential access. Note: This could be harmful for fat rows, see CASSANDRA-4937
# for further details on that topic.
preheat_kernel_page_cache: false

23
attic/get-and-start-cassandra Executable file
View File

@@ -0,0 +1,23 @@
CASSANDRA_DIST=.cassandra/dist
mkdir -p $CASSANDRA_DIST
curl -sL http://downloads.datastax.com/community/dsc.tar.gz > $CASSANDRA_DIST/dist.tgz
tar -xzf $CASSANDRA_DIST/dist.tgz -C $CASSANDRA_DIST
CASSANDRA_HOME=`find $CASSANDRA_DIST -name 'dsc-cassandra-*' -print`
if [ -z "$CASSANDRA_HOME" ]; then
echo "Couldn't determine CASSANDRA_HOME"
exit 1
fi
echo "CASSANDRA_HOME is $CASSANDRA_HOME"
# these directories must match what's in test-support/cassandra/conf/cassandra.yaml
mkdir -p .cassandra/var/lib/cassandra
mkdir -p .cassandra/var/log/cassandra
mv $CASSANDRA_HOME/conf/cassandra.yaml $CASSANDRA_HOME/conf/cassandra.yaml.original
cp test-support/cassandra/conf/cassandra.yaml $CASSANDRA_HOME/conf/cassandra.yaml
$CASSANDRA_HOME/bin/cassandra -p $CASSANDRA_HOME/cassandra.pid
sleep 5

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2011-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.cassandra.core;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.DriverException;
/**
* Created a PreparedStatement and retrieved the PreparedStatement from cache if the statement has been prepared
* previously. In general, this creator should be used over the {@link SimplePreparedStatementCreator} as it provides
* better performance.
*
* <p>
* There is overhead in Cassandra when Preparing a Statement. This is negligible on a single data center configuration,
* but when your cluster spans multiple data centers, preparing the same statement over and over again is not necessary
* and causes performance issues in high throughput use cases.
* </p>
*
* @author David Webb
*
*/
public class CachedPreparedStatementCreator implements PreparedStatementCreator, CqlProvider {
private static Logger log = LoggerFactory.getLogger(CachedPreparedStatementCreator.class);
private final String cql;
private PreparedStatement cache;
/**
* Create a CachedPreparedStatementCreator from the provided CQL.
*
* @param cql
*/
public CachedPreparedStatementCreator(String cql) {
Assert.notNull(cql, "CQL is required to create a PreparedStatement");
this.cql = cql;
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.PreparedStatementCreator#createPreparedStatement(com.datastax.driver.core.Session)
*/
@Override
public PreparedStatement createPreparedStatement(Session session) throws DriverException {
if (cache == null) {
log.debug("PreparedStatement cache is null, preparing new Statement");
cache = session.prepare(getCql());
} else {
log.debug("Using cached PreparedStatement");
}
return cache;
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CqlProvider#getCql()
*/
@Override
public String getCql() {
return this.cql;
}
}

View File

@@ -0,0 +1,401 @@
/*
* Copyright 2011-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.cassandra.core;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.springframework.dao.DataAccessException;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Session;
/**
* Operations for interacting with Cassandra at the lowest level. This interface provides Exception Translation.
*
* @author David Webb
* @author Matthew Adams
*/
public interface CassandraOperations {
/**
* Executes the supplied {@link SessionCallback} in the current Template Session. The implementation of
* SessionCallback can decide whether or not to <code>execute()</code> or <code>executeAsync()</code> the operation.
*
* @param sessionCallback
* @return Type<T> defined in the SessionCallback
*/
<T> T execute(SessionCallback<T> sessionCallback) throws DataAccessException;
/**
* Executes the supplied CQL Query and returns nothing.
*
* @param cql
*/
void execute(final String cql) throws DataAccessException;
/**
* Executes the supplied CQL Query Asynchronously and returns nothing.
*
* @param cql The CQL Statement to execute
*/
void executeAsynchronously(final String cql) throws DataAccessException;
/**
* Executes the provided CQL Query, and extracts the results with the ResultSetExtractor.
*
* @param cql The Query
* @param rse The implementation for extracting the ResultSet
*
* @return Type <T> specified in the ResultSetExtractor
* @throws DataAccessException
*/
<T> T query(final String cql, ResultSetExtractor<T> rse) throws DataAccessException;
/**
* Executes the provided CQL Query asynchronously, and extracts the results with the ResultSetFutureExtractor
*
* @param cql The Query
* @param rse The implementation for extracting the future results
* @return
* @throws DataAccessException
*/
<T> T queryAsynchronously(final String cql, ResultSetFutureExtractor<T> rse) throws DataAccessException;
/**
* Executes the provided CQL Query, and then processes the results with the <code>RowCallbackHandler</code>.
*
* @param cql The Query
* @param rch The implementation for processing the rows returned.
* @throws DataAccessException
*/
void query(final String cql, RowCallbackHandler rch) throws DataAccessException;
/**
* Processes the ResultSet through the RowCallbackHandler and return nothing. This is used internal to the Template
* for core operations, but is made available through Operations in the event you have a ResultSet to process. The
* ResultsSet could come from a ResultSetFuture after an asynchronous query.
*
* @param resultSet Results to process
* @param rch RowCallbackHandler with the processing implementation
* @throws DataAccessException
*/
void process(ResultSet resultSet, RowCallbackHandler rch) throws DataAccessException;
/**
* Executes the provided CQL Query, and maps all Rows returned with the supplied RowMapper.
*
* @param cql The Query
* @param rowMapper The implementation for mapping all rows
* @return List of <T> processed by the RowMapper
* @throws DataAccessException
*/
<T> List<T> query(final String cql, RowMapper<T> rowMapper) throws DataAccessException;
/**
* Processes the ResultSet through the RowMapper and returns the List of mapped Rows. This is used internal to the
* Template for core operations, but is made available through Operations in the event you have a ResultSet to
* process. The ResultsSet could come from a ResultSetFuture after an asynchronous query.
*
* @param resultSet Results to process
* @param rowMapper RowMapper with the processing implementation
* @return List of <T> generated by the RowMapper
* @throws DataAccessException
*/
<T> List<T> process(ResultSet resultSet, RowMapper<T> rowMapper) throws DataAccessException;
/**
* Executes the provided CQL Query, and maps <b>ONE</b> Row returned with the supplied RowMapper.
*
* <p>
* This expects only ONE row to be returned. More than one Row will cause an Exception to be thrown.
* </p>
*
* @param cql The Query
* @param rowMapper The implementation for convert the Row to <T>
* @return Object<T>
* @throws DataAccessException
*/
<T> T queryForObject(final String cql, RowMapper<T> rowMapper) throws DataAccessException;
/**
* Process a ResultSet through a RowMapper. This is used internal to the Template for core operations, but is made
* available through Operations in the event you have a ResultSet to process. The ResultsSet could come from a
* ResultSetFuture after an asynchronous query.
*
* @param resultSet
* @param rowMapper
* @return
* @throws DataAccessException
*/
<T> T processOne(ResultSet resultSet, RowMapper<T> rowMapper) throws DataAccessException;
/**
* Executes the provided query and tries to return the first column of the first Row as a Class<T>.
*
* @param cql The Query
* @param requiredType Valid Class that Cassandra Data Types can be converted to.
* @return The Object<T> - item [0,0] in the result table of the query.
* @throws DataAccessException
*/
<T> T queryForObject(final String cql, Class<T> requiredType) throws DataAccessException;
/**
* Process a ResultSet, trying to convert the first columns of the first Row to Class<T>. This is used internal to the
* Template for core operations, but is made available through Operations in the event you have a ResultSet to
* process. The ResultsSet could come from a ResultSetFuture after an asynchronous query.
*
* @param resultSet
* @param requiredType
* @return
* @throws DataAccessException
*/
<T> T processOne(ResultSet resultSet, Class<T> requiredType) throws DataAccessException;
/**
* Executes the provided CQL Query and maps <b>ONE</b> Row to a basic Map of Strings and Objects. If more than one Row
* is returned from the Query, an exception will be thrown.
*
* @param cql The Query
* @return Map representing the results of the Query
* @throws DataAccessException
*/
Map<String, Object> queryForMap(final String cql) throws DataAccessException;
/**
* Process a ResultSet with <b>ONE</b> Row and convert to a Map. This is used internal to the Template for core
* operations, but is made available through Operations in the event you have a ResultSet to process. The ResultsSet
* could come from a ResultSetFuture after an asynchronous query.
*
* @param resultSet
* @return
* @throws DataAccessException
*/
Map<String, Object> processMap(ResultSet resultSet) throws DataAccessException;
/**
* Executes the provided CQL and returns all values in the first column of the Results as a List of the Type in the
* second argument.
*
* @param cql The Query
* @param elementType Type to cast the data values to
* @return List of elementType
* @throws DataAccessException
*/
<T> List<T> queryForList(final String cql, Class<T> elementType) throws DataAccessException;
/**
* Process a ResultSet and convert the first column of the results to a List. This is used internal to the Template
* for core operations, but is made available through Operations in the event you have a ResultSet to process. The
* ResultsSet could come from a ResultSetFuture after an asynchronous query.
*
* @param resultSet
* @param elementType
* @return
* @throws DataAccessException
*/
<T> List<T> processList(ResultSet resultSet, Class<T> elementType) throws DataAccessException;
/**
* Executes the provided CQL and converts the results to a basic List of Maps. Each element in the List represents a
* Row returned from the Query. Each Row's columns are put into the map as column/value.
*
* @param cql The Query
* @return List of Maps with the query results
* @throws DataAccessException
*/
List<Map<String, Object>> queryForListOfMap(final String cql) throws DataAccessException;
/**
* Process a ResultSet and convert it to a List of Maps with column/value. This is used internal to the Template for
* core operations, but is made available through Operations in the event you have a ResultSet to process. The
* ResultsSet could come from a ResultSetFuture after an asynchronous query.
*
* @param resultSet
* @return
* @throws DataAccessException
*/
List<Map<String, Object>> processListOfMap(ResultSet resultSet) throws DataAccessException;
/**
* Converts the CQL provided into a {@link SimplePreparedStatementCreator}. <b>This can only be used for CQL
* Statements that do not have data binding.</b> The results of the PreparedStatement are processed with
* PreparedStatementCallback implementation provided by the Application Code.
*
* @param cql The CQL Statement to Execute
* @param action What to do with the results of the PreparedStatement
* @return Type<T> as determined by the supplied Callback.
* @throws DataAccessException
*/
<T> T execute(String cql, PreparedStatementCallback<T> action) throws DataAccessException;
/**
* Uses the provided PreparedStatementCreator to prepare a new Session call, then executes the statement and processes
* the statement using the provided Callback. <b>This can only be used for CQL Statements that do not have data
* binding.</b> The results of the PreparedStatement are processed with PreparedStatementCallback implementation
* provided by the Application Code.
*
* @param psc The implementation to create the PreparedStatement
* @param action What to do with the results of the PreparedStatement
* @return Type<T> as determined by the supplied Callback.
* @throws DataAccessException
*/
<T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action) throws DataAccessException;
/**
* Converts the CQL provided into a {@link SimplePreparedStatementCreator}. Then, the PreparedStatementBinder will
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
* processed with the ResultSetExtractor implementation provided by the Application Code. The can return any object,
* including a List of Objects to support the ResultSet processing.
*
* @param cql The Query to Prepare
* @param psb The Binding implementation
* @param rse The implementation for extracting the results of the query.
* @return Type<T> generated by the ResultSetExtractor
* @throws DataAccessException
*/
<T> T query(final String cql, PreparedStatementBinder psb, ResultSetExtractor<T> rse) throws DataAccessException;
/**
* Converts the CQL provided into a {@link SimplePreparedStatementCreator}. Then, the PreparedStatementBinder will
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
* processed with the RowCallbackHandler implementation provided and nothing is returned.
*
* @param cql The Query to Prepare
* @param psb The Binding implementation
* @param rch The RowCallbackHandler for processing the ResultSet
* @throws DataAccessException
*/
void query(final String cql, PreparedStatementBinder psb, RowCallbackHandler rch) throws DataAccessException;
/**
* Converts the CQL provided into a {@link SimplePreparedStatementCreator}. Then, the PreparedStatementBinder will
* bind its values to the bind variables in the provided CQL String. The results of the PreparedStatement are
* processed with the RowMapper implementation provided and a List is returned with elements of Type <T> for each Row
* returned.
*
* @param cql The Query to Prepare
* @param psb The Binding implementation
* @param rowMapper The implementation for Mapping a Row to Type <T>
* @return List of <T> for each Row returned from the Query.
* @throws DataAccessException
*/
<T> List<T> query(final String cql, PreparedStatementBinder psb, RowMapper<T> rowMapper) throws DataAccessException;
/**
* Uses the provided PreparedStatementCreator to prepare a new Session call. <b>This can only be used for CQL
* Statements that do not have data binding.</b> The results of the PreparedStatement are processed with
* ResultSetExtractor implementation provided by the Application Code.
*
* @param psc The implementation to create the PreparedStatement
* @param rse Implementation for extracting from the ResultSet
* @return Type <T> which is the output of the ResultSetExtractor
* @throws DataAccessException
*/
<T> T query(PreparedStatementCreator psc, ResultSetExtractor<T> rse) throws DataAccessException;
/**
* Uses the provided PreparedStatementCreator to prepare a new Session call. <b>This can only be used for CQL
* Statements that do not have data binding.</b> The results of the PreparedStatement are processed with
* RowCallbackHandler and nothing is returned.
*
* @param psc The implementation to create the PreparedStatement
* @param rch The implementation to process Results
* @throws DataAccessException
*/
void query(PreparedStatementCreator psc, RowCallbackHandler rch) throws DataAccessException;
/**
* Uses the provided PreparedStatementCreator to prepare a new Session call. <b>This can only be used for CQL
* Statements that do not have data binding.</b> The results of the PreparedStatement are processed with RowMapper
* implementation provided and a List is returned with elements of Type <T> for each Row returned.
*
* @param psc The implementation to create the PreparedStatement
* @param rowMapper The implementation for mapping each Row returned.
* @return List of Type <T> mapped from each Row in the Results
* @throws DataAccessException
*/
<T> List<T> query(PreparedStatementCreator psc, RowMapper<T> rowMapper) throws DataAccessException;
/**
* Uses the provided PreparedStatementCreator to prepare a new Session call. Binds the values from the
* PreparedStatementBinder to the available bind variables. The results of the PreparedStatement are processed with
* ResultSetExtractor implementation provided by the Application Code.
*
* @param psc The implementation to create the PreparedStatement
* @param psb The implementation to bind variables to values
* @param rse Implementation for extracting from the ResultSet
* @return Type <T> which is the output of the ResultSetExtractor
* @throws DataAccessException
*/
<T> T query(PreparedStatementCreator psc, final PreparedStatementBinder psb, final ResultSetExtractor<T> rse)
throws DataAccessException;
/**
* Uses the provided PreparedStatementCreator to prepare a new Session call. Binds the values from the
* PreparedStatementBinder to the available bind variables. The results of the PreparedStatement are processed with
* RowCallbackHandler and nothing is returned.
*
* @param psc The implementation to create the PreparedStatement
* @param psb The implementation to bind variables to values
* @param rch The implementation to process Results
* @return Type <T> which is the output of the ResultSetExtractor
* @throws DataAccessException
*/
void query(PreparedStatementCreator psc, final PreparedStatementBinder psb, final RowCallbackHandler rch)
throws DataAccessException;
/**
* Uses the provided PreparedStatementCreator to prepare a new Session call. Binds the values from the
* PreparedStatementBinder to the available bind variables. The results of the PreparedStatement are processed with
* RowMapper implementation provided and a List is returned with elements of Type <T> for each Row returned.
*
* @param psc The implementation to create the PreparedStatement
* @param psb The implementation to bind variables to values
* @param rowMapper The implementation for mapping each Row returned.
* @return Type <T> which is the output of the ResultSetExtractor
* @throws DataAccessException
*/
<T> List<T> query(PreparedStatementCreator psc, final PreparedStatementBinder psb, final RowMapper<T> rowMapper)
throws DataAccessException;
/**
* Describe the current Ring. This uses the provided {@link RingMemberHostMapper} to provide the basics of the
* Cassandra Ring topology.
*
* @return The list of ring tokens that are active in the cluster
*/
List<RingMember> describeRing() throws DataAccessException;
/**
* Describe the current Ring. Application code must provide its own {@link HostMapper} implementation to process the
* lists of hosts returned by the Cassandra Cluster Metadata.
*
* @param hostMapper The implementation to use for host mapping.
* @return Collection generated by the provided HostMapper.
* @throws DataAccessException
*/
<T> Collection<T> describeRing(HostMapper<T> hostMapper) throws DataAccessException;
/**
* Get the current Session used for operations in the implementing class.
*
* @return The DataStax Driver Session Object
*/
Session getSession();
}

View File

@@ -0,0 +1,563 @@
/*
* Copyright 2011-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.cassandra.core;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.cassandra.support.CassandraAccessor;
import org.springframework.dao.DataAccessException;
import org.springframework.util.Assert;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.ColumnDefinitions;
import com.datastax.driver.core.ColumnDefinitions.Definition;
import com.datastax.driver.core.DataType;
import com.datastax.driver.core.Host;
import com.datastax.driver.core.Metadata;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.ResultSetFuture;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.DriverException;
/**
* <b>This is the Central class in the Cassandra core package.</b> It simplifies the use of Cassandra and helps to avoid
* common errors. It executes the core Cassandra workflow, leaving application code to provide CQL and result
* extraction. This class execute CQL Queries, provides different ways to extract/map results, and provides Exception
* translation to the generic, more informative exception hierarchy defined in the <code>org.springframework.dao</code>
* package.
*
* <p>
* For working with POJOs, use the {@link CassandraDataTemplate}.
* </p>
*
* @author David Webb
* @author Matthew Adams
*/
public class CassandraTemplate extends CassandraAccessor implements CassandraOperations {
/**
* Blank constructor. You must wire in the Session before use.
*
*/
public CassandraTemplate() {
}
/**
* Constructor used for a basic template configuration
*
* @param session must not be {@literal null}.
*/
public CassandraTemplate(Session session) {
setSession(session);
afterPropertiesSet();
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#execute(org.springframework.data.cassandra.core.SessionCallback)
*/
@Override
public <T> T execute(SessionCallback<T> sessionCallback) throws DataAccessException {
return doExecute(sessionCallback);
}
/* (non-Javadoc)
* @see org.springframework.data.cassandra.core.CassandraOperations#execute(java.lang.String)
*/
@Override
public void execute(final String cql) throws DataAccessException {
doExecute(cql);
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#query(java.lang.String, org.springframework.cassandra.core.FutureResultSetExtractor)
*/
@Override
public <T> T queryAsynchronously(final String cql, ResultSetFutureExtractor<T> rse) throws DataAccessException {
return rse.extractData(execute(new SessionCallback<ResultSetFuture>() {
@Override
public ResultSetFuture doInSession(Session s) throws DataAccessException {
return s.executeAsync(cql);
}
}));
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#query(java.lang.String, org.springframework.cassandra.core.ResultSetExtractor)
*/
public <T> T query(String cql, ResultSetExtractor<T> rse) throws DataAccessException {
ResultSet rs = doExecute(cql);
return rse.extractData(rs);
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#query(java.lang.String, org.springframework.cassandra.core.RowCallbackHandler)
*/
public void query(String cql, RowCallbackHandler rch) throws DataAccessException {
process(doExecute(cql), rch);
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#query(java.lang.String, org.springframework.cassandra.core.RowMapper)
*/
public <T> List<T> query(String cql, RowMapper<T> rowMapper) throws DataAccessException {
return process(doExecute(cql), rowMapper);
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#queryForList(java.lang.String)
*/
public List<Map<String, Object>> queryForListOfMap(String cql) throws DataAccessException {
return processListOfMap(doExecute(cql));
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#queryForList(java.lang.String, java.lang.Class)
*/
public <T> List<T> queryForList(String cql, Class<T> elementType) throws DataAccessException {
return processList(doExecute(cql), elementType);
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#queryForMap(java.lang.String)
*/
public Map<String, Object> queryForMap(String cql) throws DataAccessException {
return processMap(doExecute(cql));
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#queryForObject(java.lang.String, java.lang.Class)
*/
public <T> T queryForObject(String cql, Class<T> requiredType) throws DataAccessException {
return processOne(doExecute(cql), requiredType);
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#queryForObject(java.lang.String, org.springframework.cassandra.core.RowMapper)
*/
public <T> T queryForObject(String cql, RowMapper<T> rowMapper) throws DataAccessException {
return processOne(doExecute(cql), rowMapper);
}
/**
* Execute a command at the Session Level
*
* @param callback
* @return
*/
protected <T> T doExecute(SessionCallback<T> callback) {
Assert.notNull(callback);
try {
return callback.doInSession(getSession());
} catch (DataAccessException e) {
throw throwTranslated(e);
}
}
/**
* Execute a command at the Session Level
*
* @param callback
* @return
*/
protected ResultSet doExecute(final String cql) {
return doExecute(new SessionCallback<ResultSet>() {
@Override
public ResultSet doInSession(Session s) throws DataAccessException {
return s.execute(cql);
}
});
}
/**
* Execute a command at the Session Level
*
* @param callback
* @return
*/
protected ResultSet doExecute(final BoundStatement bs) {
return doExecute(new SessionCallback<ResultSet>() {
@Override
public ResultSet doInSession(Session s) throws DataAccessException {
return s.execute(bs);
}
});
}
/**
* @param row
* @return
*/
protected Object firstColumnToObject(Row row) {
ColumnDefinitions cols = row.getColumnDefinitions();
if (cols.size() == 0) {
return null;
}
return cols.getType(0).deserialize(row.getBytesUnsafe(0));
}
/**
* @param row
* @return
*/
protected Map<String, Object> toMap(Row row) {
if (row == null) {
return null;
}
ColumnDefinitions cols = row.getColumnDefinitions();
Map<String, Object> map = new HashMap<String, Object>(cols.size());
for (Definition def : cols.asList()) {
String name = def.getName();
DataType dataType = def.getType();
map.put(name, def.getType().deserialize(row.getBytesUnsafe(name)));
}
return map;
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#describeRing()
*/
@Override
public List<RingMember> describeRing() throws DataAccessException {
return new ArrayList<RingMember>(describeRing(new RingMemberHostMapper()));
}
/**
* Pulls the list of Hosts for the current Session
*
* @return
*/
private Set<Host> getHosts() {
/*
* Get the cluster metadata for this session
*/
Metadata clusterMetadata = doExecute(new SessionCallback<Metadata>() {
@Override
public Metadata doInSession(Session s) throws DataAccessException {
return s.getCluster().getMetadata();
}
});
/*
* Get all hosts in the cluster
*/
Set<Host> hosts = clusterMetadata.getAllHosts();
return hosts;
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#describeRing(org.springframework.cassandra.core.HostMapper)
*/
@Override
public <T> Collection<T> describeRing(HostMapper<T> hostMapper) throws DataAccessException {
Set<Host> hosts = getHosts();
return hostMapper.mapHosts(hosts);
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#executeAsynchronously(java.lang.String)
*/
@Override
public void executeAsynchronously(final String cql) throws DataAccessException {
execute(new SessionCallback<Object>() {
@Override
public Object doInSession(Session s) throws DataAccessException {
return s.executeAsync(cql);
}
});
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#process(com.datastax.driver.core.ResultSet, org.springframework.cassandra.core.RowCallbackHandler)
*/
@Override
public void process(ResultSet resultSet, RowCallbackHandler rch) throws DataAccessException {
try {
for (Row row : resultSet.all()) {
rch.processRow(row);
}
} catch (DriverException dx) {
throwTranslated(dx);
}
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#process(com.datastax.driver.core.ResultSet, org.springframework.cassandra.core.RowMapper)
*/
@Override
public <T> List<T> process(ResultSet resultSet, RowMapper<T> rowMapper) throws DataAccessException {
List<T> mappedRows = new ArrayList<T>();
try {
int i = 0;
for (Row row : resultSet.all()) {
mappedRows.add(rowMapper.mapRow(row, i++));
}
} catch (DriverException dx) {
throwTranslated(dx);
}
return mappedRows;
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#processOne(com.datastax.driver.core.ResultSet, org.springframework.cassandra.core.RowMapper)
*/
@Override
public <T> T processOne(ResultSet resultSet, RowMapper<T> rowMapper) throws DataAccessException {
T row = null;
Assert.notNull(resultSet, "ResultSet cannot be null");
try {
List<Row> rows = resultSet.all();
Assert.notNull(rows, "null row list returned from query");
Assert.isTrue(rows.size() == 1, "row list has " + rows.size() + " rows instead of one");
row = rowMapper.mapRow(rows.get(0), 0);
} catch (DriverException dx) {
throwTranslated(dx);
}
return row;
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#processOne(com.datastax.driver.core.ResultSet, java.lang.Class)
*/
@SuppressWarnings("unchecked")
@Override
public <T> T processOne(ResultSet resultSet, Class<T> requiredType) throws DataAccessException {
if (resultSet == null) {
return null;
}
Row row = resultSet.one();
if (row == null) {
return null;
}
return (T) firstColumnToObject(row);
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#processMap(com.datastax.driver.core.ResultSet)
*/
@Override
public Map<String, Object> processMap(ResultSet resultSet) throws DataAccessException {
if (resultSet == null) {
return null;
}
return toMap(resultSet.one());
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#processList(com.datastax.driver.core.ResultSet, java.lang.Class)
*/
@Override
public <T> List<T> processList(ResultSet resultSet, Class<T> elementType) throws DataAccessException {
List<Row> rows = resultSet.all();
List<T> list = new ArrayList<T>(rows.size());
for (Row row : rows) {
list.add((T) firstColumnToObject(row));
}
return list;
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#processListOfMap(com.datastax.driver.core.ResultSet)
*/
@Override
public List<Map<String, Object>> processListOfMap(ResultSet resultSet) throws DataAccessException {
List<Row> rows = resultSet.all();
List<Map<String, Object>> list = new ArrayList<Map<String, Object>>(rows.size());
for (Row row : rows) {
list.add(toMap(row));
}
return list;
}
/**
* Attempt to translate a Runtime Exception to a Spring Data Exception
*
* @param ex
* @return
*/
protected RuntimeException throwTranslated(RuntimeException ex) {
RuntimeException resolved = getExceptionTranslator().translateExceptionIfPossible(ex);
return resolved == null ? ex : resolved;
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#execute(org.springframework.cassandra.core.PreparedStatementCreator, org.springframework.cassandra.core.PreparedStatementCallback)
*/
@Override
public <T> T execute(PreparedStatementCreator psc, PreparedStatementCallback<T> action) {
try {
PreparedStatement ps = psc.createPreparedStatement(getSession());
return action.doInPreparedStatement(ps);
} catch (DriverException dx) {
throwTranslated(dx);
}
return null;
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#execute(java.lang.String, org.springframework.cassandra.core.PreparedStatementCallback)
*/
@Override
public <T> T execute(String cql, PreparedStatementCallback<T> action) {
return execute(new SimplePreparedStatementCreator(cql), action);
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#query(org.springframework.cassandra.core.PreparedStatementCreator, org.springframework.cassandra.core.ResultSetExtractor)
*/
@Override
public <T> T query(PreparedStatementCreator psc, ResultSetExtractor<T> rse) throws DataAccessException {
return query(psc, null, rse);
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#query(org.springframework.cassandra.core.PreparedStatementCreator, org.springframework.cassandra.core.RowCallbackHandler)
*/
@Override
public void query(PreparedStatementCreator psc, RowCallbackHandler rch) throws DataAccessException {
query(psc, null, rch);
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#query(org.springframework.cassandra.core.PreparedStatementCreator, org.springframework.cassandra.core.RowMapper)
*/
@Override
public <T> List<T> query(PreparedStatementCreator psc, RowMapper<T> rowMapper) throws DataAccessException {
return query(psc, null, rowMapper);
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#query(org.springframework.cassandra.core.PreparedStatementCreator, org.springframework.cassandra.core.PreparedStatementSetter, org.springframework.cassandra.core.ResultSetExtractor)
*/
public <T> T query(PreparedStatementCreator psc, final PreparedStatementBinder psb, final ResultSetExtractor<T> rse)
throws DataAccessException {
Assert.notNull(rse, "ResultSetExtractor must not be null");
logger.debug("Executing prepared CQL query");
return execute(psc, new PreparedStatementCallback<T>() {
public T doInPreparedStatement(PreparedStatement ps) throws DriverException {
ResultSet rs = null;
BoundStatement bs = null;
if (psb != null) {
bs = psb.bindValues(ps);
} else {
bs = ps.bind();
}
rs = doExecute(bs);
return rse.extractData(rs);
}
});
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#query(java.lang.String, org.springframework.cassandra.core.PreparedStatementSetter, org.springframework.cassandra.core.ResultSetExtractor)
*/
@Override
public <T> T query(String cql, PreparedStatementBinder psb, ResultSetExtractor<T> rse) throws DataAccessException {
return query(new SimplePreparedStatementCreator(cql), psb, rse);
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#query(java.lang.String, org.springframework.cassandra.core.PreparedStatementSetter, org.springframework.cassandra.core.RowCallbackHandler)
*/
@Override
public void query(String cql, PreparedStatementBinder psb, RowCallbackHandler rch) throws DataAccessException {
query(new SimplePreparedStatementCreator(cql), psb, rch);
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#query(java.lang.String, org.springframework.cassandra.core.PreparedStatementSetter, org.springframework.cassandra.core.RowMapper)
*/
@Override
public <T> List<T> query(String cql, PreparedStatementBinder psb, RowMapper<T> rowMapper) throws DataAccessException {
return query(new SimplePreparedStatementCreator(cql), psb, rowMapper);
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#query(org.springframework.cassandra.core.PreparedStatementCreator, org.springframework.cassandra.core.PreparedStatementBinder, org.springframework.cassandra.core.RowCallbackHandler)
*/
@Override
public void query(PreparedStatementCreator psc, final PreparedStatementBinder psb, final RowCallbackHandler rch)
throws DataAccessException {
Assert.notNull(rch, "RowCallbackHandler must not be null");
logger.debug("Executing prepared CQL query");
execute(psc, new PreparedStatementCallback<Object>() {
public Object doInPreparedStatement(PreparedStatement ps) throws DriverException {
ResultSet rs = null;
BoundStatement bs = null;
if (psb != null) {
bs = psb.bindValues(ps);
} else {
bs = ps.bind();
}
rs = doExecute(bs);
process(rs, rch);
return null;
}
});
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CassandraOperations#query(org.springframework.cassandra.core.PreparedStatementCreator, org.springframework.cassandra.core.PreparedStatementBinder, org.springframework.cassandra.core.RowMapper)
*/
@Override
public <T> List<T> query(PreparedStatementCreator psc, final PreparedStatementBinder psb, final RowMapper<T> rowMapper)
throws DataAccessException {
Assert.notNull(rowMapper, "RowMapper must not be null");
logger.debug("Executing prepared CQL query");
return execute(psc, new PreparedStatementCallback<List<T>>() {
public List<T> doInPreparedStatement(PreparedStatement ps) throws DriverException {
ResultSet rs = null;
BoundStatement bs = null;
if (psb != null) {
bs = psb.bindValues(ps);
} else {
bs = ps.bind();
}
rs = doExecute(bs);
return process(rs, rowMapper);
}
});
}
}

View File

@@ -0,0 +1,139 @@
/*
* Copyright 2011-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.cassandra.core;
import java.util.LinkedList;
import java.util.List;
import org.springframework.util.Assert;
import com.datastax.driver.core.DataType;
/**
* @author David Webb
*
*/
public class CqlParameter {
/** The name of the parameter, if any */
private String name;
/** SQL type constant from {@link DataType} */
private final DataType type;
/** The scale to apply in case of a NUMERIC or DECIMAL type, if any */
private Integer scale;
/**
* Create a new anonymous CqlParameter, supplying the SQL type.
*
* @param type Cassandra Data Type of the parameter according to {@link DataType}
*/
public CqlParameter(DataType type) {
this.type = type;
}
/**
* Create a new anonymous CqlParameter, supplying the SQL type.
*
* @param type Cassandra Data Type of the parameter according to {@link DataType}
* @param scale the number of digits after the decimal point
*/
public CqlParameter(DataType type, int scale) {
this.type = type;
this.scale = scale;
}
/**
* Create a new CqlParameter, supplying name and SQL type.
*
* @param name name of the parameter, as used in input and output maps
* @param type Cassandra Data Type of the parameter according to {@link DataType}
*/
public CqlParameter(String name, DataType type) {
this.name = name;
this.type = type;
}
/**
* Create a new CqlParameter, supplying name and SQL type.
*
* @param name name of the parameter, as used in input and output maps
* @param type Cassandra Data Type of the parameter according to {@link DataType}
* @param scale the number of digits after the decimal point (for DECIMAL and NUMERIC types)
*/
public CqlParameter(String name, DataType type, int scale) {
this.name = name;
this.type = type;
this.scale = scale;
}
/**
* Copy constructor.
*
* @param otherParam the CqlParameter object to copy from
*/
public CqlParameter(CqlParameter otherParam) {
Assert.notNull(otherParam, "CqlParameter object must not be null");
this.name = otherParam.name;
this.type = otherParam.type;
this.scale = otherParam.scale;
}
/**
* Return the name of the parameter.
*/
public String getName() {
return this.name;
}
/**
* Return the SQL type of the parameter.
*/
public DataType getType() {
return this.type;
}
/**
* Return the scale of the parameter, if any.
*/
public Integer getScale() {
return this.scale;
}
/**
* Return whether this parameter holds input values that should be set before execution even if they are {@code null}.
* <p>
* This implementation always returns {@code true}.
*/
public boolean isInputValueProvided() {
return true;
}
/**
* Convert a list of JDBC types, as defined in {@code java.sql.Types}, to a List of CqlParameter objects as used in
* this package.
*/
public static List<CqlParameter> sqlTypesToAnonymousParameterList(DataType[] types) {
List<CqlParameter> result = new LinkedList<CqlParameter>();
if (types != null) {
for (DataType type : types) {
result.add(new CqlParameter(type));
}
}
return result;
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2011-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.cassandra.core;
import com.datastax.driver.core.DataType;
/**
* @author David Webb
*
*/
public class CqlParameterValue extends CqlParameter {
private final Object value;
/**
* Create a new CqlParameterValue, supplying the Cassandra DataType.
*
* @param type Cassandra Data Type of the parameter according to {@link DataType}
* @param value the value object
*/
public CqlParameterValue(DataType type, Object value) {
super(type);
this.value = value;
}
/**
* Create a new CqlParameterValue, supplying the Cassandra DataType.
*
* @param type Cassandra Data Type of the parameter according to {@link DataType}
* @param scale the number of digits after the decimal point (for DECIMAL and NUMERIC types)
* @param value the value object
*/
public CqlParameterValue(DataType type, int scale, Object value) {
super(type, scale);
this.value = value;
}
/**
* Create a new CqlParameterValue based on the given CqlParameter declaration.
*
* @param declaredParam the declared CqlParameter to define a value for
* @param value the value object
*/
public CqlParameterValue(CqlParameter declaredParam, Object value) {
super(declaredParam);
this.value = value;
}
/**
* Return the value object that this parameter value holds.
*/
public Object getValue() {
return this.value;
}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2011-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.cassandra.core;
/**
* @author David Webb
*
*/
public interface CqlProvider {
String getCql();
}

View File

@@ -0,0 +1,13 @@
package org.springframework.cassandra.core;
import java.util.Collection;
import java.util.Set;
import com.datastax.driver.core.Host;
import com.datastax.driver.core.exceptions.DriverException;
public interface HostMapper<T> {
Collection<T> mapHosts(Set<Host> host) throws DriverException;
}

View File

@@ -0,0 +1,19 @@
package org.springframework.cassandra.core;
/**
* Values representing primary key column types.
*
* @author Matthew T. Adams
*/
public enum KeyType {
/**
* Used for a column that is a primary key and that also is or is part of the partition key.
*/
PARTITION,
/**
* Used for a primary key column that is not part of the partition key.
*/
PRIMARY
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2011-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.cassandra.core;
import com.datastax.driver.core.Session;
/**
* Simple Cassandra Keyspace object
*
* @author Alex Shvid
*/
public class Keyspace {
private final String keyspace;
private final Session session;
/**
* Constructor used for a basic keyspace configuration
*
* @param keyspace, system if {@literal null}.
* @param session must not be {@literal null}.
* @param cassandraConverter must not be {@literal null}.
*/
public Keyspace(String keyspace, Session session) {
this.keyspace = keyspace;
this.session = session;
}
public String getKeyspace() {
return keyspace;
}
public Session getSession() {
return session;
}
}

View File

@@ -0,0 +1,32 @@
package org.springframework.cassandra.core;
/**
* Enum for Cassandra primary key column ordering.
*
* @author Matthew T. Adams
*/
public enum Ordering {
/**
* Ascending Cassandra column ordering.
*/
ASCENDING("ASC"),
/**
* Descending Cassandra column ordering.
*/
DESCENDING("DESC");
private String cql;
private Ordering(String cql) {
this.cql = cql;
}
/**
* Returns the CQL keyword of this {@link Ordering}.
*/
public String cql() {
return cql;
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright 2011-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.cassandra.core;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.exceptions.DriverException;
/**
* @author David Webb
*
*/
public interface PreparedStatementBinder {
BoundStatement bindValues(PreparedStatement ps) throws DriverException;
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright 2011-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.cassandra.core;
import org.springframework.dao.DataAccessException;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.exceptions.DriverException;
/**
* @author David Webb
*
*/
public interface PreparedStatementCallback<T> {
T doInPreparedStatement(PreparedStatement ps) throws DriverException, DataAccessException;
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2011-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.cassandra.core;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.DriverException;
/**
* Creates a PreparedStatement for the usage with the DataStax Java Driver
*
* @author David Webb
*
*/
public interface PreparedStatementCreator {
/**
* Create a statement in this session. Allows implementations to use PreparedStatements. The CassandraTemlate will
* attempt to cache the PreparedStatement for future use without the overhead of re-preparing on the entire cluster.
*
* @param session Session to use to create statement
* @return a prepared statement
* @throws DriverException there is no need to catch DriverException that may be thrown in the implementation of this
* method. The CassandraTemlate class will handle them.
*/
PreparedStatement createPreparedStatement(Session session) throws DriverException;
}

View File

@@ -0,0 +1,202 @@
/*
* Copyright 2011-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.cassandra.core;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.util.Assert;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.DriverException;
/**
* @author David Webb
*
*/
public class PreparedStatementCreatorFactory {
/**
* The CQL, which won't change when the parameters change
*/
private final String cql;
/** List of CqlParameter objects. May not be {@code null}. */
private final List<CqlParameter> declaredParameters;
/**
* Create a new factory.
*/
public PreparedStatementCreatorFactory(String cql) {
this.cql = cql;
this.declaredParameters = new LinkedList<CqlParameter>();
}
/**
* Create a new factory with the given CQL and parameters.
*
* @param cql CQL
* @param declaredParameters list of {@link CqlParameter} objects
* @see CqlParameter
*/
public PreparedStatementCreatorFactory(String cql, List<CqlParameter> declaredParameters) {
this.cql = cql;
this.declaredParameters = declaredParameters;
}
/**
* Return a new PreparedStatementBinder for the given parameters.
*
* @param params list of parameters (may be {@code null})
*/
public PreparedStatementBinder newPreparedStatementBinder(List<CqlParameterValue> params) {
return new PreparedStatementCreatorImpl(params != null ? params : Collections.emptyList());
}
/**
* Return a new PreparedStatementBinder for the given parameters.
*
* @param params the parameter array (may be {@code null})
*/
public PreparedStatementBinder newPreparedStatementBinder(Object[] params) {
return new PreparedStatementCreatorImpl(params != null ? Arrays.asList(params) : Collections.emptyList());
}
/**
* Return a new PreparedStatementCreator for the given parameters.
*
* @param params list of parameters (may be {@code null})
*/
public PreparedStatementCreator newPreparedStatementCreator(List<CqlParameterValue> params) {
return new PreparedStatementCreatorImpl(params != null ? params : Collections.emptyList());
}
/**
* Return a new PreparedStatementCreator for the given parameters.
*
* @param params the parameter array (may be {@code null})
*/
public PreparedStatementCreator newPreparedStatementCreator(Object[] params) {
return new PreparedStatementCreatorImpl(params != null ? Arrays.asList(params) : Collections.emptyList());
}
/**
* Return a new PreparedStatementCreator for the given parameters.
*
* @param sqlToUse the actual SQL statement to use (if different from the factory's, for example because of named
* parameter expanding)
* @param params the parameter array (may be {@code null})
*/
public PreparedStatementCreator newPreparedStatementCreator(String sqlToUse, Object[] params) {
return new PreparedStatementCreatorImpl(sqlToUse, params != null ? Arrays.asList(params) : Collections.emptyList());
}
/**
* PreparedStatementCreator implementation returned by this class.
*/
private class PreparedStatementCreatorImpl implements PreparedStatementCreator, PreparedStatementBinder, CqlProvider {
private final String actualCql;
private final List<?> parameters;
public PreparedStatementCreatorImpl(List<?> parameters) {
this(cql, parameters);
}
/**
* @param actualCql
* @param parameters
*/
public PreparedStatementCreatorImpl(String actualCql, List<?> parameters) {
this.actualCql = actualCql;
Assert.notNull(parameters, "Parameters List must not be null");
this.parameters = parameters;
if (this.parameters.size() != declaredParameters.size()) {
Set<String> names = new HashSet<String>();
for (int i = 0; i < parameters.size(); i++) {
Object param = parameters.get(i);
if (param instanceof CqlParameterValue) {
names.add(((CqlParameterValue) param).getName());
} else {
names.add("Parameter #" + i);
}
}
if (names.size() != declaredParameters.size()) {
throw new InvalidDataAccessApiUsageException("CQL [" + cql + "]: given " + names.size()
+ " parameters but expected " + declaredParameters.size());
}
}
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.PreparedStatementCreator#createPreparedStatement(com.datastax.driver.core.Session)
*/
@Override
public PreparedStatement createPreparedStatement(Session session) throws DriverException {
return session.prepare(this.actualCql);
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.PreparedStatementBinder#bindValues(com.datastax.driver.core.PreparedStatement)
*/
@Override
public BoundStatement bindValues(PreparedStatement ps) throws DriverException {
if (this.parameters == null || this.parameters.size() == 0) {
return ps.bind();
}
// Test the type of the first value
Object v = this.parameters.get(0);
Object[] values;
if (v instanceof CqlParameterValue) {
LinkedList<Object> valuesList = new LinkedList<Object>();
for (Object value : this.parameters) {
valuesList.add(((CqlParameterValue) value).getValue());
}
values = valuesList.toArray();
} else {
values = this.parameters.toArray();
}
return ps.bind(values);
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CqlProvider#getCql()
*/
@Override
public String getCql() {
return cql;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("PreparedStatementCreatorFactory.PreparedStatementCreatorImpl: cql=[");
sb.append(cql).append("]; parameters=").append(this.parameters);
return sb.toString();
}
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2011-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.cassandra.core;
import java.util.List;
import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.DriverException;
/**
* @author David Webb
*
*/
public class PreparedStatementCreatorImpl implements PreparedStatementCreator, CqlProvider, PreparedStatementBinder {
private final String cql;
private List<Object> values;
public PreparedStatementCreatorImpl(String cql) {
this.cql = cql;
}
public PreparedStatementCreatorImpl(String cql, List<Object> values) {
this.cql = cql;
this.values = values;
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.PreparedStatementSetter#setValues(com.datastax.driver.core.PreparedStatement)
*/
@Override
public BoundStatement bindValues(PreparedStatement ps) throws DriverException {
// Nothing to set if there are no values
if (values == null) {
return null;
}
return ps.bind(values.toArray());
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CqlProvider#getCql()
*/
@Override
public String getCql() {
return this.cql;
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.PreparedStatementCreator#createPreparedStatement(com.datastax.driver.core.Session)
*/
@Override
public PreparedStatement createPreparedStatement(Session session) throws DriverException {
return session.prepare(this.cql);
}
}

View File

@@ -0,0 +1,11 @@
package org.springframework.cassandra.core;
import org.springframework.dao.DataAccessException;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.exceptions.DriverException;
public interface ResultSetExtractor<T> {
T extractData(ResultSet rs) throws DriverException, DataAccessException;
}

View File

@@ -0,0 +1,11 @@
package org.springframework.cassandra.core;
import org.springframework.dao.DataAccessException;
import com.datastax.driver.core.ResultSetFuture;
import com.datastax.driver.core.exceptions.DriverException;
public interface ResultSetFutureExtractor<T> {
T extractData(ResultSetFuture rs) throws DriverException, DataAccessException;
}

View File

@@ -0,0 +1,43 @@
/*
* 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.cassandra.core;
import java.io.Serializable;
import com.datastax.driver.core.Host;
/**
* @author David Webb
*
*/
public final class RingMember implements Serializable {
/*
* Ring attributes
*/
public String hostName;
public String address;
public String DC;
public String rack;
public RingMember(Host h) {
this.hostName = h.getAddress().getHostName();
this.address = h.getAddress().getHostAddress();
this.DC = h.getDatacenter();
this.rack = h.getRack();
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2011-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.cassandra.core;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.springframework.util.Assert;
import com.datastax.driver.core.Host;
import com.datastax.driver.core.exceptions.DriverException;
/**
* @author David Webb
* @param <T>
*
*/
public class RingMemberHostMapper implements HostMapper<RingMember> {
/* (non-Javadoc)
* @see org.springframework.cassandra.core.HostMapper#mapHosts(java.util.Set)
*/
@Override
public List<RingMember> mapHosts(Set<Host> hosts) throws DriverException {
List<RingMember> members = new ArrayList<RingMember>();
Assert.notNull(hosts);
Assert.notEmpty(hosts);
RingMember r = null;
for (Host host : hosts) {
r = new RingMember(host);
members.add(r);
}
return members;
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2011-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.cassandra.core;
import com.datastax.driver.core.Row;
/**
* Simple internal callback to allow operations on a {@link Row}.
*
* @author Alex Shvid
*/
public interface RowCallback<T> {
T doWith(Row object);
}

View File

@@ -0,0 +1,10 @@
package org.springframework.cassandra.core;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.exceptions.DriverException;
public interface RowCallbackHandler {
void processRow(Row row) throws DriverException;
}

View File

@@ -0,0 +1,10 @@
package org.springframework.cassandra.core;
import com.datastax.driver.core.Row;
import com.datastax.driver.core.exceptions.DriverException;
public interface RowMapper<T> {
T mapRow(Row row, int rowNum) throws DriverException;
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2010-2011 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.cassandra.core;
import org.springframework.dao.DataAccessException;
import com.datastax.driver.core.Session;
/**
* Interface for operations on a Cassandra Session.
*
* @author David Webb
*
* @param <T>
*/
public interface SessionCallback<T> {
/**
* Perform the operation in the given Session
*
* @param s
* @return
* @throws DataAccessException
*/
T doInSession(Session s) throws DataAccessException;
}

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2011-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.cassandra.core;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.cassandra.core.Keyspace;
import com.datastax.driver.core.Session;
/**
* @author David Webb
*
*/
public class SessionFactoryBean implements FactoryBean<Session>, InitializingBean {
private Keyspace keyspace;
public SessionFactoryBean() {
}
public SessionFactoryBean(Keyspace keyspace) {
setKeyspace(keyspace);
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet()
*/
@Override
public void afterPropertiesSet() throws Exception {
if (keyspace == null) {
throw new IllegalStateException("Keyspace required.");
}
}
/**
* @return Returns the keyspace.
*/
public Keyspace getKeyspace() {
return keyspace;
}
/**
* @param keyspace The keyspace to set.
*/
public void setKeyspace(Keyspace keyspace) {
this.keyspace = keyspace;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObject()
*/
@Override
public Session getObject() {
return keyspace.getSession();
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
@Override
public Class<?> getObjectType() {
return Session.class;
}
/* (non-Javadoc)
* @see org.springframework.beans.factory.FactoryBean#isSingleton()
*/
@Override
public boolean isSingleton() {
return true;
}
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2011-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.cassandra.core;
import org.springframework.util.Assert;
import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.exceptions.DriverException;
/**
* @author David Webb
*
*/
public class SimplePreparedStatementCreator implements PreparedStatementCreator, CqlProvider {
private final String cql;
/**
* Create a PreparedStatementCreator from the provided CQL.
*
* @param cql
*/
public SimplePreparedStatementCreator(String cql) {
Assert.notNull(cql, "CQL is required to create a PreparedStatement");
this.cql = cql;
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.CqlProvider#getCql()
*/
@Override
public String getCql() {
return this.cql;
}
/* (non-Javadoc)
* @see org.springframework.cassandra.core.PreparedStatementCreator#createPreparedStatement(com.datastax.driver.core.Session)
*/
@Override
public PreparedStatement createPreparedStatement(Session session) throws DriverException {
return session.prepare(this.cql);
}
}

View File

@@ -0,0 +1,117 @@
package org.springframework.cassandra.core.cql;
import java.util.regex.Pattern;
public class CqlStringUtils {
protected static final String SINGLE_QUOTE = "\'";
protected static final String DOUBLE_SINGLE_QUOTE = "\'\'";
protected static final String DOUBLE_QUOTE = "\"";
protected static final String DOUBLE_DOUBLE_QUOTE = "\"\"";
public static StringBuilder noNull(StringBuilder sb) {
return sb == null ? new StringBuilder() : sb;
}
public static final String UNESCAPED_DOUBLE_QUOTE_REGEX = "TODO";
public static final Pattern UNESCAPED_DOUBLE_QUOTE_PATTERN = Pattern.compile(UNESCAPED_DOUBLE_QUOTE_REGEX);
public static final String UNQUOTED_IDENTIFIER_REGEX = "[a-zA-Z_][a-zA-Z0-9_]*";
public static final Pattern UNQUOTED_IDENTIFIER_PATTERN = Pattern.compile(UNQUOTED_IDENTIFIER_REGEX);
public static boolean isUnquotedIdentifier(CharSequence chars) {
return UNQUOTED_IDENTIFIER_PATTERN.matcher(chars).matches();
}
public static void checkUnquotedIdentifier(CharSequence chars) {
if (!CqlStringUtils.isUnquotedIdentifier(chars)) {
throw new IllegalArgumentException("[" + chars + "] is not a valid CQL identifier");
}
}
public static final String QUOTED_IDENTIFIER_REGEX = "[a-zA-Z_]([a-zA-Z0-9_]|\"{2}+)*";
public static final Pattern QUOTED_IDENTIFIER_PATTERN = Pattern.compile(QUOTED_IDENTIFIER_REGEX);
public static boolean isQuotedIdentifier(CharSequence chars) {
return QUOTED_IDENTIFIER_PATTERN.matcher(chars).matches();
}
public static void checkQuotedIdentifier(CharSequence chars) {
if (!CqlStringUtils.isQuotedIdentifier(chars)) {
throw new IllegalArgumentException("[" + chars + "] is not a valid CQL quoted identifier");
}
}
public static boolean isIdentifier(CharSequence chars) {
return isUnquotedIdentifier(chars) || isQuotedIdentifier(chars);
}
public static void checkIdentifier(CharSequence chars) {
if (!CqlStringUtils.isIdentifier(chars)) {
throw new IllegalArgumentException("[" + chars + "] is not a valid CQL quoted or unquoted identifier");
}
}
/**
* Renders the given string as a legal Cassandra identifier.
* <ul>
* <li>If the given identifier is a legal unquoted identifier, it is returned unchanged.</li>
* <li>If the given identifier is a legal quoted identifier, it is returned encased in double quotes.</li>
* <li>If the given identifier is illegal, an {@link IllegalArgumentException} is thrown.</li>
* </ul>
*/
public static String identifize(String candidate) {
checkIdentifier(candidate);
if (isUnquotedIdentifier(candidate)) {
return candidate;
}
// else it must be quoted
return doubleQuote(candidate);
}
/**
* Renders the given string as a legal Cassandra string column or table option value, by escaping single quotes and
* encasing the result in single quotes. Given <code>null</code>, returns <code>null</code>.
*/
public static String valuize(String candidate) {
if (candidate == null) {
return null;
}
return singleQuote(escapeSingle(candidate));
}
/**
* Doubles single quote characters (' -&gt; ''). Given <code>null</code>, returns <code>null</code>.
*/
public static String escapeSingle(Object things) {
return things == null ? (String) null : things.toString().replace(SINGLE_QUOTE, DOUBLE_SINGLE_QUOTE);
}
/**
* Doubles double quote characters (" -&gt; ""). Given <code>null</code>, returns <code>null</code>.
*/
public static String escapeDouble(Object things) {
return things == null ? (String) null : things.toString().replace(DOUBLE_QUOTE, DOUBLE_DOUBLE_QUOTE);
}
/**
* Surrounds given object's {@link Object#toString()} with single quotes. Given <code>null</code>, returns
* <code>null</code>.
*/
public static String singleQuote(Object thing) {
return thing == null ? (String) null : new StringBuilder().append(SINGLE_QUOTE).append(thing).append(SINGLE_QUOTE)
.toString();
}
/**
* Surrounds given object's {@link Object#toString()} with double quotes. Given <code>null</code>, returns
* <code>null</code>.
*/
public static String doubleQuote(Object thing) {
return thing == null ? (String) null : new StringBuilder().append(DOUBLE_QUOTE).append(thing).append(DOUBLE_QUOTE)
.toString();
}
}

View File

@@ -0,0 +1,22 @@
package org.springframework.cassandra.core.cql.generator;
import static org.springframework.cassandra.core.cql.CqlStringUtils.noNull;
import org.springframework.cassandra.core.keyspace.AddColumnSpecification;
/**
* CQL generator for generating an <code>ADD</code> clause of an <code>ALTER TABLE</code> statement.
*
* @author Matthew T. Adams
*/
public class AddColumnCqlGenerator extends ColumnChangeCqlGenerator<AddColumnSpecification> {
public AddColumnCqlGenerator(AddColumnSpecification specification) {
super(specification);
}
public StringBuilder toCql(StringBuilder cql) {
return noNull(cql).append("ADD ").append(spec().getNameAsIdentifier()).append(" TYPE ")
.append(spec().getType().getName());
}
}

View File

@@ -0,0 +1,22 @@
package org.springframework.cassandra.core.cql.generator;
import static org.springframework.cassandra.core.cql.CqlStringUtils.noNull;
import org.springframework.cassandra.core.keyspace.AlterColumnSpecification;
/**
* CQL generator for generating an <code>ALTER</code> column clause of an <code>ALTER TABLE</code> statement.
*
* @author Matthew T. Adams
*/
public class AlterColumnCqlGenerator extends ColumnChangeCqlGenerator<AlterColumnSpecification> {
public AlterColumnCqlGenerator(AlterColumnSpecification specification) {
super(specification);
}
public StringBuilder toCql(StringBuilder cql) {
return noNull(cql).append("ALTER ").append(spec().getNameAsIdentifier()).append(" TYPE ")
.append(spec().getType().getName());
}
}

View File

@@ -0,0 +1,106 @@
package org.springframework.cassandra.core.cql.generator;
import static org.springframework.cassandra.core.cql.CqlStringUtils.noNull;
import java.util.Map;
import org.springframework.cassandra.core.keyspace.AddColumnSpecification;
import org.springframework.cassandra.core.keyspace.AlterColumnSpecification;
import org.springframework.cassandra.core.keyspace.AlterTableSpecification;
import org.springframework.cassandra.core.keyspace.ColumnChangeSpecification;
import org.springframework.cassandra.core.keyspace.DropColumnSpecification;
import org.springframework.cassandra.core.keyspace.Option;
/**
* CQL generator for generating <code>ALTER TABLE</code> statements.
*
* @author Matthew T. Adams
*/
public class AlterTableCqlGenerator extends TableOptionsCqlGenerator<AlterTableSpecification> {
public AlterTableCqlGenerator(AlterTableSpecification specification) {
super(specification);
}
public StringBuilder toCql(StringBuilder cql) {
cql = noNull(cql);
preambleCql(cql);
changesCql(cql);
optionsCql(cql);
cql.append(";");
return cql;
}
protected StringBuilder preambleCql(StringBuilder cql) {
return noNull(cql).append("ALTER TABLE ").append(spec().getNameAsIdentifier()).append(" ");
}
protected StringBuilder changesCql(StringBuilder cql) {
cql = noNull(cql);
boolean first = true;
for (ColumnChangeSpecification change : spec().getChanges()) {
if (first) {
first = false;
} else {
cql.append(" ");
}
getCqlGeneratorFor(change).toCql(cql);
}
return cql;
}
protected ColumnChangeCqlGenerator<?> getCqlGeneratorFor(ColumnChangeSpecification change) {
if (change instanceof AddColumnSpecification) {
return new AddColumnCqlGenerator((AddColumnSpecification) change);
}
if (change instanceof DropColumnSpecification) {
return new DropColumnCqlGenerator((DropColumnSpecification) change);
}
if (change instanceof AlterColumnSpecification) {
return new AlterColumnCqlGenerator((AlterColumnSpecification) change);
}
throw new Error("unknown ColumnChangeSpecification type: " + change.getClass().getName());
}
@SuppressWarnings("unchecked")
protected StringBuilder optionsCql(StringBuilder cql) {
cql = noNull(cql);
Map<String, Object> options = spec().getOptions();
if (options == null || options.isEmpty()) {
return cql;
}
cql.append(" WITH ");
boolean first = true;
for (String key : options.keySet()) {
if (first) {
first = false;
} else {
cql.append(" AND ");
}
cql.append(key);
Object value = options.get(key);
if (value == null) {
continue;
}
cql.append(" = ");
if (value instanceof Map) {
optionValueMap((Map<Option, Object>) value, cql);
continue;
}
// else just use value as string
cql.append(value.toString());
}
return cql;
}
}

View File

@@ -0,0 +1,35 @@
package org.springframework.cassandra.core.cql.generator;
import org.springframework.cassandra.core.keyspace.ColumnChangeSpecification;
import org.springframework.util.Assert;
/**
* Base class for column change CQL generators.
*
* @author Matthew T. Adams
* @param <T> The corresponding {@link ColumnChangeSpecification} type for this CQL generator.
*/
public abstract class ColumnChangeCqlGenerator<T extends ColumnChangeSpecification> {
public abstract StringBuilder toCql(StringBuilder cql);
private ColumnChangeSpecification specification;
public ColumnChangeCqlGenerator(ColumnChangeSpecification specification) {
setSpecification(specification);
}
protected void setSpecification(ColumnChangeSpecification specification) {
Assert.notNull(specification);
this.specification = specification;
}
@SuppressWarnings("unchecked")
public T getSpecification() {
return (T) specification;
}
protected T spec() {
return getSpecification();
}
}

View File

@@ -0,0 +1,173 @@
package org.springframework.cassandra.core.cql.generator;
import static org.springframework.cassandra.core.cql.CqlStringUtils.noNull;
import static org.springframework.cassandra.core.KeyType.PARTITION;
import static org.springframework.cassandra.core.KeyType.PRIMARY;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.springframework.cassandra.core.keyspace.ColumnSpecification;
import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
import org.springframework.cassandra.core.keyspace.Option;
/**
* CQL generator for generating a <code>CREATE TABLE</code> statement.
*
* @author Matthew T. Adams
*/
public class CreateTableCqlGenerator extends TableCqlGenerator<CreateTableSpecification> {
public CreateTableCqlGenerator(CreateTableSpecification specification) {
super(specification);
}
public StringBuilder toCql(StringBuilder cql) {
cql = noNull(cql);
preambleCql(cql);
columnsAndOptionsCql(cql);
cql.append(";");
return cql;
}
protected StringBuilder preambleCql(StringBuilder cql) {
return noNull(cql).append("CREATE TABLE ").append(spec().getIfNotExists() ? "IF NOT EXISTS " : "")
.append(spec().getNameAsIdentifier());
}
@SuppressWarnings("unchecked")
protected StringBuilder columnsAndOptionsCql(StringBuilder cql) {
cql = noNull(cql);
// begin columns
cql.append(" (");
List<ColumnSpecification> partitionKeys = new ArrayList<ColumnSpecification>();
List<ColumnSpecification> primaryKeys = new ArrayList<ColumnSpecification>();
for (ColumnSpecification col : spec().getColumns()) {
col.toCql(cql).append(", ");
if (col.getKeyType() == PARTITION) {
partitionKeys.add(col);
} else if (col.getKeyType() == PRIMARY) {
primaryKeys.add(col);
}
}
// begin primary key clause
cql.append("PRIMARY KEY ");
StringBuilder partitions = new StringBuilder();
StringBuilder primaries = new StringBuilder();
if (partitionKeys.size() > 1) {
partitions.append("(");
}
boolean first = true;
for (ColumnSpecification col : partitionKeys) {
if (first) {
first = false;
} else {
partitions.append(", ");
}
partitions.append(col.getName());
}
if (partitionKeys.size() > 1) {
partitions.append(")");
}
StringBuilder clustering = null;
boolean clusteringFirst = true;
first = true;
for (ColumnSpecification col : primaryKeys) {
if (first) {
first = false;
} else {
primaries.append(", ");
}
primaries.append(col.getName());
if (col.getOrdering() != null) { // then ordering specified
if (clustering == null) { // then initialize clustering clause
clustering = new StringBuilder().append("CLUSTERING ORDER BY (");
}
if (clusteringFirst) {
clusteringFirst = false;
} else {
clustering.append(", ");
}
clustering.append(col.getName()).append(" ").append(col.getOrdering().cql());
}
}
if (clustering != null) { // then end clustering option
clustering.append(")");
}
boolean parenthesize = true;// partitionKeys.size() + primaryKeys.size() > 1;
cql.append(parenthesize ? "(" : "");
cql.append(partitions);
cql.append(primaryKeys.size() > 0 ? ", " : "");
cql.append(primaries);
cql.append(parenthesize ? ")" : "");
// end primary key clause
cql.append(")");
// end columns
// begin options
// begin option clause
Map<String, Object> options = spec().getOptions();
if (clustering != null || !options.isEmpty()) {
// option preamble
first = true;
cql.append(" WITH ");
// end option preamble
if (clustering != null) {
cql.append(clustering);
first = false;
}
if (!options.isEmpty()) {
for (String name : options.keySet()) {
// append AND if we're not on first option
if (first) {
first = false;
} else {
cql.append(" AND ");
}
// append <name> = <value>
cql.append(name);
Object value = options.get(name);
if (value == null) { // then assume string-only, valueless option like "COMPACT STORAGE"
continue;
}
cql.append(" = ");
if (value instanceof Map) {
optionValueMap((Map<Option, Object>) value, cql);
continue; // end non-empty value map
}
// else just use value as string
cql.append(value.toString());
}
}
}
// end options
return cql;
}
}

View File

@@ -0,0 +1,21 @@
package org.springframework.cassandra.core.cql.generator;
import static org.springframework.cassandra.core.cql.CqlStringUtils.noNull;
import org.springframework.cassandra.core.keyspace.DropColumnSpecification;
/**
* CQL generator for generating a <code>DROP</code> column clause of an <code>ALTER TABLE</code> statement.
*
* @author Matthew T. Adams
*/
public class DropColumnCqlGenerator extends ColumnChangeCqlGenerator<DropColumnSpecification> {
public DropColumnCqlGenerator(DropColumnSpecification specification) {
super(specification);
}
public StringBuilder toCql(StringBuilder cql) {
return noNull(cql).append("DROP ").append(spec().getNameAsIdentifier());
}
}

View File

@@ -0,0 +1,22 @@
package org.springframework.cassandra.core.cql.generator;
import static org.springframework.cassandra.core.cql.CqlStringUtils.noNull;
import org.springframework.cassandra.core.keyspace.DropTableSpecification;
/**
* CQL generator for generating a <code>DROP TABLE</code> statement.
*
* @author Matthew T. Adams
*/
public class DropTableCqlGenerator extends TableNameCqlGenerator<DropTableSpecification> {
public DropTableCqlGenerator(DropTableSpecification specification) {
super(specification);
}
public StringBuilder toCql(StringBuilder cql) {
return noNull(cql).append("DROP TABLE ").append(spec().getIfExists() ? "IF EXISTS " : "")
.append(spec().getNameAsIdentifier()).append(";");
}
}

View File

@@ -0,0 +1,65 @@
package org.springframework.cassandra.core.cql.generator;
import static org.springframework.cassandra.core.cql.CqlStringUtils.escapeSingle;
import static org.springframework.cassandra.core.cql.CqlStringUtils.noNull;
import static org.springframework.cassandra.core.cql.CqlStringUtils.singleQuote;
import java.util.Map;
import org.springframework.cassandra.core.keyspace.Option;
import org.springframework.cassandra.core.keyspace.TableSpecification;
/**
* Base class that contains behavior common to CQL generation for table operations.
*
* @author Matthew T. Adams
* @param T The subtype of this class for which this is a CQL generator.
*/
public abstract class TableCqlGenerator<T extends TableSpecification<T>> extends
TableOptionsCqlGenerator<TableSpecification<T>> {
public TableCqlGenerator(TableSpecification<T> specification) {
super(specification);
}
@SuppressWarnings("unchecked")
protected T spec() {
return (T) getSpecification();
}
protected StringBuilder optionValueMap(Map<Option, Object> valueMap, StringBuilder cql) {
cql = noNull(cql);
if (valueMap == null || valueMap.isEmpty()) {
return cql;
}
// else option value is a non-empty map
// append { 'name' : 'value', ... }
cql.append("{ ");
boolean mapFirst = true;
for (Map.Entry<Option, Object> entry : valueMap.entrySet()) {
if (mapFirst) {
mapFirst = false;
} else {
cql.append(", ");
}
Option option = entry.getKey();
cql.append(singleQuote(option.getName())); // entries in map keys are always quoted
cql.append(" : ");
Object entryValue = entry.getValue();
entryValue = entryValue == null ? "" : entryValue.toString();
if (option.escapesValue()) {
entryValue = escapeSingle(entryValue);
}
if (option.quotesValue()) {
entryValue = singleQuote(entryValue);
}
cql.append(entryValue);
}
cql.append(" }");
return cql;
}
}

View File

@@ -0,0 +1,36 @@
package org.springframework.cassandra.core.cql.generator;
import org.springframework.cassandra.core.keyspace.TableNameSpecification;
import org.springframework.util.Assert;
public abstract class TableNameCqlGenerator<T extends TableNameSpecification<T>> {
public abstract StringBuilder toCql(StringBuilder cql);
private TableNameSpecification<T> specification;
public TableNameCqlGenerator(TableNameSpecification<T> specification) {
setSpecification(specification);
}
protected void setSpecification(TableNameSpecification<T> specification) {
Assert.notNull(specification);
this.specification = specification;
}
@SuppressWarnings("unchecked")
public T getSpecification() {
return (T) specification;
}
/**
* Convenient synonymous method of {@link #getSpecification()}.
*/
protected T spec() {
return getSpecification();
}
public String toCql() {
return toCql(null).toString();
}
}

View File

@@ -0,0 +1,65 @@
package org.springframework.cassandra.core.cql.generator;
import static org.springframework.cassandra.core.cql.CqlStringUtils.escapeSingle;
import static org.springframework.cassandra.core.cql.CqlStringUtils.noNull;
import static org.springframework.cassandra.core.cql.CqlStringUtils.singleQuote;
import java.util.Map;
import org.springframework.cassandra.core.keyspace.Option;
import org.springframework.cassandra.core.keyspace.TableOptionsSpecification;
/**
* Base class that contains behavior common to CQL generation for table operations.
*
* @author Matthew T. Adams
* @param T The subtype of this class for which this is a CQL generator.
*/
public abstract class TableOptionsCqlGenerator<T extends TableOptionsSpecification<T>> extends
TableNameCqlGenerator<TableOptionsSpecification<T>> {
public TableOptionsCqlGenerator(TableOptionsSpecification<T> specification) {
super(specification);
}
@SuppressWarnings("unchecked")
protected T spec() {
return (T) getSpecification();
}
protected StringBuilder optionValueMap(Map<Option, Object> valueMap, StringBuilder cql) {
cql = noNull(cql);
if (valueMap == null || valueMap.isEmpty()) {
return cql;
}
// else option value is a non-empty map
// append { 'name' : 'value', ... }
cql.append("{ ");
boolean mapFirst = true;
for (Map.Entry<Option, Object> entry : valueMap.entrySet()) {
if (mapFirst) {
mapFirst = false;
} else {
cql.append(", ");
}
Option option = entry.getKey();
cql.append(singleQuote(option.getName())); // entries in map keys are always quoted
cql.append(" : ");
Object entryValue = entry.getValue();
entryValue = entryValue == null ? "" : entryValue.toString();
if (option.escapesValue()) {
entryValue = escapeSingle(entryValue);
}
if (option.quotesValue()) {
entryValue = singleQuote(entryValue);
}
cql.append(entryValue);
}
cql.append(" }");
return cql;
}
}

View File

@@ -0,0 +1,10 @@
package org.springframework.cassandra.core.keyspace;
import com.datastax.driver.core.DataType;
public class AddColumnSpecification extends ColumnTypeChangeSpecification {
public AddColumnSpecification(String name, DataType type) {
super(name, type);
}
}

View File

@@ -0,0 +1,10 @@
package org.springframework.cassandra.core.keyspace;
import com.datastax.driver.core.DataType;
public class AlterColumnSpecification extends ColumnTypeChangeSpecification {
public AlterColumnSpecification(String name, DataType type) {
super(name, type);
}
}

View File

@@ -0,0 +1,51 @@
package org.springframework.cassandra.core.keyspace;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import com.datastax.driver.core.DataType;
/**
* Builder class to construct an <code>ALTER TABLE</code> specification.
*
* @author Matthew T. Adams
*/
public class AlterTableSpecification extends TableOptionsSpecification<AlterTableSpecification> {
/**
* The list of column changes.
*/
private List<ColumnChangeSpecification> changes = new ArrayList<ColumnChangeSpecification>();
/**
* Adds a <code>DROP</code> to the list of column changes.
*/
public AlterTableSpecification drop(String column) {
changes.add(new DropColumnSpecification(column));
return this;
}
/**
* Adds an <code>ADD</code> to the list of column changes.
*/
public AlterTableSpecification add(String column, DataType type) {
changes.add(new AddColumnSpecification(column, type));
return this;
}
/**
* Adds an <code>ALTER</code> to the list of column changes.
*/
public AlterTableSpecification alter(String column, DataType type) {
changes.add(new AlterColumnSpecification(column, type));
return this;
}
/**
* Returns an unmodifiable list of column changes.
*/
public List<ColumnChangeSpecification> getChanges() {
return Collections.unmodifiableList(changes);
}
}

View File

@@ -0,0 +1,26 @@
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
public abstract class ColumnChangeSpecification {
private String name;
public ColumnChangeSpecification(String name) {
setName(name);
}
private void setName(String name) {
checkIdentifier(name);
this.name = name;
}
public String getName() {
return name;
}
public String getNameAsIdentifier() {
return identifize(name);
}
}

View File

@@ -0,0 +1,167 @@
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
import static org.springframework.cassandra.core.cql.CqlStringUtils.noNull;
import static org.springframework.cassandra.core.KeyType.PARTITION;
import static org.springframework.cassandra.core.KeyType.PRIMARY;
import static org.springframework.cassandra.core.Ordering.ASCENDING;
import org.springframework.cassandra.core.KeyType;
import org.springframework.cassandra.core.Ordering;
import com.datastax.driver.core.DataType;
/**
* Builder class to help construct CQL statements that involve column manipulation. Not threadsafe.
* <p/>
* Use {@link #name(String)} and {@link #type(String)} to set the name and type of the column, respectively. To specify
* a <code>PRIMARY KEY</code> column, use {@link #primary()} or {@link #primary(Ordering)}. To specify that the
* <code>PRIMARY KEY</code> column is or is part of the partition key, use {@link #partition()} instead of
* {@link #primary()} or {@link #primary(Ordering)}.
*
* @author Matthew T. Adams
*/
public class ColumnSpecification {
/**
* Default ordering of primary key fields; value is {@link Ordering#ASCENDING}.
*/
public static final Ordering DFAULT_ORDERING = ASCENDING;
private String name;
private DataType type; // TODO: determining if we should be coupling this to Datastax Java Driver type?
private KeyType keyType;
private Ordering ordering;
/**
* Sets the column's name.
*
* @return this
*/
public ColumnSpecification name(String name) {
checkIdentifier(name);
this.name = name;
return this;
}
/**
* Sets the column's type.
*
* @return this
*/
public ColumnSpecification type(DataType type) {
this.type = type;
return this;
}
/**
* Identifies this column as a primary key column that is also part of a partition key. Sets the column's
* {@link #keyType} to {@link KeyType#PARTITION} and its {@link #ordering} to <code>null</code>.
*
* @return this
*/
public ColumnSpecification partition() {
return partition(true);
}
/**
* Toggles the identification of this column as a primary key column that also is or is part of a partition key. Sets
* {@link #ordering} to <code>null</code> and, if the given boolean is <code>true</code>, then sets the column's
* {@link #keyType} to {@link KeyType#PARTITION}, else sets it to <code>null</code>.
*
* @return this
*/
public ColumnSpecification partition(boolean partition) {
this.keyType = partition ? PARTITION : null;
this.ordering = null;
return this;
}
/**
* Identifies this column as a primary key column with default ordering. Sets the column's {@link #keyType} to
* {@link KeyType#PRIMARY} and its {@link #ordering} to {@link #DFAULT_ORDERING}.
*
* @return this
*/
public ColumnSpecification primary() {
return primary(DFAULT_ORDERING);
}
/**
* Identifies this column as a primary key column with the given ordering. Sets the column's {@link #keyType} to
* {@link KeyType#PRIMARY} and its {@link #ordering} to the given {@link Ordering}.
*
* @return this
*/
public ColumnSpecification primary(Ordering order) {
return primary(order, true);
}
/**
* Toggles the identification of this column as a primary key column. If the given boolean is <code>true</code>, then
* sets the column's {@link #keyType} to {@link KeyType#PARTITION} and {@link #ordering} to the given {@link Ordering}
* , else sets both {@link #keyType} and {@link #ordering} to <code>null</code>.
*
* @return this
*/
public ColumnSpecification primary(Ordering order, boolean primary) {
this.keyType = primary ? PRIMARY : null;
this.ordering = primary ? order : null;
return this;
}
/**
* Sets the column's {@link #keyType}.
*
* @return this
*/
/* package */ColumnSpecification keyType(KeyType keyType) {
this.keyType = keyType;
return this;
}
/**
* Sets the column's {@link #ordering}.
*
* @return this
*/
/* package */ColumnSpecification ordering(Ordering ordering) {
this.ordering = ordering;
return this;
}
public String getName() {
return name;
}
public String getNameAsIdentifier() {
return identifize(name);
}
public DataType getType() {
return type;
}
public KeyType getKeyType() {
return keyType;
}
public Ordering getOrdering() {
return ordering;
}
public String toCql() {
return toCql(null).toString();
}
public StringBuilder toCql(StringBuilder cql) {
return (cql = noNull(cql)).append(name).append(" ").append(type);
}
@Override
public String toString() {
return toCql(null).append(" /* keyType=").append(keyType).append(", ordering=").append(ordering).append(" */ ")
.toString();
}
}

View File

@@ -0,0 +1,24 @@
package org.springframework.cassandra.core.keyspace;
import org.springframework.util.Assert;
import com.datastax.driver.core.DataType;
public abstract class ColumnTypeChangeSpecification extends ColumnChangeSpecification {
private DataType type;
public ColumnTypeChangeSpecification(String name, DataType type) {
super(name);
setType(type);
}
private void setType(DataType type) {
Assert.notNull(type);
this.type = type;
}
public DataType getType() {
return type;
}
}

View File

@@ -0,0 +1,34 @@
package org.springframework.cassandra.core.keyspace;
/**
* Builder class to construct a <code>CREATE TABLE</code> specification.
*
* @author Matthew T. Adams
*/
public class CreateTableSpecification extends TableSpecification<CreateTableSpecification> {
private boolean ifNotExists = false;
/**
* Causes the inclusion of an <code>IF NOT EXISTS</code> clause.
*
* @return this
*/
public CreateTableSpecification ifNotExists() {
return ifNotExists(true);
}
/**
* Toggles the inclusion of an <code>IF NOT EXISTS</code> clause.
*
* @return this
*/
public CreateTableSpecification ifNotExists(boolean ifNotExists) {
this.ifNotExists = ifNotExists;
return this;
}
public boolean getIfNotExists() {
return ifNotExists;
}
}

View File

@@ -0,0 +1,157 @@
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.escapeSingle;
import static org.springframework.cassandra.core.cql.CqlStringUtils.singleQuote;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.Map;
import org.springframework.util.Assert;
/**
* A default implementation of {@link Option}.
*
* @author Matthew T. Adams
*/
public class DefaultOption implements Option {
private String name;
private Class<?> type;
private boolean requiresValue;
private boolean escapesValue;
private boolean quotesValue;
public DefaultOption(String name, Class<?> type, boolean requiresValue, boolean escapesValue, boolean quotesValue) {
setName(name);
setType(type);
this.requiresValue = requiresValue;
this.escapesValue = escapesValue;
this.quotesValue = quotesValue;
}
protected void setName(String name) {
Assert.hasLength(name);
this.name = name;
}
protected void setType(Class<?> type) {
if (type != null) {
if (type.isInterface() && !(Map.class.isAssignableFrom(type) || Collection.class.isAssignableFrom(type))) {
throw new IllegalArgumentException("given type [" + type.getName() + "] must be a class, Map or Collection");
}
}
this.type = type;
}
@SuppressWarnings({ "unchecked", "rawtypes" })
public boolean isCoerceable(Object value) {
if (value == null || type == null) {
return true;
}
// check map
if (Map.class.isAssignableFrom(type)) {
return Map.class.isAssignableFrom(value.getClass());
}
// check collection
if (Collection.class.isAssignableFrom(type)) {
return Collection.class.isAssignableFrom(value.getClass());
}
// check enum
if (type.isEnum()) {
try {
String name = value instanceof Enum ? name = ((Enum) value).name() : value.toString();
Enum.valueOf((Class<? extends Enum>) type, name);
return true;
} catch (NullPointerException x) {
return false;
} catch (IllegalArgumentException x) {
return false;
}
}
// check class via String constructor
try {
Constructor<?> ctor = type.getConstructor(String.class);
if (!ctor.isAccessible()) {
ctor.setAccessible(true);
}
ctor.newInstance(value.toString());
return true;
} catch (InstantiationException e) {
} catch (IllegalAccessException e) {
} catch (IllegalArgumentException e) {
} catch (InvocationTargetException e) {
} catch (NoSuchMethodException e) {
} catch (SecurityException e) {
}
return false;
}
public Class<?> getType() {
return type;
}
public String getName() {
return name;
}
public boolean takesValue() {
return type != null;
}
public boolean requiresValue() {
return this.requiresValue;
}
public boolean escapesValue() {
return this.escapesValue;
}
public boolean quotesValue() {
return this.quotesValue;
}
public void checkValue(Object value) {
if (takesValue()) {
if (value == null) {
if (requiresValue) {
throw new IllegalArgumentException("Option [" + getName() + "] requires a value");
}
return; // doesn't require a value, so null is ok
}
// else value is not null
if (isCoerceable(value)) {
return;
}
// else value is not coerceable into the expected type
throw new IllegalArgumentException("Option [" + getName() + "] takes value coerceable to type ["
+ getType().getName() + "]");
}
// else this option doesn't take a value
if (value != null) {
throw new IllegalArgumentException("Option [" + getName() + "] takes no value");
}
}
public String toString(Object value) {
if (value == null) {
return null;
}
checkValue(value);
String string = value.toString();
string = escapesValue ? escapeSingle(string) : string;
string = quotesValue ? singleQuote(string) : string;
return string;
}
@Override
public String toString() {
return "[name=" + name + ", type=" + type.getName() + ", requiresValue=" + requiresValue + ", escapesValue="
+ escapesValue + ", quotesValue=" + quotesValue + "]";
}
}

View File

@@ -0,0 +1,17 @@
package org.springframework.cassandra.core.keyspace;
/**
* Convenient default implementation of {@link TableDescriptor} as an extension of {@link TableSpecification} that
* doesn't require the use of generics.
*
* @author Matthew T. Adams
*/
public class DefaultTableDescriptor extends TableSpecification<DefaultTableDescriptor> {
/**
* Factory method to produce a new {@link DefaultTableDescriptor}. Convenient if imported statically.
*/
public static DefaultTableDescriptor table() {
return new DefaultTableDescriptor();
}
}

View File

@@ -0,0 +1,8 @@
package org.springframework.cassandra.core.keyspace;
public class DropColumnSpecification extends ColumnChangeSpecification {
public DropColumnSpecification(String name) {
super(name);
}
}

View File

@@ -0,0 +1,24 @@
package org.springframework.cassandra.core.keyspace;
/**
* Builder class that supports the construction of <code>DROP TABLE</code> specifications.
*
* @author Matthew T. Adams
*/
public class DropTableSpecification extends TableNameSpecification<DropTableSpecification> {
private boolean ifExists;
public DropTableSpecification ifExists() {
return ifExists(true);
}
public DropTableSpecification ifExists(boolean ifExists) {
this.ifExists = ifExists;
return this;
}
public boolean getIfExists() {
return ifExists;
}
}

View File

@@ -0,0 +1,61 @@
package org.springframework.cassandra.core.keyspace;
/**
* Interface to represent option types.
*
* @author Matthew T. Adams
*/
public interface Option {
/**
* The type that values must be able to be coerced into for this option.
*/
Class<?> getType();
/**
* The (usually lower-cased, underscore-separated) name of this table option.
*/
String getName();
/**
* Whether this option takes a value.
*/
boolean takesValue();
/**
* Whether this option should escape single quotes in its value.
*/
boolean escapesValue();
/**
* Whether this option's value should be single-quoted.
*/
boolean quotesValue();
/**
* Whether this option requires a value.
*/
boolean requiresValue();
/**
* Checks that the given value can be coerced into the type given by {@link #getType()}.
*/
void checkValue(Object value);
/**
* Tests whether the given value can be coerced into the type given by {@link #getType()}.
*/
boolean isCoerceable(Object value);
/**
* First ensures that the given value is coerceable into the type expected by this option, then returns the result of
* {@link Object#toString()} called on the given value. If this option is escaping quotes ({@link #escapesValue()} is
* <code>true</code>), then single quotes will be escaped, and if this option is quoting values (
* {@link #quotesValue()} is <code>true</code>), then the value will be surrounded by single quotes. Given
* <code>null</code>, returns <code>null</code>.
*
* @see #escapesValue()
* @see #quotesValue()
*/
String toString(Object value);
}

View File

@@ -0,0 +1,52 @@
package org.springframework.cassandra.core.keyspace;
import java.util.List;
import java.util.Map;
/**
* Describes a table.
*
* @author Matthew T. Adams
*/
public interface TableDescriptor {
/**
* Returns the name of the table.
*/
String getName();
/**
* Returns the name of the table as an identifer or quoted identifier as appropriate.
*/
String getNameAsIdentifier();
/**
* Returns an unmodifiable {@link List} of {@link ColumnSpecification}s.
*/
List<ColumnSpecification> getColumns();
/**
* Returns an unmodifiable list of all partition key columns.
*/
public List<ColumnSpecification> getPartitionKeyColumns();
/**
* Returns an unmodifiable list of all primary key columns that are not also partition key columns.
*/
public List<ColumnSpecification> getPrimaryKeyColumns();
/**
* Returns an unmodifiable list of all partition and primary key columns.
*/
public List<ColumnSpecification> getKeyColumns();
/**
* Returns an unmodifiable list of all non-key columns.
*/
public List<ColumnSpecification> getNonKeyColumns();
/**
* Returns an unmodifiable {@link Map} of table options.
*/
Map<String, Object> getOptions();
}

View File

@@ -0,0 +1,38 @@
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.checkIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.identifize;
/**
* Abstract builder class to support the construction of table specifications.
*
* @author Matthew T. Adams
* @param <T> The subtype of the {@link TableNameSpecification}
*/
public abstract class TableNameSpecification<T extends TableNameSpecification<T>> {
/**
* The name of the table.
*/
private String name;
/**
* Sets the table name.
*
* @return this
*/
@SuppressWarnings("unchecked")
public T name(String name) {
checkIdentifier(name);
this.name = name;
return (T) this;
}
public String getName() {
return name;
}
public String getNameAsIdentifier() {
return identifize(name);
}
}

View File

@@ -0,0 +1,34 @@
package org.springframework.cassandra.core.keyspace;
/**
* Class that offers static methods as entry points into the fluent API for building create, drop and alter table
* specifications. These methods are most convenient when imported statically.
*
* @author Matthew T. Adams
*/
public class TableOperations {
/**
* Entry point into the {@link CreateTableSpecification}'s fluent API to create a table. Convenient if imported
* statically.
*/
public static CreateTableSpecification createTable() {
return new CreateTableSpecification();
}
/**
* Entry point into the {@link DropTableSpecification}'s fluent API to drop a table. Convenient if imported
* statically.
*/
public static DropTableSpecification dropTable() {
return new DropTableSpecification();
}
/**
* Entry point into the {@link AlterTableSpecification}'s fluent API to alter a table. Convenient if imported
* statically.
*/
public static AlterTableSpecification alterTable() {
return new AlterTableSpecification();
}
}

View File

@@ -0,0 +1,287 @@
package org.springframework.cassandra.core.keyspace;
import java.util.Map;
/**
* Enumeration that represents all known table options. If a table option is not listed here, but is supported by
* Cassandra, use the method {@link CreateTableSpecification#with(String, Object, boolean, boolean)} to write the raw
* value.
*
* Implements {@link Option} via delegation, since {@link Enum}s can't extend anything.
*
* @author Matthew T. Adams
* @see CompactionOption
* @see CompressionOption
* @see CachingOption
*/
public enum TableOption implements Option {
/**
* <code>comment</code>
*/
COMMENT("comment", String.class, true, true, true),
/**
* <code>COMPACT STORAGE</code>
*/
COMPACT_STORAGE("COMPACT STORAGE", null, false, false, false),
/**
* <code>compaction</code>. Value is a <code>Map&lt;CompactionOption,Object&gt;</code>.
*
* @see CompactionOption
*/
COMPACTION("compaction", Map.class, true, false, false),
/**
* <code>compression</code>. Value is a <code>Map&lt;CompressionOption,Object&gt;</code>.
*
* @see {@link CompressionOption}
*/
COMPRESSION("compression", Map.class, true, false, false),
/**
* <code>replicate_on_write</code>
*/
REPLICATE_ON_WRITE("replicate_on_write", Boolean.class, true, false, false),
/**
* <code>caching</code>
*
* @see CachingOption
*/
CACHING("caching", CachingOption.class, true, false, false),
/**
* <code>bloom_filter_fp_chance</code>
*/
BLOOM_FILTER_FP_CHANCE("bloom_filter_fp_chance", Double.class, true, false, false),
/**
* <code>read_repair_chance</code>
*/
READ_REPAIR_CHANCE("read_repair_chance", Double.class, true, false, false),
/**
* <code>dclocal_read_repair_chance</code>
*/
DCLOCAL_READ_REPAIR_CHANCE("dclocal_read_repair_chance", Double.class, true, false, false),
/**
* <code>gc_grace_seconds</code>
*/
GC_GRACE_SECONDS("gc_grace_seconds", Long.class, true, false, false);
private Option delegate;
private TableOption(String name, Class<?> type, boolean requiresValue, boolean escapesValue, boolean quotesValue) {
this.delegate = new DefaultOption(name, type, requiresValue, escapesValue, quotesValue);
}
public Class<?> getType() {
return delegate.getType();
}
public boolean takesValue() {
return delegate.takesValue();
}
public String getName() {
return delegate.getName();
}
public boolean escapesValue() {
return delegate.escapesValue();
}
public boolean quotesValue() {
return delegate.quotesValue();
}
public boolean requiresValue() {
return delegate.requiresValue();
}
public void checkValue(Object value) {
delegate.checkValue(value);
}
public boolean isCoerceable(Object value) {
return delegate.isCoerceable(value);
}
public String toString() {
return delegate.toString();
}
public String toString(Object value) {
return delegate.toString(value);
}
/**
* Known caching options.
*
* @author Matthew T. Adams
*/
public enum CachingOption {
ALL("all"), KEYS_ONLY("keys_only"), ROWS_ONLY("rows_only"), NONE("none");
private String value;
private CachingOption(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public String toString() {
return getValue();
}
}
/**
* Known compaction options.
*
* @author Matthew T. Adams
*/
public enum CompactionOption implements Option {
/**
* <code>tombstone_threshold</code>
*/
TOMBSTONE_THRESHOLD("tombstone_threshold", Double.class, true, false, false),
/**
* <code>tombstone_compaction_interval</code>
*/
TOMBSTONE_COMPACTION_INTERVAL("tombstone_compaction_interval", Double.class, true, false, false),
/**
* <code>min_sstable_size</code>
*/
MIN_SSTABLE_SIZE("min_sstable_size", Long.class, true, false, false),
/**
* <code>min_threshold</code>
*/
MIN_THRESHOLD("min_threshold", Long.class, true, false, false),
/**
* <code>max_threshold</code>
*/
MAX_THRESHOLD("max_threshold", Long.class, true, false, false),
/**
* <code>bucket_low</code>
*/
BUCKET_LOW("bucket_low", Double.class, true, false, false),
/**
* <code>bucket_high</code>
*/
BUCKET_HIGH("bucket_high", Double.class, true, false, false),
/**
* <code>sstable_size_in_mb</code>
*/
SSTABLE_SIZE_IN_MB("sstable_size_in_mb", Long.class, true, false, false);
private Option delegate;
private CompactionOption(String name, Class<?> type, boolean requiresValue, boolean escapesValue,
boolean quotesValue) {
this.delegate = new DefaultOption(name, type, requiresValue, escapesValue, quotesValue);
}
public Class<?> getType() {
return delegate.getType();
}
public boolean takesValue() {
return delegate.takesValue();
}
public String getName() {
return delegate.getName();
}
public boolean escapesValue() {
return delegate.escapesValue();
}
public boolean quotesValue() {
return delegate.quotesValue();
}
public boolean requiresValue() {
return delegate.requiresValue();
}
public void checkValue(Object value) {
delegate.checkValue(value);
}
public boolean isCoerceable(Object value) {
return delegate.isCoerceable(value);
}
public String toString() {
return delegate.toString();
}
public String toString(Object value) {
return delegate.toString(value);
}
}
/**
* Known compression options.
*
* @author Matthew T. Adams
*/
public enum CompressionOption implements Option {
/**
* <code>sstable_compression</code>
*/
STABLE_COMPRESSION("sstable_compression", String.class, true, false, false),
/**
* <code>chunk_length_kb</code>
*/
CHUNK_LENGTH_KB("chunk_length_kb", Long.class, true, false, false),
/**
* <code>crc_check_chance</code>
*/
CRC_CHECK_CHANCE("crc_check_chance", Double.class, true, false, false);
private Option delegate;
private CompressionOption(String name, Class<?> type, boolean requiresValue, boolean escapesValue,
boolean quotesValue) {
this.delegate = new DefaultOption(name, type, requiresValue, escapesValue, quotesValue);
}
public Class<?> getType() {
return delegate.getType();
}
public boolean takesValue() {
return delegate.takesValue();
}
public String getName() {
return delegate.getName();
}
public boolean escapesValue() {
return delegate.escapesValue();
}
public boolean quotesValue() {
return delegate.quotesValue();
}
public boolean requiresValue() {
return delegate.requiresValue();
}
public void checkValue(Object value) {
delegate.checkValue(value);
}
public boolean isCoerceable(Object value) {
return delegate.isCoerceable(value);
}
public String toString() {
return delegate.toString();
}
public String toString(Object value) {
return delegate.toString(value);
}
}
}

View File

@@ -0,0 +1,93 @@
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.cql.CqlStringUtils.escapeSingle;
import static org.springframework.cassandra.core.cql.CqlStringUtils.singleQuote;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import org.springframework.cassandra.core.cql.CqlStringUtils;
/**
* Abstract builder class to support the construction of table specifications that have table options, that is, those
* options normally specified by <code>WITH ... AND ...</code>.
* <p/>
* It is important to note that although this class depends on {@link TableOption} for convenient and typesafe use, it
* ultimately stores its options in a <code>Map<String,Object></code> for flexibility. This means that
* {@link #with(TableOption)} and {@link #with(TableOption, Object)} delegate to
* {@link #with(String, Object, boolean, boolean)}. This design allows the API to support new Cassandra options as they
* are introduced without having to update the code immediately.
*
* @author Matthew T. Adams
* @param <T> The subtype of the {@link TableOptionsSpecification}.
*/
public abstract class TableOptionsSpecification<T extends TableOptionsSpecification<T>> extends
TableNameSpecification<TableOptionsSpecification<T>> {
protected Map<String, Object> options = new LinkedHashMap<String, Object>();
@SuppressWarnings("unchecked")
public T name(String name) {
return (T) super.name(name);
}
/**
* Convenience method that calls <code>with(option, null)</code>.
*
* @return this
*/
public T with(TableOption option) {
return with(option, null);
}
/**
* Sets the given table option. This is a convenience method that calls
* {@link #with(String, Object, boolean, boolean)} appropriately from the given {@link TableOption} and value for that
* option.
*
* @param option The option to set.
* @param value The value of the option. Must be type-compatible with the {@link TableOption}.
* @return this
* @see #with(String, Object, boolean, boolean)
*/
public T with(TableOption option, Object value) {
option.checkValue(value);
return (T) with(option.getName(), value, option.escapesValue(), option.quotesValue());
}
/**
* Adds the given option by name to this table's options.
* <p/>
* Options that have <code>null</code> values are considered single string options where the name of the option is the
* string to be used. Otherwise, the result of {@link Object#toString()} is considered to be the value of the option
* with the given name. The value, after conversion to string, may have embedded single quotes escaped according to
* parameter <code>escape</code> and may be single-quoted according to parameter <code>quote</code>.
*
* @param name The name of the option
* @param value The value of the option. If <code>null</code>, the value is ignored and the option is considered to be
* composed of only the name, otherwise the value's {@link Object#toString()} value is used.
* @param escape Whether to escape the value via {@link CqlStringUtils#escapeSingle(Object)}. Ignored if given value
* is an instance of a {@link Map}.
* @param quote Whether to quote the value via {@link CqlStringUtils#singleQuote(Object)}. Ignored if given value is
* an instance of a {@link Map}.
* @return this
*/
@SuppressWarnings("unchecked")
public T with(String name, Object value, boolean escape, boolean quote) {
if (!(value instanceof Map)) {
if (escape) {
value = escapeSingle(value);
}
if (quote) {
value = singleQuote(value);
}
}
options.put(name, value);
return (T) this;
}
public Map<String, Object> getOptions() {
return Collections.unmodifiableMap(options);
}
}

View File

@@ -0,0 +1,162 @@
package org.springframework.cassandra.core.keyspace;
import static org.springframework.cassandra.core.KeyType.PARTITION;
import static org.springframework.cassandra.core.KeyType.PRIMARY;
import static org.springframework.cassandra.core.Ordering.ASCENDING;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.cassandra.core.KeyType;
import org.springframework.cassandra.core.Ordering;
import com.datastax.driver.core.DataType;
/**
* Builder class to support the construction of table specifications that have columns. This class can also be used as a
* standalone {@link TableDescriptor}, independent of {@link CreateTableSpecification}.
*
* @author Matthew T. Adams
*/
public class TableSpecification<T> extends TableOptionsSpecification<TableSpecification<T>> implements TableDescriptor {
/**
* List of all columns.
*/
private List<ColumnSpecification> columns = new ArrayList<ColumnSpecification>();
/**
* List of only those columns that comprise the partition key.
*/
private List<ColumnSpecification> partitionKeyColumns = new ArrayList<ColumnSpecification>();
/**
* List of only those columns that comprise the primary key that are not also part of the partition key.
*/
private List<ColumnSpecification> primaryKeyColumns = new ArrayList<ColumnSpecification>();
/**
* List of only those columns that are not partition or primary key columns.
*/
private List<ColumnSpecification> nonKeyColumns = new ArrayList<ColumnSpecification>();
/**
* Adds the given non-key column to the table. Must be specified after all primary key columns.
*
* @param name The column name; must be a valid unquoted or quoted identifier without the surrounding double quotes.
* @param type The data type of the column.
*/
public T column(String name, DataType type) {
return column(name, type, null, null);
}
/**
* Adds the given partition key column to the table. Must be specified before any other columns.
*
* @param name The column name; must be a valid unquoted or quoted identifier without the surrounding double quotes.
* @param type The data type of the column.
* @return this
*/
public T partitionKeyColumn(String name, DataType type) {
return column(name, type, PARTITION, null);
}
/**
* Adds the given primary key column to the table with ascending ordering. Must be specified after all partition key
* columns and before any non-key columns.
*
* @param name The column name; must be a valid unquoted or quoted identifier without the surrounding double quotes.
* @param type The data type of the column.
* @return this
*/
public T primaryKeyColumn(String name, DataType type) {
return primaryKeyColumn(name, type, ASCENDING);
}
/**
* Adds the given primary key column to the table with the given ordering (<code>null</code> meaning ascending). Must
* be specified after all partition key columns and before any non-key columns.
*
* @param name The column name; must be a valid unquoted or quoted identifier without the surrounding double quotes.
* @param type The data type of the column.
* @return this
*/
public T primaryKeyColumn(String name, DataType type, Ordering ordering) {
return column(name, type, PRIMARY, ordering);
}
/**
* Adds the given info as a new column to the table. Partition key columns must precede primary key columns, which
* must precede non-key columns.
*
* @param name The column name; must be a valid unquoted or quoted identifier without the surrounding double quotes.
* @param type The data type of the column.
* @param keyType Indicates key type. Null means that the column is not a key column.
* @param ordering If the given {@link KeyType} is {@link KeyType#PRIMARY}, then the given ordering is used, else
* ignored.
* @return this
*/
@SuppressWarnings("unchecked")
protected T column(String name, DataType type, KeyType keyType, Ordering ordering) {
ColumnSpecification column = new ColumnSpecification().name(name).type(type).keyType(keyType)
.ordering(keyType == PRIMARY ? ordering : null);
columns.add(column);
if (keyType == KeyType.PARTITION) {
partitionKeyColumns.add(column);
}
if (keyType == KeyType.PRIMARY) {
primaryKeyColumns.add(column);
}
if (keyType == null) {
nonKeyColumns.add(column);
}
return (T) this;
}
/**
* Returns an unmodifiable list of all columns.
*/
public List<ColumnSpecification> getColumns() {
return Collections.unmodifiableList(columns);
}
/**
* Returns an unmodifiable list of all partition key columns.
*/
public List<ColumnSpecification> getPartitionKeyColumns() {
return Collections.unmodifiableList(partitionKeyColumns);
}
/**
* Returns an unmodifiable list of all primary key columns that are not also partition key columns.
*/
public List<ColumnSpecification> getPrimaryKeyColumns() {
return Collections.unmodifiableList(primaryKeyColumns);
}
/**
* Returns an unmodifiable list of all primary key columns that are not also partition key columns.
*/
public List<ColumnSpecification> getKeyColumns() {
ArrayList<ColumnSpecification> keyColumns = new ArrayList<ColumnSpecification>();
keyColumns.addAll(partitionKeyColumns);
keyColumns.addAll(primaryKeyColumns);
return Collections.unmodifiableList(keyColumns);
}
/**
* Returns an unmodifiable list of all non-key columns.
*/
public List<ColumnSpecification> getNonKeyColumns() {
return Collections.unmodifiableList(nonKeyColumns);
}
}

View File

@@ -0,0 +1,126 @@
package org.springframework.cassandra.core.util;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
/**
* Builder for maps, which also conveniently implements {@link Map} via delegation for convenience so you don't have to
* actually {@link #build()} it (or forget to).
*
* @author Matthew T. Adams
* @param <K> The key type of the map.
* @param <V> The value type of the map.
*/
public class MapBuilder<K, V> implements Map<K, V> {
/**
* Factory method to construct a new <code>MapBuilder&lt;Object,Object&gt;</code>. Convenient if imported statically.
*/
public static MapBuilder<Object, Object> map() {
return map(Object.class, Object.class);
}
/**
* Factory method to construct a new builder with the given key &amp; value types. Convenient if imported statically.
*/
public static <K, V> MapBuilder<K, V> map(Class<K> keyType, Class<V> valueType) {
return new MapBuilder<K, V>();
}
/**
* Factory method to construct a new builder with a shallow copy of the given map. Convenient if imported statically.
*/
public static <K, V> MapBuilder<K, V> map(Map<K, V> source) {
return new MapBuilder<K, V>(source);
}
private Map<K, V> map;
public MapBuilder() {
this(new LinkedHashMap<K, V>());
}
/**
* Constructs a new instance with a copy of the given map.
*/
public MapBuilder(Map<K, V> source) {
this.map = new LinkedHashMap<K, V>(source);
}
/**
* Adds an entry to this map, then returns <code>this</code>.
*
* @return this
*/
public MapBuilder<K, V> entry(K key, V value) {
map.put(key, value);
return this;
}
/**
* Returns a new map based on the current state of this builder's map.
*
* @return A new Map<K, V> with this builder's map's current content.
*/
public Map<K, V> build() {
return new LinkedHashMap<K, V>(map);
}
public int size() {
return map.size();
}
public boolean isEmpty() {
return map.isEmpty();
}
public boolean containsKey(Object key) {
return map.containsKey(key);
}
public boolean containsValue(Object value) {
return map.containsValue(value);
}
public V get(Object key) {
return map.get(key);
}
public V put(K key, V value) {
return map.put(key, value);
}
public V remove(Object key) {
return map.remove(key);
}
public void putAll(Map<? extends K, ? extends V> m) {
map.putAll(m);
}
public void clear() {
map.clear();
}
public Set<K> keySet() {
return map.keySet();
}
public Collection<V> values() {
return map.values();
}
public Set<java.util.Map.Entry<K, V>> entrySet() {
return map.entrySet();
}
public boolean equals(Object o) {
return map.equals(o);
}
public int hashCode() {
return map.hashCode();
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2011-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.cassandra.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import com.datastax.driver.core.Session;
/**
* @author David Webb
*
*/
public class CassandraAccessor implements InitializingBean {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
private Session session;
private CassandraExceptionTranslator exceptionTranslator;
/**
* Set the exception translator for this instance.
*
* @see org.springframework.cassandra.support.CassandraExceptionTranslator
*/
public void setExceptionTranslator(CassandraExceptionTranslator exceptionTranslator) {
this.exceptionTranslator = exceptionTranslator;
}
/**
* Return the exception translator for this instance.
*/
public CassandraExceptionTranslator getExceptionTranslator() {
return this.exceptionTranslator;
}
/**
* Ensure that the Cassandra Session has been set
*/
public void afterPropertiesSet() {
if (getSession() == null) {
throw new IllegalArgumentException("Property 'session' is required");
}
}
/**
* @return Returns the session.
*/
public Session getSession() {
return session;
}
/**
* @param session The session to set.
*/
public void setSession(Session session) {
this.session = session;
}
}

View File

@@ -0,0 +1,136 @@
/*
* 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.cassandra.support;
import org.springframework.cassandra.support.exception.CassandraAuthenticationException;
import org.springframework.cassandra.support.exception.CassandraConnectionFailureException;
import org.springframework.cassandra.support.exception.CassandraInsufficientReplicasAvailableException;
import org.springframework.cassandra.support.exception.CassandraInternalException;
import org.springframework.cassandra.support.exception.CassandraInvalidConfigurationInQueryException;
import org.springframework.cassandra.support.exception.CassandraInvalidQueryException;
import org.springframework.cassandra.support.exception.CassandraKeyspaceExistsException;
import org.springframework.cassandra.support.exception.CassandraQuerySyntaxException;
import org.springframework.cassandra.support.exception.CassandraReadTimeoutException;
import org.springframework.cassandra.support.exception.CassandraTableExistsException;
import org.springframework.cassandra.support.exception.CassandraTraceRetrievalException;
import org.springframework.cassandra.support.exception.CassandraTruncateException;
import org.springframework.cassandra.support.exception.CassandraTypeMismatchException;
import org.springframework.cassandra.support.exception.CassandraUnauthorizedException;
import org.springframework.cassandra.support.exception.CassandraUncategorizedException;
import org.springframework.cassandra.support.exception.CassandraWriteTimeoutException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import com.datastax.driver.core.WriteType;
import com.datastax.driver.core.exceptions.AlreadyExistsException;
import com.datastax.driver.core.exceptions.AuthenticationException;
import com.datastax.driver.core.exceptions.DriverException;
import com.datastax.driver.core.exceptions.DriverInternalError;
import com.datastax.driver.core.exceptions.InvalidConfigurationInQueryException;
import com.datastax.driver.core.exceptions.InvalidQueryException;
import com.datastax.driver.core.exceptions.InvalidTypeException;
import com.datastax.driver.core.exceptions.NoHostAvailableException;
import com.datastax.driver.core.exceptions.ReadTimeoutException;
import com.datastax.driver.core.exceptions.SyntaxError;
import com.datastax.driver.core.exceptions.TraceRetrievalException;
import com.datastax.driver.core.exceptions.TruncateException;
import com.datastax.driver.core.exceptions.UnauthorizedException;
import com.datastax.driver.core.exceptions.UnavailableException;
import com.datastax.driver.core.exceptions.WriteTimeoutException;
/**
* Simple {@link PersistenceExceptionTranslator} for Cassandra. Convert the given runtime exception to an appropriate
* exception from the {@code org.springframework.dao} hierarchy. Return {@literal null} if no translation is
* appropriate: any other exception may have resulted from user code, and should not be translated.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
public class CassandraExceptionTranslator implements PersistenceExceptionTranslator {
/*
* (non-Javadoc)
*
* @see org.springframework.dao.support.PersistenceExceptionTranslator#
* translateExceptionIfPossible(java.lang.RuntimeException)
*/
public DataAccessException translateExceptionIfPossible(RuntimeException x) {
if (!(x instanceof DriverException)) {
return null;
}
if (x instanceof DataAccessException) {
return (DataAccessException) x;
}
// Remember: subclasses must come before superclasses, otherwise the
// superclass would match before the subclass!
if (x instanceof AuthenticationException) {
return new CassandraAuthenticationException(((AuthenticationException) x).getHost(), x.getMessage(), x);
}
if (x instanceof DriverInternalError) {
return new CassandraInternalException(x.getMessage(), x);
}
if (x instanceof InvalidTypeException) {
return new CassandraTypeMismatchException(x.getMessage(), x);
}
if (x instanceof NoHostAvailableException) {
return new CassandraConnectionFailureException(((NoHostAvailableException) x).getErrors(), x.getMessage(), x);
}
if (x instanceof ReadTimeoutException) {
return new CassandraReadTimeoutException(((ReadTimeoutException) x).wasDataRetrieved(), x.getMessage(), x);
}
if (x instanceof WriteTimeoutException) {
WriteType writeType = ((WriteTimeoutException) x).getWriteType();
return new CassandraWriteTimeoutException(writeType == null ? null : writeType.name(), x.getMessage(), x);
}
if (x instanceof TruncateException) {
return new CassandraTruncateException(x.getMessage(), x);
}
if (x instanceof UnavailableException) {
UnavailableException ux = (UnavailableException) x;
return new CassandraInsufficientReplicasAvailableException(ux.getRequiredReplicas(), ux.getAliveReplicas(),
x.getMessage(), x);
}
if (x instanceof AlreadyExistsException) {
AlreadyExistsException aex = (AlreadyExistsException) x;
return aex.wasTableCreation() ? new CassandraTableExistsException(aex.getTable(), x.getMessage(), x)
: new CassandraKeyspaceExistsException(aex.getKeyspace(), x.getMessage(), x);
}
if (x instanceof InvalidConfigurationInQueryException) {
return new CassandraInvalidConfigurationInQueryException(x.getMessage(), x);
}
if (x instanceof InvalidQueryException) {
return new CassandraInvalidQueryException(x.getMessage(), x);
}
if (x instanceof SyntaxError) {
return new CassandraQuerySyntaxException(x.getMessage(), x);
}
if (x instanceof UnauthorizedException) {
return new CassandraUnauthorizedException(x.getMessage(), x);
}
if (x instanceof TraceRetrievalException) {
return new CassandraTraceRetrievalException(x.getMessage(), x);
}
// unknown or unhandled exception
return new CassandraUncategorizedException(x.getMessage(), x);
}
}

View File

@@ -0,0 +1,42 @@
/*
* 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.cassandra.support.exception;
import java.net.InetAddress;
import org.springframework.dao.PermissionDeniedDataAccessException;
/**
* Spring data access exception for a Cassandra authentication failure.
*
* @author Matthew T. Adams
*/
public class CassandraAuthenticationException extends PermissionDeniedDataAccessException {
private static final long serialVersionUID = 8556304586797273927L;
private InetAddress host;
public CassandraAuthenticationException(InetAddress host, String msg, Throwable cause) {
super(msg, cause);
this.host = host;
}
public InetAddress getHost() {
return host;
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.cassandra.support.exception;
import java.net.InetAddress;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.springframework.dao.DataAccessResourceFailureException;
/**
* Spring data access exception for Cassandra when no host is available.
*
* @author Matthew T. Adams
*/
public class CassandraConnectionFailureException extends DataAccessResourceFailureException {
private static final long serialVersionUID = 6299912054261646552L;
private final Map<InetAddress, String> messagesByHost = new HashMap<InetAddress, String>();
public CassandraConnectionFailureException(Map<InetAddress, String> messagesByHost, String msg, Throwable cause) {
super(msg, cause);
this.messagesByHost.putAll(messagesByHost);
}
public Map<InetAddress, String> getMessagesByHost() {
return Collections.unmodifiableMap(messagesByHost);
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.cassandra.support.exception;
import org.springframework.dao.TransientDataAccessException;
/**
* Spring data access exception for Cassandra when insufficient replicas are available for a given consistency level.
*
* @author Matthew T. Adams
*/
public class CassandraInsufficientReplicasAvailableException extends TransientDataAccessException {
private static final long serialVersionUID = 6415130674604814905L;
private int numberRequired;
private int numberAlive;
public CassandraInsufficientReplicasAvailableException(String msg) {
super(msg);
}
public CassandraInsufficientReplicasAvailableException(int numberRequired, int numberAlive, String msg,
Throwable cause) {
super(msg, cause);
this.numberRequired = numberRequired;
this.numberAlive = numberAlive;
}
public int getNumberRequired() {
return numberRequired;
}
public int getNumberAlive() {
return numberAlive;
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.cassandra.support.exception;
import org.springframework.dao.DataAccessException;
/**
* Spring data access exception for a Cassandra internal error.
*
* @author Matthew T. Adams
*/
public class CassandraInternalException extends DataAccessException {
private static final long serialVersionUID = 433061676465346338L;
public CassandraInternalException(String msg) {
super(msg);
}
public CassandraInternalException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,38 @@
/*
* 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.cassandra.support.exception;
import org.springframework.dao.InvalidDataAccessApiUsageException;
/**
* Spring data access exception for a Cassandra query that is syntactically correct but has an invalid configuration
* clause.
*
* @author Matthew T. Adams
*/
public class CassandraInvalidConfigurationInQueryException extends InvalidDataAccessApiUsageException {
private static final long serialVersionUID = 4594321191806182918L;
public CassandraInvalidConfigurationInQueryException(String msg) {
super(msg);
}
public CassandraInvalidConfigurationInQueryException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.cassandra.support.exception;
import org.springframework.dao.InvalidDataAccessApiUsageException;
/**
* Spring data access exception for a Cassandra query that's syntactically correct but invalid.
*
* @author Matthew T. Adams
*/
public class CassandraInvalidQueryException extends InvalidDataAccessApiUsageException {
private static final long serialVersionUID = 4594321191806182918L;
public CassandraInvalidQueryException(String msg) {
super(msg);
}
public CassandraInvalidQueryException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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.cassandra.support.exception;
/**
* Spring data access exception for Cassandra when a keyspace being created already exists.
*
* @author Matthew T. Adams
*/
public class CassandraKeyspaceExistsException extends CassandraSchemaElementExistsException {
private static final long serialVersionUID = 6032967419751410352L;
public CassandraKeyspaceExistsException(String keyspaceName, String msg, Throwable cause) {
super(keyspaceName, ElementType.KEYSPACE, msg, cause);
}
public String getKeyspaceName() {
return getElementName();
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.cassandra.support.exception;
import org.springframework.dao.InvalidDataAccessApiUsageException;
/**
* Spring data access exception for a Cassandra query syntax error.
*
* @author Matthew T. Adams
*/
public class CassandraQuerySyntaxException extends InvalidDataAccessApiUsageException {
private static final long serialVersionUID = 4398474399882434154L;
public CassandraQuerySyntaxException(String msg) {
super(msg);
}
public CassandraQuerySyntaxException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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.cassandra.support.exception;
import org.springframework.dao.QueryTimeoutException;
/**
* Spring data access exception for a Cassandra read timeout.
*
* @author Matthew T. Adams
*/
public class CassandraReadTimeoutException extends QueryTimeoutException {
private static final long serialVersionUID = -787022307935203387L;
private boolean wasDataReceived;
public CassandraReadTimeoutException(boolean wasDataReceived, String msg, Throwable cause) {
super(msg);
this.wasDataReceived = wasDataReceived;
}
public boolean getWasDataReceived() {
return wasDataReceived;
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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.cassandra.support.exception;
import org.springframework.dao.NonTransientDataAccessException;
/**
* Spring data access exception for when Cassandra schema element being created already exists.
*
* @author Matthew T. Adams
*/
public class CassandraSchemaElementExistsException extends NonTransientDataAccessException {
private static final long serialVersionUID = 7798361273692300162L;
public enum ElementType {
KEYSPACE, TABLE, COLUMN, INDEX;
}
private String elementName;
private ElementType elementType;
public CassandraSchemaElementExistsException(String elementName, ElementType elementType, String msg, Throwable cause) {
super(msg, cause);
this.elementName = elementName;
this.elementType = elementType;
}
public String getElementName() {
return elementName;
}
public ElementType getElementType() {
return elementType;
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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.cassandra.support.exception;
/**
* Spring data access exception for when a Cassandra table being created already exists.
*
* @author Matthew T. Adams
*/
public class CassandraTableExistsException extends CassandraSchemaElementExistsException {
private static final long serialVersionUID = 6032967419751410352L;
public CassandraTableExistsException(String tableName, String msg, Throwable cause) {
super(tableName, ElementType.TABLE, msg, cause);
}
public String getTableName() {
return getElementName();
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.cassandra.support.exception;
import org.springframework.dao.TransientDataAccessException;
/**
* Spring data access exception for a Cassandra trace retrieval exception.
*
* @author Matthew T. Adams
*/
public class CassandraTraceRetrievalException extends TransientDataAccessException {
private static final long serialVersionUID = -3163557220324700239L;
public CassandraTraceRetrievalException(String msg) {
super(msg);
}
public CassandraTraceRetrievalException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.cassandra.support.exception;
import org.springframework.dao.TransientDataAccessException;
/**
* Spring data access exception for a Cassandra truncate exception.
*
* @author Matthew T. Adams
*/
public class CassandraTruncateException extends TransientDataAccessException {
private static final long serialVersionUID = 5730642491362430311L;
public CassandraTruncateException(String msg) {
super(msg);
}
public CassandraTruncateException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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.cassandra.support.exception;
import org.springframework.dao.TypeMismatchDataAccessException;
/**
* Spring data access exception for a Cassandra type mismatch exception.
*
* @author Matthew T. Adams
*/
public class CassandraTypeMismatchException extends TypeMismatchDataAccessException {
private static final long serialVersionUID = -7420058975444905629L;
public CassandraTypeMismatchException(String msg) {
super(msg);
}
public CassandraTypeMismatchException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,33 @@
/*
* 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.cassandra.support.exception;
import org.springframework.dao.PermissionDeniedDataAccessException;
/**
* Spring data access exception for when access to a Cassandra element is denied.
*
* @author Matthew T. Adams
*/
public class CassandraUnauthorizedException extends PermissionDeniedDataAccessException {
private static final long serialVersionUID = 4618185356687726647L;
public CassandraUnauthorizedException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2011-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.cassandra.support.exception;
import org.springframework.dao.UncategorizedDataAccessException;
/**
* Spring data access exception for an uncategorized Cassandra exception.
*
* @author Alex Shvid
* @author Matthew T. Adams
*/
public class CassandraUncategorizedException extends UncategorizedDataAccessException {
private static final long serialVersionUID = 1029525121238025444L;
public CassandraUncategorizedException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,40 @@
/*
* 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.cassandra.support.exception;
import org.springframework.dao.QueryTimeoutException;
/**
* Spring data access exception for a Cassandra write timeout.
*
* @author Matthew T. Adams
*/
public class CassandraWriteTimeoutException extends QueryTimeoutException {
private static final long serialVersionUID = -4374826375213670718L;
private String writeType;
public CassandraWriteTimeoutException(String writeType, String msg, Throwable cause) {
super(msg, cause);
this.writeType = writeType;
}
public String getWriteType() {
return writeType;
}
}

View File

@@ -0,0 +1,71 @@
package org.springframework.cassandra.test.integration.core.cql.generator;
import java.io.IOException;
import java.util.UUID;
import org.apache.cassandra.exceptions.ConfigurationException;
import org.apache.thrift.transport.TTransportException;
import org.cassandraunit.utils.EmbeddedCassandraServerHelper;
import org.junit.Before;
import org.junit.BeforeClass;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.KeyspaceMetadata;
import com.datastax.driver.core.Session;
public abstract class AbstractEmbeddedCassandraIntegrationTest {
@BeforeClass
public static void beforeClass() throws ConfigurationException, TTransportException, IOException,
InterruptedException {
EmbeddedCassandraServerHelper.startEmbeddedCassandra("cassandra.yaml");
}
/**
* Whether to connect to Cassandra.
*/
protected boolean connect = true;
/**
* The {@link Cluster} that's connected to Cassandra.
*/
protected Cluster cluster;
/**
* If not <code>null</code>, get a {@link Session} for the from the {@link #cluster}.
*/
protected String keyspace = "ks" + UUID.randomUUID().toString().replace("-", "");
/**
* The {@link Session} for the {@link #keyspace} from the {@link #cluster}.
*/
protected Session session;
/**
* Returns whether we're currently connected to the cluster.
*/
public boolean connected() {
return session != null;
}
public Cluster cluster() {
return Cluster.builder().addContactPoint("localhost").withPort(9042).build();
}
@Before
public void before() {
if (connect && !connected()) {
cluster = cluster();
if (keyspace == null) {
session = cluster.connect();
} else {
KeyspaceMetadata kmd = cluster.getMetadata().getKeyspace(keyspace);
if (kmd == null) { // then create keyspace
session = cluster.connect();
session.execute("CREATE KEYSPACE " + keyspace
+ " WITH replication = {'class': 'SimpleStrategy', 'replication_factor' : 1};");
session.execute("USE " + keyspace + ";");
} // else keyspace already exists
}
}
}
}

View File

@@ -0,0 +1,144 @@
package org.springframework.cassandra.test.integration.core.cql.generator;
import static junit.framework.Assert.assertEquals;
import java.util.List;
import java.util.Map;
import org.springframework.cassandra.core.keyspace.ColumnSpecification;
import org.springframework.cassandra.core.keyspace.TableDescriptor;
import org.springframework.cassandra.core.keyspace.TableOption;
import org.springframework.cassandra.core.keyspace.TableOption.CachingOption;
import com.datastax.driver.core.ColumnMetadata;
import com.datastax.driver.core.Session;
import com.datastax.driver.core.TableMetadata;
import com.datastax.driver.core.TableMetadata.Options;
public class CqlTableSpecificationAssertions {
public static double DELTA = 1e-6; // delta for comparisons of doubles
public static void assertTable(TableDescriptor expected, String keyspace, Session session) {
TableMetadata tmd = session.getCluster().getMetadata().getKeyspace(keyspace.toLowerCase())
.getTable(expected.getName());
assertEquals(expected.getName().toLowerCase(), tmd.getName().toLowerCase());
assertPartitionKeyColumns(expected, tmd);
assertPrimaryKeyColumns(expected, tmd);
assertColumns(expected.getColumns(), tmd.getColumns());
assertOptions(expected.getOptions(), tmd.getOptions());
}
public static void assertPartitionKeyColumns(TableDescriptor expected, TableMetadata actual) {
assertColumns(expected.getPartitionKeyColumns(), actual.getPartitionKey());
}
public static void assertPrimaryKeyColumns(TableDescriptor expected, TableMetadata actual) {
assertColumns(expected.getKeyColumns(), actual.getPrimaryKey());
}
public static void assertOptions(Map<String, Object> expected, Options actual) {
for (String key : expected.keySet()) {
Object value = expected.get(key);
TableOption tableOption = getTableOptionFor(key);
if (tableOption == null && key.equalsIgnoreCase(TableOption.COMPACT_STORAGE.getName())) {
// TODO: figure out how to tell if COMPACT STORAGE was used
continue;
}
assertOption(tableOption, key, value, getOptionFor(tableOption, tableOption.getType(), actual));
}
}
@SuppressWarnings({ "unchecked", "incomplete-switch" })
public static void assertOption(TableOption tableOption, String key, Object expected, Object actual) {
if (tableOption == null) { // then this is a string-only or unknown value
key.equalsIgnoreCase(actual.toString()); // TODO: determine if this is the right test
}
switch (tableOption) {
case BLOOM_FILTER_FP_CHANCE:
case READ_REPAIR_CHANCE:
case DCLOCAL_READ_REPAIR_CHANCE:
assertEquals((Double) expected, (Double) actual, DELTA);
return;
case CACHING:
assertEquals(CachingOption.valueOf((String) expected).getValue(), actual);
return;
case COMPACTION:
assertCompaction((Map<String, Object>) expected, (Map<String, String>) actual);
return;
case COMPRESSION:
assertCompression((Map<String, Object>) expected, (Map<String, String>) actual);
return;
}
assertEquals(expected, actual);
}
public static void assertCompaction(Map<String, Object> expected, Map<String, String> actual) {
// TODO
}
public static void assertCompression(Map<String, Object> expected, Map<String, String> actual) {
// TODO
}
public static TableOption getTableOptionFor(String key) {
try {
return TableOption.valueOf(key);
} catch (IllegalArgumentException x) {
return null;
}
}
@SuppressWarnings("unchecked")
public static <T> T getOptionFor(TableOption option, Class<?> type, Options options) {
switch (option) {
case BLOOM_FILTER_FP_CHANCE:
return (T) (Double) options.getBloomFilterFalsePositiveChance();
case CACHING:
return (T) options.getCaching();
case COMMENT:
return (T) options.getComment();
case COMPACTION:
return (T) options.getCompaction();
case COMPACT_STORAGE:
throw new Error(); // TODO: figure out
case COMPRESSION:
return (T) options.getCompression();
case DCLOCAL_READ_REPAIR_CHANCE:
return (T) (Double) options.getReadRepairChance();
case GC_GRACE_SECONDS:
return (T) new Long(options.getGcGraceInSeconds());
case READ_REPAIR_CHANCE:
return (T) (Double) options.getReadRepairChance();
case REPLICATE_ON_WRITE:
return (T) (Boolean) options.getReplicateOnWrite();
}
return null;
}
public static void assertColumns(List<ColumnSpecification> expected, List<ColumnMetadata> actual) {
for (int i = 0; i < expected.size(); i++) {
ColumnSpecification expectedColumn = expected.get(i);
ColumnMetadata actualColumn = actual.get(i);
assertColumn(expectedColumn, actualColumn);
}
}
public static void assertColumn(ColumnSpecification expected, ColumnMetadata actual) {
assertEquals(expected.getName().toLowerCase(), actual.getName().toLowerCase());
assertEquals(expected.getType(), actual.getType());
}
}

View File

@@ -0,0 +1,55 @@
package org.springframework.cassandra.test.integration.core.cql.generator;
import static org.springframework.cassandra.test.integration.core.cql.generator.CqlTableSpecificationAssertions.assertTable;
import org.junit.Test;
import org.springframework.cassandra.test.unit.core.cql.generator.CreateTableCqlGeneratorTests.BasicTest;
import org.springframework.cassandra.test.unit.core.cql.generator.CreateTableCqlGeneratorTests.CompositePartitionKeyTest;
import org.springframework.cassandra.test.unit.core.cql.generator.CreateTableCqlGeneratorTests.CreateTableTest;
/**
* Integration tests that reuse unit tests.
*
* @author Matthew T. Adams
*/
public class CreateTableCqlGeneratorIntegrationTests {
/**
* Integration test base class that knows how to do everything except instantiate the concrete unit test type T.
*
* @author Matthew T. Adams
*
* @param <T> The concrete unit test class to which this integration test corresponds.
*/
public static abstract class Base<T extends CreateTableTest> extends AbstractEmbeddedCassandraIntegrationTest {
T unit;
public abstract T unit();
@Test
public void test() {
unit = unit();
unit.prepare();
session.execute(unit.cql);
assertTable(unit.specification, keyspace, session);
}
}
public static class BasicIntegrationTest extends Base<BasicTest> {
@Override
public BasicTest unit() {
return new BasicTest();
}
}
public static class CompositePartitionKeyIntegrationTest extends Base<CompositePartitionKeyTest> {
@Override
public CompositePartitionKeyTest unit() {
return new CompositePartitionKeyTest();
}
}
}

View File

@@ -0,0 +1,19 @@
package org.springframework.cassandra.test.unit.core.cql;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import static org.springframework.cassandra.core.cql.CqlStringUtils.isQuotedIdentifier;
import static org.springframework.cassandra.core.cql.CqlStringUtils.isUnquotedIdentifier;
import org.junit.Test;
public class CqlStringUtilsTest {
@Test
public void testIsQuotedIdentifier() throws Exception {
assertFalse(isQuotedIdentifier("my\"id"));
assertTrue(isQuotedIdentifier("my\"\"id"));
assertFalse(isUnquotedIdentifier("my\"id"));
assertTrue(isUnquotedIdentifier("myid"));
}
}

View File

@@ -0,0 +1,65 @@
package org.springframework.cassandra.test.unit.core.cql.generator;
import static junit.framework.Assert.assertTrue;
import static org.springframework.cassandra.core.keyspace.TableOperations.alterTable;
import org.junit.Test;
import org.springframework.cassandra.core.cql.generator.AlterTableCqlGenerator;
import org.springframework.cassandra.core.keyspace.AlterTableSpecification;
import com.datastax.driver.core.DataType;
public class AlterTableCqlGeneratorTests {
/**
* Asserts that the preamble is first & correctly formatted in the given CQL string.
*/
public static void assertPreamble(String tableName, String cql) {
assertTrue(cql.startsWith("ALTER TABLE " + tableName + " "));
}
/**
* Asserts that the given list of columns definitions are contained in the given CQL string properly.
*
* @param columnSpec IE, "foo text, bar blob"
*/
public static void assertColumnChanges(String columnSpec, String cql) {
assertTrue(cql.contains(""));
}
/**
* Convenient base class that other test classes can use so as not to repeat the generics declarations.
*/
public static abstract class AlterTableTest extends
TableOperationCqlGeneratorTest<AlterTableSpecification, AlterTableCqlGenerator> {
}
public static class BasicTest extends AlterTableTest {
public String name = "mytable";
public DataType alteredType = DataType.text();
public String altered = "altered";
public DataType addedType = DataType.text();
public String added = "added";
public String dropped = "dropped";
public AlterTableSpecification specification() {
return alterTable().name(name).alter(altered, alteredType).add(added, addedType).drop(dropped);
}
public AlterTableCqlGenerator generator() {
return new AlterTableCqlGenerator(specification);
}
@Test
public void test() {
prepare();
assertPreamble(name, cql);
assertColumnChanges(
String.format("ALTER %s TYPE %s, ADD %s %s, DROP %s", altered, alteredType, added, addedType, dropped), cql);
}
}
}

View File

@@ -0,0 +1,100 @@
package org.springframework.cassandra.test.unit.core.cql.generator;
import static junit.framework.Assert.assertTrue;
import static org.springframework.cassandra.core.keyspace.TableOperations.createTable;
import org.junit.Test;
import org.springframework.cassandra.core.cql.generator.CreateTableCqlGenerator;
import org.springframework.cassandra.core.keyspace.CreateTableSpecification;
import com.datastax.driver.core.DataType;
public class CreateTableCqlGeneratorTests {
/**
* Asserts that the preamble is first & correctly formatted in the given CQL string.
*/
public static void assertPreamble(String tableName, String cql) {
assertTrue(cql.startsWith("CREATE TABLE " + tableName + " "));
}
/**
* Asserts that the given primary key definition is contained in the given CQL string properly.
*
* @param primaryKeyString IE, "foo", "foo, bar, baz", "(foo, bar), baz", etc
*/
public static void assertPrimaryKey(String primaryKeyString, String cql) {
assertTrue(cql.contains(", PRIMARY KEY (" + primaryKeyString + "))"));
}
/**
* Asserts that the given list of columns definitions are contained in the given CQL string properly.
*
* @param columnSpec IE, "foo text, bar blob"
*/
public static void assertColumns(String columnSpec, String cql) {
assertTrue(cql.contains("(" + columnSpec + ","));
}
/**
* Convenient base class that other test classes can use so as not to repeat the generics declarations or
* {@link #generator()} method.
*/
public static abstract class CreateTableTest extends
TableOperationCqlGeneratorTest<CreateTableSpecification, CreateTableCqlGenerator> {
public CreateTableCqlGenerator generator() {
return new CreateTableCqlGenerator(specification);
}
}
public static class BasicTest extends CreateTableTest {
public String name = "mytable";
public DataType partitionKeyType0 = DataType.text();
public String partitionKey0 = "partitionKey0";
public DataType columnType1 = DataType.text();
public String column1 = "column1";
public CreateTableSpecification specification() {
return createTable().name(name).partitionKeyColumn(partitionKey0, partitionKeyType0).column(column1, columnType1);
}
@Test
public void test() {
prepare();
assertPreamble(name, cql);
assertColumns(String.format("%s %s, %s %s", partitionKey0, partitionKeyType0, column1, columnType1), cql);
assertPrimaryKey(partitionKey0, cql);
}
}
public static class CompositePartitionKeyTest extends CreateTableTest {
public String name = "composite_partition_key_table";
public DataType partKeyType0 = DataType.text();
public String partKey0 = "partKey0";
public DataType partKeyType1 = DataType.text();
public String partKey1 = "partKey1";
public String column0 = "column0";
public DataType columnType0 = DataType.text();
@Override
public CreateTableSpecification specification() {
return createTable().name(name).partitionKeyColumn(partKey0, partKeyType0)
.partitionKeyColumn(partKey1, partKeyType1).column(column0, columnType0);
}
@Test
public void test() {
prepare();
assertPreamble(name, cql);
assertColumns(
String.format("%s %s, %s %s, %s %s", partKey0, partKeyType0, partKey1, partKeyType1, column0, columnType0),
cql);
assertPrimaryKey(String.format("(%s, %s)", partKey0, partKey1), cql);
}
}
}

View File

@@ -0,0 +1,54 @@
package org.springframework.cassandra.test.unit.core.cql.generator;
import static junit.framework.Assert.assertTrue;
import static org.springframework.cassandra.core.keyspace.TableOperations.dropTable;
import org.junit.Test;
import org.springframework.cassandra.core.cql.generator.DropTableCqlGenerator;
import org.springframework.cassandra.core.keyspace.DropTableSpecification;
public class DropTableCqlGeneratorTests {
/**
* Asserts that the preamble is first & correctly formatted in the given CQL string.
*/
public static void assertStatement(String tableName, String cql) {
assertTrue(cql.equals("DROP TABLE " + tableName + ";"));
}
/**
* Asserts that the given list of columns definitions are contained in the given CQL string properly.
*
* @param columnSpec IE, "foo text, bar blob"
*/
public static void assertColumnChanges(String columnSpec, String cql) {
assertTrue(cql.contains(""));
}
/**
* Convenient base class that other test classes can use so as not to repeat the generics declarations.
*/
public static abstract class DropTableTest extends
TableOperationCqlGeneratorTest<DropTableSpecification, DropTableCqlGenerator> {
}
public static class BasicTest extends DropTableTest {
public String name = "mytable";
public DropTableSpecification specification() {
return dropTable().name(name);
}
public DropTableCqlGenerator generator() {
return new DropTableCqlGenerator(specification);
}
@Test
public void test() {
prepare();
assertStatement(name, cql);
}
}
}

View File

@@ -0,0 +1,36 @@
package org.springframework.cassandra.test.unit.core.cql.generator;
import org.springframework.cassandra.core.cql.generator.TableNameCqlGenerator;
import org.springframework.cassandra.core.keyspace.TableNameSpecification;
/**
* Useful test class that specifies just about as much as you can for a CQL generation test. Intended to be extended by
* classes that contain methods annotated with {@link Test}. Everything is public because this is a test class with no
* need for encapsulation, and it makes for easier reuse in other tests like integration tests (hint hint).
*
* @author Matthew T. Adams
*
* @param <S> The type of the {@link TableNameSpecification}
* @param <G> The type of the {@link TableNameCqlGenerator}
*/
public abstract class TableOperationCqlGeneratorTest<S extends TableNameSpecification<?>, G extends TableNameCqlGenerator<?>> {
public abstract S specification();
public abstract G generator();
public String tableName;
public S specification;
public G generator;
public String cql;
public void prepare() {
this.specification = specification();
this.generator = generator();
this.cql = generateCql();
}
public String generateCql() {
return generator.toCql();
}
}

View File

@@ -0,0 +1,104 @@
package org.springframework.cassandra.test.unit.core.keyspace;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertTrue;
import java.lang.annotation.RetentionPolicy;
import org.junit.Test;
import org.springframework.cassandra.core.keyspace.DefaultOption;
import org.springframework.cassandra.core.keyspace.Option;
public class OptionTest {
@Test(expected = IllegalArgumentException.class)
public void testOptionWithNullName() {
new DefaultOption(null, Object.class, true, true, true);
}
@Test(expected = IllegalArgumentException.class)
public void testOptionWithEmptyName() {
new DefaultOption("", Object.class, true, true, true);
}
@Test
public void testOptionWithNullType() {
new DefaultOption("opt", null, true, true, true);
new DefaultOption("opt", null, false, true, true);
}
@Test
public void testOptionWithNullTypeIsCoerceable() {
Option op = new DefaultOption("opt", null, true, true, true);
assertTrue(op.isCoerceable(""));
assertTrue(op.isCoerceable(null));
}
@Test
public void testOptionValueCoercion() {
String name = "my_option";
Class<?> type = String.class;
boolean requires = true;
boolean escapes = true;
boolean quotes = true;
Option op = new DefaultOption(name, type, requires, escapes, quotes);
assertTrue(op.isCoerceable("opt"));
assertEquals("'opt'", op.toString("opt"));
assertEquals("'opt''n'", op.toString("opt'n"));
type = Long.class;
escapes = false;
quotes = false;
op = new DefaultOption(name, type, requires, escapes, quotes);
String expected = "1";
for (Object value : new Object[] { 1, "1" }) {
assertTrue(op.isCoerceable(value));
assertEquals(expected, op.toString(value));
}
assertFalse(op.isCoerceable("x"));
assertTrue(op.isCoerceable(null));
type = Long.class;
escapes = false;
quotes = true;
op = new DefaultOption(name, type, requires, escapes, quotes);
expected = "'1'";
for (Object value : new Object[] { 1, "1" }) {
assertTrue(op.isCoerceable(value));
assertEquals(expected, op.toString(value));
}
assertFalse(op.isCoerceable("x"));
assertTrue(op.isCoerceable(null));
type = Double.class;
escapes = false;
quotes = false;
op = new DefaultOption(name, type, requires, escapes, quotes);
String[] expecteds = new String[] { "1", "1.0", "1.0", "1", "1.0", null };
Object[] values = new Object[] { 1, 1.0F, 1.0D, "1", "1.0", null };
for (int i = 0; i < values.length; i++) {
assertTrue(op.isCoerceable(values[i]));
assertEquals(expecteds[i], op.toString(values[i]));
}
assertFalse(op.isCoerceable("x"));
assertTrue(op.isCoerceable(null));
type = RetentionPolicy.class;
escapes = false;
quotes = false;
op = new DefaultOption(name, type, requires, escapes, quotes);
assertTrue(op.isCoerceable(null));
assertTrue(op.isCoerceable(RetentionPolicy.CLASS));
assertTrue(op.isCoerceable("CLASS"));
assertFalse(op.isCoerceable("x"));
assertEquals("CLASS", op.toString("CLASS"));
assertEquals("CLASS", op.toString(RetentionPolicy.CLASS));
}
}

View File

@@ -0,0 +1,71 @@
package org.springframework.cassandra.test.unit.support;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.springframework.cassandra.support.CassandraExceptionTranslator;
import org.springframework.cassandra.support.exception.CassandraInvalidConfigurationInQueryException;
import org.springframework.cassandra.support.exception.CassandraInvalidQueryException;
import org.springframework.cassandra.support.exception.CassandraKeyspaceExistsException;
import org.springframework.cassandra.support.exception.CassandraSchemaElementExistsException;
import org.springframework.cassandra.support.exception.CassandraTableExistsException;
import org.springframework.dao.DataAccessException;
import com.datastax.driver.core.exceptions.AlreadyExistsException;
import com.datastax.driver.core.exceptions.InvalidConfigurationInQueryException;
import com.datastax.driver.core.exceptions.InvalidQueryException;
public class CassandraExceptionTranslatorTest {
CassandraExceptionTranslator tx = new CassandraExceptionTranslator();
@Test
public void testTableExistsException() {
String keyspace = "";
String table = "tbl";
AlreadyExistsException cx = new AlreadyExistsException(keyspace, table);
DataAccessException dax = tx.translateExceptionIfPossible(cx);
assertNotNull(dax);
assertTrue(dax instanceof CassandraTableExistsException);
CassandraTableExistsException x = (CassandraTableExistsException) dax;
assertEquals(table, x.getTableName());
assertEquals(x.getTableName(), x.getElementName());
assertEquals(CassandraSchemaElementExistsException.ElementType.TABLE, x.getElementType());
assertEquals(cx, x.getCause());
}
@Test
public void testKeyspaceExistsException() {
String keyspace = "ks";
String table = "";
AlreadyExistsException cx = new AlreadyExistsException(keyspace, table);
DataAccessException dax = tx.translateExceptionIfPossible(cx);
assertNotNull(dax);
assertTrue(dax instanceof CassandraKeyspaceExistsException);
CassandraKeyspaceExistsException x = (CassandraKeyspaceExistsException) dax;
assertEquals(keyspace, x.getKeyspaceName());
assertEquals(x.getKeyspaceName(), x.getElementName());
assertEquals(CassandraSchemaElementExistsException.ElementType.KEYSPACE, x.getElementType());
assertEquals(cx, x.getCause());
}
@Test
public void testInvalidConfigurationInQueryException() {
String msg = "msg";
InvalidQueryException cx = new InvalidConfigurationInQueryException(msg);
DataAccessException dax = tx.translateExceptionIfPossible(cx);
assertNotNull(dax);
assertTrue(dax instanceof CassandraInvalidConfigurationInQueryException);
assertEquals(cx, dax.getCause());
cx = new InvalidQueryException(msg);
dax = tx.translateExceptionIfPossible(cx);
assertNotNull(dax);
assertTrue(dax instanceof CassandraInvalidQueryException);
assertEquals(cx, dax.getCause());
}
}

View File

@@ -0,0 +1,3 @@
name: test
replicationFactor: 1
strategy: org.apache.cassandra.locator.SimpleStrategy

View File

@@ -0,0 +1,690 @@
# Cassandra storage config YAML
# NOTE:
# See http://wiki.apache.org/cassandra/StorageConfiguration for
# full explanations of configuration directives
# /NOTE
# The name of the cluster. This is mainly used to prevent machines in
# one logical cluster from joining another.
cluster_name: 'Test Cluster'
# This defines the number of tokens randomly assigned to this node on the ring
# The more tokens, relative to other nodes, the larger the proportion of data
# that this node will store. You probably want all nodes to have the same number
# of tokens assuming they have equal hardware capability.
#
# If you leave this unspecified, Cassandra will use the default of 1 token for legacy compatibility,
# and will use the initial_token as described below.
#
# Specifying initial_token will override this setting.
#
# If you already have a cluster with 1 token per node, and wish to migrate to
# multiple tokens per node, see http://wiki.apache.org/cassandra/Operations
# num_tokens: 256
# If you haven't specified num_tokens, or have set it to the default of 1 then
# you should always specify InitialToken when setting up a production
# cluster for the first time, and often when adding capacity later.
# The principle is that each node should be given an equal slice of
# the token ring; see http://wiki.apache.org/cassandra/Operations
# for more details.
#
# If blank, Cassandra will request a token bisecting the range of
# the heaviest-loaded existing node. If there is no load information
# available, such as is the case with a new cluster, it will pick
# a random token, which will lead to hot spots.
initial_token:
# See http://wiki.apache.org/cassandra/HintedHandoff
hinted_handoff_enabled: true
# this defines the maximum amount of time a dead host will have hints
# generated. After it has been dead this long, new hints for it will not be
# created until it has been seen alive and gone down again.
max_hint_window_in_ms: 10800000 # 3 hours
# throttle in KBs per second, per delivery thread
hinted_handoff_throttle_in_kb: 1024
# Number of threads with which to deliver hints;
# Consider increasing this number when you have multi-dc deployments, since
# cross-dc handoff tends to be slower
max_hints_delivery_threads: 2
# The following setting populates the page cache on memtable flush and compaction
# WARNING: Enable this setting only when the whole node's data fits in memory.
# Defaults to: false
# populate_io_cache_on_flush: false
# Authentication backend, implementing IAuthenticator; used to identify users
# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthenticator,
# PasswordAuthenticator}.
#
# - AllowAllAuthenticator performs no checks - set it to disable authentication.
# - PasswordAuthenticator relies on username/password pairs to authenticate
# users. It keeps usernames and hashed passwords in system_auth.credentials table.
# Please increase system_auth keyspace replication factor if you use this authenticator.
authenticator: org.apache.cassandra.auth.AllowAllAuthenticator
# Authorization backend, implementing IAuthorizer; used to limit access/provide permissions
# Out of the box, Cassandra provides org.apache.cassandra.auth.{AllowAllAuthorizer,
# CassandraAuthorizer}.
#
# - AllowAllAuthorizer allows any action to any user - set it to disable authorization.
# - CassandraAuthorizer stores permissions in system_auth.permissions table. Please
# increase system_auth keyspace replication factor if you use this authorizer.
authorizer: org.apache.cassandra.auth.AllowAllAuthorizer
# Validity period for permissions cache (fetching permissions can be an
# expensive operation depending on the authorizer, CassandraAuthorizer is
# one example). Defaults to 2000, set to 0 to disable.
# Will be disabled automatically for AllowAllAuthorizer.
# permissions_validity_in_ms: 2000
# The partitioner is responsible for distributing rows (by key) across
# nodes in the cluster. Any IPartitioner may be used, including your
# own as long as it is on the classpath. Out of the box, Cassandra
# provides org.apache.cassandra.dht.{Murmur3Partitioner, RandomPartitioner
# ByteOrderedPartitioner, OrderPreservingPartitioner (deprecated)}.
#
# - RandomPartitioner distributes rows across the cluster evenly by md5.
# This is the default prior to 1.2 and is retained for compatibility.
# - Murmur3Partitioner is similar to RandomPartioner but uses Murmur3_128
# Hash Function instead of md5. When in doubt, this is the best option.
# - ByteOrderedPartitioner orders rows lexically by key bytes. BOP allows
# scanning rows in key order, but the ordering can generate hot spots
# for sequential insertion workloads.
# - OrderPreservingPartitioner is an obsolete form of BOP, that stores
# - keys in a less-efficient format and only works with keys that are
# UTF8-encoded Strings.
# - CollatingOPP collates according to EN,US rules rather than lexical byte
# ordering. Use this as an example if you need custom collation.
#
# See http://wiki.apache.org/cassandra/Operations for more on
# partitioners and token selection.
partitioner: org.apache.cassandra.dht.Murmur3Partitioner
# Directories where Cassandra should store data on disk. Cassandra
# will spread data evenly across them, subject to the granularity of
# the configured compaction strategy.
data_file_directories:
- target/embeddedCassandra/data
# commit log
commitlog_directory: target/embeddedCassandra/commitlog
# policy for data disk failures:
# stop: shut down gossip and Thrift, leaving the node effectively dead, but
# can still be inspected via JMX.
# best_effort: stop using the failed disk and respond to requests based on
# remaining available sstables. This means you WILL see obsolete
# data at CL.ONE!
# ignore: ignore fatal errors and let requests fail, as in pre-1.2 Cassandra
disk_failure_policy: stop
# Maximum size of the key cache in memory.
#
# Each key cache hit saves 1 seek and each row cache hit saves 2 seeks at the
# minimum, sometimes more. The key cache is fairly tiny for the amount of
# time it saves, so it's worthwhile to use it at large numbers.
# The row cache saves even more time, but must contain the entire row,
# so it is extremely space-intensive. It's best to only use the
# row cache if you have hot rows or static rows.
#
# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
#
# Default value is empty to make it "auto" (min(5% of Heap (in MB), 100MB)). Set to 0 to disable key cache.
key_cache_size_in_mb:
# Duration in seconds after which Cassandra should
# save the key cache. Caches are saved to saved_caches_directory as
# specified in this configuration file.
#
# Saved caches greatly improve cold-start speeds, and is relatively cheap in
# terms of I/O for the key cache. Row cache saving is much more expensive and
# has limited use.
#
# Default is 14400 or 4 hours.
key_cache_save_period: 14400
# Number of keys from the key cache to save
# Disabled by default, meaning all keys are going to be saved
# key_cache_keys_to_save: 100
# Maximum size of the row cache in memory.
# NOTE: if you reduce the size, you may not get you hottest keys loaded on startup.
#
# Default value is 0, to disable row caching.
row_cache_size_in_mb: 0
# Duration in seconds after which Cassandra should
# safe the row cache. Caches are saved to saved_caches_directory as specified
# in this configuration file.
#
# Saved caches greatly improve cold-start speeds, and is relatively cheap in
# terms of I/O for the key cache. Row cache saving is much more expensive and
# has limited use.
#
# Default is 0 to disable saving the row cache.
row_cache_save_period: 0
# Number of keys from the row cache to save
# Disabled by default, meaning all keys are going to be saved
# row_cache_keys_to_save: 100
# The provider for the row cache to use.
#
# Supported values are: ConcurrentLinkedHashCacheProvider, SerializingCacheProvider
#
# SerializingCacheProvider serialises the contents of the row and stores
# it in native memory, i.e., off the JVM Heap. Serialized rows take
# significantly less memory than "live" rows in the JVM, so you can cache
# more rows in a given memory footprint. And storing the cache off-heap
# means you can use smaller heap sizes, reducing the impact of GC pauses.
# Note however that when a row is requested from the row cache, it must be
# deserialized into the heap for use.
#
# It is also valid to specify the fully-qualified class name to a class
# that implements org.apache.cassandra.cache.IRowCacheProvider.
#
# Defaults to SerializingCacheProvider
row_cache_provider: SerializingCacheProvider
# saved caches
saved_caches_directory: target/embeddedCassandra/saved_caches
# commitlog_sync may be either "periodic" or "batch."
# When in batch mode, Cassandra won't ack writes until the commit log
# has been fsynced to disk. It will wait up to
# commitlog_sync_batch_window_in_ms milliseconds for other writes, before
# performing the sync.
#
# commitlog_sync: batch
# commitlog_sync_batch_window_in_ms: 50
#
# the other option is "periodic" where writes may be acked immediately
# and the CommitLog is simply synced every commitlog_sync_period_in_ms
# milliseconds.
commitlog_sync: periodic
commitlog_sync_period_in_ms: 10000
# The size of the individual commitlog file segments. A commitlog
# segment may be archived, deleted, or recycled once all the data
# in it (potentially from each columnfamily in the system) has been
# flushed to sstables.
#
# The default size is 32, which is almost always fine, but if you are
# archiving commitlog segments (see commitlog_archiving.properties),
# then you probably want a finer granularity of archiving; 8 or 16 MB
# is reasonable.
commitlog_segment_size_in_mb: 32
# any class that implements the SeedProvider interface and has a
# constructor that takes a Map<String, String> of parameters will do.
seed_provider:
# Addresses of hosts that are deemed contact points.
# Cassandra nodes use this list of hosts to find each other and learn
# the topology of the ring. You must change this if you are running
# multiple nodes!
- class_name: org.apache.cassandra.locator.SimpleSeedProvider
parameters:
# seeds is actually a comma-delimited list of addresses.
# Ex: "<ip1>,<ip2>,<ip3>"
- seeds: "127.0.0.1"
# emergency pressure valve: each time heap usage after a full (CMS)
# garbage collection is above this fraction of the max, Cassandra will
# flush the largest memtables.
#
# Set to 1.0 to disable. Setting this lower than
# CMSInitiatingOccupancyFraction is not likely to be useful.
#
# RELYING ON THIS AS YOUR PRIMARY TUNING MECHANISM WILL WORK POORLY:
# it is most effective under light to moderate load, or read-heavy
# workloads; under truly massive write load, it will often be too
# little, too late.
flush_largest_memtables_at: 0.75
# emergency pressure valve #2: the first time heap usage after a full
# (CMS) garbage collection is above this fraction of the max,
# Cassandra will reduce cache maximum _capacity_ to the given fraction
# of the current _size_. Should usually be set substantially above
# flush_largest_memtables_at, since that will have less long-term
# impact on the system.
#
# Set to 1.0 to disable. Setting this lower than
# CMSInitiatingOccupancyFraction is not likely to be useful.
reduce_cache_sizes_at: 0.85
reduce_cache_capacity_to: 0.6
# For workloads with more data than can fit in memory, Cassandra's
# bottleneck will be reads that need to fetch data from
# disk. "concurrent_reads" should be set to (16 * number_of_drives) in
# order to allow the operations to enqueue low enough in the stack
# that the OS and drives can reorder them.
#
# On the other hand, since writes are almost never IO bound, the ideal
# number of "concurrent_writes" is dependent on the number of cores in
# your system; (8 * number_of_cores) is a good rule of thumb.
concurrent_reads: 32
concurrent_writes: 32
# Total memory to use for memtables. Cassandra will flush the largest
# memtable when this much memory is used.
# If omitted, Cassandra will set it to 1/3 of the heap.
# memtable_total_space_in_mb: 2048
# Total space to use for commitlogs. Since commitlog segments are
# mmapped, and hence use up address space, the default size is 32
# on 32-bit JVMs, and 1024 on 64-bit JVMs.
#
# If space gets above this value (it will round up to the next nearest
# segment multiple), Cassandra will flush every dirty CF in the oldest
# segment and remove it. So a small total commitlog space will tend
# to cause more flush activity on less-active columnfamilies.
# commitlog_total_space_in_mb: 4096
# This sets the amount of memtable flush writer threads. These will
# be blocked by disk io, and each one will hold a memtable in memory
# while blocked. If you have a large heap and many data directories,
# you can increase this value for better flush performance.
# By default this will be set to the amount of data directories defined.
#memtable_flush_writers: 1
# the number of full memtables to allow pending flush, that is,
# waiting for a writer thread. At a minimum, this should be set to
# the maximum number of secondary indexes created on a single CF.
memtable_flush_queue_size: 4
# Whether to, when doing sequential writing, fsync() at intervals in
# order to force the operating system to flush the dirty
# buffers. Enable this to avoid sudden dirty buffer flushing from
# impacting read latencies. Almost always a good idea on SSDs; not
# necessarily on platters.
trickle_fsync: false
trickle_fsync_interval_in_kb: 10240
# TCP port, for commands and data
storage_port: 7000
# SSL port, for encrypted communication. Unused unless enabled in
# encryption_options
ssl_storage_port: 7001
# Address to bind to and tell other Cassandra nodes to connect to. You
# _must_ change this if you want multiple nodes to be able to
# communicate!
#
# Leaving it blank leaves it up to InetAddress.getLocalHost(). This
# will always do the Right Thing _if_ the node is properly configured
# (hostname, name resolution, etc), and the Right Thing is to use the
# address associated with the hostname (it might not be).
#
# Setting this to 0.0.0.0 is always wrong.
listen_address: localhost
# Address to broadcast to other Cassandra nodes
# Leaving this blank will set it to the same value as listen_address
# broadcast_address: 1.2.3.4
# Internode authentication backend, implementing IInternodeAuthenticator;
# used to allow/disallow connections from peer nodes.
# internode_authenticator: org.apache.cassandra.auth.AllowAllInternodeAuthenticator
# Whether to start the native transport server.
# Please note that the address on which the native transport is bound is the
# same as the rpc_address. The port however is different and specified below.
start_native_transport: true
# port for the CQL native transport to listen for clients on
native_transport_port: 9042
# The minimum and maximum threads for handling requests when the native
# transport is used. They are similar to rpc_min_threads and rpc_max_threads,
# though the defaults differ slightly.
# native_transport_min_threads: 16
# native_transport_max_threads: 128
# Whether to start the thrift rpc server.
start_rpc: true
# The address to bind the Thrift RPC service to -- clients connect
# here. Unlike ListenAddress above, you _can_ specify 0.0.0.0 here if
# you want Thrift to listen on all interfaces.
#
# Leaving this blank has the same effect it does for ListenAddress,
# (i.e. it will be based on the configured hostname of the node).
rpc_address: localhost
# port for Thrift to listen for clients on
rpc_port: 9160
# enable or disable keepalive on rpc connections
rpc_keepalive: true
# Cassandra provides three out-of-the-box options for the RPC Server:
#
# sync -> One thread per thrift connection. For a very large number of clients, memory
# will be your limiting factor. On a 64 bit JVM, 180KB is the minimum stack size
# per thread, and that will correspond to your use of virtual memory (but physical memory
# may be limited depending on use of stack space).
#
# hsha -> Stands for "half synchronous, half asynchronous." All thrift clients are handled
# asynchronously using a small number of threads that does not vary with the amount
# of thrift clients (and thus scales well to many clients). The rpc requests are still
# synchronous (one thread per active request).
#
# The default is sync because on Windows hsha is about 30% slower. On Linux,
# sync/hsha performance is about the same, with hsha of course using less memory.
#
# Alternatively, can provide your own RPC server by providing the fully-qualified class name
# of an o.a.c.t.TServerFactory that can create an instance of it.
rpc_server_type: sync
# Uncomment rpc_min|max_thread to set request pool size limits.
#
# Regardless of your choice of RPC server (see above), the number of maximum requests in the
# RPC thread pool dictates how many concurrent requests are possible (but if you are using the sync
# RPC server, it also dictates the number of clients that can be connected at all).
#
# The default is unlimited and thus provides no protection against clients overwhelming the server. You are
# encouraged to set a maximum that makes sense for you in production, but do keep in mind that
# rpc_max_threads represents the maximum number of client requests this server may execute concurrently.
#
# rpc_min_threads: 16
# rpc_max_threads: 2048
# uncomment to set socket buffer sizes on rpc connections
# rpc_send_buff_size_in_bytes:
# rpc_recv_buff_size_in_bytes:
# Uncomment to set socket buffer size for internode communication
# Note that when setting this, the buffer size is limited by net.core.wmem_max
# and when not setting it it is defined by net.ipv4.tcp_wmem
# See:
# /proc/sys/net/core/wmem_max
# /proc/sys/net/core/rmem_max
# /proc/sys/net/ipv4/tcp_wmem
# /proc/sys/net/ipv4/tcp_wmem
# and: man tcp
# internode_send_buff_size_in_bytes:
# internode_recv_buff_size_in_bytes:
# Frame size for thrift (maximum field length).
thrift_framed_transport_size_in_mb: 15
# The max length of a thrift message, including all fields and
# internal thrift overhead.
thrift_max_message_length_in_mb: 16
# Set to true to have Cassandra create a hard link to each sstable
# flushed or streamed locally in a backups/ subdirectory of the
# keyspace data. Removing these links is the operator's
# responsibility.
incremental_backups: false
# Whether or not to take a snapshot before each compaction. Be
# careful using this option, since Cassandra won't clean up the
# snapshots for you. Mostly useful if you're paranoid when there
# is a data format change.
snapshot_before_compaction: false
# Whether or not a snapshot is taken of the data before keyspace truncation
# or dropping of column families. The STRONGLY advised default of true
# should be used to provide data safety. If you set this flag to false, you will
# lose data on truncation or drop.
auto_snapshot: true
# Add column indexes to a row after its contents reach this size.
# Increase if your column values are large, or if you have a very large
# number of columns. The competing causes are, Cassandra has to
# deserialize this much of the row to read a single column, so you want
# it to be small - at least if you do many partial-row reads - but all
# the index data is read for each access, so you don't want to generate
# that wastefully either.
column_index_size_in_kb: 64
# Size limit for rows being compacted in memory. Larger rows will spill
# over to disk and use a slower two-pass compaction process. A message
# will be logged specifying the row key.
in_memory_compaction_limit_in_mb: 64
# Number of simultaneous compactions to allow, NOT including
# validation "compactions" for anti-entropy repair. Simultaneous
# compactions can help preserve read performance in a mixed read/write
# workload, by mitigating the tendency of small sstables to accumulate
# during a single long running compactions. The default is usually
# fine and if you experience problems with compaction running too
# slowly or too fast, you should look at
# compaction_throughput_mb_per_sec first.
#
# concurrent_compactors defaults to the number of cores.
# Uncomment to make compaction mono-threaded, the pre-0.8 default.
#concurrent_compactors: 1
# Multi-threaded compaction. When enabled, each compaction will use
# up to one thread per core, plus one thread per sstable being merged.
# This is usually only useful for SSD-based hardware: otherwise,
# your concern is usually to get compaction to do LESS i/o (see:
# compaction_throughput_mb_per_sec), not more.
multithreaded_compaction: false
# Throttles compaction to the given total throughput across the entire
# system. The faster you insert data, the faster you need to compact in
# order to keep the sstable count down, but in general, setting this to
# 16 to 32 times the rate you are inserting data is more than sufficient.
# Setting this to 0 disables throttling. Note that this account for all types
# of compaction, including validation compaction.
compaction_throughput_mb_per_sec: 16
# Track cached row keys during compaction, and re-cache their new
# positions in the compacted sstable. Disable if you use really large
# key caches.
compaction_preheat_key_cache: true
# Throttles all outbound streaming file transfers on this node to the
# given total throughput in Mbps. This is necessary because Cassandra does
# mostly sequential IO when streaming data during bootstrap or repair, which
# can lead to saturating the network connection and degrading rpc performance.
# When unset, the default is 200 Mbps or 25 MB/s.
# stream_throughput_outbound_megabits_per_sec: 200
# How long the coordinator should wait for read operations to complete
read_request_timeout_in_ms: 10000
# How long the coordinator should wait for seq or index scans to complete
range_request_timeout_in_ms: 10000
# How long the coordinator should wait for writes to complete
write_request_timeout_in_ms: 10000
# How long the coordinator should wait for truncates to complete
# (This can be much longer, because unless auto_snapshot is disabled
# we need to flush first so we can snapshot before removing the data.)
truncate_request_timeout_in_ms: 60000
# The default timeout for other, miscellaneous operations
request_timeout_in_ms: 10000
# Enable operation timeout information exchange between nodes to accurately
# measure request timeouts, If disabled cassandra will assuming the request
# was forwarded to the replica instantly by the coordinator
#
# Warning: before enabling this property make sure to ntp is installed
# and the times are synchronized between the nodes.
cross_node_timeout: false
# Enable socket timeout for streaming operation.
# When a timeout occurs during streaming, streaming is retried from the start
# of the current file. This _can_ involve re-streaming an important amount of
# data, so you should avoid setting the value too low.
# Default value is 0, which never timeout streams.
# streaming_socket_timeout_in_ms: 0
# phi value that must be reached for a host to be marked down.
# most users should never need to adjust this.
# phi_convict_threshold: 8
# endpoint_snitch -- Set this to a class that implements
# IEndpointSnitch. The snitch has two functions:
# - it teaches Cassandra enough about your network topology to route
# requests efficiently
# - it allows Cassandra to spread replicas around your cluster to avoid
# correlated failures. It does this by grouping machines into
# "datacenters" and "racks." Cassandra will do its best not to have
# more than one replica on the same "rack" (which may not actually
# be a physical location)
#
# IF YOU CHANGE THE SNITCH AFTER DATA IS INSERTED INTO THE CLUSTER,
# YOU MUST RUN A FULL REPAIR, SINCE THE SNITCH AFFECTS WHERE REPLICAS
# ARE PLACED.
#
# Out of the box, Cassandra provides
# - SimpleSnitch:
# Treats Strategy order as proximity. This improves cache locality
# when disabling read repair, which can further improve throughput.
# Only appropriate for single-datacenter deployments.
# - PropertyFileSnitch:
# Proximity is determined by rack and data center, which are
# explicitly configured in cassandra-topology.properties.
# - GossipingPropertyFileSnitch
# The rack and datacenter for the local node are defined in
# cassandra-rackdc.properties and propagated to other nodes via gossip. If
# cassandra-topology.properties exists, it is used as a fallback, allowing
# migration from the PropertyFileSnitch.
# - RackInferringSnitch:
# Proximity is determined by rack and data center, which are
# assumed to correspond to the 3rd and 2nd octet of each node's
# IP address, respectively. Unless this happens to match your
# deployment conventions (as it did Facebook's), this is best used
# as an example of writing a custom Snitch class.
# - Ec2Snitch:
# Appropriate for EC2 deployments in a single Region. Loads Region
# and Availability Zone information from the EC2 API. The Region is
# treated as the datacenter, and the Availability Zone as the rack.
# Only private IPs are used, so this will not work across multiple
# Regions.
# - Ec2MultiRegionSnitch:
# Uses public IPs as broadcast_address to allow cross-region
# connectivity. (Thus, you should set seed addresses to the public
# IP as well.) You will need to open the storage_port or
# ssl_storage_port on the public IP firewall. (For intra-Region
# traffic, Cassandra will switch to the private IP after
# establishing a connection.)
#
# You can use a custom Snitch by setting this to the full class name
# of the snitch, which will be assumed to be on your classpath.
endpoint_snitch: SimpleSnitch
# controls how often to perform the more expensive part of host score
# calculation
dynamic_snitch_update_interval_in_ms: 100
# controls how often to reset all host scores, allowing a bad host to
# possibly recover
dynamic_snitch_reset_interval_in_ms: 600000
# if set greater than zero and read_repair_chance is < 1.0, this will allow
# 'pinning' of replicas to hosts in order to increase cache capacity.
# The badness threshold will control how much worse the pinned host has to be
# before the dynamic snitch will prefer other replicas over it. This is
# expressed as a double which represents a percentage. Thus, a value of
# 0.2 means Cassandra would continue to prefer the static snitch values
# until the pinned host was 20% worse than the fastest.
dynamic_snitch_badness_threshold: 0.1
# request_scheduler -- Set this to a class that implements
# RequestScheduler, which will schedule incoming client requests
# according to the specific policy. This is useful for multi-tenancy
# with a single Cassandra cluster.
# NOTE: This is specifically for requests from the client and does
# not affect inter node communication.
# org.apache.cassandra.scheduler.NoScheduler - No scheduling takes place
# org.apache.cassandra.scheduler.RoundRobinScheduler - Round robin of
# client requests to a node with a separate queue for each
# request_scheduler_id. The scheduler is further customized by
# request_scheduler_options as described below.
request_scheduler: org.apache.cassandra.scheduler.NoScheduler
# Scheduler Options vary based on the type of scheduler
# NoScheduler - Has no options
# RoundRobin
# - throttle_limit -- The throttle_limit is the number of in-flight
# requests per client. Requests beyond
# that limit are queued up until
# running requests can complete.
# The value of 80 here is twice the number of
# concurrent_reads + concurrent_writes.
# - default_weight -- default_weight is optional and allows for
# overriding the default which is 1.
# - weights -- Weights are optional and will default to 1 or the
# overridden default_weight. The weight translates into how
# many requests are handled during each turn of the
# RoundRobin, based on the scheduler id.
#
# request_scheduler_options:
# throttle_limit: 80
# default_weight: 5
# weights:
# Keyspace1: 1
# Keyspace2: 5
# request_scheduler_id -- An identifier based on which to perform
# the request scheduling. Currently the only valid option is keyspace.
# request_scheduler_id: keyspace
# index_interval controls the sampling of entries from the primrary
# row index in terms of space versus time. The larger the interval,
# the smaller and less effective the sampling will be. In technicial
# terms, the interval coresponds to the number of index entries that
# are skipped between taking each sample. All the sampled entries
# must fit in memory. Generally, a value between 128 and 512 here
# coupled with a large key cache size on CFs results in the best trade
# offs. This value is not often changed, however if you have many
# very small rows (many to an OS page), then increasing this will
# often lower memory usage without a impact on performance.
index_interval: 128
# Enable or disable inter-node encryption
# Default settings are TLS v1, RSA 1024-bit keys (it is imperative that
# users generate their own keys) TLS_RSA_WITH_AES_128_CBC_SHA as the cipher
# suite for authentication, key exchange and encryption of the actual data transfers.
# NOTE: No custom encryption options are enabled at the moment
# The available internode options are : all, none, dc, rack
#
# If set to dc cassandra will encrypt the traffic between the DCs
# If set to rack cassandra will encrypt the traffic between the racks
#
# The passwords used in these options must match the passwords used when generating
# the keystore and truststore. For instructions on generating these files, see:
# http://download.oracle.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html#CreateKeystore
#
server_encryption_options:
internode_encryption: none
keystore: conf/.keystore
keystore_password: cassandra
truststore: conf/.truststore
truststore_password: cassandra
# More advanced defaults below:
# protocol: TLS
# algorithm: SunX509
# store_type: JKS
# cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA]
# require_client_auth: false
# enable or disable client/server encryption.
client_encryption_options:
enabled: false
keystore: conf/.keystore
keystore_password: cassandra
# require_client_auth: false
# Set trustore and truststore_password if require_client_auth is true
# truststore: conf/.truststore
# truststore_password: cassandra
# More advanced defaults below:
# protocol: TLS
# algorithm: SunX509
# store_type: JKS
# cipher_suites: [TLS_RSA_WITH_AES_128_CBC_SHA,TLS_RSA_WITH_AES_256_CBC_SHA]
# internode_compression controls whether traffic between nodes is
# compressed.
# can be: all - all traffic is compressed
# dc - traffic between different datacenters is compressed
# none - nothing is compressed.
internode_compression: all
# Enable or disable tcp_nodelay for inter-dc communication.
# Disabling it will result in larger (but fewer) network packets being sent,
# reducing overhead from the TCP protocol itself, at the cost of increasing
# latency if you block for cross-datacenter responses.
# inter_dc_tcp_nodelay: true

View File

@@ -0,0 +1,3 @@
create table book (isbn text, title text, author text, pages int, PRIMARY KEY (isbn));
create table book_alt (isbn text, title text, author text, pages int, PRIMARY KEY (isbn));
insert into book (isbn, title, author, pages) values ('999999999', 'Book of Nines', 'Nine Nine', 999);

View File

@@ -0,0 +1,3 @@
create table book (isbn text, title text, author text, pages int, PRIMARY KEY (isbn));
create table book_alt (isbn text, title text, author text, pages int, PRIMARY KEY (isbn));
/*insert into book (isbn, title, author, pages) values ('999999999', 'Book of Nines', 'Nine Nine', 999);*/

View File

@@ -0,0 +1,6 @@
log4j.rootLogger=WARN, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d [%t] %-5p %c - %m%n
log4j.logger.org.springframework.data.cassandra=INFO

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cassandra="http://www.springframework.org/schema/data/cassandra"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/data/cassandra http://www.springframework.org/schema/data/cassandra/spring-cassandra-1.0.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:property-placeholder
location="classpath:/org/springframework/data/cassandra/test/integration/config/cassandra.properties" />
<cassandra:cluster id="cassandra-cluster"
contactPoints="${cassandra.contactPoints}" port="${cassandra.port}"
compression="SNAPPY">
<cassandra:local-pooling-options
min-simultaneous-requests="25" max-simultaneous-requests="100"
core-connections="2" max-connections="8" />
<cassandra:remote-pooling-options
min-simultaneous-requests="25" max-simultaneous-requests="100"
core-connections="1" max-connections="2" />
<cassandra:socket-options
connect-timeout-mls="5000" keep-alive="true" reuse-address="true"
so-linger="60" tcp-no-delay="true" receive-buffer-size="65536"
send-buffer-size="65536" />
</cassandra:cluster>
<bean id="cassandra-mapping"
class=" org.springframework.data.cassandra.mapping.CassandraMappingContext" />
<bean id="cassandra-converter"
class=" org.springframework.data.cassandra.convert.MappingCassandraConverter">
<constructor-arg ref="cassandra-mapping" />
</bean>
<cassandra:keyspace id="cassandra-keyspace" name="${cassandra.keyspace}"
cassandra-cluster-ref="cassandra-cluster" cassandra-converter-ref="cassandra-converter">
<cassandra:keyspace-attributes auto="update"
replication-stategy="SimpleStrategy" replication-factor="1"
durable-writes="true">
<cassandra:table entity="org.springframework.data.cassandra.test.integration.table.Comment" />
<cassandra:table
entity="org.springframework.data.cassandra.test.integration.table.Notification" />
<cassandra:table entity="org.springframework.data.cassandra.test.integration.table.Post" />
<cassandra:table entity="org.springframework.data.cassandra.test.integration.table.Timeline" />
<cassandra:table entity="org.springframework.data.cassandra.test.integration.table.User" />
</cassandra:keyspace-attributes>
</cassandra:keyspace>
<cassandra:session id="cassandra-session" cassandra-keyspace-ref="cassandra-keyspace"/>
<bean id="cassandraTemplate" class="org.springframework.cassandra.core.CassandraTemplate">
<constructor-arg ref="cassandra-session" />
</bean>
</beans>

View File

@@ -0,0 +1,7 @@
cassandra.contactPoints=localhost
cassandra.port=9042
cassandra.keyspace=TestKS123

View File

@@ -0,0 +1,29 @@
Bundle-SymbolicName: org.springframework.cassandra
Bundle-Name: Spring Cassandra
Bundle-ManifestVersion: 2
Import-Package:
sun.reflect;version="0";resolution:=optional
Import-Template:
org.springframework.beans.*;version="[3.1.0, 4.0.0)",
org.springframework.cache.*;version="[3.1.0, 4.0.0)",
org.springframework.context.*;version="[3.1.0, 4.0.0)",
org.springframework.core.*;version="[3.1.0, 4.0.0)",
org.springframework.dao.*;version="[3.1.0, 4.0.0)",
org.springframework.scheduling.*;resolution:="optional";version="[3.1.0, 4.0.0)",
org.springframework.util.*;version="[3.1.0, 4.0.0)",
org.springframework.oxm.*;resolution:="optional";version="[3.1.0, 4.0.0)",
org.springframework.transaction.support.*;version="[3.1.0, 4.0.0)",
org.springframework.data.*;version="[1.5.0, 2.0.0)",
org.springframework.expression.*;version="[3.1.0, 4.0.0)",
org.aopalliance.*;version="[1.0.0, 2.0.0)";resolution:=optional,
org.apache.commons.logging.*;version="[1.1.1, 2.0.0)",
org.w3c.dom.*;version="0",
javax.xml.transform.*;resolution:="optional";version="0",
com.datastax.driver.core.*;resolution:="optional";version="[0.1.0, 1.0.0)",
org.apache.cassandra.db.marshal.*;version="[1.2.0, 1.3.0)",
org.slf4j.*;version="[1.5.0, 1.8.0)",
org.idevlab.rjc.*;resolution:="optional";version="[0.6.4, 0.6.4]",
org.apache.commons.pool.impl.*;resolution:="optional";version="[1.0.0, 3.0.0)",
org.codehaus.jackson.*;resolution:="optional";version="[1.6, 2.0.0)",
org.apache.commons.beanutils.*;resolution:="optional";version=1.8.5,
com.google.common.*;resolution:="optional";version="[11.0.0, 20.0.0)"

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2011-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.cassandra;
/**
* @author David Webb
*
*/
public interface Constants {
}

View File

@@ -0,0 +1,203 @@
/*
* Copyright 2011-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.cassandra.config;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.cassandra.core.CassandraOperations;
import org.springframework.cassandra.core.CassandraTemplate;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.cassandra.convert.CassandraConverter;
import org.springframework.data.cassandra.convert.MappingCassandraConverter;
import org.springframework.data.cassandra.core.CassandraAdminOperations;
import org.springframework.data.cassandra.core.CassandraAdminTemplate;
import org.springframework.data.cassandra.core.Keyspace;
import org.springframework.data.cassandra.mapping.CassandraMappingContext;
import org.springframework.data.cassandra.mapping.CassandraPersistentEntity;
import org.springframework.data.cassandra.mapping.CassandraPersistentProperty;
import org.springframework.data.cassandra.mapping.Table;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Session;
/**
* Base class for Spring Data Cassandra configuration using JavaConfig.
*
* @author Alex Shvid
*/
@Configuration
public abstract class AbstractCassandraConfiguration implements BeanClassLoaderAware {
/**
* Used by CassandraTemplate and CassandraAdminTemplate
*/
private ClassLoader beanClassLoader;
/**
* Return the name of the keyspace to connect to.
*
* @return must not be {@literal null}.
*/
protected abstract String getKeyspaceName();
/**
* Return the {@link Cluster} instance to connect to.
*
* @return
* @throws Exception
*/
@Bean
public abstract Cluster cluster() throws Exception;
/**
* Creates a {@link Session} to be used by the {@link Keyspace}. Will use the {@link Cluster} instance configured in
* {@link #cluster()}.
*
* @see #cluster()
* @see #Keyspace()
* @return
* @throws Exception
*/
@Bean
public Session session() throws Exception {
String keyspace = getKeyspaceName();
if (StringUtils.hasText(keyspace)) {
return cluster().connect(keyspace);
} else {
return cluster().connect();
}
}
/**
* Creates a {@link Keyspace} to be used by the {@link CassandraTemplate}. Will use the {@link Session} instance
* configured in {@link #session()} and {@link CassandraConverter} configured in {@link #converter()}.
*
* @see #cluster()
* @see #Keyspace()
* @return
* @throws Exception
*/
@Bean
public Keyspace keyspace() throws Exception {
return new Keyspace(getKeyspaceName(), session(), converter());
}
/**
* Return the base package to scan for mapped {@link Table}s. Will return the package name of the configuration class'
* (the concrete class, not this one here) by default. So if you have a {@code com.acme.AppConfig} extending
* {@link AbstractCassandraConfiguration} the base package will be considered {@code com.acme} unless the method is
* overriden to implement alternate behaviour.
*
* @return the base package to scan for mapped {@link Table} classes or {@literal null} to not enable scanning for
* entities.
*/
protected String getMappingBasePackage() {
return getClass().getPackage().getName();
}
/**
* Creates a {@link CassandraTemplate}.
*
* @return
* @throws Exception
*/
@Bean
public CassandraOperations cassandraTemplate() throws Exception {
return new CassandraTemplate(session());
}
/**
* Creates a {@link CassandraAdminTemplate}.
*
* @return
* @throws Exception
*/
@Bean
public CassandraAdminOperations cassandraAdminTemplate() throws Exception {
return new CassandraAdminTemplate(keyspace());
}
/**
* Return the {@link MappingContext} instance to map Entities to properties.
*
* @return
* @throws Exception
*/
@Bean
public MappingContext<? extends CassandraPersistentEntity<?>, CassandraPersistentProperty> mappingContext() {
return new CassandraMappingContext();
}
/**
* Return the {@link CassandraConverter} instance to convert Rows to Objects, Objects to BuiltStatements
*
* @return
* @throws Exception
*/
@Bean
public CassandraConverter converter() {
MappingCassandraConverter converter = new MappingCassandraConverter(mappingContext());
converter.setBeanClassLoader(beanClassLoader);
return converter;
}
/**
* Scans the mapping base package for classes annotated with {@link Table}.
*
* @see #getMappingBasePackage()
* @return
* @throws ClassNotFoundException
*/
protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
String basePackage = getMappingBasePackage();
Set<Class<?>> initialEntitySet = new HashSet<Class<?>>();
if (StringUtils.hasText(basePackage)) {
ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(
false);
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Table.class));
componentProvider.addIncludeFilter(new AnnotationTypeFilter(Persistent.class));
for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(),
AbstractCassandraConfiguration.class.getClassLoader()));
}
}
return initialEntitySet;
}
/**
* Bean ClassLoader Aware for CassandraTemplate/CassandraAdminTemplate
*/
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
}

View File

@@ -0,0 +1,31 @@
/*
* Copyright (c) 2011 by the original author(s).
*
* 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.cassandra.config;
/**
* @author Alex Shvid
* @author David Webb
*/
public final class BeanNames {
private BeanNames() {
}
public static final String CASSANDRA_CLUSTER = "cassandra-cluster";
public static final String CASSANDRA_KEYSPACE = "cassandra-keyspace";
public static final String CASSANDRA_SESSION = "cassandra-session";
}

View File

@@ -0,0 +1,118 @@
/*
* Copyright 2011-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.cassandra.config;
import java.util.List;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.cassandra.core.CassandraClusterFactoryBean;
import org.springframework.data.config.ParsingUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Parser for &lt;cluster;gt; definitions.
*
* @author Alex Shvid
*/
public class CassandraClusterParser extends AbstractSimpleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return CassandraClusterFactoryBean.class;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#resolveId(org.w3c.dom.Element, org.springframework.beans.factory.support.AbstractBeanDefinition, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
throws BeanDefinitionStoreException {
String id = super.resolveId(element, definition, parserContext);
return StringUtils.hasText(id) ? id : BeanNames.CASSANDRA_CLUSTER;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String contactPoints = element.getAttribute("contactPoints");
if (StringUtils.hasText(contactPoints)) {
builder.addPropertyValue("contactPoints", contactPoints);
}
String port = element.getAttribute("port");
if (StringUtils.hasText(port)) {
builder.addPropertyValue("port", port);
}
String compression = element.getAttribute("compression");
if (StringUtils.hasText(compression)) {
builder.addPropertyValue("compressionType", CompressionType.valueOf(compression));
}
postProcess(builder, element);
}
@Override
protected void postProcess(BeanDefinitionBuilder builder, Element element) {
List<Element> subElements = DomUtils.getChildElements(element);
// parse nested elements
for (Element subElement : subElements) {
String name = subElement.getLocalName();
if ("local-pooling-options".equals(name)) {
builder.addPropertyValue("localPoolingOptions", parsePoolingOptions(subElement));
} else if ("remote-pooling-options".equals(name)) {
builder.addPropertyValue("remotePoolingOptions", parsePoolingOptions(subElement));
} else if ("socket-options".equals(name)) {
builder.addPropertyValue("socketOptions", parseSocketOptions(subElement));
}
}
}
private BeanDefinition parsePoolingOptions(Element element) {
BeanDefinitionBuilder defBuilder = BeanDefinitionBuilder.genericBeanDefinition(PoolingOptionsConfig.class);
ParsingUtils.setPropertyValue(defBuilder, element, "min-simultaneous-requests", "minSimultaneousRequests");
ParsingUtils.setPropertyValue(defBuilder, element, "max-simultaneous-requests", "maxSimultaneousRequests");
ParsingUtils.setPropertyValue(defBuilder, element, "core-connections", "coreConnections");
ParsingUtils.setPropertyValue(defBuilder, element, "max-connections", "maxConnections");
return defBuilder.getBeanDefinition();
}
private BeanDefinition parseSocketOptions(Element element) {
BeanDefinitionBuilder defBuilder = BeanDefinitionBuilder.genericBeanDefinition(SocketOptionsConfig.class);
ParsingUtils.setPropertyValue(defBuilder, element, "connect-timeout-mls", "connectTimeoutMls");
ParsingUtils.setPropertyValue(defBuilder, element, "keep-alive", "keepAlive");
ParsingUtils.setPropertyValue(defBuilder, element, "reuse-address", "reuseAddress");
ParsingUtils.setPropertyValue(defBuilder, element, "so-linger", "soLinger");
ParsingUtils.setPropertyValue(defBuilder, element, "tcp-no-delay", "tcpNoDelay");
ParsingUtils.setPropertyValue(defBuilder, element, "receive-buffer-size", "receiveBufferSize");
ParsingUtils.setPropertyValue(defBuilder, element, "send-buffer-size", "sendBufferSize");
return defBuilder.getBeanDefinition();
}
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2011-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.cassandra.config;
import java.util.List;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.ManagedList;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext;
import org.springframework.data.cassandra.core.CassandraKeyspaceFactoryBean;
import org.springframework.data.config.ParsingUtils;
import org.springframework.util.StringUtils;
import org.springframework.util.xml.DomUtils;
import org.w3c.dom.Element;
/**
* Parser for &lt;keyspace;gt; definitions.
*
* @author Alex Shvid
*/
public class CassandraKeyspaceParser extends AbstractSimpleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return CassandraKeyspaceFactoryBean.class;
}
/*
* (non-Javadoc)
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#resolveId(org.w3c.dom.Element, org.springframework.beans.factory.support.AbstractBeanDefinition, org.springframework.beans.factory.xml.ParserContext)
*/
@Override
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext)
throws BeanDefinitionStoreException {
String id = super.resolveId(element, definition, parserContext);
return StringUtils.hasText(id) ? id : BeanNames.CASSANDRA_KEYSPACE;
}
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
String name = element.getAttribute("name");
if (StringUtils.hasText(name)) {
builder.addPropertyValue("keyspace", name);
}
String clusterRef = element.getAttribute("cassandra-cluster-ref");
if (!StringUtils.hasText(clusterRef)) {
clusterRef = BeanNames.CASSANDRA_CLUSTER;
}
builder.addPropertyReference("cluster", clusterRef);
String converterRef = element.getAttribute("cassandra-converter-ref");
if (StringUtils.hasText(converterRef)) {
builder.addPropertyReference("converter", converterRef);
}
postProcess(builder, element);
}
@Override
protected void postProcess(BeanDefinitionBuilder builder, Element element) {
List<Element> subElements = DomUtils.getChildElements(element);
// parse nested elements
for (Element subElement : subElements) {
String name = subElement.getLocalName();
if ("keyspace-attributes".equals(name)) {
builder.addPropertyValue("keyspaceAttributes", parseKeyspaceAttributes(subElement));
}
}
}
private BeanDefinition parseKeyspaceAttributes(Element element) {
BeanDefinitionBuilder defBuilder = BeanDefinitionBuilder.genericBeanDefinition(KeyspaceAttributes.class);
ParsingUtils.setPropertyValue(defBuilder, element, "auto", "auto");
ParsingUtils.setPropertyValue(defBuilder, element, "replication-strategy", "replicationStrategy");
ParsingUtils.setPropertyValue(defBuilder, element, "replication-factor", "replicationFactor");
ParsingUtils.setPropertyValue(defBuilder, element, "durable-writes", "durableWrites");
List<Element> subElements = DomUtils.getChildElements(element);
ManagedList<Object> tables = new ManagedList<Object>(subElements.size());
// parse nested elements
for (Element subElement : subElements) {
String name = subElement.getLocalName();
if ("table".equals(name)) {
tables.add(parseTable(subElement));
}
}
if (!tables.isEmpty()) {
defBuilder.addPropertyValue("tables", tables);
}
return defBuilder.getBeanDefinition();
}
private BeanDefinition parseTable(Element element) {
BeanDefinitionBuilder defBuilder = BeanDefinitionBuilder.genericBeanDefinition(TableAttributes.class);
ParsingUtils.setPropertyValue(defBuilder, element, "entity", "entity");
ParsingUtils.setPropertyValue(defBuilder, element, "name", "name");
return defBuilder.getBeanDefinition();
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2011-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.cassandra.config;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
/**
* Namespace handler for &lt;cassandra;gt;.
*
* @author Alex Shvid
*/
public class CassandraNamespaceHandler extends NamespaceHandlerSupport {
public void init() {
registerBeanDefinitionParser("cluster", new CassandraClusterParser());
registerBeanDefinitionParser("keyspace", new CassandraKeyspaceParser());
registerBeanDefinitionParser("session", new CassandraSessionParser());
}
}

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