spring-cassandra compiling
This commit is contained in:
664
attic/cassandra/conf/cassandra.yaml
Normal file
664
attic/cassandra/conf/cassandra.yaml
Normal 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
23
attic/get-and-start-cassandra
Executable 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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 (' -> ''). 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 (" -> ""). 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();
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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(";");
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 + "]";
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package org.springframework.cassandra.core.keyspace;
|
||||
|
||||
public class DropColumnSpecification extends ColumnChangeSpecification {
|
||||
|
||||
public DropColumnSpecification(String name) {
|
||||
super(name);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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<CompactionOption,Object></code>.
|
||||
*
|
||||
* @see CompactionOption
|
||||
*/
|
||||
COMPACTION("compaction", Map.class, true, false, false),
|
||||
/**
|
||||
* <code>compression</code>. Value is a <code>Map<CompressionOption,Object></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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<Object,Object></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 & 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();
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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"));
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
name: test
|
||||
replicationFactor: 1
|
||||
strategy: org.apache.cassandra.locator.SimpleStrategy
|
||||
690
spring-cassandra/src/test/resources/cassandra.yaml
Normal file
690
spring-cassandra/src/test/resources/cassandra.yaml
Normal 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
|
||||
@@ -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);
|
||||
3
spring-cassandra/src/test/resources/cql-dataload.cql
Normal file
3
spring-cassandra/src/test/resources/cql-dataload.cql
Normal 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);*/
|
||||
6
spring-cassandra/src/test/resources/log4j.properties
Normal file
6
spring-cassandra/src/test/resources/log4j.properties
Normal 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
|
||||
|
||||
@@ -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>
|
||||
@@ -0,0 +1,7 @@
|
||||
cassandra.contactPoints=localhost
|
||||
cassandra.port=9042
|
||||
cassandra.keyspace=TestKS123
|
||||
|
||||
|
||||
|
||||
|
||||
29
spring-cassandra/template.mf
Normal file
29
spring-cassandra/template.mf
Normal 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)"
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
}
|
||||
@@ -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 <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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 <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
Reference in New Issue
Block a user