Compare commits

..

95 commits

Author SHA1 Message Date
Greg Burd
9db3500e3a Repair the copyright per Casser author's request. Also fix DOS line endings. 2018-11-10 08:50:02 -05:00
Greg Burd
d179539a31 Only unwrap cache implementation for clients who are expecting the Map interface. 2018-04-23 12:37:49 -04:00
Greg Burd
06fe21d08e Update visibility to allow subclasses to change log message. 2018-04-19 10:44:39 -04:00
Greg Burd
099fd999ee Don't stop the stopwatch if it's not running. 2018-04-04 14:34:59 -04:00
Greg Burd
662a697d03 Changes to andThen/orElse and adding exceptionally logic. On commit one or the other of andThen or orElse will fire based on the path (commit, abort). If during any of those an exception is thrown the exceptionally is called with the exception. 2018-04-02 11:45:49 -04:00
b449817659
Merge pull request #2 from onshape/jgdef_delete_and_fixes
Delegate-based delete-tracking cache implementation, fixes to javax.cache.Cache implementation, and unit tests
2018-04-01 08:59:41 -04:00
John de Freitas
e6051b12e9 Delegate-based delete-tracking cache implementation, fixes to javax.cache.Cache implementation, and unit tests 2018-03-31 22:24:12 -04:00
John de Freitas
9f511cde74 Revert "Not-yet-tested tracking of UOW deletes"
This reverts commit b27bc7d9a9.
2018-03-31 22:21:28 -04:00
John de Freitas
4b9187ebe5 Revert "Delegate-based evict-tracking cache, fixes to core javax.cache.Cache implemenation MapCache, and unit tests"
This reverts commit 93a81e7fd0.
2018-03-31 22:21:18 -04:00
John de Freitas
93a81e7fd0 Delegate-based evict-tracking cache, fixes to core javax.cache.Cache implemenation MapCache, and unit tests 2018-03-31 22:18:28 -04:00
John de Freitas
3169d0c100 Not-yet-tested tracking of UOW deletes 2018-03-31 22:17:00 -04:00
John de Freitas
b27bc7d9a9 Not-yet-tested tracking of UOW deletes 2018-03-30 11:12:00 -04:00
Greg Burd
1f4c2154e2 Remove purpse guessing logic, it's overhead and buggy. 2018-03-29 11:57:43 -04:00
Greg Burd
6788cea1a0 Fix array out of bound bug in UnitOfWork purpose discovery logic. 2018-03-29 11:16:57 -04:00
Greg Burd
9d94e865b6 Allow subclasses access to the session. 2018-03-28 09:15:15 -04:00
Greg Burd
654f4434bf Remove Zipkin. Revert abstracted UnitOfWork into single class. 2018-03-28 08:27:49 -04:00
Greg Burd
6c245c121e Avoid NPE when batch passed in was null. 2018-03-05 08:37:48 -05:00
Greg Burd
ef455ac032 Find table name after binding facets. Revert duplicate null check. 2018-03-01 06:38:47 -07:00
Greg Burd
b1e333009c Don't merge in keys with null values, JCache doesn't support null values. 2018-03-01 05:23:43 -07:00
Greg Burd
ca6afc326c Qualify index names with their table name. 2018-02-28 06:48:30 -05:00
Greg Burd
af4156079d Formatting and fixes to use MapCache in the UOW. 2018-02-10 12:32:54 -05:00
Greg Burd
b023ec359b Moving toward javax.cache.Cache-based UOW cache API. 2018-02-09 21:55:23 -05:00
Greg Burd
76b603f3d3 Move the Guava JCache provider into the test targets only, don't assume a CacheManager instance exists. 2018-02-08 14:46:31 -05:00
Greg Burd
d69d8a3b1e Finish first steps of JCache integration, UnitOfWork statement cache now merges into available JCache at commit. 2018-02-08 10:09:23 -05:00
Greg Burd
6858cf6f48 Integrate JCache for cached objects outside of a UnitOfWork. 2018-02-07 18:41:39 -05:00
5215749de1
Merge pull request #1 from onshape/pbeaman/remove-jamm-0.2.5-dependency
Remove test-scope dependency on jamm-0.2.5 because it conflicts with …
2018-01-27 06:41:46 -05:00
pbeaman
2299939be3 Remove test-scope dependency on jamm-0.2.5 because it conflicts with a later version and causes NoSuchMethodError when running unit tests in Eclipse 2018-01-26 10:42:13 -05:00
Greg Burd
0ddacec354 Removing Gradle files until switch is complete. 2018-01-25 16:45:13 -05:00
Greg Burd
287e1a5b8b Use DataStax/Cassandra timestamp generator. 2018-01-25 14:21:21 -05:00
Greg Burd
8b9d582fa5 Mutation only needs to be different and can occur even when current is null. 2018-01-25 12:57:37 -05:00
Greg Burd
96a8476fd8 Null values have no keys in the map, so this test fails. In most casses we're here after we resolve a getter so we know this key is valid. Skip the check. 2018-01-25 12:41:13 -05:00
Greg Burd
f168b33f6a Formatting. 2018-01-25 11:30:41 -05:00
Greg Burd
11de7015c2 Change mutate on drafts to be a bit faster. 2018-01-25 11:26:12 -05:00
Greg Burd
f9b1563bdd Ensure there is a batch. 2018-01-24 16:11:44 -05:00
Greg Burd
e2f45f82c9 Fix logged batch syntax and timestamp logic. 2018-01-24 14:33:37 -05:00
Greg Burd
27dd9a4eff Include the post commit/abort function execution time that happens within/associated-with the scope of a UOW. Fix reset method to check both maps for updates. 2018-01-24 09:28:19 -05:00
Greg Burd
26f41dab75 Add notion of statement cache to UnitOfWork. Ignore call to useKeyspace when session isn't valid. 2018-01-17 12:38:33 -05:00
Greg Burd
1da822ce57 Fix build. Change scripts to use /usr/bin/env so that they work on NiXOS. Revert abort() logic. 2018-01-16 13:10:12 -05:00
Greg Burd
1ef50ae179 Allow an init path that doesn't require a non-null Cassandra/DataStax session/cluster context. 2018-01-16 11:59:51 -05:00
Greg Burd
26c67e391a Review related fixes. 2018-01-15 11:28:04 -05:00
Greg Burd
3554b7ecb5 Add isDone() method on UnitOfWork. 2018-01-14 12:51:49 -05:00
Greg Burd
7b4e46431f Update DataStax driver version. 2017-12-14 10:19:38 -05:00
Greg Burd
60b040e7a9 Always start the timer. 2017-12-11 16:04:28 -05:00
Greg Burd
7a470bd5d7 Formatting. 2017-11-15 22:39:50 -05:00
Greg Burd
0827291253 Spoil futures not completed before an abort/commit of the UOW they belong too. Track read set for Entity/Drafted model objects. 2017-11-15 13:56:03 -05:00
Greg Burd
50f656bc8a Fix commit.andThen() logic. 2017-11-15 09:16:15 -05:00
Greg Burd
9df97b3e44 WIP: commit.exceptionally() is working but somehow in the process I broke commit.andThen(). 2017-11-14 22:37:37 -05:00
Greg Burd
33b4b35912 Formatting. 2017-11-14 15:42:16 -05:00
Greg Burd
1eccb631f3 Fix logic that was failing to cache results on cache miss. 2017-11-14 15:26:16 -05:00
Greg Burd
e932d0dcf2 Check to ensure value not null. 2017-11-14 10:06:13 -05:00
Greg Burd
618a7ea380 Draft instances map is mutable and so are collection values inside the map, this makes the UPDATE logic straight forward when mutating in-cache draft objects. Also, fix one or two logic bugs with isAssignableFrom(). 2017-11-14 09:55:03 -05:00
Greg Burd
7a56059036 Fix misuse of Drafted interface in tests. WIP fixing use of immutable collections in UPDATE/draft logic. 2017-11-13 15:55:24 -05:00
Greg Burd
33d2459538 Sometimes there are no filters. 2017-11-13 11:01:30 -05:00
Greg Burd
a63a1be4b6 Paranoia. 2017-11-13 10:47:48 -05:00
Greg Burd
d30361538c Operations now default to non-idempotent unless explictly set in the statement or if they contain fields that are idempotent (e.g. @Column(idempotent=true) or part of the primary key for the row). 2017-11-13 10:36:16 -05:00
Greg Burd
a993af6c29 Enable toggle for showing values in logged CQL statements. Default to true. 2017-11-12 22:32:58 -05:00
Greg Burd
39a8643103 Fix a few things. 2017-11-12 21:37:59 -05:00
Greg Burd
c025dc35a7 Formatting. 2017-11-12 20:14:31 -05:00
Greg Burd
d19a9c741d Fixing a few corners of caching when using drafted entity objects. Working out kinks in merge logic for entity instances in UOWs. 2017-11-10 22:48:40 -05:00
Greg Burd
6ff188f241 Move statement logging into Operation, cover special case for batches. Cleanup UOW commit logging a bit. 2017-11-09 15:03:30 -05:00
Greg Burd
2f0801d36f Fix test to see if select is of Fun<?> type. 2017-11-09 13:32:16 -05:00
Greg Burd
b4dca9c710 finish up batch feature for now 2017-11-08 21:11:58 -05:00
Greg Burd
41e5d8c1e5 wip: working on batch update times. 2017-11-08 15:40:56 -05:00
Greg Burd
5570a97dff Improved support for batched statements. 2017-11-08 13:50:39 -05:00
Greg Burd
a198989a76 Cleanup. 2017-11-07 23:01:43 -05:00
Greg Burd
eb22e3c72e Added batch() to perform mutations as a batch at the commit of a unit of work. Lots of fixes. 2017-11-07 21:23:33 -05:00
Greg Burd
a79e7dacf1 Cache data from MaterializedViews under their parent name. Add some relationship constraints that can decorate getters in model classes and be used in some cases to better cache data. 2017-11-06 13:49:34 -05:00
Greg Burd
fe47531984 Formatting. 2017-11-03 13:32:57 -04:00
Greg Burd
a600c0bd23 Correct logic error. 2017-11-03 11:41:09 -04:00
Greg Burd
b777e9c051 Remove redundant dependency. 2017-11-03 10:42:46 -04:00
Greg Burd
0b86d33725 Change ALLOW FILTERING logic and session cache merge a bit. 2017-11-03 08:49:31 -04:00
Greg Burd
690cd1e064 Formatting. 2017-11-02 16:32:09 -04:00
Greg Burd
ef4f9054ac Formatting. 2017-11-02 16:23:45 -04:00
Greg Burd
465c7d6f2a Don't reset is first index every iteration 2017-11-02 16:12:46 -04:00
Greg Burd
864c4af5af We only transition to allow filtering and only if we've not already set it. 2017-11-02 16:09:47 -04:00
Greg Burd
d1fe54b0ce Add ALLOW FILTERING in the cases Cassandra requires it based on query. More result info formatting. 2017-11-02 15:46:49 -04:00
Greg Burd
962145bf46 Formatting. 2017-11-02 15:08:58 -04:00
Greg Burd
e4cda1a268 Re-enable (for now) the addition of ALLOW FILTERING to SELECT statements that include a filter column indexed by a non-standard index type (e.g. SASI, Lucene, etc.) as that's required and mysterious to end users. 2017-11-02 15:05:43 -04:00
Greg Burd
377191f12a No longer add allowFiltering based on entity properties, force user to add as required. Add information about Cassandra execution to the UOW log line when DEBUG. Throw errors when execution doesn't complete properly. 2017-11-02 14:46:55 -04:00
Greg Burd
792d2b6598 Find proper stack frame to use for purpose. 2017-11-02 10:42:15 -04:00
Greg Burd
2ee300e420 Formatting. 2017-11-01 16:19:07 -04:00
Greg Burd
13eaa7e7ea Change how we find the frame to use for the UOW's purpose. 2017-11-01 16:17:00 -04:00
Greg Burd
5905663c58 Cached values were being updated to their database-specific values (Enum -> String) rather than their intended values. 2017-11-01 14:24:03 -04:00
Greg Burd
857eadff45 Avoid complications with merges in the session cache, evict and then update. 2017-11-01 11:09:14 -04:00
Greg Burd
e1884cf52d Remove the 'final' from HelenusSession. 2017-11-01 09:57:58 -04:00
Greg Burd
09a7fbc405 Address some issues identified by FindBugs. 2017-10-31 21:10:15 -04:00
Greg Burd
01a458a7f6 A few minor fixes. 2017-10-31 17:43:52 -04:00
Greg Burd
48545c1e84 Finish implementation of proxy model objects. 2017-10-30 15:21:43 -04:00
Greg Burd
dcea254fdb Formatting. 2017-10-30 11:52:12 -04:00
Greg Burd
5605824886 Minor fixes. 2017-10-30 11:49:48 -04:00
Greg Burd
c3f9b83770 Log the purposes of nested UOWs as well as the parent. Create a serialization proxy for mapped objects. 2017-10-30 09:40:41 -04:00
Greg Burd
52dab5872c Formatting. Also, need to redo merge so as to avoid ValueProviderMap immutability. 2017-10-27 15:05:08 -04:00
Greg Burd
a65b775faa Abstract the SessionCache and allow outside implementations. 2017-10-27 14:33:04 -04:00
Greg Burd
62be0e6ccd Merge branch 'gburd/wip-abstract-cache' into develop 2017-10-27 14:01:08 -04:00
Greg Burd
7b14eda9b3 More caching fixes. 2017-10-27 14:00:24 -04:00
315 changed files with 19447 additions and 16462 deletions

View file

@ -3,7 +3,6 @@
<component name="EclipseCodeFormatterProjectSettings"> <component name="EclipseCodeFormatterProjectSettings">
<option name="projectSpecificProfile"> <option name="projectSpecificProfile">
<ProjectSpecificProfile> <ProjectSpecificProfile>
<option name="formatter" value="ECLIPSE" />
<option name="pathToConfigFileJava" value="$PROJECT_DIR$/../newton/formatting/onshape-eclipse-general-preferences.epf" /> <option name="pathToConfigFileJava" value="$PROJECT_DIR$/../newton/formatting/onshape-eclipse-general-preferences.epf" />
</ProjectSpecificProfile> </ProjectSpecificProfile>
</option> </option>

274
NOTES
View file

@ -1,172 +1,35 @@
Operation/
|-- AbstractStatementOperation
| |-- AbstractOperation
| | |-- AbstractFilterOperation
| | | |-- CountOperation
| | | |-- DeleteOperation
| | | `-- UpdateOperation
| | |-- BoundOperation
| | `-- InsertOperation
| |-- AbstractOptionalOperation
| | |-- AbstractFilterOptionalOperation
| | | |-- SelectFirstOperation
| | | `-- SelectFirstTransformingOperation
| | `-- BoundOptionalOperation
| `-- AbstractStreamOperation
| |-- AbstractFilterStreamOperation
| | |-- SelectOperation
| | `-- SelectTransformingOperation
| `-- BoundStreamOperation
|-- PreparedOperation
|-- PreparedOptionalOperation
`-- PreparedStreamOperation
----
@CompoundIndex()
create a new col in the same table called __idx_a_b_c that the hash of the concatenated values in that order is stored, create a normal index for that (CREATE INDEX ...)
if a query matches that set of columns then use that indexed col to fetch the desired results from that table
could also work with .in() query if materialized view exists
----
--- Cache
// `E` is the type of the Entity class or one of:
// - ResultSet
// - ArrayTuple{N}
// - Count
// `F` is the type argument passed to us from HelenusSession DSL and carried on via one of the
// Operation classes, it is going to be one of:
// - ResultSet
// - ArrayTuple{N}
// - or a type previously registered as a HelenusEntity.
// In the form of a:
// - Stream<?> or an
// - Optional<?>
//
// Operation/
// |-- AbstractStatementOperation
// | |-- AbstractOperation
// | | |-- AbstractFilterOperation
// | | | |-- CountOperation
// | | | |-- DeleteOperation
// | | | `-- UpdateOperation
// | | |-- BoundOperation
// | | `-- InsertOperation
// | |-- AbstractOptionalOperation
// | | |-- AbstractFilterOptionalOperation
// | | | |-- SelectFirstOperation
// | | | `-- SelectFirstTransformingOperation
// | | `-- BoundOptionalOperation
// | `-- AbstractStreamOperation
// | |-- AbstractFilterStreamOperation
// | | |-- SelectOperation
// | | `-- SelectTransformingOperation
// | `-- BoundStreamOperation
// |-- PreparedOperation
// |-- PreparedOptionalOperation
// `-- PreparedStreamOperation
//
// These all boil down to: Select, Update, Insert, Delete and Count
//
// -- Select:
// 1) Select statements that contain all primary key information will be "distinct" and
// result in a single value or no match.
// If present, return cached entity otherwise execute query and cache result.
//
// 2) Otherwise the result is a set, possibly empty, of values that match.
// When within a UOW:
// If present, return the cached value(s) from the statement cache matching the query string.
// Otherwise, execute query and cache the result in the statement cache and update/merge the
// entites into the entity cache.
// NOTE: When we read data from the database we augment the select clause with TTL and write time
// stamps for all columns that record such information so as to be able to properlty expire
// and merge values in the cache.
//
// -- Update:
// Execute the database statement and then iff successs upsert the entity being updated into the
// entity cache.
//
// -- Insert/Upsert:
// Same as Update.
//
// -- Delete:
// Same as update, only remove the cached value from all caches on success.
//
// -- Count:
// If operating within a UOW lookup count in statement cache, if not present execute query and cache result.
//
if (delegate instanceof SelectOperation) {
SelectOperation<E> op = (SelectOperation<E>) delegate;
// Determine if we are caching and if so where.
AbstractCache<CacheKey, Set<E>> cache = delegate.getCache();
boolean prepareStatementForCaching = cache != null;
if (uow != null) {
prepareStatementForCaching = true;
cache = uow.<Set<E>>getCacheEnclosing(cache);
}
// The delegate will provide the cache key becuase it will either be:
// a) when distinct: the combination of the partition/cluster key columns
// b) otherwise: the table name followed by the portion of the SQL statement that would form the WHERE clause
CacheKey key = (cache == null) ? null : delegate.getCacheKey();
if (key != null && cache != null) {
Set<E> value = cache.get(key);
if (value != null) {
// Select will always return a Stream<E>
// TODO(gburd): SelectTransforming... apply fn here?
result = (E) value.stream();
if (cacheHitCounter != null) {
cacheHitCounter.inc();
}
if (log != null) {
log.info("cache hit");
}
return result;
} else {
if (cacheMissCounter != null) {
cacheMissCounter.inc();
}
if (log != null) {
log.info("cache miss");
}
}
}
}
if (cache != null) {
Object obj = delegate.unwrap(result);
if (obj != null) {
cache.put(key, obj);
}
delegate.<E>extract(result, key, cache);
}
}
}
// TODO: first, ask the delegate for the cacheKey
// if this is a SELECT query:
// if not in cache build the statement, execute the future, cache the result, transform the result then cache the transformations
// if INSERT/UPSERT/UPDATE
// if DELETE
// if COUNT
----------------------------
@Override
public CacheKey getCacheKey() {
List<String>keys = new ArrayList<>(filters.size());
HelenusEntity entity = props.get(0).getEntity();
for (HelenusPropertyNode prop : props) {
switch(prop.getProperty().getColumnType()) {
case PARTITION_KEY:
case CLUSTERING_COLUMN:
Filter filter = filters.get(prop.getProperty());
if (filter != null) {
keys.add(filter.toString());
} else {
// we're missing a part of the primary key, so we can't create a proper cache key
return null;
}
break;
default:
// We've past the primary key components in this ordered list, so we're done building
// the cache key.
if (keys.size() > 0) {
return new CacheKey(entity, Joiner.on(",").join(keys));
}
return null;
}
}
return null;
}
---------------------------
// TODO(gburd): create a statement that matches one that wasn't prepared // TODO(gburd): create a statement that matches one that wasn't prepared
//String key = //String key =
// "use " + preparedStatement.getQueryKeyspace() + "; " + preparedStatement.getQueryString(); // "use " + preparedStatement.getQueryKeyspace() + "; " + preparedStatement.getQueryString();
@ -175,64 +38,6 @@
//} //}
------------------------
package net.helenus.core.operation;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.ResultSetFuture;
import com.datastax.driver.core.Statement;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.RemovalListener;
import com.google.common.cache.RemovalNotification;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
public abstract class AbstractCache<K, V> {
final Logger logger = LoggerFactory.getLogger(getClass());
public Cache<K, V> cache;
public AbstractCache() {
RemovalListener<K, V> listener =
new RemovalListener<K, V>() {
@Override
public void onRemoval(RemovalNotification<K, V> n) {
if (n.wasEvicted()) {
String cause = n.getCause().name();
logger.info(cause);
}
}
};
cache = CacheBuilder.newBuilder()
.maximumSize(10_000)
.expireAfterAccess(20, TimeUnit.MINUTES)
.weakKeys()
.softValues()
.removalListener(listener)
.build();
}
V get(K key) {
return cache.getIfPresent(key);
}
void put(K key, V value) {
cache.put(key, value);
}
}
------------------------------------------------------------------------------------------------
cache entites (2 methods) marked @Cacheable
cache entites in txn context
cache results when .cache() chained before .{a}sync() call, return a EvictableCacheItem<E> that has an .evict() method
fix txn .andThen() chains
primitive types have default values, (e.g. boolean, int, ...) but primative wrapper classes do not and can be null (e.g. Boolean, Integer, ...) primitive types have default values, (e.g. boolean, int, ...) but primative wrapper classes do not and can be null (e.g. Boolean, Integer, ...)
@ -372,3 +177,26 @@ begin:
cache.put cache.put
} }
*/ */
------------------
InsertOperation
Class<?> iface = entity.getMappingInterface();
boolean includesNonIdentityValues = values.stream().map(t -> {
ColumnType type = t._1.getProperty().getColumnType();
return !((type == ColumnType.PARTITION_KEY) || (type == ColumnType.CLUSTERING_COLUMN));
})
.reduce(false, (acc, t) -> acc || t);
if (resultType == iface) {
if (values.size() > 0 && includesNonIdentityValues) {
boolean immutable = iface.isAssignableFrom(Drafted.class);
-------------------
final Object value;
if (method.getParameterCount() == 1 && args[0] instanceof Boolean && src instanceof ValueProviderMap) {
value = ((ValueProviderMap)src).get(methodName, (Boolean)args[0]);
} else {
value = src.get(methodName);
}
--------------------

View file

@ -1,3 +1,3 @@
#!/bin/bash #!/usr/bin/env bash
mvn clean jar:jar javadoc:jar source:jar deploy -Prelease mvn clean jar:jar javadoc:jar source:jar deploy -Prelease

View file

@ -1,7 +1,14 @@
#!/bin/bash #!/usr/bin/env bash
for f in $(find ./src -name \*.java); do if [ "X$1" == "Xall" ]; then
echo Formatting $f for f in $(find ./src -name \*.java); do
java -jar ./lib/google-java-format-1.3-all-deps.jar --replace $f echo Formatting $f
done java -jar ./lib/google-java-format-1.3-all-deps.jar --replace $f
done
else
for file in $(git status --short | awk '{print $2}'); do
echo $file
java -jar ./lib/google-java-format-1.3-all-deps.jar --replace $file
done
fi

View file

@ -1,3 +1,3 @@
#!/bin/bash #!/usr/bin/env bash
mvn clean jar:jar javadoc:jar source:jar install -Prelease mvn clean jar:jar javadoc:jar source:jar install -Prelease

View file

@ -1,90 +0,0 @@
// gradle wrapper
// ./gradlew clean generateLock saveLock
// ./gradlew compileJava
// ./gradlew run
// ./gradlew run --debug-jvm
// ./gradlew publishToMavenLocal
buildscript {
ext {}
repositories {
jcenter()
mavenLocal()
mavenCentral()
maven { url "https://clojars.org/repo" }
maven { url "https://plugins.gradle.org/m2/" }
}
dependencies {
classpath 'com.netflix.nebula:gradle-dependency-lock-plugin:4.+'
classpath 'com.uber:okbuck:0.19.0'
}
}
apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'eclipse'
apply plugin: 'java-library'
apply plugin: 'maven-publish'
apply plugin: 'com.uber.okbuck'
apply plugin: 'nebula.dependency-lock'
task wrapper(type: Wrapper) {
gradleVersion = '4.0.2'
}
jar {
baseName = 'helenus'
group = 'net.helenus'
version = '2.0.17-SNAPSHOT'
}
description = """helenus"""
sourceCompatibility = 1.8
targetCompatibility = 1.8
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
configurations.all {
}
repositories {
jcenter()
mavenLocal()
mavenCentral()
maven { url "file:///Users/gburd/ws/helenus/lib" }
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
maven { url "http://repo.maven.apache.org/maven2" }
}
dependencies {
compile group: 'com.datastax.cassandra', name: 'cassandra-driver-core', version: '3.3.0'
compile group: 'org.aspectj', name: 'aspectjrt', version: '1.8.10'
compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.8.10'
compile group: 'org.apache.commons', name: 'commons-lang3', version: '3.6'
compile group: 'org.springframework', name: 'spring-core', version: '4.3.10.RELEASE'
compile group: 'com.google.guava', name: 'guava', version: '20.0'
compile group: 'com.diffplug.durian', name: 'durian', version: '3.+'
compile group: 'io.zipkin.java', name: 'zipkin', version: '1.29.2'
compile group: 'io.zipkin.brave', name: 'brave', version: '4.0.6'
compile group: 'io.dropwizard.metrics', name: 'metrics-core', version: '3.2.2'
compile group: 'javax.validation', name: 'validation-api', version: '2.0.0.CR3'
compile group: 'org.slf4j', name: 'slf4j-api', version: '1.7.1'
runtime group: 'org.slf4j', name: 'jcl-over-slf4j', version: '1.7.1'
testCompile group: 'org.codehaus.jackson', name: 'jackson-mapper-asl', version: '1.9.13'
testCompile group: 'com.anthemengineering.mojo', name: 'infer-maven-plugin', version: '0.1.0'
testCompile group: 'org.codehaus.jackson', name: 'jackson-core-asl', version: '1.9.13'
testCompile(group: 'org.cassandraunit', name: 'cassandra-unit', version: '3.1.4.0-SNAPSHOT') {
exclude(module: 'cassandra-driver-core')
}
testCompile group: 'org.apache.cassandra', name: 'cassandra-all', version: '3.11.0'
testCompile group: 'commons-io', name: 'commons-io', version: '2.5'
testCompile group: 'junit', name: 'junit', version: '4.12'
testCompile group: 'com.github.stephenc', name: 'jamm', version: '0.2.5'
testCompile group: 'org.hamcrest', name: 'hamcrest-library', version: '1.3'
testCompile group: 'org.hamcrest', name: 'hamcrest-core', version: '1.3'
testCompile group: 'org.mockito', name: 'mockito-core', version: '2.8.47'
}

View file

@ -1,648 +0,0 @@
{
"compile": {
"com.datastax.cassandra:cassandra-driver-core": {
"locked": "3.3.0",
"requested": "3.3.0"
},
"com.diffplug.durian:durian": {
"locked": "3.5.0-SNAPSHOT",
"requested": "3.+"
},
"com.google.guava:guava": {
"locked": "20.0",
"requested": "20.0"
},
"io.dropwizard.metrics:metrics-core": {
"locked": "3.2.2",
"requested": "3.2.2"
},
"io.zipkin.brave:brave": {
"locked": "4.0.6",
"requested": "4.0.6"
},
"io.zipkin.java:zipkin": {
"locked": "1.29.2",
"requested": "1.29.2"
},
"javax.validation:validation-api": {
"locked": "2.0.0.CR3",
"requested": "2.0.0.CR3"
},
"org.apache.commons:commons-lang3": {
"locked": "3.6",
"requested": "3.6"
},
"org.aspectj:aspectjrt": {
"locked": "1.8.10",
"requested": "1.8.10"
},
"org.aspectj:aspectjweaver": {
"locked": "1.8.10",
"requested": "1.8.10"
},
"org.slf4j:slf4j-api": {
"locked": "1.7.25",
"requested": "1.7.1"
},
"org.springframework:spring-core": {
"locked": "4.3.10.RELEASE",
"requested": "4.3.10.RELEASE"
}
},
"compileClasspath": {
"com.datastax.cassandra:cassandra-driver-core": {
"locked": "3.3.0",
"requested": "3.3.0"
},
"com.diffplug.durian:durian": {
"locked": "3.5.0-SNAPSHOT",
"requested": "3.+"
},
"com.google.guava:guava": {
"locked": "20.0",
"requested": "20.0"
},
"io.dropwizard.metrics:metrics-core": {
"locked": "3.2.2",
"requested": "3.2.2"
},
"io.zipkin.brave:brave": {
"locked": "4.0.6",
"requested": "4.0.6"
},
"io.zipkin.java:zipkin": {
"locked": "1.29.2",
"requested": "1.29.2"
},
"javax.validation:validation-api": {
"locked": "2.0.0.CR3",
"requested": "2.0.0.CR3"
},
"org.apache.commons:commons-lang3": {
"locked": "3.6",
"requested": "3.6"
},
"org.aspectj:aspectjrt": {
"locked": "1.8.10",
"requested": "1.8.10"
},
"org.aspectj:aspectjweaver": {
"locked": "1.8.10",
"requested": "1.8.10"
},
"org.slf4j:slf4j-api": {
"locked": "1.7.25",
"requested": "1.7.1"
},
"org.springframework:spring-core": {
"locked": "4.3.10.RELEASE",
"requested": "4.3.10.RELEASE"
}
},
"default": {
"com.datastax.cassandra:cassandra-driver-core": {
"locked": "3.3.0",
"requested": "3.3.0"
},
"com.diffplug.durian:durian": {
"locked": "3.5.0-SNAPSHOT",
"requested": "3.+"
},
"com.google.guava:guava": {
"locked": "20.0",
"requested": "20.0"
},
"io.dropwizard.metrics:metrics-core": {
"locked": "3.2.2",
"requested": "3.2.2"
},
"io.zipkin.brave:brave": {
"locked": "4.0.6",
"requested": "4.0.6"
},
"io.zipkin.java:zipkin": {
"locked": "1.29.2",
"requested": "1.29.2"
},
"javax.validation:validation-api": {
"locked": "2.0.0.CR3",
"requested": "2.0.0.CR3"
},
"org.apache.commons:commons-lang3": {
"locked": "3.6",
"requested": "3.6"
},
"org.aspectj:aspectjrt": {
"locked": "1.8.10",
"requested": "1.8.10"
},
"org.aspectj:aspectjweaver": {
"locked": "1.8.10",
"requested": "1.8.10"
},
"org.slf4j:jcl-over-slf4j": {
"locked": "1.7.1",
"requested": "1.7.1"
},
"org.slf4j:slf4j-api": {
"locked": "1.7.25",
"requested": "1.7.1"
},
"org.springframework:spring-core": {
"locked": "4.3.10.RELEASE",
"requested": "4.3.10.RELEASE"
}
},
"runtime": {
"com.datastax.cassandra:cassandra-driver-core": {
"locked": "3.3.0",
"requested": "3.3.0"
},
"com.diffplug.durian:durian": {
"locked": "3.5.0-SNAPSHOT",
"requested": "3.+"
},
"com.google.guava:guava": {
"locked": "20.0",
"requested": "20.0"
},
"io.dropwizard.metrics:metrics-core": {
"locked": "3.2.2",
"requested": "3.2.2"
},
"io.zipkin.brave:brave": {
"locked": "4.0.6",
"requested": "4.0.6"
},
"io.zipkin.java:zipkin": {
"locked": "1.29.2",
"requested": "1.29.2"
},
"javax.validation:validation-api": {
"locked": "2.0.0.CR3",
"requested": "2.0.0.CR3"
},
"org.apache.commons:commons-lang3": {
"locked": "3.6",
"requested": "3.6"
},
"org.aspectj:aspectjrt": {
"locked": "1.8.10",
"requested": "1.8.10"
},
"org.aspectj:aspectjweaver": {
"locked": "1.8.10",
"requested": "1.8.10"
},
"org.slf4j:jcl-over-slf4j": {
"locked": "1.7.1",
"requested": "1.7.1"
},
"org.slf4j:slf4j-api": {
"locked": "1.7.25",
"requested": "1.7.1"
},
"org.springframework:spring-core": {
"locked": "4.3.10.RELEASE",
"requested": "4.3.10.RELEASE"
}
},
"runtimeClasspath": {
"com.datastax.cassandra:cassandra-driver-core": {
"locked": "3.3.0",
"requested": "3.3.0"
},
"com.diffplug.durian:durian": {
"locked": "3.5.0-SNAPSHOT",
"requested": "3.+"
},
"com.google.guava:guava": {
"locked": "20.0",
"requested": "20.0"
},
"io.dropwizard.metrics:metrics-core": {
"locked": "3.2.2",
"requested": "3.2.2"
},
"io.zipkin.brave:brave": {
"locked": "4.0.6",
"requested": "4.0.6"
},
"io.zipkin.java:zipkin": {
"locked": "1.29.2",
"requested": "1.29.2"
},
"javax.validation:validation-api": {
"locked": "2.0.0.CR3",
"requested": "2.0.0.CR3"
},
"org.apache.commons:commons-lang3": {
"locked": "3.6",
"requested": "3.6"
},
"org.aspectj:aspectjrt": {
"locked": "1.8.10",
"requested": "1.8.10"
},
"org.aspectj:aspectjweaver": {
"locked": "1.8.10",
"requested": "1.8.10"
},
"org.slf4j:jcl-over-slf4j": {
"locked": "1.7.1",
"requested": "1.7.1"
},
"org.slf4j:slf4j-api": {
"locked": "1.7.25",
"requested": "1.7.1"
},
"org.springframework:spring-core": {
"locked": "4.3.10.RELEASE",
"requested": "4.3.10.RELEASE"
}
},
"testCompile": {
"com.anthemengineering.mojo:infer-maven-plugin": {
"locked": "0.1.0",
"requested": "0.1.0"
},
"com.datastax.cassandra:cassandra-driver-core": {
"locked": "3.3.0",
"requested": "3.3.0"
},
"com.diffplug.durian:durian": {
"locked": "3.5.0-SNAPSHOT",
"requested": "3.+"
},
"com.github.stephenc:jamm": {
"locked": "0.2.5",
"requested": "0.2.5"
},
"com.google.guava:guava": {
"locked": "21.0",
"requested": "20.0"
},
"commons-io:commons-io": {
"locked": "2.5",
"requested": "2.5"
},
"io.dropwizard.metrics:metrics-core": {
"locked": "3.2.2",
"requested": "3.2.2"
},
"io.zipkin.brave:brave": {
"locked": "4.0.6",
"requested": "4.0.6"
},
"io.zipkin.java:zipkin": {
"locked": "1.29.2",
"requested": "1.29.2"
},
"javax.validation:validation-api": {
"locked": "2.0.0.CR3",
"requested": "2.0.0.CR3"
},
"junit:junit": {
"locked": "4.12",
"requested": "4.12"
},
"org.apache.cassandra:cassandra-all": {
"locked": "3.11.0",
"requested": "3.11.0"
},
"org.apache.commons:commons-lang3": {
"locked": "3.6",
"requested": "3.6"
},
"org.aspectj:aspectjrt": {
"locked": "1.8.10",
"requested": "1.8.10"
},
"org.aspectj:aspectjweaver": {
"locked": "1.8.10",
"requested": "1.8.10"
},
"org.cassandraunit:cassandra-unit": {
"locked": "3.1.4.0-SNAPSHOT",
"requested": "3.1.4.0-SNAPSHOT"
},
"org.codehaus.jackson:jackson-core-asl": {
"locked": "1.9.13",
"requested": "1.9.13"
},
"org.codehaus.jackson:jackson-mapper-asl": {
"locked": "1.9.13",
"requested": "1.9.13"
},
"org.hamcrest:hamcrest-core": {
"locked": "1.3",
"requested": "1.3"
},
"org.hamcrest:hamcrest-library": {
"locked": "1.3",
"requested": "1.3"
},
"org.mockito:mockito-core": {
"locked": "2.8.47",
"requested": "2.8.47"
},
"org.slf4j:slf4j-api": {
"locked": "1.7.25",
"requested": "1.7.1"
},
"org.springframework:spring-core": {
"locked": "4.3.10.RELEASE",
"requested": "4.3.10.RELEASE"
}
},
"testCompileClasspath": {
"com.anthemengineering.mojo:infer-maven-plugin": {
"locked": "0.1.0",
"requested": "0.1.0"
},
"com.datastax.cassandra:cassandra-driver-core": {
"locked": "3.3.0",
"requested": "3.3.0"
},
"com.diffplug.durian:durian": {
"locked": "3.5.0-SNAPSHOT",
"requested": "3.+"
},
"com.github.stephenc:jamm": {
"locked": "0.2.5",
"requested": "0.2.5"
},
"com.google.guava:guava": {
"locked": "21.0",
"requested": "20.0"
},
"commons-io:commons-io": {
"locked": "2.5",
"requested": "2.5"
},
"io.dropwizard.metrics:metrics-core": {
"locked": "3.2.2",
"requested": "3.2.2"
},
"io.zipkin.brave:brave": {
"locked": "4.0.6",
"requested": "4.0.6"
},
"io.zipkin.java:zipkin": {
"locked": "1.29.2",
"requested": "1.29.2"
},
"javax.validation:validation-api": {
"locked": "2.0.0.CR3",
"requested": "2.0.0.CR3"
},
"junit:junit": {
"locked": "4.12",
"requested": "4.12"
},
"org.apache.cassandra:cassandra-all": {
"locked": "3.11.0",
"requested": "3.11.0"
},
"org.apache.commons:commons-lang3": {
"locked": "3.6",
"requested": "3.6"
},
"org.aspectj:aspectjrt": {
"locked": "1.8.10",
"requested": "1.8.10"
},
"org.aspectj:aspectjweaver": {
"locked": "1.8.10",
"requested": "1.8.10"
},
"org.cassandraunit:cassandra-unit": {
"locked": "3.1.4.0-SNAPSHOT",
"requested": "3.1.4.0-SNAPSHOT"
},
"org.codehaus.jackson:jackson-core-asl": {
"locked": "1.9.13",
"requested": "1.9.13"
},
"org.codehaus.jackson:jackson-mapper-asl": {
"locked": "1.9.13",
"requested": "1.9.13"
},
"org.hamcrest:hamcrest-core": {
"locked": "1.3",
"requested": "1.3"
},
"org.hamcrest:hamcrest-library": {
"locked": "1.3",
"requested": "1.3"
},
"org.mockito:mockito-core": {
"locked": "2.8.47",
"requested": "2.8.47"
},
"org.slf4j:slf4j-api": {
"locked": "1.7.25",
"requested": "1.7.1"
},
"org.springframework:spring-core": {
"locked": "4.3.10.RELEASE",
"requested": "4.3.10.RELEASE"
}
},
"testRuntime": {
"com.anthemengineering.mojo:infer-maven-plugin": {
"locked": "0.1.0",
"requested": "0.1.0"
},
"com.datastax.cassandra:cassandra-driver-core": {
"locked": "3.3.0",
"requested": "3.3.0"
},
"com.diffplug.durian:durian": {
"locked": "3.5.0-SNAPSHOT",
"requested": "3.+"
},
"com.github.stephenc:jamm": {
"locked": "0.2.5",
"requested": "0.2.5"
},
"com.google.guava:guava": {
"locked": "21.0",
"requested": "20.0"
},
"commons-io:commons-io": {
"locked": "2.5",
"requested": "2.5"
},
"io.dropwizard.metrics:metrics-core": {
"locked": "3.2.2",
"requested": "3.2.2"
},
"io.zipkin.brave:brave": {
"locked": "4.0.6",
"requested": "4.0.6"
},
"io.zipkin.java:zipkin": {
"locked": "1.29.2",
"requested": "1.29.2"
},
"javax.validation:validation-api": {
"locked": "2.0.0.CR3",
"requested": "2.0.0.CR3"
},
"junit:junit": {
"locked": "4.12",
"requested": "4.12"
},
"org.apache.cassandra:cassandra-all": {
"locked": "3.11.0",
"requested": "3.11.0"
},
"org.apache.commons:commons-lang3": {
"locked": "3.6",
"requested": "3.6"
},
"org.aspectj:aspectjrt": {
"locked": "1.8.10",
"requested": "1.8.10"
},
"org.aspectj:aspectjweaver": {
"locked": "1.8.10",
"requested": "1.8.10"
},
"org.cassandraunit:cassandra-unit": {
"locked": "3.1.4.0-SNAPSHOT",
"requested": "3.1.4.0-SNAPSHOT"
},
"org.codehaus.jackson:jackson-core-asl": {
"locked": "1.9.13",
"requested": "1.9.13"
},
"org.codehaus.jackson:jackson-mapper-asl": {
"locked": "1.9.13",
"requested": "1.9.13"
},
"org.hamcrest:hamcrest-core": {
"locked": "1.3",
"requested": "1.3"
},
"org.hamcrest:hamcrest-library": {
"locked": "1.3",
"requested": "1.3"
},
"org.mockito:mockito-core": {
"locked": "2.8.47",
"requested": "2.8.47"
},
"org.slf4j:jcl-over-slf4j": {
"locked": "1.7.7",
"requested": "1.7.1"
},
"org.slf4j:slf4j-api": {
"locked": "1.7.25",
"requested": "1.7.1"
},
"org.springframework:spring-core": {
"locked": "4.3.10.RELEASE",
"requested": "4.3.10.RELEASE"
}
},
"testRuntimeClasspath": {
"com.anthemengineering.mojo:infer-maven-plugin": {
"locked": "0.1.0",
"requested": "0.1.0"
},
"com.datastax.cassandra:cassandra-driver-core": {
"locked": "3.3.0",
"requested": "3.3.0"
},
"com.diffplug.durian:durian": {
"locked": "3.5.0-SNAPSHOT",
"requested": "3.+"
},
"com.github.stephenc:jamm": {
"locked": "0.2.5",
"requested": "0.2.5"
},
"com.google.guava:guava": {
"locked": "21.0",
"requested": "20.0"
},
"commons-io:commons-io": {
"locked": "2.5",
"requested": "2.5"
},
"io.dropwizard.metrics:metrics-core": {
"locked": "3.2.2",
"requested": "3.2.2"
},
"io.zipkin.brave:brave": {
"locked": "4.0.6",
"requested": "4.0.6"
},
"io.zipkin.java:zipkin": {
"locked": "1.29.2",
"requested": "1.29.2"
},
"javax.validation:validation-api": {
"locked": "2.0.0.CR3",
"requested": "2.0.0.CR3"
},
"junit:junit": {
"locked": "4.12",
"requested": "4.12"
},
"org.apache.cassandra:cassandra-all": {
"locked": "3.11.0",
"requested": "3.11.0"
},
"org.apache.commons:commons-lang3": {
"locked": "3.6",
"requested": "3.6"
},
"org.aspectj:aspectjrt": {
"locked": "1.8.10",
"requested": "1.8.10"
},
"org.aspectj:aspectjweaver": {
"locked": "1.8.10",
"requested": "1.8.10"
},
"org.cassandraunit:cassandra-unit": {
"locked": "3.1.4.0-SNAPSHOT",
"requested": "3.1.4.0-SNAPSHOT"
},
"org.codehaus.jackson:jackson-core-asl": {
"locked": "1.9.13",
"requested": "1.9.13"
},
"org.codehaus.jackson:jackson-mapper-asl": {
"locked": "1.9.13",
"requested": "1.9.13"
},
"org.hamcrest:hamcrest-core": {
"locked": "1.3",
"requested": "1.3"
},
"org.hamcrest:hamcrest-library": {
"locked": "1.3",
"requested": "1.3"
},
"org.mockito:mockito-core": {
"locked": "2.8.47",
"requested": "2.8.47"
},
"org.slf4j:jcl-over-slf4j": {
"locked": "1.7.7",
"requested": "1.7.1"
},
"org.slf4j:slf4j-api": {
"locked": "1.7.25",
"requested": "1.7.1"
},
"org.springframework:spring-core": {
"locked": "4.3.10.RELEASE",
"requested": "4.3.10.RELEASE"
}
}
}

View file

@ -11,7 +11,7 @@
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: com.datastax.cassandra:cassandra-driver-core:3.3.0" level="project" /> <orderEntry type="library" name="Maven: com.datastax.cassandra:cassandra-driver-core:3.3.2" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-handler:4.0.47.Final" level="project" /> <orderEntry type="library" name="Maven: io.netty:netty-handler:4.0.47.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-buffer:4.0.47.Final" level="project" /> <orderEntry type="library" name="Maven: io.netty:netty-buffer:4.0.47.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-common:4.0.47.Final" level="project" /> <orderEntry type="library" name="Maven: io.netty:netty-common:4.0.47.Final" level="project" />
@ -28,16 +28,14 @@
<orderEntry type="library" name="Maven: com.github.jnr:jnr-x86asm:1.0.2" level="project" /> <orderEntry type="library" name="Maven: com.github.jnr:jnr-x86asm:1.0.2" level="project" />
<orderEntry type="library" name="Maven: com.github.jnr:jnr-posix:3.0.27" level="project" /> <orderEntry type="library" name="Maven: com.github.jnr:jnr-posix:3.0.27" level="project" />
<orderEntry type="library" name="Maven: com.github.jnr:jnr-constants:0.9.0" level="project" /> <orderEntry type="library" name="Maven: com.github.jnr:jnr-constants:0.9.0" level="project" />
<orderEntry type="library" name="Maven: com.datastax.cassandra:cassandra-driver-extras:3.3.2" level="project" />
<orderEntry type="library" name="Maven: com.diffplug.durian:durian:3.4.0" level="project" /> <orderEntry type="library" name="Maven: com.diffplug.durian:durian:3.4.0" level="project" />
<orderEntry type="library" name="Maven: org.aspectj:aspectjrt:1.8.10" level="project" />
<orderEntry type="library" name="Maven: org.aspectj:aspectjweaver:1.8.10" level="project" /> <orderEntry type="library" name="Maven: org.aspectj:aspectjweaver:1.8.10" level="project" />
<orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.6" level="project" /> <orderEntry type="library" name="Maven: org.apache.commons:commons-lang3:3.6" level="project" />
<orderEntry type="library" name="Maven: org.springframework:spring-core:4.3.10.RELEASE" level="project" /> <orderEntry type="library" name="Maven: org.springframework:spring-core:4.3.10.RELEASE" level="project" />
<orderEntry type="library" name="Maven: commons-logging:commons-logging:1.2" level="project" /> <orderEntry type="library" name="Maven: commons-logging:commons-logging:1.2" level="project" />
<orderEntry type="library" name="Maven: javax.cache:cache-api:1.1.0" level="project" />
<orderEntry type="library" name="Maven: com.google.guava:guava:20.0" level="project" /> <orderEntry type="library" name="Maven: com.google.guava:guava:20.0" level="project" />
<orderEntry type="library" name="Maven: io.zipkin.java:zipkin:1.29.2" level="project" />
<orderEntry type="library" name="Maven: io.zipkin.brave:brave:4.0.6" level="project" />
<orderEntry type="library" name="Maven: io.zipkin.reporter:zipkin-reporter:0.6.12" level="project" />
<orderEntry type="library" name="Maven: io.dropwizard.metrics:metrics-core:3.2.2" level="project" /> <orderEntry type="library" name="Maven: io.dropwizard.metrics:metrics-core:3.2.2" level="project" />
<orderEntry type="library" name="Maven: javax.validation:validation-api:2.0.0.CR3" level="project" /> <orderEntry type="library" name="Maven: javax.validation:validation-api:2.0.0.CR3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.codehaus.jackson:jackson-mapper-asl:1.9.13" level="project" /> <orderEntry type="library" scope="TEST" name="Maven: org.codehaus.jackson:jackson-mapper-asl:1.9.13" level="project" />
@ -116,20 +114,15 @@
<orderEntry type="library" scope="TEST" name="Maven: org.caffinitas.ohc:ohc-core:0.4.4" level="project" /> <orderEntry type="library" scope="TEST" name="Maven: org.caffinitas.ohc:ohc-core:0.4.4" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.github.ben-manes.caffeine:caffeine:2.2.6" level="project" /> <orderEntry type="library" scope="TEST" name="Maven: com.github.ben-manes.caffeine:caffeine:2.2.6" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.jctools:jctools-core:1.2.1" level="project" /> <orderEntry type="library" scope="TEST" name="Maven: org.jctools:jctools-core:1.2.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: ca.exprofesso:guava-jcache:1.0.4" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: commons-io:commons-io:2.5" level="project" /> <orderEntry type="library" scope="TEST" name="Maven: commons-io:commons-io:2.5" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.12" level="project" /> <orderEntry type="library" scope="TEST" name="Maven: junit:junit:4.12" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.github.stephenc:jamm:0.2.5" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-library:1.3" level="project" /> <orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-library:1.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" /> <orderEntry type="library" scope="TEST" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-core:2.8.47" level="project" /> <orderEntry type="library" scope="TEST" name="Maven: org.mockito:mockito-core:2.8.47" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.bytebuddy:byte-buddy:1.6.14" level="project" /> <orderEntry type="library" scope="TEST" name="Maven: net.bytebuddy:byte-buddy:1.6.14" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: net.bytebuddy:byte-buddy-agent:1.6.14" level="project" /> <orderEntry type="library" scope="TEST" name="Maven: net.bytebuddy:byte-buddy-agent:1.6.14" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: org.objenesis:objenesis:2.5" level="project" /> <orderEntry type="library" scope="TEST" name="Maven: org.objenesis:objenesis:2.5" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.github.ben-manes.caffeine:jcache:2.5.6" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: javax.cache:cache-api:1.0.0" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.typesafe:config:1.3.1" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: javax.inject:javax.inject:1" level="project" />
<orderEntry type="library" name="Maven: net.spy:spymemcached:2.12.3" level="project" />
<orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.1" level="project" /> <orderEntry type="library" name="Maven: org.slf4j:slf4j-api:1.7.1" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: org.slf4j:jcl-over-slf4j:1.7.1" level="project" /> <orderEntry type="library" scope="RUNTIME" name="Maven: org.slf4j:jcl-over-slf4j:1.7.1" level="project" />
</component> </component>

74
pom.xml
View file

@ -109,7 +109,13 @@
<dependency> <dependency>
<groupId>com.datastax.cassandra</groupId> <groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-core</artifactId> <artifactId>cassandra-driver-core</artifactId>
<version>3.3.0</version> <version>3.3.2</version>
</dependency>
<dependency>
<groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-extras</artifactId>
<version>3.3.2</version>
</dependency> </dependency>
<dependency> <dependency>
@ -118,12 +124,6 @@
<version>3.4.0</version> <version>3.4.0</version>
</dependency> </dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.10</version>
</dependency>
<dependency> <dependency>
<groupId>org.aspectj</groupId> <groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId> <artifactId>aspectjweaver</artifactId>
@ -142,25 +142,19 @@
<version>4.3.10.RELEASE</version> <version>4.3.10.RELEASE</version>
</dependency> </dependency>
<dependency>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
<version>1.1.0</version>
</dependency>
<dependency> <dependency>
<groupId>com.google.guava</groupId> <groupId>com.google.guava</groupId>
<artifactId>guava</artifactId> <artifactId>guava</artifactId>
<version>20.0</version> <version>20.0</version>
</dependency> </dependency>
<!-- Metrics and tracing --> <!-- Metrics -->
<dependency>
<groupId>io.zipkin.java</groupId>
<artifactId>zipkin</artifactId>
<version>1.29.2</version>
</dependency>
<dependency>
<groupId>io.zipkin.brave</groupId>
<artifactId>brave</artifactId>
<version>4.0.6</version>
</dependency>
<dependency> <dependency>
<groupId>io.dropwizard.metrics</groupId> <groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId> <artifactId>metrics-core</artifactId>
@ -217,6 +211,24 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>ca.exprofesso</groupId>
<artifactId>guava-jcache</artifactId>
<version>1.0.4</version>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
<exclusion>
<groupId>javax.cache</groupId>
<artifactId>cache-api</artifactId>
</exclusion>
</exclusions>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>commons-io</groupId> <groupId>commons-io</groupId>
<artifactId>commons-io</artifactId> <artifactId>commons-io</artifactId>
@ -231,13 +243,6 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>com.github.stephenc</groupId>
<artifactId>jamm</artifactId>
<version>0.2.5</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.hamcrest</groupId> <groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId> <artifactId>hamcrest-library</artifactId>
@ -259,20 +264,6 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<!-- Caching -->
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>jcache</artifactId>
<version>2.5.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>net.spy</groupId>
<artifactId>spymemcached</artifactId>
<version>2.12.3</version>
</dependency>
<!-- Logging --> <!-- Logging -->
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
@ -286,7 +277,6 @@
<version>1.7.1</version> <version>1.7.1</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build> <build>

View file

@ -1 +0,0 @@
rootProject.name = 'helenus-core'

View file

@ -5,19 +5,19 @@ import java.util.List;
public class DefaultMetadata extends Metadata { public class DefaultMetadata extends Metadata {
public DefaultMetadata() { public DefaultMetadata() {
super(null); super(null);
} }
private DefaultMetadata(Cluster.Manager cluster) { private DefaultMetadata(Cluster.Manager cluster) {
super(cluster); super(cluster);
} }
public TupleType newTupleType(DataType... types) { public TupleType newTupleType(DataType... types) {
return newTupleType(Arrays.asList(types)); return newTupleType(Arrays.asList(types));
} }
public TupleType newTupleType(List<DataType> types) { public TupleType newTupleType(List<DataType> types) {
return new TupleType(types, ProtocolVersion.NEWEST_SUPPORTED, CodecRegistry.DEFAULT_INSTANCE); return new TupleType(types, ProtocolVersion.NEWEST_SUPPORTED, CodecRegistry.DEFAULT_INSTANCE);
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,35 +16,34 @@
*/ */
package com.datastax.driver.core.querybuilder; package com.datastax.driver.core.querybuilder;
import java.util.List;
import com.datastax.driver.core.CodecRegistry; import com.datastax.driver.core.CodecRegistry;
import java.util.List;
public class IsNotNullClause extends Clause { public class IsNotNullClause extends Clause {
final String name; final String name;
public IsNotNullClause(String name) { public IsNotNullClause(String name) {
this.name = name; this.name = name;
} }
@Override @Override
String name() { String name() {
return name; return name;
} }
@Override @Override
Object firstValue() { Object firstValue() {
return null; return null;
} }
@Override @Override
void appendTo(StringBuilder sb, List<Object> variables, CodecRegistry codecRegistry) { void appendTo(StringBuilder sb, List<Object> variables, CodecRegistry codecRegistry) {
Utils.appendName(name, sb).append(" IS NOT NULL"); Utils.appendName(name, sb).append(" IS NOT NULL");
} }
@Override @Override
boolean containsBindMarker() { boolean containsBindMarker() {
return false; return false;
} }
} }

View file

@ -6,143 +6,150 @@ import com.google.common.base.Optional;
public class CreateCustomIndex extends CreateIndex { public class CreateCustomIndex extends CreateIndex {
private String indexName; private String indexName;
private boolean ifNotExists = false; private boolean ifNotExists = false;
private Optional<String> keyspaceName = Optional.absent(); private Optional<String> keyspaceName = Optional.absent();
private String tableName; private String tableName;
private String columnName; private String columnName;
private boolean keys; private boolean keys;
CreateCustomIndex(String indexName) { CreateCustomIndex(String indexName) {
super(indexName); super(indexName);
validateNotEmpty(indexName, "Index name"); validateNotEmpty(indexName, "Index name");
validateNotKeyWord(indexName, validateNotKeyWord(
String.format("The index name '%s' is not allowed because it is a reserved keyword", indexName)); indexName,
this.indexName = indexName; String.format(
} "The index name '%s' is not allowed because it is a reserved keyword", indexName));
this.indexName = indexName;
}
/** /**
* Add the 'IF NOT EXISTS' condition to this CREATE INDEX statement. * Add the 'IF NOT EXISTS' condition to this CREATE INDEX statement.
* *
* @return this CREATE INDEX statement. * @return this CREATE INDEX statement.
*/ */
public CreateIndex ifNotExists() { public CreateIndex ifNotExists() {
this.ifNotExists = true; this.ifNotExists = true;
return this; return this;
} }
/** /**
* Specify the keyspace and table to create the index on. * Specify the keyspace and table to create the index on.
* *
* @param keyspaceName * @param keyspaceName the keyspace name.
* the keyspace name. * @param tableName the table name.
* @param tableName * @return a {@link CreateIndex.CreateIndexOn} that will allow the specification of the column.
* the table name. */
* @return a {@link CreateIndex.CreateIndexOn} that will allow the specification public CreateIndex.CreateIndexOn onTable(String keyspaceName, String tableName) {
* of the column. validateNotEmpty(keyspaceName, "Keyspace name");
*/ validateNotEmpty(tableName, "Table name");
public CreateIndex.CreateIndexOn onTable(String keyspaceName, String tableName) { validateNotKeyWord(
validateNotEmpty(keyspaceName, "Keyspace name"); keyspaceName,
validateNotEmpty(tableName, "Table name"); String.format(
validateNotKeyWord(keyspaceName, "The keyspace name '%s' is not allowed because it is a reserved keyword",
String.format("The keyspace name '%s' is not allowed because it is a reserved keyword", keyspaceName)); keyspaceName));
validateNotKeyWord(tableName, validateNotKeyWord(
String.format("The table name '%s' is not allowed because it is a reserved keyword", tableName)); tableName,
this.keyspaceName = Optional.fromNullable(keyspaceName); String.format(
this.tableName = tableName; "The table name '%s' is not allowed because it is a reserved keyword", tableName));
return new CreateCustomIndex.CreateIndexOn(); this.keyspaceName = Optional.fromNullable(keyspaceName);
} this.tableName = tableName;
return new CreateCustomIndex.CreateIndexOn();
}
/** /**
* Specify the table to create the index on. * Specify the table to create the index on.
* *
* @param tableName * @param tableName the table name.
* the table name. * @return a {@link CreateIndex.CreateIndexOn} that will allow the specification of the column.
* @return a {@link CreateIndex.CreateIndexOn} that will allow the specification */
* of the column. public CreateIndex.CreateIndexOn onTable(String tableName) {
*/ validateNotEmpty(tableName, "Table name");
public CreateIndex.CreateIndexOn onTable(String tableName) { validateNotKeyWord(
validateNotEmpty(tableName, "Table name"); tableName,
validateNotKeyWord(tableName, String.format(
String.format("The table name '%s' is not allowed because it is a reserved keyword", tableName)); "The table name '%s' is not allowed because it is a reserved keyword", tableName));
this.tableName = tableName; this.tableName = tableName;
return new CreateCustomIndex.CreateIndexOn(); return new CreateCustomIndex.CreateIndexOn();
} }
String getCustomClassName() { String getCustomClassName() {
return ""; return "";
} }
String getOptions() { String getOptions() {
return ""; return "";
} }
@Override @Override
public String buildInternal() { public String buildInternal() {
StringBuilder createStatement = new StringBuilder(STATEMENT_START).append("CREATE CUSTOM INDEX "); StringBuilder createStatement =
new StringBuilder(STATEMENT_START).append("CREATE CUSTOM INDEX ");
if (ifNotExists) { if (ifNotExists) {
createStatement.append("IF NOT EXISTS "); createStatement.append("IF NOT EXISTS ");
} }
createStatement.append(indexName).append(" ON "); createStatement.append(indexName).append(" ON ");
if (keyspaceName.isPresent()) { if (keyspaceName.isPresent()) {
createStatement.append(keyspaceName.get()).append("."); createStatement.append(keyspaceName.get()).append(".");
} }
createStatement.append(tableName); createStatement.append(tableName);
createStatement.append("("); createStatement.append("(");
if (keys) { if (keys) {
createStatement.append("KEYS("); createStatement.append("KEYS(");
} }
createStatement.append(columnName); createStatement.append(columnName);
if (keys) { if (keys) {
createStatement.append(")"); createStatement.append(")");
} }
createStatement.append(")"); createStatement.append(")");
createStatement.append(" USING '"); createStatement.append(" USING '");
createStatement.append(getCustomClassName()); createStatement.append(getCustomClassName());
createStatement.append("' WITH OPTIONS = {"); createStatement.append("' WITH OPTIONS = {");
createStatement.append(getOptions()); createStatement.append(getOptions());
createStatement.append(" }"); createStatement.append(" }");
return createStatement.toString(); return createStatement.toString();
} }
public class CreateIndexOn extends CreateIndex.CreateIndexOn { public class CreateIndexOn extends CreateIndex.CreateIndexOn {
/** /**
* Specify the column to create the index on. * Specify the column to create the index on.
* *
* @param columnName * @param columnName the column name.
* the column name. * @return the final CREATE INDEX statement.
* @return the final CREATE INDEX statement. */
*/ public SchemaStatement andColumn(String columnName) {
public SchemaStatement andColumn(String columnName) { validateNotEmpty(columnName, "Column name");
validateNotEmpty(columnName, "Column name"); validateNotKeyWord(
validateNotKeyWord(columnName, columnName,
String.format("The column name '%s' is not allowed because it is a reserved keyword", columnName)); String.format(
CreateCustomIndex.this.columnName = columnName; "The column name '%s' is not allowed because it is a reserved keyword", columnName));
return SchemaStatement.fromQueryString(buildInternal()); CreateCustomIndex.this.columnName = columnName;
} return SchemaStatement.fromQueryString(buildInternal());
}
/** /**
* Create an index on the keys of the given map column. * Create an index on the keys of the given map column.
* *
* @param columnName * @param columnName the column name.
* the column name. * @return the final CREATE INDEX statement.
* @return the final CREATE INDEX statement. */
*/ public SchemaStatement andKeysOfColumn(String columnName) {
public SchemaStatement andKeysOfColumn(String columnName) { validateNotEmpty(columnName, "Column name");
validateNotEmpty(columnName, "Column name"); validateNotKeyWord(
validateNotKeyWord(columnName, columnName,
String.format("The column name '%s' is not allowed because it is a reserved keyword", columnName)); String.format(
CreateCustomIndex.this.columnName = columnName; "The column name '%s' is not allowed because it is a reserved keyword", columnName));
CreateCustomIndex.this.keys = true; CreateCustomIndex.this.columnName = columnName;
return SchemaStatement.fromQueryString(buildInternal()); CreateCustomIndex.this.keys = true;
} return SchemaStatement.fromQueryString(buildInternal());
} }
}
} }

View file

@ -5,48 +5,53 @@ import com.datastax.driver.core.querybuilder.Select;
public class CreateMaterializedView extends Create { public class CreateMaterializedView extends Create {
private String viewName; private final String viewName;
private Select.Where selection; private Select.Where selection;
private String primaryKey; private String primaryKey;
private String clustering; private String clustering;
public CreateMaterializedView(String keyspaceName, String viewName, Select.Where selection, String primaryKey, public CreateMaterializedView(
String clustering) { String keyspaceName,
super(keyspaceName, viewName); String viewName,
this.viewName = viewName; Select.Where selection,
this.selection = selection; String primaryKey,
this.primaryKey = primaryKey; String clustering) {
this.clustering = clustering; super(keyspaceName, viewName);
} this.viewName = viewName;
this.selection = selection;
this.primaryKey = primaryKey;
this.clustering = clustering;
}
public String getQueryString(CodecRegistry codecRegistry) { public String getQueryString(CodecRegistry codecRegistry) {
return buildInternal(); return buildInternal();
} }
public String buildInternal() { public String buildInternal() {
StringBuilder createStatement = new StringBuilder(STATEMENT_START).append("CREATE MATERIALIZED VIEW"); StringBuilder createStatement =
if (ifNotExists) { new StringBuilder(STATEMENT_START).append("CREATE MATERIALIZED VIEW");
createStatement.append(" IF NOT EXISTS"); if (ifNotExists) {
} createStatement.append(" IF NOT EXISTS");
createStatement.append(" "); }
if (keyspaceName.isPresent()) { createStatement.append(" ");
createStatement.append(keyspaceName.get()).append("."); if (keyspaceName.isPresent()) {
} createStatement.append(keyspaceName.get()).append(".");
createStatement.append(viewName); }
createStatement.append(" AS "); createStatement.append(viewName);
createStatement.append(selection.getQueryString()); createStatement.append(" AS ");
createStatement.setLength(createStatement.length() - 1); createStatement.append(selection.getQueryString());
createStatement.append(" "); createStatement.setLength(createStatement.length() - 1);
createStatement.append(primaryKey); createStatement.append(" ");
if (clustering != null) { createStatement.append(primaryKey);
createStatement.append(" ").append(clustering); if (clustering != null) {
} createStatement.append(" ").append(clustering);
createStatement.append(";"); }
createStatement.append(";");
return createStatement.toString(); return createStatement.toString();
} }
public String toString() { public String toString() {
return buildInternal(); return buildInternal();
} }
} }

View file

@ -2,16 +2,17 @@ package com.datastax.driver.core.schemabuilder;
public class CreateSasiIndex extends CreateCustomIndex { public class CreateSasiIndex extends CreateCustomIndex {
public CreateSasiIndex(String indexName) { public CreateSasiIndex(String indexName) {
super(indexName); super(indexName);
} }
String getCustomClassName() { String getCustomClassName() {
return "org.apache.cassandra.index.sasi.SASIIndex"; return "org.apache.cassandra.index.sasi.SASIIndex";
} }
String getOptions() { String getOptions() {
return "'analyzer_class': " + "'org.apache.cassandra.index.sasi.analyzer.NonTokenizingAnalyzer', " return "'analyzer_class': "
+ "'case_sensitive': 'false'"; + "'org.apache.cassandra.index.sasi.analyzer.NonTokenizingAnalyzer', "
} + "'case_sensitive': 'false'";
}
} }

View file

@ -20,19 +20,19 @@ import com.datastax.driver.core.CodecRegistry;
/** A built CREATE TABLE statement. */ /** A built CREATE TABLE statement. */
public class CreateTable extends Create { public class CreateTable extends Create {
public CreateTable(String keyspaceName, String tableName) { public CreateTable(String keyspaceName, String tableName) {
super(keyspaceName, tableName); super(keyspaceName, tableName);
} }
public CreateTable(String tableName) { public CreateTable(String tableName) {
super(tableName); super(tableName);
} }
public String getQueryString(CodecRegistry codecRegistry) { public String getQueryString(CodecRegistry codecRegistry) {
return buildInternal(); return buildInternal();
} }
public String toString() { public String toString() {
return buildInternal(); return buildInternal();
} }
} }

View file

@ -4,46 +4,49 @@ import com.google.common.base.Optional;
public class DropMaterializedView extends Drop { public class DropMaterializedView extends Drop {
private final String itemType = "MATERIALIZED VIEW"; private Optional<String> keyspaceName = Optional.absent();
private Optional<String> keyspaceName = Optional.absent(); private String itemName;
private String itemName; private boolean ifExists = true;
private boolean ifExists = true;
public DropMaterializedView(String keyspaceName, String viewName) {
this(keyspaceName, viewName, DroppedItem.MATERIALIZED_VIEW);
}
private DropMaterializedView(String keyspaceName, String viewName, DroppedItem itemType) { public DropMaterializedView(String keyspaceName, String viewName) {
super(keyspaceName, viewName, Drop.DroppedItem.TABLE); this(keyspaceName, viewName, DroppedItem.MATERIALIZED_VIEW);
validateNotEmpty(keyspaceName, "Keyspace name"); }
this.keyspaceName = Optional.fromNullable(keyspaceName);
this.itemName = viewName;
}
/** private DropMaterializedView(String keyspaceName, String viewName, DroppedItem itemType) {
* Add the 'IF EXISTS' condition to this DROP statement. super(keyspaceName, viewName, Drop.DroppedItem.TABLE);
* validateNotEmpty(keyspaceName, "Keyspace name");
* @return this statement. this.keyspaceName = Optional.fromNullable(keyspaceName);
*/ this.itemName = viewName;
public Drop ifExists() { }
this.ifExists = true;
return this;
}
@Override /**
public String buildInternal() { * Add the 'IF EXISTS' condition to this DROP statement.
StringBuilder dropStatement = new StringBuilder("DROP " + itemType + " "); *
if (ifExists) { * @return this statement.
dropStatement.append("IF EXISTS "); */
} public Drop ifExists() {
if (keyspaceName.isPresent()) { this.ifExists = true;
dropStatement.append(keyspaceName.get()).append("."); return this;
} }
dropStatement.append(itemName); @Override
return dropStatement.toString(); public String buildInternal() {
} StringBuilder dropStatement = new StringBuilder("DROP MATERIALIZED VIEW ");
if (ifExists) {
dropStatement.append("IF EXISTS ");
}
if (keyspaceName.isPresent()) {
dropStatement.append(keyspaceName.get()).append(".");
}
enum DroppedItem { dropStatement.append(itemName);
TABLE, TYPE, INDEX, MATERIALIZED_VIEW return dropStatement.toString();
} }
enum DroppedItem {
TABLE,
TYPE,
INDEX,
MATERIALIZED_VIEW
}
} }

View file

@ -1,48 +1,48 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* * Copyright (C) 2015-2018 The Helenus Authors
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. * Licensed under the Apache License, Version 2.0 (the "License");
* You may obtain a copy of the License at * 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 *
* * 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, * Unless required by applicable law or agreed to in writing, software
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * distributed under the License is distributed on an "AS IS" BASIS,
* See the License for the specific language governing permissions and * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* limitations under the License. * See the License for the specific language governing permissions and
*/ * limitations under the License.
package net.helenus.config; */
package net.helenus.config;
import java.lang.reflect.Method;
import java.util.function.Function; import java.lang.reflect.Method;
import java.util.function.Function;
import net.helenus.core.DslInstantiator; import net.helenus.core.DslInstantiator;
import net.helenus.core.MapperInstantiator; import net.helenus.core.MapperInstantiator;
import net.helenus.core.reflect.ReflectionDslInstantiator; import net.helenus.core.reflect.ReflectionDslInstantiator;
import net.helenus.core.reflect.ReflectionMapperInstantiator; import net.helenus.core.reflect.ReflectionMapperInstantiator;
import net.helenus.mapping.convert.CamelCaseToUnderscoreConverter; import net.helenus.mapping.convert.CamelCaseToUnderscoreConverter;
public class DefaultHelenusSettings implements HelenusSettings { public class DefaultHelenusSettings implements HelenusSettings {
@Override @Override
public Function<String, String> getPropertyToColumnConverter() { public Function<String, String> getPropertyToColumnConverter() {
return CamelCaseToUnderscoreConverter.INSTANCE; return CamelCaseToUnderscoreConverter.INSTANCE;
} }
@Override @Override
public Function<Method, Boolean> getGetterMethodDetector() { public Function<Method, Boolean> getGetterMethodDetector() {
return GetterMethodDetector.INSTANCE; return GetterMethodDetector.INSTANCE;
} }
@Override @Override
public DslInstantiator getDslInstantiator() { public DslInstantiator getDslInstantiator() {
return ReflectionDslInstantiator.INSTANCE; return ReflectionDslInstantiator.INSTANCE;
} }
@Override @Override
public MapperInstantiator getMapperInstantiator() { public MapperInstantiator getMapperInstantiator() {
return ReflectionMapperInstantiator.INSTANCE; return ReflectionMapperInstantiator.INSTANCE;
} }
} }

View file

@ -1,49 +1,49 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* * Copyright (C) 2015-2018 The Helenus Authors
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. * Licensed under the Apache License, Version 2.0 (the "License");
* You may obtain a copy of the License at * 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 *
* * 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, * Unless required by applicable law or agreed to in writing, software
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * distributed under the License is distributed on an "AS IS" BASIS,
* See the License for the specific language governing permissions and * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* limitations under the License. * See the License for the specific language governing permissions and
*/ * limitations under the License.
package net.helenus.config; */
package net.helenus.config;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Method;
import java.util.function.Function; import java.lang.reflect.Modifier;
import java.util.function.Function;
import net.helenus.mapping.annotation.Transient; import net.helenus.mapping.annotation.Transient;
public enum GetterMethodDetector implements Function<Method, Boolean> { public enum GetterMethodDetector implements Function<Method, Boolean> {
INSTANCE; INSTANCE;
@Override @Override
public Boolean apply(Method method) { public Boolean apply(Method method) {
if (method == null) { if (method == null) {
throw new IllegalArgumentException("empty parameter"); throw new IllegalArgumentException("empty parameter");
} }
if (method.getParameterCount() != 0 || method.getReturnType() == void.class) { if (method.getParameterCount() != 0 || method.getReturnType() == void.class) {
return false; return false;
} }
if (Modifier.isStatic(method.getModifiers())) { if (Modifier.isStatic(method.getModifiers())) {
return false; return false;
} }
// Methods marked "Transient" are not mapped, skip them. // Methods marked "Transient" are not mapped, skip them.
if (method.getDeclaredAnnotation(Transient.class) != null) { if (method.getDeclaredAnnotation(Transient.class) != null) {
return false; return false;
} }
return true; return true;
} }
} }

View file

@ -1,33 +1,33 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* * Copyright (C) 2015-2018 The Helenus Authors
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. * Licensed under the Apache License, Version 2.0 (the "License");
* You may obtain a copy of the License at * 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 *
* * 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, * Unless required by applicable law or agreed to in writing, software
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * distributed under the License is distributed on an "AS IS" BASIS,
* See the License for the specific language governing permissions and * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* limitations under the License. * See the License for the specific language governing permissions and
*/ * limitations under the License.
package net.helenus.config; */
package net.helenus.config;
import java.lang.reflect.Method;
import java.util.function.Function; import java.lang.reflect.Method;
import java.util.function.Function;
import net.helenus.core.DslInstantiator; import net.helenus.core.DslInstantiator;
import net.helenus.core.MapperInstantiator; import net.helenus.core.MapperInstantiator;
public interface HelenusSettings { public interface HelenusSettings {
Function<String, String> getPropertyToColumnConverter(); Function<String, String> getPropertyToColumnConverter();
Function<Method, Boolean> getGetterMethodDetector(); Function<Method, Boolean> getGetterMethodDetector();
DslInstantiator getDslInstantiator(); DslInstantiator getDslInstantiator();
MapperInstantiator getMapperInstantiator(); MapperInstantiator getMapperInstantiator();
} }

View file

@ -3,37 +3,36 @@ package net.helenus.core;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.ZoneId; import java.time.ZoneId;
import java.util.Date; import java.util.Date;
import net.helenus.core.reflect.MapExportable; import net.helenus.core.reflect.MapExportable;
public abstract class AbstractAuditedEntityDraft<E> extends AbstractEntityDraft<E> { public abstract class AbstractAuditedEntityDraft<E> extends AbstractEntityDraft<E> {
public AbstractAuditedEntityDraft(MapExportable entity) { public AbstractAuditedEntityDraft(MapExportable entity) {
super(entity); super(entity);
Date in = new Date(); Date in = new Date();
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault()); LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());
Date now = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant()); Date now = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
String who = getCurrentAuditor(); String who = getCurrentAuditor();
if (entity == null) { if (entity == null) {
if (who != null) { if (who != null) {
set("createdBy", who); set("createdBy", who);
} }
set("createdAt", now); set("createdAt", now);
} }
if (who != null) { if (who != null) {
set("modifiedBy", who); set("modifiedBy", who);
} }
set("modifiedAt", now); set("modifiedAt", now);
} }
protected String getCurrentAuditor() { protected String getCurrentAuditor() {
return null; return null;
} }
public Date createdAt() { public Date createdAt() {
return (Date) get("createdAt", Date.class); return get("createdAt", Date.class);
} }
} }

View file

@ -1,164 +1,202 @@
package net.helenus.core; package net.helenus.core;
import com.google.common.primitives.Primitives;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import com.google.common.primitives.Primitives;
import net.helenus.core.reflect.DefaultPrimitiveTypes; import net.helenus.core.reflect.DefaultPrimitiveTypes;
import net.helenus.core.reflect.Drafted; import net.helenus.core.reflect.Drafted;
import net.helenus.core.reflect.MapExportable; import net.helenus.core.reflect.MapExportable;
import net.helenus.mapping.HelenusProperty;
import net.helenus.mapping.MappingUtil; import net.helenus.mapping.MappingUtil;
import org.apache.commons.lang3.SerializationUtils;
public abstract class AbstractEntityDraft<E> implements Drafted<E> { public abstract class AbstractEntityDraft<E> implements Drafted<E> {
private final Map<String, Object> backingMap = new HashMap<String, Object>(); private final MapExportable entity;
private final MapExportable entity; private final Map<String, Object> valuesMap;
private final Map<String, Object> entityMap; private final Set<String> readSet;
private final Map<String, Object> mutationsMap = new HashMap<String, Object>();
public AbstractEntityDraft(MapExportable entity) { public AbstractEntityDraft(MapExportable entity) {
this.entity = entity; this.entity = entity;
this.entityMap = entity != null ? entity.toMap() : new HashMap<String, Object>(); // Entities can mutate their map.
} if (entity != null) {
this.valuesMap = entity.toMap(true);
this.readSet = entity.toReadSet();
} else {
this.valuesMap = new HashMap<String, Object>();
this.readSet = new HashSet<String>();
}
}
public abstract Class<E> getEntityClass(); public abstract Class<E> getEntityClass();
public E build() { public E build() {
return Helenus.map(getEntityClass(), toMap()); return Helenus.map(getEntityClass(), toMap());
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected <T> T get(Getter<T> getter, Class<?> returnType) { public <T> T get(Getter<T> getter, Class<?> returnType) {
return (T) get(this.<T>methodNameFor(getter), returnType); return (T) get(this.<T>methodNameFor(getter), returnType);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected <T> T get(String key, Class<?> returnType) { public <T> T get(String key, Class<?> returnType) {
T value = (T) backingMap.get(key); readSet.add(key);
T value = (T) mutationsMap.get(key);
if (value == null) { if (value == null) {
value = (T) entityMap.get(key); value = (T) valuesMap.get(key);
if (value == null) { if (value == null) {
if (Primitives.allPrimitiveTypes().contains(returnType)) { if (Primitives.allPrimitiveTypes().contains(returnType)) {
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType); DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType);
if (type == null) { if (type == null) {
throw new RuntimeException("unknown primitive type " + returnType); throw new RuntimeException("unknown primitive type " + returnType);
} }
return (T) type.getDefaultValue(); return (T) type.getDefaultValue();
} }
} } else {
} // Collections fetched from the valuesMap
if (value instanceof Collection) {
value = (T) SerializationUtils.<Serializable>clone((Serializable) value);
}
}
}
return value; return value;
} }
protected <T> Object set(Getter<T> getter, Object value) { public <T> Object set(Getter<T> getter, Object value) {
return set(this.<T>methodNameFor(getter), value); HelenusProperty prop = MappingUtil.resolveMappingProperty(getter).getProperty();
} String key = prop.getPropertyName();
protected Object set(String key, Object value) { HelenusValidator.INSTANCE.validate(prop, value);
if (key == null || value == null) {
return null;
}
backingMap.put(key, value); if (key == null || value == null) {
return value; return null;
} }
@SuppressWarnings("unchecked") mutationsMap.put(key, value);
protected <T> T mutate(Getter<T> getter, T value) { return value;
return (T) mutate(this.<T>methodNameFor(getter), value); }
}
protected Object mutate(String key, Object value) { public Object set(String key, Object value) {
Objects.requireNonNull(key); if (key == null || value == null) {
return null;
}
if (value == null) { mutationsMap.put(key, value);
return null; return value;
} }
if (entity != null) { public void put(String key, Object value) {
Map<String, Object> map = entity.toMap(); mutationsMap.put(key, value);
}
if (map.containsKey(key) && !value.equals(map.get(key))) { @SuppressWarnings("unchecked")
backingMap.put(key, value); public <T> T mutate(Getter<T> getter, T value) {
return value; return (T) mutate(this.<T>methodNameFor(getter), value);
} }
return map.get(key); public <T> T mutate(String key, T value) {
} else { Objects.requireNonNull(key);
backingMap.put(key, value);
return null; if (value != null) {
} if (entity != null) {
} T currentValue = this.<T>fetch(key);
if (!value.equals(currentValue)) {
mutationsMap.put(key, value);
return value;
}
} else {
mutationsMap.put(key, value);
}
}
return null;
}
private <T> String methodNameFor(Getter<T> getter) { private <T> String methodNameFor(Getter<T> getter) {
return MappingUtil.resolveMappingProperty(getter).getProperty().getPropertyName(); return MappingUtil.resolveMappingProperty(getter).getProperty().getPropertyName();
} }
public <T> Object unset(Getter<T> getter) { public <T> Object unset(Getter<T> getter) {
return unset(methodNameFor(getter)); return unset(methodNameFor(getter));
} }
public Object unset(String key) { public Object unset(String key) {
if (key != null) { if (key != null) {
Object value = backingMap.get(key); Object value = mutationsMap.get(key);
backingMap.put(key, null); mutationsMap.put(key, null);
return value; return value;
} }
return null; return null;
} }
public <T> boolean reset(Getter<T> getter, T desiredValue) { public <T> boolean reset(Getter<T> getter, T desiredValue) {
return this.<T>reset(this.<T>methodNameFor(getter), desiredValue); return this.<T>reset(this.<T>methodNameFor(getter), desiredValue);
} }
public <T> boolean reset(String key, T desiredValue) { private <T> T fetch(String key) {
if (key != null && desiredValue != null) { T value = (T) mutationsMap.get(key);
@SuppressWarnings("unchecked") if (value == null) {
T currentValue = (T) backingMap.get(key); value = (T) valuesMap.get(key);
if (currentValue == null || !currentValue.equals(desiredValue)) { }
set(key, desiredValue); return value;
return true; }
}
}
return false;
}
@Override public <T> boolean reset(String key, T desiredValue) {
public Map<String, Object> toMap() { if (key != null && desiredValue != null) {
return toMap(entityMap); @SuppressWarnings("unchecked")
} T currentValue = (T) this.<T>fetch(key);
if (currentValue == null || !currentValue.equals(desiredValue)) {
set(key, desiredValue);
return true;
}
}
return false;
}
public Map<String, Object> toMap(Map<String, Object> entityMap) { @Override
Map<String, Object> combined; public Map<String, Object> toMap() {
if (entityMap != null && entityMap.size() > 0) { return toMap(valuesMap);
combined = new HashMap<String, Object>(entityMap.size()); }
for (String key : entityMap.keySet()) {
combined.put(key, entityMap.get(key));
}
} else {
combined = new HashMap<String, Object>(backingMap.size());
}
for (String key : mutated()) {
combined.put(key, backingMap.get(key));
}
return combined;
}
@Override public Map<String, Object> toMap(Map<String, Object> entityMap) {
public Set<String> mutated() { Map<String, Object> combined;
return backingMap.keySet(); if (entityMap != null && entityMap.size() > 0) {
} combined = new HashMap<String, Object>(entityMap.size());
for (Map.Entry<String, Object> e : entityMap.entrySet()) {
combined.put(e.getKey(), e.getValue());
}
} else {
combined = new HashMap<String, Object>(mutationsMap.size());
}
for (String key : mutated()) {
combined.put(key, mutationsMap.get(key));
}
return combined;
}
@Override @Override
public String toString() { public Set<String> mutated() {
return backingMap.toString(); return mutationsMap.keySet();
} }
@Override
public Set<String> read() {
return readSet;
}
@Override
public String toString() {
return mutationsMap.toString();
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,143 +16,118 @@
*/ */
package net.helenus.core; package net.helenus.core;
import java.io.PrintStream;
import java.util.List;
import java.util.concurrent.Executor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.codahale.metrics.MetricRegistry; import com.codahale.metrics.MetricRegistry;
import com.datastax.driver.core.*; import com.datastax.driver.core.*;
import com.google.common.base.Stopwatch; import com.google.common.base.Stopwatch;
import com.google.common.collect.Table; import com.google.common.collect.Table;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import java.io.PrintStream;
import brave.Tracer; import java.util.List;
import java.util.concurrent.Executor;
import net.helenus.core.cache.Facet; import net.helenus.core.cache.Facet;
import net.helenus.core.operation.Operation;
import net.helenus.mapping.value.ColumnValuePreparer; import net.helenus.mapping.value.ColumnValuePreparer;
import net.helenus.mapping.value.ColumnValueProvider; import net.helenus.mapping.value.ColumnValueProvider;
import net.helenus.support.Either; import net.helenus.support.Either;
import net.helenus.support.HelenusException; import net.helenus.support.HelenusException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AbstractSessionOperations { public abstract class AbstractSessionOperations {
private static final Logger LOG = LoggerFactory.getLogger(AbstractSessionOperations.class); private static final Logger LOG = LoggerFactory.getLogger(AbstractSessionOperations.class);
public abstract Session currentSession(); public abstract Session currentSession();
public abstract String usingKeyspace(); public abstract String usingKeyspace();
public abstract boolean isShowCql(); public abstract boolean isShowCql();
public abstract PrintStream getPrintStream(); public abstract boolean showValues();
public abstract Executor getExecutor(); public abstract PrintStream getPrintStream();
public abstract SessionRepository getSessionRepository(); public abstract Executor getExecutor();
public abstract ColumnValueProvider getValueProvider(); public abstract SessionRepository getSessionRepository();
public abstract ColumnValuePreparer getValuePreparer(); public abstract ColumnValueProvider getValueProvider();
public abstract ConsistencyLevel getDefaultConsistencyLevel(); public abstract ColumnValuePreparer getValuePreparer();
public abstract boolean getDefaultQueryIdempotency(); public abstract ConsistencyLevel getDefaultConsistencyLevel();
public PreparedStatement prepare(RegularStatement statement) { public abstract boolean getDefaultQueryIdempotency();
try {
logStatement(statement, false);
return currentSession().prepare(statement);
} catch (RuntimeException e) {
throw translateException(e);
}
}
public ListenableFuture<PreparedStatement> prepareAsync(RegularStatement statement) { public PreparedStatement prepare(RegularStatement statement) {
try { try {
logStatement(statement, false); return currentSession().prepare(statement);
return currentSession().prepareAsync(statement); } catch (RuntimeException e) {
} catch (RuntimeException e) { throw translateException(e);
throw translateException(e); }
} }
}
public ResultSet execute(Statement statement, boolean showValues) { public ListenableFuture<PreparedStatement> prepareAsync(RegularStatement statement) {
return execute(statement, null, null, showValues); try {
} return currentSession().prepareAsync(statement);
} catch (RuntimeException e) {
throw translateException(e);
}
}
public ResultSet execute(Statement statement, Stopwatch timer, boolean showValues) { public ResultSet execute(Statement statement) {
return execute(statement, null, timer, showValues); return execute(statement, null, null);
} }
public ResultSet execute(Statement statement, UnitOfWork uow, boolean showValues) { public ResultSet execute(Statement statement, Stopwatch timer) {
return execute(statement, uow, null, showValues); return execute(statement, null, timer);
} }
public ResultSet execute(Statement statement, UnitOfWork uow, Stopwatch timer, boolean showValues) { public ResultSet execute(Statement statement, UnitOfWork uow) {
return executeAsync(statement, uow, timer, showValues).getUninterruptibly(); return execute(statement, uow, null);
} }
public ResultSetFuture executeAsync(Statement statement, boolean showValues) { public ResultSet execute(Statement statement, UnitOfWork uow, Stopwatch timer) {
return executeAsync(statement, null, null, showValues); return executeAsync(statement, uow, timer).getUninterruptibly();
} }
public ResultSetFuture executeAsync(Statement statement, Stopwatch timer, boolean showValues) { public ResultSetFuture executeAsync(Statement statement) {
return executeAsync(statement, null, timer, showValues); return executeAsync(statement, null, null);
} }
public ResultSetFuture executeAsync(Statement statement, UnitOfWork uow, boolean showValues) { public ResultSetFuture executeAsync(Statement statement, Stopwatch timer) {
return executeAsync(statement, uow, null, showValues); return executeAsync(statement, null, timer);
} }
public ResultSetFuture executeAsync(Statement statement, UnitOfWork uow, Stopwatch timer, boolean showValues) { public ResultSetFuture executeAsync(Statement statement, UnitOfWork uow) {
try { return executeAsync(statement, uow, null);
logStatement(statement, showValues); }
return currentSession().executeAsync(statement);
} catch (RuntimeException e) {
throw translateException(e);
}
}
private void logStatement(Statement statement, boolean showValues) { public ResultSetFuture executeAsync(Statement statement, UnitOfWork uow, Stopwatch timer) {
if (isShowCql()) { try {
printCql(Operation.queryString(statement, showValues)); return currentSession().executeAsync(statement);
} else if (LOG.isInfoEnabled()) { } catch (RuntimeException e) {
LOG.info("CQL> " + Operation.queryString(statement, showValues)); throw translateException(e);
} }
} }
public Tracer getZipkinTracer() { public MetricRegistry getMetricRegistry() {
return null; return null;
} }
public MetricRegistry getMetricRegistry() { public void mergeCache(Table<String, String, Either<Object, List<Facet>>> uowCache) {}
return null;
}
public void mergeCache(Table<String, String, Either<Object, List<Facet>>> uowCache) { RuntimeException translateException(RuntimeException e) {
} if (e instanceof HelenusException) {
return e;
}
throw new HelenusException(e);
}
RuntimeException translateException(RuntimeException e) { public Object checkCache(String tableName, List<Facet> facets) {
if (e instanceof HelenusException) { return null;
return e; }
}
throw new HelenusException(e);
}
public Object checkCache(String tableName, List<Facet> facets) { public void updateCache(Object pojo, List<Facet> facets) {}
return null;
}
public void updateCache(Object pojo, List<Facet> facets) { public void cacheEvict(List<Facet> facets) {}
}
void printCql(String cql) {
getPrintStream().println(cql);
}
public void cacheEvict(List<Facet> facets) {
}
} }

View file

@ -1,354 +0,0 @@
/*
* Copyright (C) 2015 The Helenus 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 net.helenus.core;
import static net.helenus.core.HelenusSession.deleted;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.diffplug.common.base.Errors;
import com.google.common.base.Stopwatch;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.google.common.collect.TreeTraverser;
import net.helenus.core.cache.CacheUtil;
import net.helenus.core.cache.Facet;
import net.helenus.support.Either;
/** Encapsulates the concept of a "transaction" as a unit-of-work. */
public abstract class AbstractUnitOfWork<E extends Exception> implements UnitOfWork<E>, AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(AbstractUnitOfWork.class);
private final List<AbstractUnitOfWork<E>> nested = new ArrayList<>();
private final HelenusSession session;
private final AbstractUnitOfWork<E> parent;
private final Table<String, String, Either<Object, List<Facet>>> cache = HashBasedTable.create();
protected String purpose;
protected int cacheHits = 0;
protected int cacheMisses = 0;
protected int databaseLookups = 0;
protected Stopwatch elapsedTime;
protected Map<String, Double> databaseTime = new HashMap<>();
protected double cacheLookupTime = 0.0;
private List<CommitThunk> postCommit = new ArrayList<CommitThunk>();
private boolean aborted = false;
private boolean committed = false;
protected AbstractUnitOfWork(HelenusSession session, AbstractUnitOfWork<E> parent) {
Objects.requireNonNull(session, "containing session cannot be null");
this.session = session;
this.parent = parent;
}
@Override
public void addDatabaseTime(String name, Stopwatch amount) {
Double time = databaseTime.get(name);
if (time == null) {
databaseTime.put(name, (double) amount.elapsed(TimeUnit.MICROSECONDS));
} else {
databaseTime.put(name, time + amount.elapsed(TimeUnit.MICROSECONDS));
}
}
@Override
public void addCacheLookupTime(Stopwatch amount) {
cacheLookupTime += amount.elapsed(TimeUnit.MICROSECONDS);
}
@Override
public void addNestedUnitOfWork(UnitOfWork<E> uow) {
synchronized (nested) {
nested.add((AbstractUnitOfWork<E>) uow);
}
}
@Override
public synchronized UnitOfWork<E> begin() {
elapsedTime = Stopwatch.createStarted();
// log.recordCacheAndDatabaseOperationCount(txn::start)
return this;
}
@Override
public UnitOfWork setPurpose(String purpose) {
this.purpose = purpose;
return this;
}
@Override
public void recordCacheAndDatabaseOperationCount(int cache, int ops) {
if (cache > 0) {
cacheHits += cache;
} else {
cacheMisses += Math.abs(cache);
}
if (ops > 0) {
databaseLookups += ops;
}
}
public String logTimers(String what) {
double e = (double) elapsedTime.elapsed(TimeUnit.MICROSECONDS) / 1000.0;
double d = 0.0;
double c = cacheLookupTime / 1000.0;
double fc = (c / e) * 100.0;
String database = "";
if (databaseTime.size() > 0) {
List<String> dbt = new ArrayList<>(databaseTime.size());
for (String name : databaseTime.keySet()) {
double t = databaseTime.get(name) / 1000.0;
d += t;
dbt.add(String.format("%s took %,.3fms %,2.2f%%", name, t, (t / e) * 100.0));
}
double fd = (d / e) * 100.0;
database = String.format(", %d quer%s (%,.3fms %,2.2f%% - %s)", databaseLookups,
(databaseLookups > 1) ? "ies" : "y", d, fd, String.join(", ", dbt));
}
String cache = "";
if (cacheLookupTime > 0) {
int cacheLookups = cacheHits + cacheMisses;
cache = String.format(" with %d cache lookup%s (%,.3fms %,2.2f%% - %,d hit, %,d miss)", cacheLookups,
cacheLookups > 1 ? "s" : "", c, fc, cacheHits, cacheMisses);
}
String da = "";
if (databaseTime.size() > 0 || cacheLookupTime > 0) {
double dat = d + c;
double daf = (dat / e) * 100;
da = String.format(" consuming %,.3fms for data access, or %,2.2f%% of total UOW time.", dat, daf);
}
String n = nested.stream().map(uow -> String.valueOf(uow.hashCode())).collect(Collectors.joining(", "));
String s = String.format(Locale.US, "UOW(%s%s) %s in %,.3fms%s%s%s%s", hashCode(),
(nested.size() > 0 ? ", [" + n + "]" : ""), what, e, cache, database, da,
(purpose == null ? "" : " " + purpose));
return s;
}
private void applyPostCommitFunctions() {
if (!postCommit.isEmpty()) {
for (CommitThunk f : postCommit) {
f.apply();
}
}
if (LOG.isInfoEnabled()) {
LOG.info(logTimers("committed"));
}
}
@Override
public Optional<Object> cacheLookup(List<Facet> facets) {
String tableName = CacheUtil.schemaName(facets);
Optional<Object> result = Optional.empty();
for (Facet facet : facets) {
if (!facet.fixed()) {
String columnName = facet.name() + "==" + facet.value();
Either<Object, List<Facet>> eitherValue = cache.get(tableName, columnName);
if (eitherValue != null) {
Object value = deleted;
if (eitherValue.isLeft()) {
value = eitherValue.getLeft();
}
result = Optional.of(value);
break;
}
}
}
if (!result.isPresent()) {
// Be sure to check all enclosing UnitOfWork caches as well, we may be nested.
if (parent != null) {
return parent.cacheLookup(facets);
}
}
return result;
}
@Override
public List<Facet> cacheEvict(List<Facet> facets) {
Either<Object, List<Facet>> deletedObjectFacets = Either.right(facets);
String tableName = CacheUtil.schemaName(facets);
Optional<Object> optionalValue = cacheLookup(facets);
if (optionalValue.isPresent()) {
Object value = optionalValue.get();
for (Facet facet : facets) {
if (!facet.fixed()) {
String columnKey = facet.name() + "==" + facet.value();
// mark the value identified by the facet to `deleted`
cache.put(tableName, columnKey, deletedObjectFacets);
}
}
// look for other row/col pairs that referenced the same object, mark them
// `deleted`
cache.columnKeySet().forEach(columnKey -> {
Either<Object, List<Facet>> eitherCachedValue = cache.get(tableName, columnKey);
if (eitherCachedValue.isLeft()) {
Object cachedValue = eitherCachedValue.getLeft();
if (cachedValue == value) {
cache.put(tableName, columnKey, deletedObjectFacets);
String[] parts = columnKey.split("==");
facets.add(new Facet<String>(parts[0], parts[1]));
}
}
});
}
return facets;
}
@Override
public void cacheUpdate(Object value, List<Facet> facets) {
String tableName = CacheUtil.schemaName(facets);
for (Facet facet : facets) {
if (!facet.fixed()) {
String columnName = facet.name() + "==" + facet.value();
cache.put(tableName, columnName, Either.left(value));
}
}
}
private Iterator<AbstractUnitOfWork<E>> getChildNodes() {
return nested.iterator();
}
/**
* Checks to see if the work performed between calling begin and now can be
* committed or not.
*
* @return a function from which to chain work that only happens when commit is
* successful
* @throws E
* when the work overlaps with other concurrent writers.
*/
public PostCommitFunction<Void, Void> commit() throws E {
// All nested UnitOfWork should be committed (not aborted) before calls to
// commit, check.
boolean canCommit = true;
TreeTraverser<AbstractUnitOfWork<E>> traverser = TreeTraverser.using(node -> node::getChildNodes);
for (AbstractUnitOfWork<E> uow : traverser.postOrderTraversal(this)) {
if (this != uow) {
canCommit &= (!uow.aborted && uow.committed);
}
}
// log.recordCacheAndDatabaseOperationCount(txn::provisionalCommit)
// examine log for conflicts in read-set and write-set between begin and
// provisional commit
// if (conflict) { throw new ConflictingUnitOfWorkException(this) }
// else return function so as to enable commit.andThen(() -> { do something iff
// commit was successful; })
if (canCommit) {
committed = true;
aborted = false;
nested.forEach((uow) -> Errors.rethrow().wrap(uow::commit));
elapsedTime.stop();
if (parent == null) {
// Apply all post-commit functions, this is the outter-most UnitOfWork.
traverser.postOrderTraversal(this).forEach(uow -> {
uow.applyPostCommitFunctions();
});
// Merge our cache into the session cache.
session.mergeCache(cache);
return new PostCommitFunction(this, null);
} else {
// Merge cache and statistics into parent if there is one.
parent.mergeCache(cache);
parent.cacheHits += cacheHits;
parent.cacheMisses += cacheMisses;
parent.databaseLookups += databaseLookups;
parent.cacheLookupTime += cacheLookupTime;
for (String name : databaseTime.keySet()) {
if (parent.databaseTime.containsKey(name)) {
double t = parent.databaseTime.get(name);
parent.databaseTime.put(name, t + databaseTime.get(name));
} else {
parent.databaseTime.put(name, databaseTime.get(name));
}
}
}
}
// else {
// Constructor<T> ctor = clazz.getConstructor(conflictExceptionClass);
// T object = ctor.newInstance(new Object[] { String message });
// }
return new PostCommitFunction(this, postCommit);
}
/* Explicitly discard the work and mark it as as such in the log. */
public synchronized void abort() {
TreeTraverser<AbstractUnitOfWork<E>> traverser = TreeTraverser.using(node -> node::getChildNodes);
traverser.postOrderTraversal(this).forEach(uow -> {
uow.committed = false;
uow.aborted = true;
});
// log.recordCacheAndDatabaseOperationCount(txn::abort)
// cache.invalidateSince(txn::start time)
if (!hasAborted()) {
elapsedTime.stop();
if (LOG.isInfoEnabled()) {
LOG.info(logTimers("aborted"));
}
}
}
private void mergeCache(Table<String, String, Either<Object, List<Facet>>> from) {
Table<String, String, Either<Object, List<Facet>>> to = this.cache;
from.rowMap().forEach((rowKey, columnMap) -> {
columnMap.forEach((columnKey, value) -> {
if (to.contains(rowKey, columnKey)) {
// TODO(gburd):...
to.put(rowKey, columnKey, Either.left(CacheUtil.merge(to.get(rowKey, columnKey).getLeft(),
from.get(rowKey, columnKey).getLeft())));
} else {
to.put(rowKey, columnKey, from.get(rowKey, columnKey));
}
});
});
}
public String describeConflicts() {
return "it's complex...";
}
@Override
public void close() throws E {
// Closing a AbstractUnitOfWork will abort iff we've not already aborted or
// committed this unit of work.
if (aborted == false && committed == false) {
abort();
}
}
public boolean hasAborted() {
return aborted;
}
public boolean hasCommitted() {
return committed;
}
}

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,5 +17,8 @@
package net.helenus.core; package net.helenus.core;
public enum AutoDdl { public enum AutoDdl {
VALIDATE, UPDATE, CREATE, CREATE_DROP; VALIDATE,
UPDATE,
CREATE,
CREATE_DROP;
} }

View file

@ -1,6 +0,0 @@
package net.helenus.core;
@FunctionalInterface
public interface CommitThunk {
void apply();
}

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,9 +19,9 @@ package net.helenus.core;
public class ConflictingUnitOfWorkException extends Exception { public class ConflictingUnitOfWorkException extends Exception {
final UnitOfWork uow; final UnitOfWork uow;
ConflictingUnitOfWorkException(UnitOfWork uow) { ConflictingUnitOfWorkException(UnitOfWork uow) {
this.uow = uow; this.uow = uow;
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,13 +16,15 @@
*/ */
package net.helenus.core; package net.helenus.core;
import java.util.Optional;
import com.datastax.driver.core.Metadata; import com.datastax.driver.core.Metadata;
import java.util.Optional;
import net.helenus.core.reflect.HelenusPropertyNode; import net.helenus.core.reflect.HelenusPropertyNode;
public interface DslInstantiator { public interface DslInstantiator {
<E> E instantiate(Class<E> iface, ClassLoader classLoader, Optional<HelenusPropertyNode> parent, Metadata metadata); <E> E instantiate(
Class<E> iface,
ClassLoader classLoader,
Optional<HelenusPropertyNode> parent,
Metadata metadata);
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,102 +16,108 @@
*/ */
package net.helenus.core; package net.helenus.core;
import java.util.Objects;
import com.datastax.driver.core.querybuilder.Clause; import com.datastax.driver.core.querybuilder.Clause;
import java.util.Objects;
import net.helenus.core.reflect.HelenusPropertyNode; import net.helenus.core.reflect.HelenusPropertyNode;
import net.helenus.mapping.MappingUtil; import net.helenus.mapping.MappingUtil;
import net.helenus.mapping.value.ColumnValuePreparer; import net.helenus.mapping.value.ColumnValuePreparer;
public final class Filter<V> { public final class Filter<V> {
private final HelenusPropertyNode node; private final HelenusPropertyNode node;
private final Postulate<V> postulate; private final Postulate<V> postulate;
private Filter(HelenusPropertyNode node, Postulate<V> postulate) { private Filter(HelenusPropertyNode node, Postulate<V> postulate) {
this.node = node; this.node = node;
this.postulate = postulate; this.postulate = postulate;
} }
public static <V> Filter<V> equal(Getter<V> getter, V val) { public static <V> Filter<V> equal(Getter<V> getter, V val) {
return create(getter, Operator.EQ, val); return create(getter, Operator.EQ, val);
} }
public static <V> Filter<V> in(Getter<V> getter, V... vals) { public static <V> Filter<V> in(Getter<V> getter, V... vals) {
Objects.requireNonNull(getter, "empty getter"); Objects.requireNonNull(getter, "empty getter");
Objects.requireNonNull(vals, "empty values"); Objects.requireNonNull(vals, "empty values");
if (vals.length == 0) { if (vals.length == 0) {
throw new IllegalArgumentException("values array is empty"); throw new IllegalArgumentException("values array is empty");
} }
for (int i = 0; i != vals.length; ++i) { for (int i = 0; i != vals.length; ++i) {
Objects.requireNonNull(vals[i], "value[" + i + "] is empty"); Objects.requireNonNull(vals[i], "value[" + i + "] is empty");
} }
HelenusPropertyNode node = MappingUtil.resolveMappingProperty(getter); HelenusPropertyNode node = MappingUtil.resolveMappingProperty(getter);
Postulate<V> postulate = Postulate.of(Operator.IN, vals); Postulate<V> postulate = Postulate.of(Operator.IN, vals);
return new Filter<V>(node, postulate); return new Filter<V>(node, postulate);
} }
public static <V> Filter<V> greaterThan(Getter<V> getter, V val) { public static <V> Filter<V> greaterThan(Getter<V> getter, V val) {
return create(getter, Operator.GT, val); return create(getter, Operator.GT, val);
} }
public static <V> Filter<V> lessThan(Getter<V> getter, V val) { public static <V> Filter<V> lessThan(Getter<V> getter, V val) {
return create(getter, Operator.LT, val); return create(getter, Operator.LT, val);
} }
public static <V> Filter<V> greaterThanOrEqual(Getter<V> getter, V val) { public static <V> Filter<V> greaterThanOrEqual(Getter<V> getter, V val) {
return create(getter, Operator.GTE, val); return create(getter, Operator.GTE, val);
} }
public static <V> Filter<V> lessThanOrEqual(Getter<V> getter, V val) { public static <V> Filter<V> lessThanOrEqual(Getter<V> getter, V val) {
return create(getter, Operator.LTE, val); return create(getter, Operator.LTE, val);
} }
public static <V> Filter<V> create(Getter<V> getter, Postulate<V> postulate) { public static <V> Filter<V> create(Getter<V> getter, Postulate<V> postulate) {
Objects.requireNonNull(getter, "empty getter"); Objects.requireNonNull(getter, "empty getter");
Objects.requireNonNull(postulate, "empty operator"); Objects.requireNonNull(postulate, "empty operator");
HelenusPropertyNode node = MappingUtil.resolveMappingProperty(getter); HelenusPropertyNode node = MappingUtil.resolveMappingProperty(getter);
return new Filter<V>(node, postulate); return new Filter<V>(node, postulate);
} }
public static <V> Filter<V> create(Getter<V> getter, Operator op, V val) { public static <V> Filter<V> create(
Objects.requireNonNull(getter, "empty getter"); Getter<V> getter, HelenusPropertyNode node, Postulate<V> postulate) {
Objects.requireNonNull(op, "empty op"); Objects.requireNonNull(getter, "empty getter");
Objects.requireNonNull(val, "empty value"); Objects.requireNonNull(postulate, "empty operator");
return new Filter<V>(node, postulate);
}
if (op == Operator.IN) { public static <V> Filter<V> create(Getter<V> getter, Operator op, V val) {
throw new IllegalArgumentException("invalid usage of the 'in' operator, use Filter.in() static method"); Objects.requireNonNull(getter, "empty getter");
} Objects.requireNonNull(op, "empty op");
Objects.requireNonNull(val, "empty value");
HelenusPropertyNode node = MappingUtil.resolveMappingProperty(getter); if (op == Operator.IN) {
throw new IllegalArgumentException(
"invalid usage of the 'in' operator, use Filter.in() static method");
}
Postulate<V> postulate = Postulate.of(op, val); HelenusPropertyNode node = MappingUtil.resolveMappingProperty(getter);
return new Filter<V>(node, postulate); Postulate<V> postulate = Postulate.of(op, val);
}
public HelenusPropertyNode getNode() { return new Filter<V>(node, postulate);
return node; }
}
public Clause getClause(ColumnValuePreparer valuePreparer) { public HelenusPropertyNode getNode() {
return postulate.getClause(node, valuePreparer); return node;
} }
public V[] postulateValues() { public Clause getClause(ColumnValuePreparer valuePreparer) {
return postulate.values(); return postulate.getClause(node, valuePreparer);
} }
@Override public V[] postulateValues() {
public String toString() { return postulate.values();
return node.getColumnName() + postulate.toString(); }
}
@Override
public String toString() {
return node.getColumnName() + postulate.toString();
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,5 +18,5 @@ package net.helenus.core;
public interface Getter<V> { public interface Getter<V> {
V get(); V get();
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,17 +16,15 @@
*/ */
package net.helenus.core; package net.helenus.core;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Metadata;
import com.datastax.driver.core.Session;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;
import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Metadata;
import com.datastax.driver.core.Session;
import net.helenus.config.DefaultHelenusSettings; import net.helenus.config.DefaultHelenusSettings;
import net.helenus.config.HelenusSettings; import net.helenus.config.HelenusSettings;
import net.helenus.core.reflect.DslExportable; import net.helenus.core.reflect.DslExportable;
@ -35,161 +34,170 @@ import net.helenus.support.HelenusMappingException;
public final class Helenus { public final class Helenus {
private static final ConcurrentMap<Class<?>, Object> dslCache = new ConcurrentHashMap<Class<?>, Object>(); private static final ConcurrentMap<Class<?>, Object> dslCache =
private static final ConcurrentMap<Class<?>, Metadata> metadataForEntity = new ConcurrentHashMap<Class<?>, Metadata>(); new ConcurrentHashMap<Class<?>, Object>();
private static final Set<HelenusSession> sessions = new HashSet<HelenusSession>(); private static final ConcurrentMap<Class<?>, Metadata> metadataForEntity =
private static volatile HelenusSettings settings = new DefaultHelenusSettings(); new ConcurrentHashMap<Class<?>, Metadata>();
private static volatile HelenusSession singleton; private static final Set<HelenusSession> sessions = new HashSet<HelenusSession>();
private static volatile HelenusSettings settings = new DefaultHelenusSettings();
private static volatile HelenusSession singleton;
private Helenus() { private Helenus() {}
}
protected static void setSession(HelenusSession session) { protected static void setSession(HelenusSession session) {
sessions.add(session); sessions.add(session);
singleton = session; singleton = session;
} }
public static HelenusSession session() { public static HelenusSession session() {
return singleton; return singleton;
} }
public static void shutdown() { public static void shutdown() {
sessions.forEach((session) -> { sessions.forEach(
session.close(); (session) -> {
sessions.remove(session); session.close();
}); sessions.remove(session);
dslCache.clear(); });
} dslCache.clear();
}
public static HelenusSettings settings() { public static HelenusSettings settings() {
return settings; return settings;
} }
public static HelenusSettings settings(HelenusSettings overrideSettings) { public static HelenusSettings settings(HelenusSettings overrideSettings) {
HelenusSettings old = settings; HelenusSettings old = settings;
settings = overrideSettings; settings = overrideSettings;
return old; return old;
} }
public static SessionInitializer connect(Cluster cluster) { public static SessionInitializer connect(Cluster cluster) {
Session session = cluster.connect(); Session session = cluster.connect();
return new SessionInitializer(session); return new SessionInitializer(session);
} }
public static SessionInitializer connect(Cluster cluster, String keyspace) { public static SessionInitializer connect(Cluster cluster, String keyspace) {
Session session = cluster.connect(keyspace); Session session = cluster.connect(keyspace);
return new SessionInitializer(session); return new SessionInitializer(session);
} }
public static SessionInitializer init(Session session) { public static SessionInitializer init(Session session, String keyspace) {
return new SessionInitializer(session, keyspace);
}
if (session == null) { public static SessionInitializer init(Session session) {
throw new IllegalArgumentException("empty session");
}
return new SessionInitializer(session); if (session == null) {
} throw new IllegalArgumentException("empty session");
}
public static void clearDslCache() { return new SessionInitializer(session);
dslCache.clear(); }
}
public static <E> E dsl(Class<E> iface) { public static void clearDslCache() {
return dsl(iface, null); dslCache.clear();
} }
public static <E> E dsl(Class<E> iface, Metadata metadata) { public static <E> E dsl(Class<E> iface) {
return dsl(iface, iface.getClassLoader(), Optional.empty(), metadata); return dsl(iface, null);
} }
public static <E> E dsl(Class<E> iface, ClassLoader classLoader, Metadata metadata) { public static <E> E dsl(Class<E> iface, Metadata metadata) {
return dsl(iface, classLoader, Optional.empty(), metadata); return dsl(iface, iface.getClassLoader(), Optional.empty(), metadata);
} }
public static <E> E dsl(Class<E> iface, ClassLoader classLoader, Optional<HelenusPropertyNode> parent, public static <E> E dsl(Class<E> iface, ClassLoader classLoader, Metadata metadata) {
Metadata metadata) { return dsl(iface, classLoader, Optional.empty(), metadata);
}
Object instance = null; public static <E> E dsl(
Class<E> iface,
ClassLoader classLoader,
Optional<HelenusPropertyNode> parent,
Metadata metadata) {
if (!parent.isPresent()) { Object instance = null;
instance = dslCache.get(iface);
}
if (instance == null) { if (!parent.isPresent()) {
instance = dslCache.get(iface);
}
instance = settings.getDslInstantiator().instantiate(iface, classLoader, parent, metadata); if (instance == null) {
if (!parent.isPresent()) { instance = settings.getDslInstantiator().instantiate(iface, classLoader, parent, metadata);
Object c = dslCache.putIfAbsent(iface, instance); if (!parent.isPresent()) {
if (c != null) {
instance = c;
}
}
}
return (E) instance; Object c = dslCache.putIfAbsent(iface, instance);
} if (c != null) {
instance = c;
}
}
}
public static <E> E map(Class<E> iface, Map<String, Object> src) { return (E) instance;
return map(iface, src, iface.getClassLoader()); }
}
public static <E> E map(Class<E> iface, Map<String, Object> src, ClassLoader classLoader) { public static <E> E map(Class<E> iface, Map<String, Object> src) {
return settings.getMapperInstantiator().instantiate(iface, src, classLoader); return map(iface, src, iface.getClassLoader());
} }
public static HelenusEntity entity(Class<?> iface) { public static <E> E map(Class<E> iface, Map<String, Object> src, ClassLoader classLoader) {
Metadata metadata = metadataForEntity.get(iface); return settings.getMapperInstantiator().instantiate(iface, src, classLoader);
if (metadata == null) { }
HelenusSession session = session();
if (session != null) {
metadata = session.getMetadata();
}
}
return entity(iface, metadata);
}
public static HelenusEntity entity(Class<?> iface, Metadata metadata) { public static HelenusEntity entity(Class<?> iface) {
Metadata metadata = metadataForEntity.get(iface);
if (metadata == null) {
HelenusSession session = session();
if (session != null) {
metadata = session.getMetadata();
}
}
return entity(iface, metadata);
}
Object dsl = dsl(iface, metadata); public static HelenusEntity entity(Class<?> iface, Metadata metadata) {
DslExportable e = (DslExportable) dsl; Object dsl = dsl(iface, metadata);
return e.getHelenusMappingEntity(); DslExportable e = (DslExportable) dsl;
}
public static HelenusEntity resolve(Object ifaceOrDsl) { return e.getHelenusMappingEntity();
return resolve(ifaceOrDsl, metadataForEntity.get(ifaceOrDsl)); }
}
public static HelenusEntity resolve(Object ifaceOrDsl, Metadata metadata) { public static HelenusEntity resolve(Object ifaceOrDsl) {
return resolve(ifaceOrDsl, metadataForEntity.get(ifaceOrDsl));
}
if (ifaceOrDsl == null) { public static HelenusEntity resolve(Object ifaceOrDsl, Metadata metadata) {
throw new HelenusMappingException("ifaceOrDsl is null");
}
if (ifaceOrDsl instanceof DslExportable) { if (ifaceOrDsl == null) {
throw new HelenusMappingException("ifaceOrDsl is null");
}
DslExportable e = (DslExportable) ifaceOrDsl; if (ifaceOrDsl instanceof DslExportable) {
return e.getHelenusMappingEntity(); DslExportable e = (DslExportable) ifaceOrDsl;
}
if (ifaceOrDsl instanceof Class) { return e.getHelenusMappingEntity();
}
Class<?> iface = (Class<?>) ifaceOrDsl; if (ifaceOrDsl instanceof Class) {
if (!iface.isInterface()) { Class<?> iface = (Class<?>) ifaceOrDsl;
throw new HelenusMappingException("class is not an interface " + iface);
}
if (metadata != null) { if (!iface.isInterface()) {
metadataForEntity.putIfAbsent(iface, metadata); throw new HelenusMappingException("class is not an interface " + iface);
} }
return entity(iface, metadata);
}
throw new HelenusMappingException("unknown dsl object or mapping interface " + ifaceOrDsl); if (metadata != null) {
} metadataForEntity.putIfAbsent(iface, metadata);
}
return entity(iface, metadata);
}
throw new HelenusMappingException("unknown dsl object or mapping interface " + ifaceOrDsl);
}
} }

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,33 +17,32 @@
package net.helenus.core; package net.helenus.core;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidator;
import net.helenus.mapping.HelenusProperty; import net.helenus.mapping.HelenusProperty;
import net.helenus.support.HelenusException; import net.helenus.support.HelenusException;
import net.helenus.support.HelenusMappingException; import net.helenus.support.HelenusMappingException;
public enum HelenusValidator implements PropertyValueValidator { public enum HelenusValidator implements PropertyValueValidator {
INSTANCE; INSTANCE;
public void validate(HelenusProperty prop, Object value) { public void validate(HelenusProperty prop, Object value) {
for (ConstraintValidator<? extends Annotation, ?> validator : prop.getValidators()) { for (ConstraintValidator<? extends Annotation, ?> validator : prop.getValidators()) {
ConstraintValidator typeless = (ConstraintValidator) validator; ConstraintValidator typeless = (ConstraintValidator) validator;
boolean valid = false; boolean valid = false;
try { try {
valid = typeless.isValid(value, null); valid = typeless.isValid(value, null);
} catch (ClassCastException e) { } catch (ClassCastException e) {
throw new HelenusMappingException("validator was used for wrong type '" + value + "' in " + prop, e); throw new HelenusMappingException(
} "validator was used for wrong type '" + value + "' in " + prop, e);
}
if (!valid) { if (!valid) {
throw new HelenusException("wrong value '" + value + "' for " + prop); throw new HelenusException("wrong value '" + value + "' for " + prop);
} }
} }
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,5 +20,5 @@ import java.util.Map;
public interface MapperInstantiator { public interface MapperInstantiator {
<E> E instantiate(Class<E> iface, Map<String, Object> src, ClassLoader classLoader); <E> E instantiate(Class<E> iface, Map<String, Object> src, ClassLoader classLoader);
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,10 +16,8 @@
*/ */
package net.helenus.core; package net.helenus.core;
import java.util.function.Function;
import com.datastax.driver.core.Row; import com.datastax.driver.core.Row;
import java.util.function.Function;
import net.helenus.core.reflect.HelenusPropertyNode; import net.helenus.core.reflect.HelenusPropertyNode;
import net.helenus.mapping.HelenusProperty; import net.helenus.mapping.HelenusProperty;
import net.helenus.mapping.value.ColumnValueProvider; import net.helenus.mapping.value.ColumnValueProvider;
@ -26,161 +25,203 @@ import net.helenus.support.Fun;
public final class Mappers { public final class Mappers {
private Mappers() { private Mappers() {}
}
public static final class Mapper1<A> implements Function<Row, Fun.Tuple1<A>> { public static final class Mapper1<A> implements Function<Row, Fun.Tuple1<A>> {
private final ColumnValueProvider provider; private final ColumnValueProvider provider;
private final HelenusProperty p1; private final HelenusProperty p1;
public Mapper1(ColumnValueProvider provider, HelenusPropertyNode p1) { public Mapper1(ColumnValueProvider provider, HelenusPropertyNode p1) {
this.provider = provider; this.provider = provider;
this.p1 = p1.getProperty(); this.p1 = p1.getProperty();
} }
@Override @Override
public Fun.Tuple1<A> apply(Row row) { public Fun.Tuple1<A> apply(Row row) {
return new Fun.Tuple1<A>(provider.getColumnValue(row, 0, p1)); return new Fun.Tuple1<A>(provider.getColumnValue(row, 0, p1));
} }
} }
public static final class Mapper2<A, B> implements Function<Row, Fun.Tuple2<A, B>> { public static final class Mapper2<A, B> implements Function<Row, Fun.Tuple2<A, B>> {
private final ColumnValueProvider provider; private final ColumnValueProvider provider;
private final HelenusProperty p1; private final HelenusProperty p1;
private final HelenusProperty p2; private final HelenusProperty p2;
public Mapper2(ColumnValueProvider provider, HelenusPropertyNode p1, HelenusPropertyNode p2) { public Mapper2(ColumnValueProvider provider, HelenusPropertyNode p1, HelenusPropertyNode p2) {
this.provider = provider; this.provider = provider;
this.p1 = p1.getProperty(); this.p1 = p1.getProperty();
this.p2 = p2.getProperty(); this.p2 = p2.getProperty();
} }
@Override @Override
public Fun.Tuple2<A, B> apply(Row row) { public Fun.Tuple2<A, B> apply(Row row) {
return new Fun.Tuple2<A, B>(provider.getColumnValue(row, 0, p1), provider.getColumnValue(row, 1, p2)); return new Fun.Tuple2<A, B>(
} provider.getColumnValue(row, 0, p1), provider.getColumnValue(row, 1, p2));
} }
}
public static final class Mapper3<A, B, C> implements Function<Row, Fun.Tuple3<A, B, C>> { public static final class Mapper3<A, B, C> implements Function<Row, Fun.Tuple3<A, B, C>> {
private final ColumnValueProvider provider; private final ColumnValueProvider provider;
private final HelenusProperty p1; private final HelenusProperty p1;
private final HelenusProperty p2; private final HelenusProperty p2;
private final HelenusProperty p3; private final HelenusProperty p3;
public Mapper3(ColumnValueProvider provider, HelenusPropertyNode p1, HelenusPropertyNode p2, public Mapper3(
HelenusPropertyNode p3) { ColumnValueProvider provider,
this.provider = provider; HelenusPropertyNode p1,
this.p1 = p1.getProperty(); HelenusPropertyNode p2,
this.p2 = p2.getProperty(); HelenusPropertyNode p3) {
this.p3 = p3.getProperty(); this.provider = provider;
} this.p1 = p1.getProperty();
this.p2 = p2.getProperty();
this.p3 = p3.getProperty();
}
@Override @Override
public Fun.Tuple3<A, B, C> apply(Row row) { public Fun.Tuple3<A, B, C> apply(Row row) {
return new Fun.Tuple3<A, B, C>(provider.getColumnValue(row, 0, p1), provider.getColumnValue(row, 1, p2), return new Fun.Tuple3<A, B, C>(
provider.getColumnValue(row, 2, p3)); provider.getColumnValue(row, 0, p1),
} provider.getColumnValue(row, 1, p2),
} provider.getColumnValue(row, 2, p3));
}
}
public static final class Mapper4<A, B, C, D> implements Function<Row, Fun.Tuple4<A, B, C, D>> { public static final class Mapper4<A, B, C, D> implements Function<Row, Fun.Tuple4<A, B, C, D>> {
private final ColumnValueProvider provider; private final ColumnValueProvider provider;
private final HelenusProperty p1; private final HelenusProperty p1;
private final HelenusProperty p2; private final HelenusProperty p2;
private final HelenusProperty p3; private final HelenusProperty p3;
private final HelenusProperty p4; private final HelenusProperty p4;
public Mapper4(ColumnValueProvider provider, HelenusPropertyNode p1, HelenusPropertyNode p2, public Mapper4(
HelenusPropertyNode p3, HelenusPropertyNode p4) { ColumnValueProvider provider,
this.provider = provider; HelenusPropertyNode p1,
this.p1 = p1.getProperty(); HelenusPropertyNode p2,
this.p2 = p2.getProperty(); HelenusPropertyNode p3,
this.p3 = p3.getProperty(); HelenusPropertyNode p4) {
this.p4 = p4.getProperty(); this.provider = provider;
} this.p1 = p1.getProperty();
this.p2 = p2.getProperty();
this.p3 = p3.getProperty();
this.p4 = p4.getProperty();
}
@Override @Override
public Fun.Tuple4<A, B, C, D> apply(Row row) { public Fun.Tuple4<A, B, C, D> apply(Row row) {
return new Fun.Tuple4<A, B, C, D>(provider.getColumnValue(row, 0, p1), provider.getColumnValue(row, 1, p2), return new Fun.Tuple4<A, B, C, D>(
provider.getColumnValue(row, 2, p3), provider.getColumnValue(row, 3, p4)); provider.getColumnValue(row, 0, p1),
} provider.getColumnValue(row, 1, p2),
} provider.getColumnValue(row, 2, p3),
provider.getColumnValue(row, 3, p4));
}
}
public static final class Mapper5<A, B, C, D, E> implements Function<Row, Fun.Tuple5<A, B, C, D, E>> { public static final class Mapper5<A, B, C, D, E>
implements Function<Row, Fun.Tuple5<A, B, C, D, E>> {
private final ColumnValueProvider provider; private final ColumnValueProvider provider;
private final HelenusProperty p1, p2, p3, p4, p5; private final HelenusProperty p1, p2, p3, p4, p5;
public Mapper5(ColumnValueProvider provider, HelenusPropertyNode p1, HelenusPropertyNode p2, public Mapper5(
HelenusPropertyNode p3, HelenusPropertyNode p4, HelenusPropertyNode p5) { ColumnValueProvider provider,
this.provider = provider; HelenusPropertyNode p1,
this.p1 = p1.getProperty(); HelenusPropertyNode p2,
this.p2 = p2.getProperty(); HelenusPropertyNode p3,
this.p3 = p3.getProperty(); HelenusPropertyNode p4,
this.p4 = p4.getProperty(); HelenusPropertyNode p5) {
this.p5 = p5.getProperty(); this.provider = provider;
} this.p1 = p1.getProperty();
this.p2 = p2.getProperty();
this.p3 = p3.getProperty();
this.p4 = p4.getProperty();
this.p5 = p5.getProperty();
}
@Override @Override
public Fun.Tuple5<A, B, C, D, E> apply(Row row) { public Fun.Tuple5<A, B, C, D, E> apply(Row row) {
return new Fun.Tuple5<A, B, C, D, E>(provider.getColumnValue(row, 0, p1), return new Fun.Tuple5<A, B, C, D, E>(
provider.getColumnValue(row, 1, p2), provider.getColumnValue(row, 2, p3), provider.getColumnValue(row, 0, p1),
provider.getColumnValue(row, 3, p4), provider.getColumnValue(row, 4, p5)); provider.getColumnValue(row, 1, p2),
} provider.getColumnValue(row, 2, p3),
} provider.getColumnValue(row, 3, p4),
provider.getColumnValue(row, 4, p5));
}
}
public static final class Mapper6<A, B, C, D, E, F> implements Function<Row, Fun.Tuple6<A, B, C, D, E, F>> { public static final class Mapper6<A, B, C, D, E, F>
implements Function<Row, Fun.Tuple6<A, B, C, D, E, F>> {
private final ColumnValueProvider provider; private final ColumnValueProvider provider;
private final HelenusProperty p1, p2, p3, p4, p5, p6; private final HelenusProperty p1, p2, p3, p4, p5, p6;
public Mapper6(ColumnValueProvider provider, HelenusPropertyNode p1, HelenusPropertyNode p2, public Mapper6(
HelenusPropertyNode p3, HelenusPropertyNode p4, HelenusPropertyNode p5, HelenusPropertyNode p6) { ColumnValueProvider provider,
this.provider = provider; HelenusPropertyNode p1,
this.p1 = p1.getProperty(); HelenusPropertyNode p2,
this.p2 = p2.getProperty(); HelenusPropertyNode p3,
this.p3 = p3.getProperty(); HelenusPropertyNode p4,
this.p4 = p4.getProperty(); HelenusPropertyNode p5,
this.p5 = p5.getProperty(); HelenusPropertyNode p6) {
this.p6 = p6.getProperty(); this.provider = provider;
} this.p1 = p1.getProperty();
this.p2 = p2.getProperty();
this.p3 = p3.getProperty();
this.p4 = p4.getProperty();
this.p5 = p5.getProperty();
this.p6 = p6.getProperty();
}
@Override @Override
public Fun.Tuple6<A, B, C, D, E, F> apply(Row row) { public Fun.Tuple6<A, B, C, D, E, F> apply(Row row) {
return new Fun.Tuple6<A, B, C, D, E, F>(provider.getColumnValue(row, 0, p1), return new Fun.Tuple6<A, B, C, D, E, F>(
provider.getColumnValue(row, 1, p2), provider.getColumnValue(row, 2, p3), provider.getColumnValue(row, 0, p1),
provider.getColumnValue(row, 3, p4), provider.getColumnValue(row, 4, p5), provider.getColumnValue(row, 1, p2),
provider.getColumnValue(row, 5, p6)); provider.getColumnValue(row, 2, p3),
} provider.getColumnValue(row, 3, p4),
} provider.getColumnValue(row, 4, p5),
provider.getColumnValue(row, 5, p6));
}
}
public static final class Mapper7<A, B, C, D, E, F, G> implements Function<Row, Fun.Tuple7<A, B, C, D, E, F, G>> { public static final class Mapper7<A, B, C, D, E, F, G>
implements Function<Row, Fun.Tuple7<A, B, C, D, E, F, G>> {
private final ColumnValueProvider provider; private final ColumnValueProvider provider;
private final HelenusProperty p1, p2, p3, p4, p5, p6, p7; private final HelenusProperty p1, p2, p3, p4, p5, p6, p7;
public Mapper7(ColumnValueProvider provider, HelenusPropertyNode p1, HelenusPropertyNode p2, public Mapper7(
HelenusPropertyNode p3, HelenusPropertyNode p4, HelenusPropertyNode p5, HelenusPropertyNode p6, ColumnValueProvider provider,
HelenusPropertyNode p7) { HelenusPropertyNode p1,
this.provider = provider; HelenusPropertyNode p2,
this.p1 = p1.getProperty(); HelenusPropertyNode p3,
this.p2 = p2.getProperty(); HelenusPropertyNode p4,
this.p3 = p3.getProperty(); HelenusPropertyNode p5,
this.p4 = p4.getProperty(); HelenusPropertyNode p6,
this.p5 = p5.getProperty(); HelenusPropertyNode p7) {
this.p6 = p6.getProperty(); this.provider = provider;
this.p7 = p7.getProperty(); this.p1 = p1.getProperty();
} this.p2 = p2.getProperty();
this.p3 = p3.getProperty();
this.p4 = p4.getProperty();
this.p5 = p5.getProperty();
this.p6 = p6.getProperty();
this.p7 = p7.getProperty();
}
@Override @Override
public Fun.Tuple7<A, B, C, D, E, F, G> apply(Row row) { public Fun.Tuple7<A, B, C, D, E, F, G> apply(Row row) {
return new Fun.Tuple7<A, B, C, D, E, F, G>(provider.getColumnValue(row, 0, p1), return new Fun.Tuple7<A, B, C, D, E, F, G>(
provider.getColumnValue(row, 1, p2), provider.getColumnValue(row, 2, p3), provider.getColumnValue(row, 0, p1),
provider.getColumnValue(row, 3, p4), provider.getColumnValue(row, 4, p5), provider.getColumnValue(row, 1, p2),
provider.getColumnValue(row, 5, p6), provider.getColumnValue(row, 6, p7)); provider.getColumnValue(row, 2, p3),
} provider.getColumnValue(row, 3, p4),
} provider.getColumnValue(row, 4, p5),
provider.getColumnValue(row, 5, p6),
provider.getColumnValue(row, 6, p7));
}
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,37 +20,37 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
public enum Operator { public enum Operator {
EQ("=="), EQ("=="),
IN("in"), IN("in"),
GT(">"), GT(">"),
LT("<"), LT("<"),
GTE(">="), GTE(">="),
LTE("<="); LTE("<=");
private static final Map<String, Operator> indexByName = new HashMap<String, Operator>(); private static final Map<String, Operator> indexByName = new HashMap<String, Operator>();
static { static {
for (Operator fo : Operator.values()) { for (Operator fo : Operator.values()) {
indexByName.put(fo.getName(), fo); indexByName.put(fo.getName(), fo);
} }
} }
private final String name; private final String name;
private Operator(String name) { private Operator(String name) {
this.name = name; this.name = name;
} }
public static Operator findByOperator(String name) { public static Operator findByOperator(String name) {
return indexByName.get(name); return indexByName.get(name);
} }
public String getName() { public String getName() {
return name; return name;
} }
} }

View file

@ -1,10 +1,8 @@
package net.helenus.core; package net.helenus.core;
import java.util.Objects;
import com.datastax.driver.core.querybuilder.Ordering; import com.datastax.driver.core.querybuilder.Ordering;
import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.QueryBuilder;
import java.util.Objects;
import net.helenus.core.reflect.HelenusPropertyNode; import net.helenus.core.reflect.HelenusPropertyNode;
import net.helenus.mapping.ColumnType; import net.helenus.mapping.ColumnType;
import net.helenus.mapping.MappingUtil; import net.helenus.mapping.MappingUtil;
@ -13,34 +11,34 @@ import net.helenus.support.HelenusMappingException;
public final class Ordered { public final class Ordered {
private final Getter<?> getter; private final Getter<?> getter;
private final OrderingDirection direction; private final OrderingDirection direction;
public Ordered(Getter<?> getter, OrderingDirection direction) { public Ordered(Getter<?> getter, OrderingDirection direction) {
this.getter = getter; this.getter = getter;
this.direction = direction; this.direction = direction;
} }
public Ordering getOrdering() { public Ordering getOrdering() {
Objects.requireNonNull(getter, "property is null"); Objects.requireNonNull(getter, "property is null");
Objects.requireNonNull(direction, "direction is null"); Objects.requireNonNull(direction, "direction is null");
HelenusPropertyNode propNode = MappingUtil.resolveMappingProperty(getter); HelenusPropertyNode propNode = MappingUtil.resolveMappingProperty(getter);
if (propNode.getProperty().getColumnType() != ColumnType.CLUSTERING_COLUMN) { if (propNode.getProperty().getColumnType() != ColumnType.CLUSTERING_COLUMN) {
throw new HelenusMappingException( throw new HelenusMappingException(
"property must be a clustering column " + propNode.getProperty().getPropertyName()); "property must be a clustering column " + propNode.getProperty().getPropertyName());
} }
switch (direction) { switch (direction) {
case ASC : case ASC:
return QueryBuilder.asc(propNode.getColumnName()); return QueryBuilder.asc(propNode.getColumnName());
case DESC : case DESC:
return QueryBuilder.desc(propNode.getColumnName()); return QueryBuilder.desc(propNode.getColumnName());
} }
throw new HelenusMappingException("invalid direction " + direction); throw new HelenusMappingException("invalid direction " + direction);
} }
} }

View file

@ -2,28 +2,77 @@ package net.helenus.core;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import net.helenus.support.CheckedRunnable;
public class PostCommitFunction<T, R> implements java.util.function.Function<T, R> { public class PostCommitFunction<T, R> implements java.util.function.Function<T, R> {
public static final PostCommitFunction<Void, Void> NULL_ABORT = new PostCommitFunction<Void, Void>(null, null, null, false);
public static final PostCommitFunction<Void, Void> NULL_COMMIT = new PostCommitFunction<Void, Void>(null, null, null, true);
private final UnitOfWork uow; private final List<CheckedRunnable> commitThunks;
private final List<CommitThunk> postCommit; private final List<CheckedRunnable> abortThunks;
private Consumer<? super Throwable> exceptionallyThunk;
private boolean committed;
PostCommitFunction(UnitOfWork uow, List<CommitThunk> postCommit) { PostCommitFunction(List<CheckedRunnable> postCommit, List<CheckedRunnable> abortThunks,
this.uow = uow; Consumer<? super Throwable> exceptionallyThunk,
this.postCommit = postCommit; boolean committed) {
} this.commitThunks = postCommit;
this.abortThunks = abortThunks;
this.exceptionallyThunk = exceptionallyThunk;
this.committed = committed;
}
public void andThen(CommitThunk after) { private void apply(CheckedRunnable... fns) {
Objects.requireNonNull(after); try {
if (postCommit == null) { for (CheckedRunnable fn : fns) {
after.apply(); fn.run();
} else { }
postCommit.add(after); } catch (Throwable t) {
} if (exceptionallyThunk != null) {
} exceptionallyThunk.accept(t);
}
}
}
@Override public PostCommitFunction<T, R> andThen(CheckedRunnable... after) {
public R apply(T t) { Objects.requireNonNull(after);
return null; if (commitThunks == null) {
} if (committed) {
apply(after);
}
} else {
for (CheckedRunnable fn : after) {
commitThunks.add(fn);
}
}
return this;
}
public PostCommitFunction<T, R> orElse(CheckedRunnable... after) {
Objects.requireNonNull(after);
if (abortThunks == null) {
if (!committed) {
apply(after);
}
} else {
for (CheckedRunnable fn : after) {
abortThunks.add(fn);
}
}
return this;
}
public PostCommitFunction<T, R> exceptionally(Consumer<? super Throwable> fn) {
Objects.requireNonNull(fn);
exceptionallyThunk = fn;
return this;
}
@Override
public R apply(T t) {
return null;
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,85 +18,84 @@ package net.helenus.core;
import com.datastax.driver.core.querybuilder.Clause; import com.datastax.driver.core.querybuilder.Clause;
import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.QueryBuilder;
import net.helenus.core.reflect.HelenusPropertyNode; import net.helenus.core.reflect.HelenusPropertyNode;
import net.helenus.mapping.value.ColumnValuePreparer; import net.helenus.mapping.value.ColumnValuePreparer;
import net.helenus.support.HelenusMappingException; import net.helenus.support.HelenusMappingException;
public final class Postulate<V> { public final class Postulate<V> {
private final Operator operator; private final Operator operator;
private final V[] values; private final V[] values;
protected Postulate(Operator op, V[] values) { protected Postulate(Operator op, V[] values) {
this.operator = op; this.operator = op;
this.values = values; this.values = values;
} }
public static <V> Postulate<V> of(Operator op, V... values) { public static <V> Postulate<V> of(Operator op, V... values) {
return new Postulate<V>(op, values); return new Postulate<V>(op, values);
} }
public Clause getClause(HelenusPropertyNode node, ColumnValuePreparer valuePreparer) { public Clause getClause(HelenusPropertyNode node, ColumnValuePreparer valuePreparer) {
switch (operator) { switch (operator) {
case EQ : case EQ:
return QueryBuilder.eq(node.getColumnName(), return QueryBuilder.eq(
valuePreparer.prepareColumnValue(values[0], node.getProperty())); node.getColumnName(), valuePreparer.prepareColumnValue(values[0], node.getProperty()));
case IN : case IN:
Object[] preparedValues = new Object[values.length]; Object[] preparedValues = new Object[values.length];
for (int i = 0; i != values.length; ++i) { for (int i = 0; i != values.length; ++i) {
preparedValues[i] = valuePreparer.prepareColumnValue(values[i], node.getProperty()); preparedValues[i] = valuePreparer.prepareColumnValue(values[i], node.getProperty());
} }
return QueryBuilder.in(node.getColumnName(), preparedValues); return QueryBuilder.in(node.getColumnName(), preparedValues);
case LT : case LT:
return QueryBuilder.lt(node.getColumnName(), return QueryBuilder.lt(
valuePreparer.prepareColumnValue(values[0], node.getProperty())); node.getColumnName(), valuePreparer.prepareColumnValue(values[0], node.getProperty()));
case LTE : case LTE:
return QueryBuilder.lte(node.getColumnName(), return QueryBuilder.lte(
valuePreparer.prepareColumnValue(values[0], node.getProperty())); node.getColumnName(), valuePreparer.prepareColumnValue(values[0], node.getProperty()));
case GT : case GT:
return QueryBuilder.gt(node.getColumnName(), return QueryBuilder.gt(
valuePreparer.prepareColumnValue(values[0], node.getProperty())); node.getColumnName(), valuePreparer.prepareColumnValue(values[0], node.getProperty()));
case GTE : case GTE:
return QueryBuilder.gte(node.getColumnName(), return QueryBuilder.gte(
valuePreparer.prepareColumnValue(values[0], node.getProperty())); node.getColumnName(), valuePreparer.prepareColumnValue(values[0], node.getProperty()));
default : default:
throw new HelenusMappingException("unknown filter operation " + operator); throw new HelenusMappingException("unknown filter operation " + operator);
} }
} }
public V[] values() { public V[] values() {
return values; return values;
} }
@Override @Override
public String toString() { public String toString() {
if (operator == Operator.IN) { if (operator == Operator.IN) {
if (values == null) { if (values == null) {
return "in()"; return "in()";
} }
int len = values.length; int len = values.length;
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
b.append("in("); b.append("in(");
for (int i = 0; i != len; i++) { for (int i = 0; i != len; i++) {
if (b.length() > 3) { if (b.length() > 3) {
b.append(", "); b.append(", ");
} }
b.append(String.valueOf(values[i])); b.append(String.valueOf(values[i]));
} }
return b.append(')').toString(); return b.append(')').toString();
} }
return operator.getName() + values[0]; return operator.getName() + values[0];
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,5 +20,5 @@ import net.helenus.mapping.HelenusProperty;
public interface PropertyValueValidator { public interface PropertyValueValidator {
void validate(HelenusProperty prop, Object value); void validate(HelenusProperty prop, Object value);
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,83 +16,80 @@
*/ */
package net.helenus.core; package net.helenus.core;
import com.datastax.driver.core.querybuilder.BindMarker;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import com.datastax.driver.core.querybuilder.BindMarker;
import com.datastax.driver.core.querybuilder.QueryBuilder;
import net.helenus.mapping.OrderingDirection; import net.helenus.mapping.OrderingDirection;
/** Sugar methods for the queries */ /** Sugar methods for the queries */
public final class Query { public final class Query {
private Query() { private Query() {}
}
public static BindMarker marker() { public static BindMarker marker() {
return QueryBuilder.bindMarker(); return QueryBuilder.bindMarker();
} }
public static BindMarker marker(String name) { public static BindMarker marker(String name) {
return QueryBuilder.bindMarker(name); return QueryBuilder.bindMarker(name);
} }
public static Ordered asc(Getter<?> getter) { public static Ordered asc(Getter<?> getter) {
return new Ordered(getter, OrderingDirection.ASC); return new Ordered(getter, OrderingDirection.ASC);
} }
public static Ordered desc(Getter<?> getter) { public static Ordered desc(Getter<?> getter) {
return new Ordered(getter, OrderingDirection.DESC); return new Ordered(getter, OrderingDirection.DESC);
} }
public static <V> Postulate<V> eq(V val) { public static <V> Postulate<V> eq(V val) {
return Postulate.of(Operator.EQ, val); return Postulate.of(Operator.EQ, val);
} }
public static <V> Postulate<V> lt(V val) { public static <V> Postulate<V> lt(V val) {
return Postulate.of(Operator.LT, val); return Postulate.of(Operator.LT, val);
} }
public static <V> Postulate<V> lte(V val) { public static <V> Postulate<V> lte(V val) {
return Postulate.of(Operator.LTE, val); return Postulate.of(Operator.LTE, val);
} }
public static <V> Postulate<V> gt(V val) { public static <V> Postulate<V> gt(V val) {
return Postulate.of(Operator.GT, val); return Postulate.of(Operator.GT, val);
} }
public static <V> Postulate<V> gte(V val) { public static <V> Postulate<V> gte(V val) {
return Postulate.of(Operator.GTE, val); return Postulate.of(Operator.GTE, val);
} }
public static <V> Postulate<V> in(V[] vals) { public static <V> Postulate<V> in(V[] vals) {
return new Postulate<V>(Operator.IN, vals); return new Postulate<V>(Operator.IN, vals);
} }
public static <K, V> Getter<V> getIdx(Getter<List<V>> listGetter, int index) { public static <K, V> Getter<V> getIdx(Getter<List<V>> listGetter, int index) {
Objects.requireNonNull(listGetter, "listGetter is null"); Objects.requireNonNull(listGetter, "listGetter is null");
return new Getter<V>() { return new Getter<V>() {
@Override @Override
public V get() { public V get() {
return listGetter.get().get(index); return listGetter.get().get(index);
} }
}; };
} }
public static <K, V> Getter<V> get(Getter<Map<K, V>> mapGetter, K k) { public static <K, V> Getter<V> get(Getter<Map<K, V>> mapGetter, K k) {
Objects.requireNonNull(mapGetter, "mapGetter is null"); Objects.requireNonNull(mapGetter, "mapGetter is null");
Objects.requireNonNull(k, "key is null"); Objects.requireNonNull(k, "key is null");
return new Getter<V>() { return new Getter<V>() {
@Override @Override
public V get() { public V get() {
return mapGetter.get().get(k); return mapGetter.get().get(k);
} }
}; };
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,16 +16,14 @@
*/ */
package net.helenus.core; package net.helenus.core;
import java.util.*;
import java.util.stream.Collectors;
import com.datastax.driver.core.*; import com.datastax.driver.core.*;
import com.datastax.driver.core.querybuilder.IsNotNullClause; import com.datastax.driver.core.querybuilder.IsNotNullClause;
import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select; import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.schemabuilder.*; import com.datastax.driver.core.schemabuilder.*;
import com.datastax.driver.core.schemabuilder.Create.Options; import com.datastax.driver.core.schemabuilder.Create.Options;
import java.util.*;
import java.util.stream.Collectors;
import net.helenus.core.reflect.HelenusPropertyNode; import net.helenus.core.reflect.HelenusPropertyNode;
import net.helenus.mapping.*; import net.helenus.mapping.*;
import net.helenus.mapping.ColumnType; import net.helenus.mapping.ColumnType;
@ -35,392 +34,441 @@ import net.helenus.support.HelenusMappingException;
public final class SchemaUtil { public final class SchemaUtil {
private SchemaUtil() { private SchemaUtil() {}
}
public static RegularStatement use(String keyspace, boolean forceQuote) { public static RegularStatement use(String keyspace, boolean forceQuote) {
if (forceQuote) { if (forceQuote) {
return new SimpleStatement("USE" + CqlUtil.forceQuote(keyspace)); return new SimpleStatement("USE" + CqlUtil.forceQuote(keyspace));
} else { } else {
return new SimpleStatement("USE " + keyspace); return new SimpleStatement("USE " + keyspace);
} }
} }
public static SchemaStatement createUserType(HelenusEntity entity) { public static SchemaStatement createUserType(HelenusEntity entity) {
if (entity.getType() != HelenusEntityType.UDT) { if (entity.getType() != HelenusEntityType.UDT) {
throw new HelenusMappingException("expected UDT entity " + entity); throw new HelenusMappingException("expected UDT entity " + entity);
} }
CreateType create = SchemaBuilder.createType(entity.getName().toCql()); CreateType create = SchemaBuilder.createType(entity.getName().toCql());
for (HelenusProperty prop : entity.getOrderedProperties()) { for (HelenusProperty prop : entity.getOrderedProperties()) {
ColumnType columnType = prop.getColumnType(); ColumnType columnType = prop.getColumnType();
if (columnType == ColumnType.PARTITION_KEY || columnType == ColumnType.CLUSTERING_COLUMN) { if (columnType == ColumnType.PARTITION_KEY || columnType == ColumnType.CLUSTERING_COLUMN) {
throw new HelenusMappingException("primary key columns are not supported in UserDefinedType for " throw new HelenusMappingException(
+ prop.getPropertyName() + " in entity " + entity); "primary key columns are not supported in UserDefinedType for "
} + prop.getPropertyName()
+ " in entity "
+ entity);
}
try {
prop.getDataType().addColumn(create, prop.getColumnName());
} catch (IllegalArgumentException e) {
throw new HelenusMappingException(
"invalid column name '"
+ prop.getColumnName()
+ "' in entity '"
+ entity.getName().getName()
+ "'",
e);
}
}
return create;
}
public static List<SchemaStatement> alterUserType(
UserType userType, HelenusEntity entity, boolean dropUnusedColumns) {
if (entity.getType() != HelenusEntityType.UDT) {
throw new HelenusMappingException("expected UDT entity " + entity);
}
List<SchemaStatement> result = new ArrayList<SchemaStatement>();
/**
* TODO: In future replace SchemaBuilder.alterTable by SchemaBuilder.alterType when it will
* exist
*/
Alter alter = SchemaBuilder.alterTable(entity.getName().toCql());
final Set<String> visitedColumns =
dropUnusedColumns ? new HashSet<String>() : Collections.<String>emptySet();
for (HelenusProperty prop : entity.getOrderedProperties()) {
String columnName = prop.getColumnName().getName();
if (dropUnusedColumns) {
visitedColumns.add(columnName);
}
ColumnType columnType = prop.getColumnType();
if (columnType == ColumnType.PARTITION_KEY || columnType == ColumnType.CLUSTERING_COLUMN) {
continue;
}
DataType dataType = userType.getFieldType(columnName);
SchemaStatement stmt =
prop.getDataType()
.alterColumn(alter, prop.getColumnName(), optional(columnName, dataType));
if (stmt != null) {
result.add(stmt);
}
}
if (dropUnusedColumns) {
for (String field : userType.getFieldNames()) {
if (!visitedColumns.contains(field)) {
result.add(alter.dropColumn(field));
}
}
}
return result;
}
public static SchemaStatement dropUserType(HelenusEntity entity) {
if (entity.getType() != HelenusEntityType.UDT) {
throw new HelenusMappingException("expected UDT entity " + entity);
}
return SchemaBuilder.dropType(entity.getName().toCql()).ifExists();
}
public static SchemaStatement dropUserType(UserType type) {
return SchemaBuilder.dropType(type.getTypeName()).ifExists();
}
public static String createPrimaryKeyPhrase(Collection<HelenusProperty> properties) {
List<String> p = new ArrayList<String>(properties.size());
List<String> c = new ArrayList<String>(properties.size());
for (HelenusProperty prop : properties) {
String columnName = prop.getColumnName().toCql();
switch (prop.getColumnType()) {
case PARTITION_KEY:
p.add(columnName);
break;
case CLUSTERING_COLUMN:
c.add(columnName);
break;
default:
break;
}
}
if (p.size() == 0 && c.size() == 0)
return "{"
+ properties
.stream()
.map(HelenusProperty::getPropertyName)
.collect(Collectors.joining(", "))
+ "}";
return "("
+ ((p.size() > 1) ? "(" + String.join(", ", p) + ")" : p.get(0))
+ ((c.size() > 0)
? ", " + ((c.size() > 1) ? "(" + String.join(", ", c) + ")" : c.get(0))
: "")
+ ")";
}
public static SchemaStatement createMaterializedView(
String keyspace, String viewName, HelenusEntity entity) {
if (entity.getType() != HelenusEntityType.VIEW) {
throw new HelenusMappingException("expected view entity " + entity);
}
List<HelenusPropertyNode> props = new ArrayList<HelenusPropertyNode>();
entity
.getOrderedProperties()
.stream()
.map(p -> new HelenusPropertyNode(p, Optional.empty()))
.forEach(p -> props.add(p));
Select.Selection selection = QueryBuilder.select();
for (HelenusPropertyNode prop : props) {
String columnName = prop.getColumnName();
selection = selection.column(columnName);
}
Class<?> iface = entity.getMappingInterface();
String tableName = Helenus.entity(iface.getInterfaces()[0]).getName().toCql();
Select.Where where = selection.from(tableName).where();
List<String> o = new ArrayList<String>(props.size());
for (HelenusPropertyNode prop : props) {
String columnName = prop.getColumnName();
switch (prop.getProperty().getColumnType()) {
case PARTITION_KEY:
where = where.and(new IsNotNullClause(columnName));
break;
case CLUSTERING_COLUMN:
where = where.and(new IsNotNullClause(columnName));
ClusteringColumn clusteringColumn =
prop.getProperty().getGetterMethod().getAnnotation(ClusteringColumn.class);
if (clusteringColumn != null && clusteringColumn.ordering() != null) {
o.add(columnName + " " + clusteringColumn.ordering().cql());
}
break;
default:
break;
}
}
String primaryKey = "PRIMARY KEY " + createPrimaryKeyPhrase(entity.getOrderedProperties());
String clustering = "";
if (o.size() > 0) {
clustering = "WITH CLUSTERING ORDER BY (" + String.join(", ", o) + ")";
}
return new CreateMaterializedView(keyspace, viewName, where, primaryKey, clustering)
.ifNotExists();
}
public static SchemaStatement dropMaterializedView(
String keyspace, String viewName, HelenusEntity entity) {
return new DropMaterializedView(keyspace, viewName);
}
public static SchemaStatement createTable(HelenusEntity entity) {
if (entity.getType() != HelenusEntityType.TABLE) {
throw new HelenusMappingException("expected table entity " + entity);
}
// NOTE: There is a bug in the normal path of createTable where the
// "cache" is set too early and never unset preventing more than
// one column on a table.
// SchemaBuilder.createTable(entity.getName().toCql());
CreateTable create = new CreateTable(entity.getName().toCql());
create.ifNotExists();
List<HelenusProperty> clusteringColumns = new ArrayList<HelenusProperty>();
for (HelenusProperty prop : entity.getOrderedProperties()) {
ColumnType columnType = prop.getColumnType();
if (columnType == ColumnType.CLUSTERING_COLUMN) {
clusteringColumns.add(prop);
}
prop.getDataType().addColumn(create, prop.getColumnName());
}
if (!clusteringColumns.isEmpty()) {
Options options = create.withOptions();
clusteringColumns.forEach(
p -> options.clusteringOrder(p.getColumnName().toCql(), mapDirection(p.getOrdering())));
}
return create;
}
public static List<SchemaStatement> alterTable(
TableMetadata tmd, HelenusEntity entity, boolean dropUnusedColumns) {
if (entity.getType() != HelenusEntityType.TABLE) {
throw new HelenusMappingException("expected table entity " + entity);
}
List<SchemaStatement> result = new ArrayList<SchemaStatement>();
Alter alter = SchemaBuilder.alterTable(entity.getName().toCql());
final Set<String> visitedColumns =
dropUnusedColumns ? new HashSet<String>() : Collections.<String>emptySet();
for (HelenusProperty prop : entity.getOrderedProperties()) {
String columnName = prop.getColumnName().getName();
if (dropUnusedColumns) {
visitedColumns.add(columnName);
}
ColumnType columnType = prop.getColumnType();
if (columnType == ColumnType.PARTITION_KEY || columnType == ColumnType.CLUSTERING_COLUMN) {
continue;
}
ColumnMetadata columnMetadata = tmd.getColumn(columnName);
SchemaStatement stmt =
prop.getDataType().alterColumn(alter, prop.getColumnName(), optional(columnMetadata));
if (stmt != null) {
result.add(stmt);
}
}
if (dropUnusedColumns) {
for (ColumnMetadata cm : tmd.getColumns()) {
if (!visitedColumns.contains(cm.getName())) {
result.add(alter.dropColumn(cm.getName()));
}
}
}
return result;
}
public static SchemaStatement dropTable(HelenusEntity entity) {
if (entity.getType() != HelenusEntityType.TABLE) {
throw new HelenusMappingException("expected table entity " + entity);
}
return SchemaBuilder.dropTable(entity.getName().toCql()).ifExists();
}
public static SchemaStatement createIndex(HelenusProperty prop) {
if (prop.caseSensitiveIndex()) {
return SchemaBuilder.createIndex(indexName(prop))
.ifNotExists()
.onTable(prop.getEntity().getName().toCql())
.andColumn(prop.getColumnName().toCql());
} else {
return new CreateSasiIndex(prop.getIndexName().get().toCql())
.ifNotExists()
.onTable(prop.getEntity().getName().toCql())
.andColumn(prop.getColumnName().toCql());
}
}
public static List<SchemaStatement> createIndexes(HelenusEntity entity) {
return entity
.getOrderedProperties()
.stream()
.filter(p -> p.getIndexName().isPresent())
.map(p -> SchemaUtil.createIndex(p))
.collect(Collectors.toList());
}
public static List<SchemaStatement> alterIndexes(
TableMetadata tmd, HelenusEntity entity, boolean dropUnusedIndexes) {
List<SchemaStatement> list = new ArrayList<SchemaStatement>();
final Set<String> visitedColumns =
dropUnusedIndexes ? new HashSet<String>() : Collections.<String>emptySet();
entity
.getOrderedProperties()
.stream()
.filter(p -> p.getIndexName().isPresent())
.forEach(
p -> {
String columnName = p.getColumnName().getName();
if (dropUnusedIndexes) {
visitedColumns.add(columnName);
}
ColumnMetadata cm = tmd.getColumn(columnName);
if (cm != null) {
IndexMetadata im = tmd.getIndex(columnName);
if (im == null) {
list.add(createIndex(p));
}
} else {
list.add(createIndex(p));
}
});
if (dropUnusedIndexes) {
tmd.getColumns()
.stream()
.filter(c -> tmd.getIndex(c.getName()) != null && !visitedColumns.contains(c.getName()))
.forEach(
c -> {
list.add(SchemaBuilder.dropIndex(tmd.getIndex(c.getName()).getName()).ifExists());
});
}
return list;
}
public static SchemaStatement dropIndex(HelenusProperty prop) {
return SchemaBuilder.dropIndex(indexName(prop)).ifExists();
}
private static SchemaBuilder.Direction mapDirection(OrderingDirection o) {
switch (o) {
case ASC:
return SchemaBuilder.Direction.ASC;
case DESC:
return SchemaBuilder.Direction.DESC;
}
throw new HelenusMappingException("unknown ordering " + o);
}
public static void throwNoMapping(HelenusProperty prop) {
throw new HelenusMappingException(
"only primitive types and Set,List,Map collections and UserDefinedTypes are allowed, unknown type for property '"
+ prop.getPropertyName()
+ "' type is '"
+ prop.getJavaType()
+ "' in the entity "
+ prop.getEntity());
}
private static OptionalColumnMetadata optional(final ColumnMetadata columnMetadata) {
if (columnMetadata != null) {
return new OptionalColumnMetadata() {
@Override
public String getName() {
return columnMetadata.getName();
}
@Override
public DataType getType() {
return columnMetadata.getType();
}
};
}
return null;
}
private static OptionalColumnMetadata optional(final String name, final DataType dataType) {
if (dataType != null) {
return new OptionalColumnMetadata() {
@Override
public String getName() {
return name;
}
@Override
public DataType getType() {
return dataType;
}
};
}
return null;
}
private static String indexName(HelenusProperty prop) {
return prop.getEntity().getName().toCql() + "_" + prop.getIndexName().get().toCql();
}
try {
prop.getDataType().addColumn(create, prop.getColumnName());
} catch (IllegalArgumentException e) {
throw new HelenusMappingException("invalid column name '" + prop.getColumnName() + "' in entity '"
+ entity.getName().getName() + "'", e);
}
}
return create;
}
public static List<SchemaStatement> alterUserType(UserType userType, HelenusEntity entity,
boolean dropUnusedColumns) {
if (entity.getType() != HelenusEntityType.UDT) {
throw new HelenusMappingException("expected UDT entity " + entity);
}
List<SchemaStatement> result = new ArrayList<SchemaStatement>();
/**
* TODO: In future replace SchemaBuilder.alterTable by SchemaBuilder.alterType
* when it will exist
*/
Alter alter = SchemaBuilder.alterTable(entity.getName().toCql());
final Set<String> visitedColumns = dropUnusedColumns ? new HashSet<String>() : Collections.<String>emptySet();
for (HelenusProperty prop : entity.getOrderedProperties()) {
String columnName = prop.getColumnName().getName();
if (dropUnusedColumns) {
visitedColumns.add(columnName);
}
ColumnType columnType = prop.getColumnType();
if (columnType == ColumnType.PARTITION_KEY || columnType == ColumnType.CLUSTERING_COLUMN) {
continue;
}
DataType dataType = userType.getFieldType(columnName);
SchemaStatement stmt = prop.getDataType().alterColumn(alter, prop.getColumnName(),
optional(columnName, dataType));
if (stmt != null) {
result.add(stmt);
}
}
if (dropUnusedColumns) {
for (String field : userType.getFieldNames()) {
if (!visitedColumns.contains(field)) {
result.add(alter.dropColumn(field));
}
}
}
return result;
}
public static SchemaStatement dropUserType(HelenusEntity entity) {
if (entity.getType() != HelenusEntityType.UDT) {
throw new HelenusMappingException("expected UDT entity " + entity);
}
return SchemaBuilder.dropType(entity.getName().toCql()).ifExists();
}
public static SchemaStatement dropUserType(UserType type) {
return SchemaBuilder.dropType(type.getTypeName()).ifExists();
}
public static String createPrimaryKeyPhrase(Collection<HelenusProperty> properties) {
List<String> p = new ArrayList<String>(properties.size());
List<String> c = new ArrayList<String>(properties.size());
for (HelenusProperty prop : properties) {
String columnName = prop.getColumnName().toCql();
switch (prop.getColumnType()) {
case PARTITION_KEY :
p.add(columnName);
break;
case CLUSTERING_COLUMN :
c.add(columnName);
break;
default :
break;
}
}
return "(" + ((p.size() > 1) ? "(" + String.join(", ", p) + ")" : p.get(0))
+ ((c.size() > 0) ? ", " + ((c.size() > 1) ? "(" + String.join(", ", c) + ")" : c.get(0)) : "") + ")";
}
public static SchemaStatement createMaterializedView(String keyspace, String viewName, HelenusEntity entity) {
if (entity.getType() != HelenusEntityType.VIEW) {
throw new HelenusMappingException("expected view entity " + entity);
}
if (entity == null) {
throw new HelenusMappingException("no entity or table to select data");
}
List<HelenusPropertyNode> props = new ArrayList<HelenusPropertyNode>();
entity.getOrderedProperties().stream().map(p -> new HelenusPropertyNode(p, Optional.empty()))
.forEach(p -> props.add(p));
Select.Selection selection = QueryBuilder.select();
for (HelenusPropertyNode prop : props) {
String columnName = prop.getColumnName();
selection = selection.column(columnName);
}
Class<?> iface = entity.getMappingInterface();
String tableName = Helenus.entity(iface.getInterfaces()[0]).getName().toCql();
Select.Where where = selection.from(tableName).where();
List<String> o = new ArrayList<String>(props.size());
for (HelenusPropertyNode prop : props) {
String columnName = prop.getColumnName();
switch (prop.getProperty().getColumnType()) {
case PARTITION_KEY :
where = where.and(new IsNotNullClause(columnName));
break;
case CLUSTERING_COLUMN :
where = where.and(new IsNotNullClause(columnName));
ClusteringColumn clusteringColumn = prop.getProperty().getGetterMethod()
.getAnnotation(ClusteringColumn.class);
if (clusteringColumn != null && clusteringColumn.ordering() != null) {
o.add(columnName + " " + clusteringColumn.ordering().cql());
}
break;
default :
break;
}
}
String primaryKey = "PRIMARY KEY " + createPrimaryKeyPhrase(entity.getOrderedProperties());
String clustering = "";
if (o.size() > 0) {
clustering = "WITH CLUSTERING ORDER BY (" + String.join(", ", o) + ")";
}
return new CreateMaterializedView(keyspace, viewName, where, primaryKey, clustering).ifNotExists();
}
public static SchemaStatement dropMaterializedView(String keyspace, String viewName, HelenusEntity entity) {
return new DropMaterializedView(keyspace, viewName);
}
public static SchemaStatement createTable(HelenusEntity entity) {
if (entity.getType() != HelenusEntityType.TABLE) {
throw new HelenusMappingException("expected table entity " + entity);
}
// NOTE: There is a bug in the normal path of createTable where the
// "cache" is set too early and never unset preventing more than
// one column on a table.
// SchemaBuilder.createTable(entity.getName().toCql());
CreateTable create = new CreateTable(entity.getName().toCql());
create.ifNotExists();
List<HelenusProperty> clusteringColumns = new ArrayList<HelenusProperty>();
for (HelenusProperty prop : entity.getOrderedProperties()) {
ColumnType columnType = prop.getColumnType();
if (columnType == ColumnType.CLUSTERING_COLUMN) {
clusteringColumns.add(prop);
}
prop.getDataType().addColumn(create, prop.getColumnName());
}
if (!clusteringColumns.isEmpty()) {
Options options = create.withOptions();
clusteringColumns
.forEach(p -> options.clusteringOrder(p.getColumnName().toCql(), mapDirection(p.getOrdering())));
}
return create;
}
public static List<SchemaStatement> alterTable(TableMetadata tmd, HelenusEntity entity, boolean dropUnusedColumns) {
if (entity.getType() != HelenusEntityType.TABLE) {
throw new HelenusMappingException("expected table entity " + entity);
}
List<SchemaStatement> result = new ArrayList<SchemaStatement>();
Alter alter = SchemaBuilder.alterTable(entity.getName().toCql());
final Set<String> visitedColumns = dropUnusedColumns ? new HashSet<String>() : Collections.<String>emptySet();
for (HelenusProperty prop : entity.getOrderedProperties()) {
String columnName = prop.getColumnName().getName();
if (dropUnusedColumns) {
visitedColumns.add(columnName);
}
ColumnType columnType = prop.getColumnType();
if (columnType == ColumnType.PARTITION_KEY || columnType == ColumnType.CLUSTERING_COLUMN) {
continue;
}
ColumnMetadata columnMetadata = tmd.getColumn(columnName);
SchemaStatement stmt = prop.getDataType().alterColumn(alter, prop.getColumnName(),
optional(columnMetadata));
if (stmt != null) {
result.add(stmt);
}
}
if (dropUnusedColumns) {
for (ColumnMetadata cm : tmd.getColumns()) {
if (!visitedColumns.contains(cm.getName())) {
result.add(alter.dropColumn(cm.getName()));
}
}
}
return result;
}
public static SchemaStatement dropTable(HelenusEntity entity) {
if (entity.getType() != HelenusEntityType.TABLE) {
throw new HelenusMappingException("expected table entity " + entity);
}
return SchemaBuilder.dropTable(entity.getName().toCql()).ifExists();
}
public static SchemaStatement createIndex(HelenusProperty prop) {
if (prop.caseSensitiveIndex()) {
return SchemaBuilder.createIndex(prop.getIndexName().get().toCql()).ifNotExists()
.onTable(prop.getEntity().getName().toCql()).andColumn(prop.getColumnName().toCql());
} else {
return new CreateSasiIndex(prop.getIndexName().get().toCql()).ifNotExists()
.onTable(prop.getEntity().getName().toCql()).andColumn(prop.getColumnName().toCql());
}
}
public static List<SchemaStatement> createIndexes(HelenusEntity entity) {
return entity.getOrderedProperties().stream().filter(p -> p.getIndexName().isPresent())
.map(p -> SchemaUtil.createIndex(p)).collect(Collectors.toList());
}
public static List<SchemaStatement> alterIndexes(TableMetadata tmd, HelenusEntity entity,
boolean dropUnusedIndexes) {
List<SchemaStatement> list = new ArrayList<SchemaStatement>();
final Set<String> visitedColumns = dropUnusedIndexes ? new HashSet<String>() : Collections.<String>emptySet();
entity.getOrderedProperties().stream().filter(p -> p.getIndexName().isPresent()).forEach(p -> {
String columnName = p.getColumnName().getName();
if (dropUnusedIndexes) {
visitedColumns.add(columnName);
}
ColumnMetadata cm = tmd.getColumn(columnName);
if (cm != null) {
IndexMetadata im = tmd.getIndex(columnName);
if (im == null) {
list.add(createIndex(p));
}
} else {
list.add(createIndex(p));
}
});
if (dropUnusedIndexes) {
tmd.getColumns().stream()
.filter(c -> tmd.getIndex(c.getName()) != null && !visitedColumns.contains(c.getName()))
.forEach(c -> {
list.add(SchemaBuilder.dropIndex(tmd.getIndex(c.getName()).getName()).ifExists());
});
}
return list;
}
public static SchemaStatement dropIndex(HelenusProperty prop) {
return SchemaBuilder.dropIndex(prop.getIndexName().get().toCql()).ifExists();
}
private static SchemaBuilder.Direction mapDirection(OrderingDirection o) {
switch (o) {
case ASC :
return SchemaBuilder.Direction.ASC;
case DESC :
return SchemaBuilder.Direction.DESC;
}
throw new HelenusMappingException("unknown ordering " + o);
}
public static void throwNoMapping(HelenusProperty prop) {
throw new HelenusMappingException(
"only primitive types and Set,List,Map collections and UserDefinedTypes are allowed, unknown type for property '"
+ prop.getPropertyName() + "' type is '" + prop.getJavaType() + "' in the entity "
+ prop.getEntity());
}
private static OptionalColumnMetadata optional(final ColumnMetadata columnMetadata) {
if (columnMetadata != null) {
return new OptionalColumnMetadata() {
@Override
public String getName() {
return columnMetadata.getName();
}
@Override
public DataType getType() {
return columnMetadata.getType();
}
};
}
return null;
}
private static OptionalColumnMetadata optional(final String name, final DataType dataType) {
if (dataType != null) {
return new OptionalColumnMetadata() {
@Override
public String getName() {
return name;
}
@Override
public DataType getType() {
return dataType;
}
};
}
return null;
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,18 +16,16 @@
*/ */
package net.helenus.core; package net.helenus.core;
import com.codahale.metrics.MetricRegistry;
import com.datastax.driver.core.*;
import com.google.common.util.concurrent.MoreExecutors;
import java.io.IOException; import java.io.IOException;
import java.io.PrintStream; import java.io.PrintStream;
import java.util.*; import java.util.*;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.function.Consumer; import java.util.function.Consumer;
import javax.cache.CacheManager;
import com.codahale.metrics.MetricRegistry;
import com.datastax.driver.core.*;
import com.google.common.util.concurrent.MoreExecutors;
import brave.Tracer;
import net.helenus.core.reflect.DslExportable; import net.helenus.core.reflect.DslExportable;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
import net.helenus.mapping.HelenusEntityType; import net.helenus.mapping.HelenusEntityType;
@ -39,344 +38,428 @@ import net.helenus.support.PackageUtil;
public final class SessionInitializer extends AbstractSessionOperations { public final class SessionInitializer extends AbstractSessionOperations {
private final Session session; private final Session session;
private final List<Either<Object, Class<?>>> initList = new ArrayList<Either<Object, Class<?>>>(); private final List<Either<Object, Class<?>>> initList = new ArrayList<Either<Object, Class<?>>>();
private CodecRegistry registry; private CodecRegistry registry;
private String usingKeyspace; private String usingKeyspace;
private boolean showCql = false; private boolean showCql = false;
private ConsistencyLevel consistencyLevel; private boolean showValues = true;
private boolean idempotent = true; private ConsistencyLevel consistencyLevel;
private MetricRegistry metricRegistry = new MetricRegistry(); private boolean idempotent = false;
private Tracer zipkinTracer; private MetricRegistry metricRegistry = new MetricRegistry();
private PrintStream printStream = System.out; private PrintStream printStream = System.out;
private Executor executor = MoreExecutors.directExecutor(); private Executor executor = MoreExecutors.directExecutor();
private Class<? extends UnitOfWork> unitOfWorkClass = UnitOfWorkImpl.class; private SessionRepositoryBuilder sessionRepository;
private SessionRepositoryBuilder sessionRepository; private boolean dropUnusedColumns = false;
private boolean dropUnusedColumns = false; private boolean dropUnusedIndexes = false;
private boolean dropUnusedIndexes = false; private KeyspaceMetadata keyspaceMetadata;
private KeyspaceMetadata keyspaceMetadata; private AutoDdl autoDdl = AutoDdl.UPDATE;
private AutoDdl autoDdl = AutoDdl.UPDATE; private CacheManager cacheManager = null;
SessionInitializer(Session session) { SessionInitializer(Session session, String keyspace) {
this.session = Objects.requireNonNull(session, "empty session"); this.session = session;
this.usingKeyspace = session.getLoggedKeyspace(); // can be null this.usingKeyspace = keyspace;
this.sessionRepository = new SessionRepositoryBuilder(session); if (session != null) {
} this.sessionRepository = new SessionRepositoryBuilder(session);
}
@Override }
public Session currentSession() {
return session; SessionInitializer(Session session) {
} this.session = Objects.requireNonNull(session, "empty session");
this.usingKeyspace = session.getLoggedKeyspace(); // can be null
@Override this.sessionRepository = new SessionRepositoryBuilder(session);
public String usingKeyspace() { }
return usingKeyspace;
} @Override
public Session currentSession() {
@Override return session;
public Executor getExecutor() { }
return executor;
} @Override
public String usingKeyspace() {
@Override return usingKeyspace;
public SessionRepository getSessionRepository() { }
throw new HelenusException("not expected to call");
} @Override
public Executor getExecutor() {
@Override return executor;
public ColumnValueProvider getValueProvider() { }
throw new HelenusException("not expected to call");
} @Override
public SessionRepository getSessionRepository() {
@Override throw new HelenusException("not expected to call");
public ColumnValuePreparer getValuePreparer() { }
throw new HelenusException("not expected to call");
} @Override
public ColumnValueProvider getValueProvider() {
public SessionInitializer showCql() { throw new HelenusException("not expected to call");
this.showCql = true; }
return this;
} @Override
public ColumnValuePreparer getValuePreparer() {
public SessionInitializer showCql(boolean enabled) { throw new HelenusException("not expected to call");
this.showCql = enabled; }
return this;
} public SessionInitializer showCql() {
this.showCql = true;
public SessionInitializer metricRegistry(MetricRegistry metricRegistry) { return this;
this.metricRegistry = metricRegistry; }
return this;
} public SessionInitializer showCql(boolean enabled) {
this.showCql = enabled;
public SessionInitializer zipkinTracer(Tracer tracer) { return this;
this.zipkinTracer = tracer; }
return this;
} public SessionInitializer showQueryValuesInLog(boolean showValues) {
this.showValues = showValues;
public SessionInitializer setUnitOfWorkClass(Class<? extends UnitOfWork> e) { return this;
this.unitOfWorkClass = e; }
return this;
} public SessionInitializer showQueryValuesInLog() {
this.showValues = true;
public SessionInitializer consistencyLevel(ConsistencyLevel consistencyLevel) { return this;
this.consistencyLevel = consistencyLevel; }
return this;
} public boolean showValues() {
return showValues;
public ConsistencyLevel getDefaultConsistencyLevel() { }
return consistencyLevel;
} public SessionInitializer metricRegistry(MetricRegistry metricRegistry) {
this.metricRegistry = metricRegistry;
public SessionInitializer idempotentQueryExecution(boolean idempotent) { return this;
this.idempotent = idempotent; }
return this;
} public SessionInitializer consistencyLevel(ConsistencyLevel consistencyLevel) {
this.consistencyLevel = consistencyLevel;
public boolean getDefaultQueryIdempotency() { return this;
return idempotent; }
}
public SessionInitializer setCacheManager(CacheManager cacheManager) {
@Override this.cacheManager = cacheManager;
public PrintStream getPrintStream() { return this;
return printStream; }
}
public ConsistencyLevel getDefaultConsistencyLevel() {
public SessionInitializer printTo(PrintStream out) { return consistencyLevel;
this.printStream = out; }
return this;
} public SessionInitializer setOperationsIdempotentByDefault() {
this.idempotent = true;
public SessionInitializer withExecutor(Executor executor) { return this;
Objects.requireNonNull(executor, "empty executor"); }
this.executor = executor;
return this; public SessionInitializer idempotentQueryExecution(boolean idempotent) {
} this.idempotent = idempotent;
return this;
public SessionInitializer withCachingExecutor() { }
this.executor = Executors.newCachedThreadPool();
return this; public boolean getDefaultQueryIdempotency() {
} return idempotent;
}
public SessionInitializer dropUnusedColumns(boolean enabled) {
this.dropUnusedColumns = enabled; @Override
return this; public PrintStream getPrintStream() {
} return printStream;
}
public SessionInitializer dropUnusedIndexes(boolean enabled) {
this.dropUnusedIndexes = enabled; public SessionInitializer printTo(PrintStream out) {
return this; this.printStream = out;
} return this;
}
public SessionInitializer withCodecRegistry(CodecRegistry registry) {
this.registry = registry; public SessionInitializer withExecutor(Executor executor) {
return this; Objects.requireNonNull(executor, "empty executor");
} this.executor = executor;
return this;
@Override }
public boolean isShowCql() {
return showCql; public SessionInitializer withCachingExecutor() {
} this.executor = Executors.newCachedThreadPool();
return this;
public SessionInitializer addPackage(String packageName) { }
try {
PackageUtil.getClasses(packageName).stream().filter(c -> c.isInterface() && !c.isAnnotation()) public SessionInitializer dropUnusedColumns(boolean enabled) {
.forEach(clazz -> { this.dropUnusedColumns = enabled;
initList.add(Either.right(clazz)); return this;
}); }
} catch (IOException | ClassNotFoundException e) {
throw new HelenusException("fail to add package " + packageName, e); public SessionInitializer dropUnusedIndexes(boolean enabled) {
} this.dropUnusedIndexes = enabled;
return this; return this;
} }
public SessionInitializer add(Object... dsls) { public SessionInitializer withCodecRegistry(CodecRegistry registry) {
Objects.requireNonNull(dsls, "dsls is empty"); this.registry = registry;
int len = dsls.length; return this;
for (int i = 0; i != len; ++i) { }
Object obj = Objects.requireNonNull(dsls[i], "element " + i + " is empty");
initList.add(Either.left(obj)); @Override
} public boolean isShowCql() {
return this; return showCql;
} }
public SessionInitializer autoValidate() { public SessionInitializer addPackage(String packageName) {
this.autoDdl = AutoDdl.VALIDATE; try {
return this; PackageUtil.getClasses(packageName)
} .stream()
.filter(c -> c.isInterface() && !c.isAnnotation())
public SessionInitializer autoUpdate() { .forEach(
this.autoDdl = AutoDdl.UPDATE; clazz -> {
return this; initList.add(Either.right(clazz));
} });
} catch (IOException | ClassNotFoundException e) {
public SessionInitializer autoCreate() { throw new HelenusException("fail to add package " + packageName, e);
this.autoDdl = AutoDdl.CREATE; }
return this; return this;
} }
public SessionInitializer autoCreateDrop() { public SessionInitializer add(Object... dsls) {
this.autoDdl = AutoDdl.CREATE_DROP; Objects.requireNonNull(dsls, "dsls is empty");
return this; int len = dsls.length;
} for (int i = 0; i != len; ++i) {
Object obj = Objects.requireNonNull(dsls[i], "element " + i + " is empty");
public SessionInitializer auto(AutoDdl autoDdl) { initList.add(Either.left(obj));
this.autoDdl = autoDdl; }
return this; return this;
} }
public SessionInitializer use(String keyspace) { public SessionInitializer autoValidate() {
session.execute(SchemaUtil.use(keyspace, false)); this.autoDdl = AutoDdl.VALIDATE;
this.usingKeyspace = keyspace; return this;
return this; }
}
public SessionInitializer autoUpdate() {
public SessionInitializer use(String keyspace, boolean forceQuote) { this.autoDdl = AutoDdl.UPDATE;
session.execute(SchemaUtil.use(keyspace, forceQuote)); return this;
this.usingKeyspace = keyspace; }
return this;
} public SessionInitializer autoCreate() {
this.autoDdl = AutoDdl.CREATE;
public void singleton() { return this;
Helenus.setSession(get()); }
}
public SessionInitializer autoCreateDrop() {
public synchronized HelenusSession get() { this.autoDdl = AutoDdl.CREATE_DROP;
initialize(); return this;
return new HelenusSession(session, usingKeyspace, registry, showCql, printStream, sessionRepository, executor, }
autoDdl == AutoDdl.CREATE_DROP, consistencyLevel, idempotent, unitOfWorkClass, metricRegistry,
zipkinTracer); public SessionInitializer auto(AutoDdl autoDdl) {
} this.autoDdl = autoDdl;
return this;
private void initialize() { }
Objects.requireNonNull(usingKeyspace, "please define keyspace by 'use' operator"); public SessionInitializer use(String keyspace) {
if (session != null) {
initList.forEach((either) -> { session.execute(SchemaUtil.use(keyspace, false));
Class<?> iface = null; this.usingKeyspace = keyspace;
if (either.isLeft()) { }
iface = MappingUtil.getMappingInterface(either.getLeft()); return this;
} else { }
iface = either.getRight();
} public SessionInitializer use(String keyspace, boolean forceQuote) {
session.execute(SchemaUtil.use(keyspace, forceQuote));
DslExportable dsl = (DslExportable) Helenus.dsl(iface); this.usingKeyspace = keyspace;
dsl.setCassandraMetadataForHelenusSession(session.getCluster().getMetadata()); return this;
sessionRepository.add(dsl); }
});
public void singleton() {
TableOperations tableOps = new TableOperations(this, dropUnusedColumns, dropUnusedIndexes); Helenus.setSession(get());
UserTypeOperations userTypeOps = new UserTypeOperations(this, dropUnusedColumns); }
switch (autoDdl) { public synchronized HelenusSession get() {
case CREATE_DROP : initialize();
return new HelenusSession(
// Drop view first, otherwise a `DROP TABLE ...` will fail as the type is still session,
// referenced usingKeyspace,
// by a view. registry,
sessionRepository.entities().stream().filter(e -> e.getType() == HelenusEntityType.VIEW) showCql,
.forEach(e -> tableOps.dropView(e)); showValues,
printStream,
// Drop tables second, before DROP TYPE otherwise a `DROP TYPE ...` will fail as sessionRepository,
// the type is executor,
// still referenced by a table. autoDdl == AutoDdl.CREATE_DROP,
sessionRepository.entities().stream().filter(e -> e.getType() == HelenusEntityType.TABLE) consistencyLevel,
.forEach(e -> tableOps.dropTable(e)); idempotent,
cacheManager,
eachUserTypeInReverseOrder(userTypeOps, e -> userTypeOps.dropUserType(e)); metricRegistry);
}
// FALLTHRU to CREATE case (read: the absence of a `break;` statement here is
// intentional!) private void initialize() {
case CREATE :
eachUserTypeInOrder(userTypeOps, e -> userTypeOps.createUserType(e)); Objects.requireNonNull(usingKeyspace, "please define keyspace by 'use' operator");
sessionRepository.entities().stream().filter(e -> e.getType() == HelenusEntityType.TABLE) initList.forEach(
.forEach(e -> tableOps.createTable(e)); (either) -> {
Class<?> iface = null;
sessionRepository.entities().stream().filter(e -> e.getType() == HelenusEntityType.VIEW) if (either.isLeft()) {
.forEach(e -> tableOps.createView(e)); iface = MappingUtil.getMappingInterface(either.getLeft());
} else {
break; iface = either.getRight();
}
case VALIDATE :
eachUserTypeInOrder(userTypeOps, e -> userTypeOps.validateUserType(getUserType(e), e)); DslExportable dsl = (DslExportable) Helenus.dsl(iface);
if (session != null) {
sessionRepository.entities().stream().filter(e -> e.getType() == HelenusEntityType.TABLE) dsl.setCassandraMetadataForHelenusSession(session.getCluster().getMetadata());
.forEach(e -> tableOps.validateTable(getTableMetadata(e), e)); }
if (sessionRepository != null) {
break; sessionRepository.add(dsl);
}
case UPDATE : });
eachUserTypeInOrder(userTypeOps, e -> userTypeOps.updateUserType(getUserType(e), e));
if (session == null) return;
sessionRepository.entities().stream().filter(e -> e.getType() == HelenusEntityType.VIEW)
.forEach(e -> tableOps.dropView(e)); TableOperations tableOps = new TableOperations(this, dropUnusedColumns, dropUnusedIndexes);
UserTypeOperations userTypeOps = new UserTypeOperations(this, dropUnusedColumns);
sessionRepository.entities().stream().filter(e -> e.getType() == HelenusEntityType.TABLE)
.forEach(e -> tableOps.updateTable(getTableMetadata(e), e)); switch (autoDdl) {
case CREATE_DROP:
sessionRepository.entities().stream().filter(e -> e.getType() == HelenusEntityType.VIEW)
.forEach(e -> tableOps.createView(e)); // Drop view first, otherwise a `DROP TABLE ...` will fail as the type is still
break; // referenced
} // by a view.
sessionRepository
KeyspaceMetadata km = getKeyspaceMetadata(); .entities()
.stream()
for (UserType userType : km.getUserTypes()) { .filter(e -> e.getType() == HelenusEntityType.VIEW)
sessionRepository.addUserType(userType.getTypeName(), userType); .forEach(e -> tableOps.dropView(e));
}
} // Drop tables second, before DROP TYPE otherwise a `DROP TYPE ...` will fail as
// the type is
private void eachUserTypeInOrder(UserTypeOperations userTypeOps, Consumer<? super HelenusEntity> action) { // still referenced by a table.
sessionRepository
Set<HelenusEntity> processedSet = new HashSet<HelenusEntity>(); .entities()
Set<HelenusEntity> stack = new HashSet<HelenusEntity>(); .stream()
.filter(e -> e.getType() == HelenusEntityType.TABLE)
sessionRepository.entities().stream().filter(e -> e.getType() == HelenusEntityType.UDT).forEach(e -> { .forEach(e -> tableOps.dropTable(e));
stack.clear();
eachUserTypeInRecursion(e, processedSet, stack, userTypeOps, action); eachUserTypeInReverseOrder(userTypeOps, e -> userTypeOps.dropUserType(e));
});
} // FALLTHRU to CREATE case (read: the absence of a `break;` statement here is
// intentional!)
private void eachUserTypeInReverseOrder(UserTypeOperations userTypeOps, Consumer<? super HelenusEntity> action) { case CREATE:
ArrayDeque<HelenusEntity> deque = new ArrayDeque<>(); eachUserTypeInOrder(userTypeOps, e -> userTypeOps.createUserType(e));
eachUserTypeInOrder(userTypeOps, e -> deque.addFirst(e));
deque.stream().forEach(e -> { sessionRepository
action.accept(e); .entities()
}); .stream()
} .filter(e -> e.getType() == HelenusEntityType.TABLE)
.forEach(e -> tableOps.createTable(e));
private void eachUserTypeInRecursion(HelenusEntity e, Set<HelenusEntity> processedSet, Set<HelenusEntity> stack,
UserTypeOperations userTypeOps, Consumer<? super HelenusEntity> action) { sessionRepository
.entities()
stack.add(e); .stream()
.filter(e -> e.getType() == HelenusEntityType.VIEW)
Collection<HelenusEntity> createBefore = sessionRepository.getUserTypeUses(e); .forEach(e -> tableOps.createView(e));
for (HelenusEntity be : createBefore) { break;
if (!processedSet.contains(be) && !stack.contains(be)) {
eachUserTypeInRecursion(be, processedSet, stack, userTypeOps, action); case VALIDATE:
processedSet.add(be); eachUserTypeInOrder(userTypeOps, e -> userTypeOps.validateUserType(getUserType(e), e));
}
} sessionRepository
.entities()
if (!processedSet.contains(e)) { .stream()
action.accept(e); .filter(e -> e.getType() == HelenusEntityType.TABLE)
processedSet.add(e); .forEach(e -> tableOps.validateTable(getTableMetadata(e), e));
}
} break;
private KeyspaceMetadata getKeyspaceMetadata() { case UPDATE:
if (keyspaceMetadata == null) { eachUserTypeInOrder(userTypeOps, e -> userTypeOps.updateUserType(getUserType(e), e));
keyspaceMetadata = session.getCluster().getMetadata().getKeyspace(usingKeyspace.toLowerCase());
} sessionRepository
return keyspaceMetadata; .entities()
} .stream()
.filter(e -> e.getType() == HelenusEntityType.VIEW)
private TableMetadata getTableMetadata(HelenusEntity entity) { .forEach(e -> tableOps.dropView(e));
return getKeyspaceMetadata().getTable(entity.getName().getName());
} sessionRepository
.entities()
private UserType getUserType(HelenusEntity entity) { .stream()
return getKeyspaceMetadata().getUserType(entity.getName().getName()); .filter(e -> e.getType() == HelenusEntityType.TABLE)
} .forEach(e -> tableOps.updateTable(getTableMetadata(e), e));
sessionRepository
.entities()
.stream()
.filter(e -> e.getType() == HelenusEntityType.VIEW)
.forEach(e -> tableOps.createView(e));
break;
}
KeyspaceMetadata km = getKeyspaceMetadata();
for (UserType userType : km.getUserTypes()) {
sessionRepository.addUserType(userType.getTypeName(), userType);
}
}
private void eachUserTypeInOrder(
UserTypeOperations userTypeOps, Consumer<? super HelenusEntity> action) {
Set<HelenusEntity> processedSet = new HashSet<HelenusEntity>();
Set<HelenusEntity> stack = new HashSet<HelenusEntity>();
sessionRepository
.entities()
.stream()
.filter(e -> e.getType() == HelenusEntityType.UDT)
.forEach(
e -> {
stack.clear();
eachUserTypeInRecursion(e, processedSet, stack, userTypeOps, action);
});
}
private void eachUserTypeInReverseOrder(
UserTypeOperations userTypeOps, Consumer<? super HelenusEntity> action) {
ArrayDeque<HelenusEntity> deque = new ArrayDeque<>();
eachUserTypeInOrder(userTypeOps, e -> deque.addFirst(e));
deque
.stream()
.forEach(
e -> {
action.accept(e);
});
}
private void eachUserTypeInRecursion(
HelenusEntity e,
Set<HelenusEntity> processedSet,
Set<HelenusEntity> stack,
UserTypeOperations userTypeOps,
Consumer<? super HelenusEntity> action) {
stack.add(e);
Collection<HelenusEntity> createBefore = sessionRepository.getUserTypeUses(e);
for (HelenusEntity be : createBefore) {
if (!processedSet.contains(be) && !stack.contains(be)) {
eachUserTypeInRecursion(be, processedSet, stack, userTypeOps, action);
processedSet.add(be);
}
}
if (!processedSet.contains(e)) {
action.accept(e);
processedSet.add(e);
}
}
private KeyspaceMetadata getKeyspaceMetadata() {
if (keyspaceMetadata == null) {
keyspaceMetadata =
session.getCluster().getMetadata().getKeyspace(usingKeyspace.toLowerCase());
}
return keyspaceMetadata;
}
private TableMetadata getTableMetadata(HelenusEntity entity) {
return getKeyspaceMetadata().getTable(entity.getName().getName());
}
private UserType getUserType(HelenusEntity entity) {
return getKeyspaceMetadata().getUserType(entity.getName().getName());
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,31 +16,30 @@
*/ */
package net.helenus.core; package net.helenus.core;
import java.util.Collection;
import com.datastax.driver.core.UserType; import com.datastax.driver.core.UserType;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import java.util.Collection;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
public final class SessionRepository { public final class SessionRepository {
private final ImmutableMap<String, UserType> userTypeMap; private final ImmutableMap<String, UserType> userTypeMap;
private final ImmutableMap<Class<?>, HelenusEntity> entityMap; private final ImmutableMap<Class<?>, HelenusEntity> entityMap;
public SessionRepository(SessionRepositoryBuilder builder) { public SessionRepository(SessionRepositoryBuilder builder) {
userTypeMap = ImmutableMap.<String, UserType>builder().putAll(builder.getUserTypeMap()).build(); userTypeMap = ImmutableMap.<String, UserType>builder().putAll(builder.getUserTypeMap()).build();
entityMap = ImmutableMap.<Class<?>, HelenusEntity>builder().putAll(builder.getEntityMap()).build(); entityMap =
} ImmutableMap.<Class<?>, HelenusEntity>builder().putAll(builder.getEntityMap()).build();
}
public UserType findUserType(String name) { public UserType findUserType(String name) {
return userTypeMap.get(name.toLowerCase()); return userTypeMap.get(name.toLowerCase());
} }
public Collection<HelenusEntity> entities() { public Collection<HelenusEntity> entities() {
return entityMap.values(); return entityMap.values();
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,17 +16,15 @@
*/ */
package net.helenus.core; package net.helenus.core;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import com.datastax.driver.core.Session; import com.datastax.driver.core.Session;
import com.datastax.driver.core.UDTValue; import com.datastax.driver.core.UDTValue;
import com.datastax.driver.core.UserType; import com.datastax.driver.core.UserType;
import com.google.common.collect.HashMultimap; import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap; import com.google.common.collect.Multimap;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
import net.helenus.mapping.HelenusEntityType; import net.helenus.mapping.HelenusEntityType;
import net.helenus.mapping.HelenusProperty; import net.helenus.mapping.HelenusProperty;
@ -35,110 +34,112 @@ import net.helenus.support.HelenusMappingException;
public final class SessionRepositoryBuilder { public final class SessionRepositoryBuilder {
private static final Optional<HelenusEntityType> OPTIONAL_UDT = Optional.of(HelenusEntityType.UDT); private static final Optional<HelenusEntityType> OPTIONAL_UDT =
Optional.of(HelenusEntityType.UDT);
private final Map<Class<?>, HelenusEntity> entityMap = new HashMap<Class<?>, HelenusEntity>(); private final Map<Class<?>, HelenusEntity> entityMap = new HashMap<Class<?>, HelenusEntity>();
private final Map<String, UserType> userTypeMap = new HashMap<String, UserType>(); private final Map<String, UserType> userTypeMap = new HashMap<String, UserType>();
private final Multimap<HelenusEntity, HelenusEntity> userTypeUsesMap = HashMultimap.create(); private final Multimap<HelenusEntity, HelenusEntity> userTypeUsesMap = HashMultimap.create();
private final Session session; private final Session session;
SessionRepositoryBuilder(Session session) { SessionRepositoryBuilder(Session session) {
this.session = session; this.session = session;
} }
public SessionRepository build() { public SessionRepository build() {
return new SessionRepository(this); return new SessionRepository(this);
} }
public Collection<HelenusEntity> getUserTypeUses(HelenusEntity udtName) { public Collection<HelenusEntity> getUserTypeUses(HelenusEntity udtName) {
return userTypeUsesMap.get(udtName); return userTypeUsesMap.get(udtName);
} }
public Collection<HelenusEntity> entities() { public Collection<HelenusEntity> entities() {
return entityMap.values(); return entityMap.values();
} }
protected Map<Class<?>, HelenusEntity> getEntityMap() { protected Map<Class<?>, HelenusEntity> getEntityMap() {
return entityMap; return entityMap;
} }
protected Map<String, UserType> getUserTypeMap() { protected Map<String, UserType> getUserTypeMap() {
return userTypeMap; return userTypeMap;
} }
public void addUserType(String name, UserType userType) { public void addUserType(String name, UserType userType) {
userTypeMap.putIfAbsent(name.toLowerCase(), userType); userTypeMap.putIfAbsent(name.toLowerCase(), userType);
} }
public HelenusEntity add(Object dsl) { public HelenusEntity add(Object dsl) {
return add(dsl, Optional.empty()); return add(dsl, Optional.empty());
} }
public void addEntity(HelenusEntity entity) { public void addEntity(HelenusEntity entity) {
HelenusEntity concurrentEntity = entityMap.putIfAbsent(entity.getMappingInterface(), entity); HelenusEntity concurrentEntity = entityMap.putIfAbsent(entity.getMappingInterface(), entity);
if (concurrentEntity == null) { if (concurrentEntity == null) {
addUserDefinedTypes(entity.getOrderedProperties()); addUserDefinedTypes(entity.getOrderedProperties());
} }
} }
public HelenusEntity add(Object dsl, Optional<HelenusEntityType> type) { public HelenusEntity add(Object dsl, Optional<HelenusEntityType> type) {
HelenusEntity helenusEntity = Helenus.resolve(dsl, session.getCluster().getMetadata()); HelenusEntity helenusEntity = Helenus.resolve(dsl, session.getCluster().getMetadata());
Class<?> iface = helenusEntity.getMappingInterface(); Class<?> iface = helenusEntity.getMappingInterface();
HelenusEntity entity = entityMap.get(iface); HelenusEntity entity = entityMap.get(iface);
if (entity == null) { if (entity == null) {
entity = helenusEntity; entity = helenusEntity;
if (type.isPresent() && entity.getType() != type.get()) { if (type.isPresent() && entity.getType() != type.get()) {
throw new HelenusMappingException("unexpected entity type " + entity.getType() + " for " + entity); throw new HelenusMappingException(
} "unexpected entity type " + entity.getType() + " for " + entity);
}
HelenusEntity concurrentEntity = entityMap.putIfAbsent(iface, entity); HelenusEntity concurrentEntity = entityMap.putIfAbsent(iface, entity);
if (concurrentEntity == null) { if (concurrentEntity == null) {
addUserDefinedTypes(entity.getOrderedProperties()); addUserDefinedTypes(entity.getOrderedProperties());
} else { } else {
entity = concurrentEntity; entity = concurrentEntity;
} }
} }
return entity; return entity;
} }
private void addUserDefinedTypes(Collection<HelenusProperty> props) { private void addUserDefinedTypes(Collection<HelenusProperty> props) {
for (HelenusProperty prop : props) { for (HelenusProperty prop : props) {
AbstractDataType type = prop.getDataType(); AbstractDataType type = prop.getDataType();
if (type instanceof DTDataType) { if (type instanceof DTDataType) {
continue; continue;
} }
if (!UDTValue.class.isAssignableFrom(prop.getJavaType())) { if (!UDTValue.class.isAssignableFrom(prop.getJavaType())) {
for (Class<?> udtClass : type.getTypeArguments()) { for (Class<?> udtClass : type.getTypeArguments()) {
if (UDTValue.class.isAssignableFrom(udtClass)) { if (UDTValue.class.isAssignableFrom(udtClass)) {
continue; continue;
} }
HelenusEntity addedUserType = add(udtClass, OPTIONAL_UDT); HelenusEntity addedUserType = add(udtClass, OPTIONAL_UDT);
if (HelenusEntityType.UDT == prop.getEntity().getType()) { if (HelenusEntityType.UDT == prop.getEntity().getType()) {
userTypeUsesMap.put(prop.getEntity(), addedUserType); userTypeUsesMap.put(prop.getEntity(), addedUserType);
} }
} }
} }
} }
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,88 +16,92 @@
*/ */
package net.helenus.core; package net.helenus.core;
import java.util.List;
import com.datastax.driver.core.TableMetadata; import com.datastax.driver.core.TableMetadata;
import com.datastax.driver.core.schemabuilder.SchemaStatement; import com.datastax.driver.core.schemabuilder.SchemaStatement;
import java.util.List;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
import net.helenus.support.HelenusException; import net.helenus.support.HelenusException;
public final class TableOperations { public final class TableOperations {
private final AbstractSessionOperations sessionOps; private final AbstractSessionOperations sessionOps;
private final boolean dropUnusedColumns; private final boolean dropUnusedColumns;
private final boolean dropUnusedIndexes; private final boolean dropUnusedIndexes;
public TableOperations(AbstractSessionOperations sessionOps, boolean dropUnusedColumns, boolean dropUnusedIndexes) { public TableOperations(
this.sessionOps = sessionOps; AbstractSessionOperations sessionOps, boolean dropUnusedColumns, boolean dropUnusedIndexes) {
this.dropUnusedColumns = dropUnusedColumns; this.sessionOps = sessionOps;
this.dropUnusedIndexes = dropUnusedIndexes; this.dropUnusedColumns = dropUnusedColumns;
} this.dropUnusedIndexes = dropUnusedIndexes;
}
public void createTable(HelenusEntity entity) { public void createTable(HelenusEntity entity) {
sessionOps.execute(SchemaUtil.createTable(entity), true); sessionOps.execute(SchemaUtil.createTable(entity));
executeBatch(SchemaUtil.createIndexes(entity)); executeBatch(SchemaUtil.createIndexes(entity));
} }
public void dropTable(HelenusEntity entity) { public void dropTable(HelenusEntity entity) {
sessionOps.execute(SchemaUtil.dropTable(entity), true); sessionOps.execute(SchemaUtil.dropTable(entity));
} }
public void validateTable(TableMetadata tmd, HelenusEntity entity) { public void validateTable(TableMetadata tmd, HelenusEntity entity) {
if (tmd == null) { if (tmd == null) {
throw new HelenusException( throw new HelenusException(
"table does not exists " + entity.getName() + "for entity " + entity.getMappingInterface()); "table does not exists "
} + entity.getName()
+ "for entity "
+ entity.getMappingInterface());
}
List<SchemaStatement> list = SchemaUtil.alterTable(tmd, entity, dropUnusedColumns); List<SchemaStatement> list = SchemaUtil.alterTable(tmd, entity, dropUnusedColumns);
list.addAll(SchemaUtil.alterIndexes(tmd, entity, dropUnusedIndexes)); list.addAll(SchemaUtil.alterIndexes(tmd, entity, dropUnusedIndexes));
if (!list.isEmpty()) { if (!list.isEmpty()) {
throw new HelenusException( throw new HelenusException(
"schema changed for entity " + entity.getMappingInterface() + ", apply this command: " + list); "schema changed for entity "
} + entity.getMappingInterface()
} + ", apply this command: "
+ list);
}
}
public void updateTable(TableMetadata tmd, HelenusEntity entity) { public void updateTable(TableMetadata tmd, HelenusEntity entity) {
if (tmd == null) { if (tmd == null) {
createTable(entity); createTable(entity);
return; return;
} }
executeBatch(SchemaUtil.alterTable(tmd, entity, dropUnusedColumns)); executeBatch(SchemaUtil.alterTable(tmd, entity, dropUnusedColumns));
executeBatch(SchemaUtil.alterIndexes(tmd, entity, dropUnusedIndexes)); executeBatch(SchemaUtil.alterIndexes(tmd, entity, dropUnusedIndexes));
} }
public void createView(HelenusEntity entity) { public void createView(HelenusEntity entity) {
sessionOps.execute( sessionOps.execute(
SchemaUtil.createMaterializedView(sessionOps.usingKeyspace(), entity.getName().toCql(), entity), true); SchemaUtil.createMaterializedView(
// executeBatch(SchemaUtil.createIndexes(entity)); NOTE: Unfortunately C* 3.10 sessionOps.usingKeyspace(), entity.getName().toCql(), entity));
// does not yet support 2i on materialized views. // executeBatch(SchemaUtil.createIndexes(entity)); NOTE: Unfortunately C* 3.10 does not yet support 2i on materialized views.
} }
public void dropView(HelenusEntity entity) { public void dropView(HelenusEntity entity) {
sessionOps.execute( sessionOps.execute(
SchemaUtil.dropMaterializedView(sessionOps.usingKeyspace(), entity.getName().toCql(), entity), true); SchemaUtil.dropMaterializedView(
} sessionOps.usingKeyspace(), entity.getName().toCql(), entity));
}
public void updateView(TableMetadata tmd, HelenusEntity entity) { public void updateView(TableMetadata tmd, HelenusEntity entity) {
if (tmd == null) { if (tmd == null) {
createTable(entity); createTable(entity);
return; return;
} }
executeBatch(SchemaUtil.alterTable(tmd, entity, dropUnusedColumns)); executeBatch(SchemaUtil.alterTable(tmd, entity, dropUnusedColumns));
executeBatch(SchemaUtil.alterIndexes(tmd, entity, dropUnusedIndexes)); executeBatch(SchemaUtil.alterIndexes(tmd, entity, dropUnusedIndexes));
} }
private void executeBatch(List<SchemaStatement> list) { private void executeBatch(List<SchemaStatement> list) {
list.forEach(s -> { list.forEach(s -> sessionOps.execute(s));
sessionOps.execute(s, true); }
});
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,58 +16,801 @@
*/ */
package net.helenus.core; package net.helenus.core;
import java.util.List; import static net.helenus.core.HelenusSession.deleted;
import java.util.Optional;
import com.google.common.base.Stopwatch; import com.google.common.base.Stopwatch;
import com.google.common.collect.HashBasedTable;
import com.google.common.collect.Table;
import com.google.common.collect.TreeTraverser;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.cache.configuration.CacheEntryListenerConfiguration;
import javax.cache.configuration.Configuration;
import javax.cache.integration.CacheLoader;
import javax.cache.integration.CacheLoaderException;
import javax.cache.integration.CompletionListener;
import javax.cache.processor.EntryProcessor;
import javax.cache.processor.EntryProcessorException;
import javax.cache.processor.EntryProcessorResult;
import net.helenus.core.cache.CacheUtil;
import net.helenus.core.cache.Facet; import net.helenus.core.cache.Facet;
import net.helenus.core.cache.MapCache;
import net.helenus.core.operation.AbstractOperation;
import net.helenus.core.operation.BatchOperation;
import net.helenus.mapping.MappingUtil;
import net.helenus.support.CheckedRunnable;
import net.helenus.support.Either;
import net.helenus.support.HelenusException;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public interface UnitOfWork<X extends Exception> extends AutoCloseable { /** Encapsulates the concept of a "transaction" as a unit-of-work. */
public class UnitOfWork implements AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(UnitOfWork.class);
/** public final UnitOfWork parent;
* Marks the beginning of a transactional section of work. Will write a protected final List<UnitOfWork> nested = new ArrayList<>();
* recordCacheAndDatabaseOperationCount to the shared write-ahead log. protected final Table<String, String, Either<Object, List<Facet>>> cache = HashBasedTable.create();
* protected final EvictTrackingMapCache<String, Object> statementCache;
* @return the handle used to commit or abort the work. protected final HelenusSession session;
*/ protected String purpose;
UnitOfWork<X> begin(); protected List<String> nestedPurposes = new ArrayList<String>();
protected String info;
protected int cacheHits = 0;
protected int cacheMisses = 0;
protected int databaseLookups = 0;
protected final Stopwatch elapsedTime;
protected Map<String, Double> databaseTime = new HashMap<>();
protected double cacheLookupTimeMSecs = 0.0;
private List<CheckedRunnable> commitThunks = new ArrayList<>();
private List<CheckedRunnable> abortThunks = new ArrayList<>();
private Consumer<? super Throwable> exceptionallyThunk;
private List<CompletableFuture<?>> asyncOperationFutures = new ArrayList<CompletableFuture<?>>();
private boolean aborted = false;
private boolean committed = false;
private long committedAt = 0L;
private BatchOperation batch;
void addNestedUnitOfWork(UnitOfWork<X> uow); public UnitOfWork(HelenusSession session) {
this(session, null);
}
/** public UnitOfWork(HelenusSession session, UnitOfWork parent) {
* Checks to see if the work performed between calling begin and now can be Objects.requireNonNull(session, "containing session cannot be null");
* committed or not.
*
* @return a function from which to chain work that only happens when commit is
* successful
* @throws X
* when the work overlaps with other concurrent writers.
*/
PostCommitFunction<Void, Void> commit() throws X;
/** this.parent = parent;
* Explicitly abort the work within this unit of work. Any nested aborted unit if (parent != null) {
* of work will trigger the entire unit of work to commit. parent.addNestedUnitOfWork(this);
*/ }
void abort(); this.session = session;
CacheLoader<String, Object> cacheLoader = null;
if (parent != null) {
cacheLoader =
new CacheLoader<String, Object>() {
boolean hasAborted(); Cache<String, Object> cache = parent.getCache();
boolean hasCommitted(); @Override
public Object load(String key) throws CacheLoaderException {
return cache.get(key);
}
Optional<Object> cacheLookup(List<Facet> facets); @Override
public Map<String, Object> loadAll(Iterable<? extends String> keys)
throws CacheLoaderException {
Map<String, Object> kvp = new HashMap<String, Object>();
for (String key : keys) {
kvp.put(key, cache.get(key));
}
return kvp;
}
};
}
this.elapsedTime = Stopwatch.createUnstarted();
this.statementCache = new EvictTrackingMapCache<String, Object>(null, "UOW(" + hashCode() + ")", cacheLoader, true);
}
void cacheUpdate(Object pojo, List<Facet> facets); public void addDatabaseTime(String name, Stopwatch amount) {
Double time = databaseTime.get(name);
if (time == null) {
databaseTime.put(name, (double) amount.elapsed(TimeUnit.MICROSECONDS));
} else {
databaseTime.put(name, time + amount.elapsed(TimeUnit.MICROSECONDS));
}
}
List<Facet> cacheEvict(List<Facet> facets); public void addCacheLookupTime(Stopwatch amount) {
cacheLookupTimeMSecs += amount.elapsed(TimeUnit.MICROSECONDS);
}
UnitOfWork setPurpose(String purpose); public void addNestedUnitOfWork(UnitOfWork uow) {
synchronized (nested) {
nested.add(uow);
}
}
void addDatabaseTime(String name, Stopwatch amount); /**
void addCacheLookupTime(Stopwatch amount); * Marks the beginning of a transactional section of work. Will write a
* recordCacheAndDatabaseOperationCount to the shared write-ahead log.
*
* @return the handle used to commit or abort the work.
*/
public synchronized UnitOfWork begin() {
elapsedTime.start();
// log.record(txn::start)
return this;
}
// Cache > 0 means "cache hit", < 0 means cache miss. public String getPurpose() {
void recordCacheAndDatabaseOperationCount(int cache, int database); return purpose;
}
public UnitOfWork setPurpose(String purpose) {
this.purpose = purpose;
return this;
}
public void addFuture(CompletableFuture<?> future) {
asyncOperationFutures.add(future);
}
public void setInfo(String info) {
this.info = info;
}
public void recordCacheAndDatabaseOperationCount(int cache, int ops) {
if (cache > 0) {
cacheHits += cache;
} else {
cacheMisses += Math.abs(cache);
}
if (ops > 0) {
databaseLookups += ops;
}
}
public String logTimers(String what) {
double e = (double) elapsedTime.elapsed(TimeUnit.MICROSECONDS) / 1000.0;
double d = 0.0;
double c = cacheLookupTimeMSecs / 1000.0;
double fc = (c / e) * 100.0;
String database = "";
if (databaseTime.size() > 0) {
List<String> dbt = new ArrayList<>(databaseTime.size());
for (Map.Entry<String, Double> dt : databaseTime.entrySet()) {
double t = dt.getValue() / 1000.0;
d += t;
dbt.add(String.format("%s took %,.3fms %,2.2f%%", dt.getKey(), t, (t / e) * 100.0));
}
double fd = (d / e) * 100.0;
database =
String.format(
", %d quer%s (%,.3fms %,2.2f%% - %s)",
databaseLookups, (databaseLookups > 1) ? "ies" : "y", d, fd, String.join(", ", dbt));
}
String cache = "";
if (cacheLookupTimeMSecs > 0) {
int cacheLookups = cacheHits + cacheMisses;
cache =
String.format(
" with %d cache lookup%s (%,.3fms %,2.2f%% - %,d hit, %,d miss)",
cacheLookups, cacheLookups > 1 ? "s" : "", c, fc, cacheHits, cacheMisses);
}
String da = "";
if (databaseTime.size() > 0 || cacheLookupTimeMSecs > 0) {
double dat = d + c;
double daf = (dat / e) * 100;
da =
String.format(
" consuming %,.3fms for data access, or %,2.2f%% of total UOW time.", dat, daf);
}
String x = nestedPurposes.stream().distinct().collect(Collectors.joining(", "));
String n =
nested
.stream()
.map(uow -> String.valueOf(uow.hashCode()))
.collect(Collectors.joining(", "));
String s =
String.format(
Locale.US,
"UOW(%s%s) %s in %,.3fms%s%s%s%s%s%s",
hashCode(),
(nested.size() > 0 ? ", [" + n + "]" : ""),
what,
e,
cache,
database,
da,
(purpose == null ? "" : " " + purpose),
(nestedPurposes.isEmpty()) ? "" : ", " + x,
(info == null) ? "" : " " + info);
return s;
}
private void applyPostCommitFunctions(String what, List<CheckedRunnable> thunks, Consumer<? super Throwable> exceptionallyThunk) {
if (!thunks.isEmpty()) {
for (CheckedRunnable f : thunks) {
try {
f.run();
} catch (Throwable t) {
if (exceptionallyThunk != null) {
exceptionallyThunk.accept(t);
}
}
}
}
}
public Optional<Object> cacheLookup(List<Facet> facets) {
String tableName = CacheUtil.schemaName(facets);
Optional<Object> result = Optional.empty();
for (Facet facet : facets) {
if (!facet.fixed()) {
String columnName = facet.name() + "==" + facet.value();
Either<Object, List<Facet>> eitherValue = cache.get(tableName, columnName);
if (eitherValue != null) {
Object value = deleted;
if (eitherValue.isLeft()) {
value = eitherValue.getLeft();
}
return Optional.of(value);
}
}
}
// Be sure to check all enclosing UnitOfWork caches as well, we may be nested.
result = checkParentCache(facets);
if (result.isPresent()) {
Object r = result.get();
Class<?> iface = MappingUtil.getMappingInterface(r);
if (Helenus.entity(iface).isDraftable()) {
cacheUpdate(r, facets);
} else {
cacheUpdate(SerializationUtils.<Serializable>clone((Serializable) r), facets);
}
}
return result;
}
private Optional<Object> checkParentCache(List<Facet> facets) {
Optional<Object> result = Optional.empty();
if (parent != null) {
result = parent.checkParentCache(facets);
}
return result;
}
public List<Facet> cacheEvict(List<Facet> facets) {
Either<Object, List<Facet>> deletedObjectFacets = Either.right(facets);
String tableName = CacheUtil.schemaName(facets);
Optional<Object> optionalValue = cacheLookup(facets);
for (Facet facet : facets) {
if (!facet.fixed()) {
String columnKey = facet.name() + "==" + facet.value();
// mark the value identified by the facet to `deleted`
cache.put(tableName, columnKey, deletedObjectFacets);
}
}
// Now, look for other row/col pairs that referenced the same object, mark them
// `deleted` if the cache had a value before we added the deleted marker objects.
if (optionalValue.isPresent()) {
Object value = optionalValue.get();
cache
.columnKeySet()
.forEach(
columnKey -> {
Either<Object, List<Facet>> eitherCachedValue = cache.get(tableName, columnKey);
if (eitherCachedValue.isLeft()) {
Object cachedValue = eitherCachedValue.getLeft();
if (cachedValue == value) {
cache.put(tableName, columnKey, deletedObjectFacets);
String[] parts = columnKey.split("==");
facets.add(new Facet<String>(parts[0], parts[1]));
}
}
});
}
return facets;
}
public Cache<String, Object> getCache() {
return statementCache;
}
public Object cacheUpdate(Object value, List<Facet> facets) {
Object result = null;
String tableName = CacheUtil.schemaName(facets);
for (Facet facet : facets) {
if (!facet.fixed()) {
if (facet.alone()) {
String columnName = facet.name() + "==" + facet.value();
if (result == null) result = cache.get(tableName, columnName);
cache.put(tableName, columnName, Either.left(value));
}
}
}
return result;
}
public void batch(AbstractOperation s) {
if (batch == null) {
batch = new BatchOperation(session);
}
batch.add(s);
}
private Iterator<UnitOfWork> getChildNodes() {
return nested.iterator();
}
/**
* Checks to see if the work performed between calling begin and now can be committed or not.
*
* @return a function from which to chain work that only happens when commit is successful
* @throws HelenusException when the work overlaps with other concurrent writers.
*/
public synchronized PostCommitFunction<Void, Void> commit() throws HelenusException {
if (isDone()) {
return PostCommitFunction.NULL_ABORT;
}
// Only the outer-most UOW batches statements for commit time, execute them.
if (batch != null) {
committedAt = batch.sync(this); //TODO(gburd): update cache with writeTime...
}
// All nested UnitOfWork should be committed (not aborted) before calls to
// commit, check.
boolean canCommit = true;
TreeTraverser<UnitOfWork> traverser = TreeTraverser.using(node -> node::getChildNodes);
for (UnitOfWork uow : traverser.postOrderTraversal(this)) {
if (this != uow) {
canCommit &= (!uow.aborted && uow.committed);
}
}
if (!canCommit) {
if (parent == null) {
// Apply all post-commit abort functions, this is the outer-most UnitOfWork.
traverser
.postOrderTraversal(this)
.forEach(
uow -> {
applyPostCommitFunctions("aborted", abortThunks, exceptionallyThunk);
});
elapsedTime.stop();
if (LOG.isInfoEnabled()) {
LOG.info(logTimers("aborted"));
}
}
return PostCommitFunction.NULL_ABORT;
} else {
committed = true;
aborted = false;
if (parent == null) {
// Apply all post-commit commit functions, this is the outer-most UnitOfWork.
traverser
.postOrderTraversal(this)
.forEach(
uow -> {
applyPostCommitFunctions("committed", uow.commitThunks, exceptionallyThunk);
});
// Merge our statement cache into the session cache if it exists.
CacheManager cacheManager = session.getCacheManager();
if (cacheManager != null) {
for (Map.Entry<String, Object> entry :
(Set<Map.Entry<String, Object>>) statementCache.<Map>unwrap(Map.class).entrySet()) {
String[] keyParts = entry.getKey().split("\\.");
if (keyParts.length == 2) {
String cacheName = keyParts[0];
String key = keyParts[1];
if (!StringUtils.isBlank(cacheName) && !StringUtils.isBlank(key)) {
Cache<Object, Object> cache = cacheManager.getCache(cacheName);
if (cache != null) {
Object value = entry.getValue();
if (value == deleted) {
cache.remove(key);
} else {
cache.put(key.toString(), value);
}
}
}
}
}
}
// Merge our cache into the session cache.
session.mergeCache(cache);
// Spoil any lingering futures that may be out there.
asyncOperationFutures.forEach(
f ->
f.completeExceptionally(
new HelenusException(
"Futures must be resolved before their unit of work has committed/aborted.")));
elapsedTime.stop();
if (LOG.isInfoEnabled()) {
LOG.info(logTimers("committed"));
}
return PostCommitFunction.NULL_COMMIT;
} else {
// Merge cache and statistics into parent if there is one.
parent.statementCache.putAll(statementCache.<Map>unwrap(Map.class));
parent.statementCache.removeAll(statementCache.getDeletions());
parent.mergeCache(cache);
parent.addBatched(batch);
if (purpose != null) {
parent.nestedPurposes.add(purpose);
}
parent.cacheHits += cacheHits;
parent.cacheMisses += cacheMisses;
parent.databaseLookups += databaseLookups;
parent.cacheLookupTimeMSecs += cacheLookupTimeMSecs;
for (Map.Entry<String, Double> dt : databaseTime.entrySet()) {
String name = dt.getKey();
if (parent.databaseTime.containsKey(name)) {
double t = parent.databaseTime.get(name);
parent.databaseTime.put(name, t + dt.getValue());
} else {
parent.databaseTime.put(name, dt.getValue());
}
}
}
}
// TODO(gburd): hopefully we'll be able to detect conflicts here and so we'd want to...
// else {
// Constructor<T> ctor = clazz.getConstructor(conflictExceptionClass);
// T object = ctor.newInstance(new Object[] { String message });
// }
return new PostCommitFunction<Void, Void>(commitThunks, abortThunks, exceptionallyThunk, true);
}
private void addBatched(BatchOperation batchArg) {
if (batchArg != null) {
if (this.batch == null) {
this.batch = batchArg;
} else {
this.batch.addAll(batchArg);
}
}
}
/**
* Explicitly abort the work within this unit of work. Any nested aborted unit of work will
* trigger the entire unit of work to commit.
*/
public synchronized void abort() {
if (!aborted) {
aborted = true;
// Spoil any pending futures created within the context of this unit of work.
asyncOperationFutures.forEach(
f ->
f.completeExceptionally(
new HelenusException(
"Futures must be resolved before their unit of work has committed/aborted.")));
TreeTraverser<UnitOfWork> traverser = TreeTraverser.using(node -> node::getChildNodes);
traverser
.postOrderTraversal(this)
.forEach(
uow -> {
applyPostCommitFunctions("aborted", uow.abortThunks, exceptionallyThunk);
uow.abortThunks.clear();
});
if (parent == null) {
if (elapsedTime.isRunning()) {
elapsedTime.stop();
}
if (LOG.isInfoEnabled()) {
LOG.info(logTimers("aborted"));
}
}
// TODO(gburd): when we integrate the transaction support we'll need to...
// log.record(txn::abort)
// cache.invalidateSince(txn::start time)
}
}
private void mergeCache(Table<String, String, Either<Object, List<Facet>>> from) {
Table<String, String, Either<Object, List<Facet>>> to = this.cache;
from.rowMap()
.forEach(
(rowKey, columnMap) -> {
columnMap.forEach(
(columnKey, value) -> {
if (to.contains(rowKey, columnKey)) {
to.put(
rowKey,
columnKey,
Either.left(
CacheUtil.merge(
to.get(rowKey, columnKey).getLeft(),
from.get(rowKey, columnKey).getLeft())));
} else {
to.put(rowKey, columnKey, from.get(rowKey, columnKey));
}
});
});
}
public boolean isDone() {
return aborted || committed;
}
public String describeConflicts() {
return "it's complex...";
}
@Override
public void close() throws HelenusException {
// Closing a UnitOfWork will abort iff we've not already aborted or committed this unit of work.
if (aborted == false && committed == false) {
abort();
}
}
public boolean hasAborted() {
return aborted;
}
public boolean hasCommitted() {
return committed;
}
public long committedAt() {
return committedAt;
}
private static class EvictTrackingMapCache<K, V> implements Cache<K, V> {
private final Set<K> deletes;
private final Cache<K, V> delegate;
public EvictTrackingMapCache(CacheManager manager, String name, CacheLoader<K, V> cacheLoader,
boolean isReadThrough) {
deletes = Collections.synchronizedSet(new HashSet<>());
delegate = new MapCache<>(manager, name, cacheLoader, isReadThrough);
}
/** Non-interface method; should only be called by UnitOfWork when merging to an enclosing UnitOfWork. */
public Set<K> getDeletions() {
return new HashSet<>(deletes);
}
@Override
public V get(K key) {
if (deletes.contains(key)) {
return null;
}
return delegate.get(key);
}
@Override
public Map<K, V> getAll(Set<? extends K> keys) {
Set<? extends K> clonedKeys = new HashSet<>(keys);
clonedKeys.removeAll(deletes);
return delegate.getAll(clonedKeys);
}
@Override
public boolean containsKey(K key) {
if (deletes.contains(key)) {
return false;
}
return delegate.containsKey(key);
}
@Override
public void loadAll(Set<? extends K> keys, boolean replaceExistingValues, CompletionListener listener) {
Set<? extends K> clonedKeys = new HashSet<>(keys);
clonedKeys.removeAll(deletes);
delegate.loadAll(clonedKeys, replaceExistingValues, listener);
}
@Override
public void put(K key, V value) {
if (deletes.contains(key)) {
deletes.remove(key);
}
delegate.put(key, value);
}
@Override
public V getAndPut(K key, V value) {
if (deletes.contains(key)) {
deletes.remove(key);
}
return delegate.getAndPut(key, value);
}
@Override
public void putAll(Map<? extends K, ? extends V> map) {
deletes.removeAll(map.keySet());
delegate.putAll(map);
}
@Override
public synchronized boolean putIfAbsent(K key, V value) {
if (!delegate.containsKey(key) && deletes.contains(key)) {
deletes.remove(key);
}
return delegate.putIfAbsent(key, value);
}
@Override
public boolean remove(K key) {
boolean removed = delegate.remove(key);
deletes.add(key);
return removed;
}
@Override
public boolean remove(K key, V value) {
boolean removed = delegate.remove(key, value);
if (removed) {
deletes.add(key);
}
return removed;
}
@Override
public V getAndRemove(K key) {
V value = delegate.getAndRemove(key);
deletes.add(key);
return value;
}
@Override
public void removeAll(Set<? extends K> keys) {
Set<? extends K> cloneKeys = new HashSet<>(keys);
delegate.removeAll(cloneKeys);
deletes.addAll(cloneKeys);
}
@Override
@SuppressWarnings("unchecked")
public synchronized void removeAll() {
Map<K, V> impl = delegate.unwrap(Map.class);
Set<K> keys = impl.keySet();
delegate.removeAll();
deletes.addAll(keys);
}
@Override
public void clear() {
delegate.clear();
// TODO(gburd): all parents too
deletes.clear();
}
@Override
public boolean replace(K key, V oldValue, V newValue) {
if (deletes.contains(key)) {
return false;
}
return delegate.replace(key, oldValue, newValue);
}
@Override
public boolean replace(K key, V value) {
if (deletes.contains(key)) {
return false;
}
return delegate.replace(key, value);
}
@Override
public V getAndReplace(K key, V value) {
if (deletes.contains(key)) {
return null;
}
return delegate.getAndReplace(key, value);
}
@Override
public <C extends Configuration<K, V>> C getConfiguration(Class<C> clazz) {
return delegate.getConfiguration(clazz);
}
@Override
public <T> T invoke(K key, EntryProcessor<K, V, T> processor, Object... arguments)
throws EntryProcessorException {
if (deletes.contains(key)) {
return null;
}
return delegate.invoke(key, processor, arguments);
}
@Override
public <T> Map<K, EntryProcessorResult<T>> invokeAll(Set<? extends K> keys, EntryProcessor<K, V, T> processor,
Object... arguments) {
Set<? extends K> clonedKeys = new HashSet<>(keys);
clonedKeys.removeAll(deletes);
return delegate.invokeAll(clonedKeys, processor, arguments);
}
@Override
public String getName() {
return delegate.getName();
}
@Override
public CacheManager getCacheManager() {
return delegate.getCacheManager();
}
@Override
public void close() {
delegate.close();
}
@Override
public boolean isClosed() {
return delegate.isClosed();
}
@Override
public <T> T unwrap(Class<T> clazz) {
return delegate.unwrap(clazz);
}
@Override
public void registerCacheEntryListener(CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration) {
delegate.registerCacheEntryListener(cacheEntryListenerConfiguration);
}
@Override
public void deregisterCacheEntryListener(CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration) {
delegate.deregisterCacheEntryListener(cacheEntryListenerConfiguration);
}
@Override
public Iterator<Entry<K, V>> iterator() {
return delegate.iterator();
}
}
} }

View file

@ -1,26 +0,0 @@
/*
* Copyright (C) 2015 The Helenus 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 net.helenus.core;
import net.helenus.support.HelenusException;
class UnitOfWorkImpl extends AbstractUnitOfWork<HelenusException> {
@SuppressWarnings("unchecked")
public UnitOfWorkImpl(HelenusSession session, UnitOfWork parent) {
super(session, (AbstractUnitOfWork<HelenusException>) parent);
}
}

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,63 +16,62 @@
*/ */
package net.helenus.core; package net.helenus.core;
import java.util.List;
import com.datastax.driver.core.UserType; import com.datastax.driver.core.UserType;
import com.datastax.driver.core.schemabuilder.SchemaStatement; import com.datastax.driver.core.schemabuilder.SchemaStatement;
import java.util.List;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
import net.helenus.support.HelenusException; import net.helenus.support.HelenusException;
public final class UserTypeOperations { public final class UserTypeOperations {
private final AbstractSessionOperations sessionOps; private final AbstractSessionOperations sessionOps;
private final boolean dropUnusedColumns; private final boolean dropUnusedColumns;
public UserTypeOperations(AbstractSessionOperations sessionOps, boolean dropUnusedColumns) { public UserTypeOperations(AbstractSessionOperations sessionOps, boolean dropUnusedColumns) {
this.sessionOps = sessionOps; this.sessionOps = sessionOps;
this.dropUnusedColumns = dropUnusedColumns; this.dropUnusedColumns = dropUnusedColumns;
} }
public void createUserType(HelenusEntity entity) { public void createUserType(HelenusEntity entity) {
sessionOps.execute(SchemaUtil.createUserType(entity), true); sessionOps.execute(SchemaUtil.createUserType(entity));
} }
public void dropUserType(HelenusEntity entity) { public void dropUserType(HelenusEntity entity) {
sessionOps.execute(SchemaUtil.dropUserType(entity), true); sessionOps.execute(SchemaUtil.dropUserType(entity));
} }
public void validateUserType(UserType userType, HelenusEntity entity) { public void validateUserType(UserType userType, HelenusEntity entity) {
if (userType == null) { if (userType == null) {
throw new HelenusException( throw new HelenusException(
"userType not exists " + entity.getName() + "for entity " + entity.getMappingInterface()); "userType not exists " + entity.getName() + "for entity " + entity.getMappingInterface());
} }
List<SchemaStatement> list = SchemaUtil.alterUserType(userType, entity, dropUnusedColumns); List<SchemaStatement> list = SchemaUtil.alterUserType(userType, entity, dropUnusedColumns);
if (!list.isEmpty()) { if (!list.isEmpty()) {
throw new HelenusException( throw new HelenusException(
"schema changed for entity " + entity.getMappingInterface() + ", apply this command: " + list); "schema changed for entity "
} + entity.getMappingInterface()
} + ", apply this command: "
+ list);
}
}
public void updateUserType(UserType userType, HelenusEntity entity) { public void updateUserType(UserType userType, HelenusEntity entity) {
if (userType == null) { if (userType == null) {
createUserType(entity); createUserType(entity);
return; return;
} }
executeBatch(SchemaUtil.alterUserType(userType, entity, dropUnusedColumns)); executeBatch(SchemaUtil.alterUserType(userType, entity, dropUnusedColumns));
} }
private void executeBatch(List<SchemaStatement> list) { private void executeBatch(List<SchemaStatement> list) {
list.forEach(s -> { list.forEach(s -> sessionOps.execute(s));
sessionOps.execute(s, true); }
});
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -22,5 +23,4 @@ import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE) @Target(ElementType.TYPE)
public @interface Cacheable { public @interface Cacheable {}
}

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,14 +22,15 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import net.helenus.core.ConflictingUnitOfWorkException; import net.helenus.core.ConflictingUnitOfWorkException;
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
public @interface Retry { public @interface Retry {
Class<? extends Exception>[] on() default {ConflictingUnitOfWorkException.class, TimeoutException.class}; Class<? extends Exception>[] on() default {
ConflictingUnitOfWorkException.class, TimeoutException.class
};
int times() default 3; int times() default 3;
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,7 +19,7 @@ package net.helenus.core.aspect;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
import net.helenus.core.annotation.Retry;
import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Around;
@ -29,71 +30,69 @@ import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import net.helenus.core.annotation.Retry;
@Aspect @Aspect
public class RetryAspect { public class RetryAspect {
private static final Logger log = LoggerFactory.getLogger(RetryAspect.class); private static final Logger log = LoggerFactory.getLogger(RetryAspect.class);
@Around("@annotation(net.helenus.core.annotations.Retry)") @Around("@annotation(net.helenus.core.annotations.Retry)")
public Object retry(ProceedingJoinPoint pjp) throws Throwable { public Object retry(ProceedingJoinPoint pjp) throws Throwable {
Retry retryAnnotation = getRetryAnnotation(pjp); Retry retryAnnotation = getRetryAnnotation(pjp);
return (retryAnnotation != null) ? proceed(pjp, retryAnnotation) : proceed(pjp); return (retryAnnotation != null) ? proceed(pjp, retryAnnotation) : proceed(pjp);
} }
private Object proceed(ProceedingJoinPoint pjp) throws Throwable { private Object proceed(ProceedingJoinPoint pjp) throws Throwable {
return pjp.proceed(); return pjp.proceed();
} }
private Object proceed(ProceedingJoinPoint pjp, Retry retryAnnotation) throws Throwable { private Object proceed(ProceedingJoinPoint pjp, Retry retryAnnotation) throws Throwable {
int times = retryAnnotation.times(); int times = retryAnnotation.times();
Class<? extends Throwable>[] retryOn = retryAnnotation.on(); Class<? extends Throwable>[] retryOn = retryAnnotation.on();
Assert.isTrue(times > 0, "@Retry{times} should be greater than 0!"); Assert.isTrue(times > 0, "@Retry{times} should be greater than 0!");
Assert.isTrue(retryOn.length > 0, "@Retry{on} should have at least one Throwable!"); Assert.isTrue(retryOn.length > 0, "@Retry{on} should have at least one Throwable!");
log.info("Proceed with {} retries on {}", times, Arrays.toString(retryOn)); log.info("Proceed with {} retries on {}", times, Arrays.toString(retryOn));
return tryProceeding(pjp, times, retryOn); return tryProceeding(pjp, times, retryOn);
} }
private Object tryProceeding(ProceedingJoinPoint pjp, int times, Class<? extends Throwable>[] retryOn) private Object tryProceeding(
throws Throwable { ProceedingJoinPoint pjp, int times, Class<? extends Throwable>[] retryOn) throws Throwable {
try { try {
return proceed(pjp); return proceed(pjp);
} catch (Throwable throwable) { } catch (Throwable throwable) {
if (isRetryThrowable(throwable, retryOn) && times-- > 0) { if (isRetryThrowable(throwable, retryOn) && times-- > 0) {
log.info("Conflict detected, {} remaining retries on {}", times, Arrays.toString(retryOn)); log.info("Conflict detected, {} remaining retries on {}", times, Arrays.toString(retryOn));
return tryProceeding(pjp, times, retryOn); return tryProceeding(pjp, times, retryOn);
} }
throw throwable; throw throwable;
} }
} }
private boolean isRetryThrowable(Throwable throwable, Class<? extends Throwable>[] retryOn) { private boolean isRetryThrowable(Throwable throwable, Class<? extends Throwable>[] retryOn) {
Throwable[] causes = ExceptionUtils.getThrowables(throwable); Throwable[] causes = ExceptionUtils.getThrowables(throwable);
for (Throwable cause : causes) { for (Throwable cause : causes) {
for (Class<? extends Throwable> retryThrowable : retryOn) { for (Class<? extends Throwable> retryThrowable : retryOn) {
if (retryThrowable.isAssignableFrom(cause.getClass())) { if (retryThrowable.isAssignableFrom(cause.getClass())) {
return true; return true;
} }
} }
} }
return false; return false;
} }
private Retry getRetryAnnotation(ProceedingJoinPoint pjp) throws NoSuchMethodException { private Retry getRetryAnnotation(ProceedingJoinPoint pjp) throws NoSuchMethodException {
MethodSignature signature = (MethodSignature) pjp.getSignature(); MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod(); Method method = signature.getMethod();
Retry retryAnnotation = AnnotationUtils.findAnnotation(method, Retry.class); Retry retryAnnotation = AnnotationUtils.findAnnotation(method, Retry.class);
if (retryAnnotation != null) { if (retryAnnotation != null) {
return retryAnnotation; return retryAnnotation;
} }
Class<?>[] argClasses = new Class[pjp.getArgs().length]; Class<?>[] argClasses = new Class[pjp.getArgs().length];
for (int i = 0; i < pjp.getArgs().length; i++) { for (int i = 0; i < pjp.getArgs().length; i++) {
argClasses[i] = pjp.getArgs()[i].getClass(); argClasses[i] = pjp.getArgs()[i].getClass();
} }
method = pjp.getTarget().getClass().getMethod(pjp.getSignature().getName(), argClasses); method = pjp.getTarget().getClass().getMethod(pjp.getSignature().getName(), argClasses);
return AnnotationUtils.findAnnotation(method, Retry.class); return AnnotationUtils.findAnnotation(method, Retry.class);
} }
} }

View file

@ -2,7 +2,7 @@ package net.helenus.core.aspect;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
import net.helenus.core.annotation.Retry;
import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.exception.ExceptionUtils;
import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Around;
@ -13,71 +13,69 @@ import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils; import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert; import org.springframework.util.Assert;
import net.helenus.core.annotation.Retry;
@Aspect @Aspect
public class RetryConcurrentUnitOfWorkAspect { public class RetryConcurrentUnitOfWorkAspect {
private static final Logger log = LoggerFactory.getLogger(RetryConcurrentUnitOfWorkAspect.class); private static final Logger log = LoggerFactory.getLogger(RetryConcurrentUnitOfWorkAspect.class);
@Around("@annotation(net.helenus.core.annotations.Retry)") @Around("@annotation(net.helenus.core.annotations.Retry)")
public Object retry(ProceedingJoinPoint pjp) throws Throwable { public Object retry(ProceedingJoinPoint pjp) throws Throwable {
Retry retryAnnotation = getRetryAnnotation(pjp); Retry retryAnnotation = getRetryAnnotation(pjp);
return (retryAnnotation != null) ? proceed(pjp, retryAnnotation) : proceed(pjp); return (retryAnnotation != null) ? proceed(pjp, retryAnnotation) : proceed(pjp);
} }
private Object proceed(ProceedingJoinPoint pjp) throws Throwable { private Object proceed(ProceedingJoinPoint pjp) throws Throwable {
return pjp.proceed(); return pjp.proceed();
} }
private Object proceed(ProceedingJoinPoint pjp, Retry retryAnnotation) throws Throwable { private Object proceed(ProceedingJoinPoint pjp, Retry retryAnnotation) throws Throwable {
int times = retryAnnotation.times(); int times = retryAnnotation.times();
Class<? extends Throwable>[] retryOn = retryAnnotation.on(); Class<? extends Throwable>[] retryOn = retryAnnotation.on();
Assert.isTrue(times > 0, "@Retry{times} should be greater than 0!"); Assert.isTrue(times > 0, "@Retry{times} should be greater than 0!");
Assert.isTrue(retryOn.length > 0, "@Retry{on} should have at least one Throwable!"); Assert.isTrue(retryOn.length > 0, "@Retry{on} should have at least one Throwable!");
log.info("Proceed with {} retries on {}", times, Arrays.toString(retryOn)); log.info("Proceed with {} retries on {}", times, Arrays.toString(retryOn));
return tryProceeding(pjp, times, retryOn); return tryProceeding(pjp, times, retryOn);
} }
private Object tryProceeding(ProceedingJoinPoint pjp, int times, Class<? extends Throwable>[] retryOn) private Object tryProceeding(
throws Throwable { ProceedingJoinPoint pjp, int times, Class<? extends Throwable>[] retryOn) throws Throwable {
try { try {
return proceed(pjp); return proceed(pjp);
} catch (Throwable throwable) { } catch (Throwable throwable) {
if (isRetryThrowable(throwable, retryOn) && times-- > 0) { if (isRetryThrowable(throwable, retryOn) && times-- > 0) {
log.info("Conflict detected, {} remaining retries on {}", times, Arrays.toString(retryOn)); log.info("Conflict detected, {} remaining retries on {}", times, Arrays.toString(retryOn));
return tryProceeding(pjp, times, retryOn); return tryProceeding(pjp, times, retryOn);
} }
throw throwable; throw throwable;
} }
} }
private boolean isRetryThrowable(Throwable throwable, Class<? extends Throwable>[] retryOn) { private boolean isRetryThrowable(Throwable throwable, Class<? extends Throwable>[] retryOn) {
Throwable[] causes = ExceptionUtils.getThrowables(throwable); Throwable[] causes = ExceptionUtils.getThrowables(throwable);
for (Throwable cause : causes) { for (Throwable cause : causes) {
for (Class<? extends Throwable> retryThrowable : retryOn) { for (Class<? extends Throwable> retryThrowable : retryOn) {
if (retryThrowable.isAssignableFrom(cause.getClass())) { if (retryThrowable.isAssignableFrom(cause.getClass())) {
return true; return true;
} }
} }
} }
return false; return false;
} }
private Retry getRetryAnnotation(ProceedingJoinPoint pjp) throws NoSuchMethodException { private Retry getRetryAnnotation(ProceedingJoinPoint pjp) throws NoSuchMethodException {
MethodSignature signature = (MethodSignature) pjp.getSignature(); MethodSignature signature = (MethodSignature) pjp.getSignature();
Method method = signature.getMethod(); Method method = signature.getMethod();
Retry retryAnnotation = AnnotationUtils.findAnnotation(method, Retry.class); Retry retryAnnotation = AnnotationUtils.findAnnotation(method, Retry.class);
if (retryAnnotation != null) { if (retryAnnotation != null) {
return retryAnnotation; return retryAnnotation;
} }
Class[] argClasses = new Class[pjp.getArgs().length]; Class[] argClasses = new Class[pjp.getArgs().length];
for (int i = 0; i < pjp.getArgs().length; i++) { for (int i = 0; i < pjp.getArgs().length; i++) {
argClasses[i] = pjp.getArgs()[i].getClass(); argClasses[i] = pjp.getArgs()[i].getClass();
} }
method = pjp.getTarget().getClass().getMethod(pjp.getSignature().getName(), argClasses); method = pjp.getTarget().getClass().getMethod(pjp.getSignature().getName(), argClasses);
return AnnotationUtils.findAnnotation(method, Retry.class); return AnnotationUtils.findAnnotation(method, Retry.class);
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,24 +16,45 @@
*/ */
package net.helenus.core.cache; package net.helenus.core.cache;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import net.helenus.mapping.HelenusProperty; import net.helenus.mapping.HelenusProperty;
public class BoundFacet extends Facet<String> { public class BoundFacet extends Facet<String> {
private final Map<HelenusProperty, Object> properties; private final Map<HelenusProperty, Object> properties;
BoundFacet(String name, Map<HelenusProperty, Object> properties) { public BoundFacet(HelenusProperty property, Object value) {
super(name, super(property.getPropertyName(), value == null ? null : value.toString());
(properties.keySet().size() > 1) this.properties = new HashMap<HelenusProperty, Object>(1);
? "[" + String.join(", ", this.properties.put(property, value);
properties.keySet().stream().map(key -> properties.get(key).toString()) }
.collect(Collectors.toSet()))
+ "]"
: String.join("", properties.keySet().stream().map(key -> properties.get(key).toString())
.collect(Collectors.toSet())));
this.properties = properties;
}
public Set<HelenusProperty> getProperties() {
return properties.keySet();
}
public BoundFacet(String name, Map<HelenusProperty, Object> properties) {
super(
name,
(properties.keySet().size() > 1)
? "["
+ String.join(
", ",
properties
.keySet()
.stream()
.map(key -> properties.get(key).toString())
.collect(Collectors.toSet()))
+ "]"
: String.join(
"",
properties
.keySet()
.stream()
.map(key -> properties.get(key).toString())
.collect(Collectors.toSet())));
this.properties = properties;
}
} }

View file

@ -1,49 +1,221 @@
package net.helenus.core.cache; package net.helenus.core.cache;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import net.helenus.core.Helenus;
import net.helenus.core.reflect.Entity;
import net.helenus.core.reflect.MapExportable;
import net.helenus.mapping.HelenusEntity;
import net.helenus.mapping.HelenusProperty;
import net.helenus.mapping.MappingUtil;
import net.helenus.mapping.value.BeanColumnValueProvider;
public class CacheUtil { public class CacheUtil {
public static List<String[]> combinations(List<String> items) { public static List<String[]> combinations(List<String> items) {
int n = items.size(); int n = items.size();
if (n > 20 || n < 0) if (n > 20) throw new IllegalArgumentException(n + " is out of range");
throw new IllegalArgumentException(n + " is out of range"); long e = Math.round(Math.pow(2, n));
long e = Math.round(Math.pow(2, n)); List<String[]> out = new ArrayList<String[]>((int) e - 1);
List<String[]> out = new ArrayList<String[]>((int) e - 1); for (int k = 1; k <= items.size(); k++) {
for (int k = 1; k <= items.size(); k++) { kCombinations(items, 0, k, new String[k], out);
kCombinations(items, 0, k, new String[k], out); }
} return out;
return out; }
}
private static void kCombinations(List<String> items, int n, int k, String[] arr, List<String[]> out) { private static void kCombinations(
if (k == 0) { List<String> items, int n, int k, String[] arr, List<String[]> out) {
out.add(arr.clone()); if (k == 0) {
} else { out.add(arr.clone());
for (int i = n; i <= items.size() - k; i++) { } else {
arr[arr.length - k] = items.get(i); for (int i = n; i <= items.size() - k; i++) {
kCombinations(items, i + 1, k - 1, arr, out); arr[arr.length - k] = items.get(i);
} kCombinations(items, i + 1, k - 1, arr, out);
} }
} }
}
public static List<String[]> flattenFacets(List<Facet> facets) { public static List<String> flatKeys(String table, List<Facet> facets) {
List<String[]> combinations = CacheUtil.combinations( return flattenFacets(facets)
facets.stream().filter(facet -> !facet.fixed()).filter(facet -> facet.value() != null).map(facet -> { .stream()
return facet.name() + "==" + facet.value(); .map(
}).collect(Collectors.toList())); combination -> {
return combinations; return table + "." + Arrays.toString(combination);
} })
.collect(Collectors.toList());
}
public static Object merge(Object to, Object from) { public static List<String[]> flattenFacets(List<Facet> facets) {
return to; // TODO(gburd): yeah... List<String[]> combinations =
} CacheUtil.combinations(
facets
.stream()
.filter(facet -> !facet.fixed())
.filter(facet -> facet.value() != null)
.map(
facet -> {
return facet.name() + "==" + facet.value();
})
.collect(Collectors.toList()));
// TODO(gburd): rework so as to not generate the combinations at all rather than filter
facets =
facets
.stream()
.filter(f -> !f.fixed())
.filter(f -> !f.alone() || !f.combined())
.collect(Collectors.toList());
for (Facet facet : facets) {
combinations =
combinations
.stream()
.filter(
combo -> {
// When used alone, this facet is not distinct so don't use it as a key.
if (combo.length == 1) {
if (!facet.alone() && combo[0].startsWith(facet.name() + "==")) {
return false;
}
} else {
if (!facet.combined()) {
for (String c : combo) {
// Don't use this facet in combination with others to create keys.
if (c.startsWith(facet.name() + "==")) {
return false;
}
}
}
}
return true;
})
.collect(Collectors.toList());
}
return combinations;
}
public static String schemaName(List<Facet> facets) { /** Merge changed values in the map behind `from` into `to`. */
return facets.stream().filter(Facet::fixed).map(facet -> facet.value().toString()) public static Object merge(Object t, Object f) {
.collect(Collectors.joining(".")); HelenusEntity entity = Helenus.resolve(MappingUtil.getMappingInterface(t));
}
if (t == f) return t;
if (f == null) return t;
if (t == null) return f;
if (t instanceof MapExportable
&& t instanceof Entity
&& f instanceof MapExportable
&& f instanceof Entity) {
Entity to = (Entity) t;
Entity from = (Entity) f;
Map<String, Object> toValueMap = ((MapExportable) to).toMap();
Map<String, Object> fromValueMap = ((MapExportable) from).toMap();
for (HelenusProperty prop : entity.getOrderedProperties()) {
switch (prop.getColumnType()) {
case PARTITION_KEY:
case CLUSTERING_COLUMN:
continue;
default:
Object toVal = BeanColumnValueProvider.INSTANCE.getColumnValue(to, -1, prop, false);
Object fromVal = BeanColumnValueProvider.INSTANCE.getColumnValue(from, -1, prop, false);
String ttlKey = ttlKey(prop);
String writeTimeKey = writeTimeKey(prop);
int[] toTtlI = (int[]) toValueMap.get(ttlKey);
int toTtl = (toTtlI != null) ? toTtlI[0] : 0;
Long toWriteTime = (Long) toValueMap.get(writeTimeKey);
int[] fromTtlI = (int[]) fromValueMap.get(ttlKey);
int fromTtl = (fromTtlI != null) ? fromTtlI[0] : 0;
Long fromWriteTime = (Long) fromValueMap.get(writeTimeKey);
if (toVal != null) {
if (fromVal != null) {
if (toVal == fromVal) {
// Case: object identity
// Goal: ensure write time and ttl are also in sync
if (fromWriteTime != null
&& fromWriteTime != 0L
&& (toWriteTime == null || fromWriteTime > toWriteTime)) {
((MapExportable) to).put(writeTimeKey, fromWriteTime);
}
if (fromTtl > 0 && fromTtl > toTtl) {
((MapExportable) to).put(ttlKey, fromTtl);
}
} else if (fromWriteTime != null && fromWriteTime != 0L) {
// Case: to exists and from exists
// Goal: copy over from -> to iff from.writeTime > to.writeTime
if (toWriteTime != null && toWriteTime != 0L) {
if (fromWriteTime > toWriteTime) {
((MapExportable) to).put(prop.getPropertyName(), fromVal);
((MapExportable) to).put(writeTimeKey, fromWriteTime);
if (fromTtl > 0) {
((MapExportable) to).put(ttlKey, fromTtl);
}
}
} else {
((MapExportable) to).put(prop.getPropertyName(), fromVal);
((MapExportable) to).put(writeTimeKey, fromWriteTime);
if (fromTtl > 0) {
((MapExportable) to).put(ttlKey, fromTtl);
}
}
} else {
if (toWriteTime == null || toWriteTime == 0L) {
// Caution, entering grey area...
if (!toVal.equals(fromVal)) {
// dangerous waters here, values diverge without information that enables resolution,
// policy (for now) is to move value from -> to anyway.
((MapExportable) to).put(prop.getPropertyName(), fromVal);
if (fromTtl > 0) {
((MapExportable) to).put(ttlKey, fromTtl);
}
}
}
}
}
} else {
// Case: from exists, but to doesn't (it's null)
// Goal: copy over from -> to, include ttl and writeTime if present
if (fromVal != null) {
((MapExportable) to).put(prop.getPropertyName(), fromVal);
if (fromWriteTime != null && fromWriteTime != 0L) {
((MapExportable) to).put(writeTimeKey, fromWriteTime);
}
if (fromTtl > 0) {
((MapExportable) to).put(ttlKey, fromTtl);
}
}
}
}
}
return to;
}
return t;
}
public static String schemaName(List<Facet> facets) {
return facets
.stream()
.filter(Facet::fixed)
.map(facet -> facet.value().toString())
.collect(Collectors.joining("."));
}
public static String writeTimeKey(HelenusProperty prop) {
return writeTimeKey(prop.getColumnName().toCql(false));
}
public static String ttlKey(HelenusProperty prop) {
return ttlKey(prop.getColumnName().toCql(false));
}
public static String writeTimeKey(String columnName) {
String key = "_" + columnName + "_writeTime";
return key.toLowerCase();
}
public static String ttlKey(String columnName) {
String key = "_" + columnName + "_ttl";
return key.toLowerCase();
}
} }

View file

@ -1,43 +0,0 @@
/*
* Copyright (C) 2015 The Helenus 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 net.helenus.core.cache;
public class CaffeineCache<K, V> implements SessionCache<K, V> {
final Cache<K, V> cache;
CaffeineCache(Cache<K, V> cache) {
this.cache = cache;
}
@Override
public void invalidate(K key) {
cache.invalidate(key);
}
@Override
public V get(K key) {
return cache.getIfPresent(key);
}
@Override
public void put(K key, V value) {
cache.put(key, value);
}
}

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,38 +17,53 @@
package net.helenus.core.cache; package net.helenus.core.cache;
/** /** An Entity is identifiable via one or more Facets */
* An Entity is identifiable via one or more Facets
*/
public class Facet<T> { public class Facet<T> {
private final String name; private final String name;
private T value; private T value;
private boolean fixed = false; private boolean fixed = false;
private boolean alone = true;
private boolean combined = true;
public Facet(String name) { public Facet(String name) {
this.name = name; this.name = name;
} }
public Facet(String name, T value) { public Facet(String name, T value) {
this.name = name; this.name = name;
this.value = value; this.value = value;
} }
public String name() { public String name() {
return name; return name;
} }
public T value() { public T value() {
return value; return value;
} }
public Facet setFixed() { public Facet setFixed() {
fixed = true; fixed = true;
return this; return this;
} }
public boolean fixed() { public boolean fixed() {
return fixed; return fixed;
} }
public void setUniquelyIdentifyingWhenAlone(boolean alone) {
this.alone = alone;
}
public void setUniquelyIdentifyingWhenCombined(boolean combined) {
this.combined = combined;
}
public boolean alone() {
return alone;
}
public boolean combined() {
return combined;
}
} }

View file

@ -1,44 +0,0 @@
/*
* Copyright (C) 2015 The Helenus 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 net.helenus.core.cache;
import com.google.common.cache.Cache;
public class GuavaCache<K, V> implements SessionCache<K, V> {
final Cache<K, V> cache;
GuavaCache(Cache<K, V> cache) {
this.cache = cache;
}
@Override
public void invalidate(K key) {
cache.invalidate(key);
}
@Override
public V get(K key) {
return cache.getIfPresent(key);
}
@Override
public void put(K key, V value) {
cache.put(key, value);
}
}

View file

@ -0,0 +1,463 @@
package net.helenus.core.cache;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.cache.Cache;
import javax.cache.CacheManager;
import javax.cache.configuration.CacheEntryListenerConfiguration;
import javax.cache.configuration.Configuration;
import javax.cache.event.CacheEntryRemovedListener;
import javax.cache.integration.CacheLoader;
import javax.cache.integration.CompletionListener;
import javax.cache.processor.EntryProcessor;
import javax.cache.processor.EntryProcessorException;
import javax.cache.processor.EntryProcessorResult;
import javax.cache.processor.MutableEntry;
public class MapCache<K, V> implements Cache<K, V> {
private final CacheManager manager;
private final String name;
private Map<K, V> map = new ConcurrentHashMap<>();
private Set<CacheEntryRemovedListener<K, V>> cacheEntryRemovedListeners = new HashSet<>();
private CacheLoader<K, V> cacheLoader = null;
private boolean isReadThrough = false;
private static class MapConfiguration<K, V> implements Configuration<K, V> {
private static final long serialVersionUID = 6093947542772516209L;
@Override
public Class<K> getKeyType() {
return null;
}
@Override
public Class<V> getValueType() {
return null;
}
@Override
public boolean isStoreByValue() {
return false;
}
}
public MapCache(
CacheManager manager, String name, CacheLoader<K, V> cacheLoader, boolean isReadThrough) {
this.manager = manager;
this.name = name;
this.cacheLoader = cacheLoader;
this.isReadThrough = isReadThrough;
}
/** {@inheritDoc} */
@Override
public V get(K key) {
V value = null;
synchronized (map) {
value = map.get(key);
if (value == null && isReadThrough && cacheLoader != null) {
V loadedValue = cacheLoader.load(key);
if (loadedValue != null) {
map.put(key, loadedValue);
value = loadedValue;
}
}
}
return value;
}
/** {@inheritDoc} */
@Override
public Map<K, V> getAll(Set<? extends K> keys) {
Map<K, V> result = null;
synchronized (map) {
result = new HashMap<K, V>(keys.size());
Iterator<? extends K> it = keys.iterator();
while (it.hasNext()) {
K key = it.next();
V value = map.get(key);
if (value != null) {
result.put(key, value);
it.remove();
}
}
if (keys.size() != 0 && isReadThrough && cacheLoader != null) {
Map<K, V> loadedValues = cacheLoader.loadAll(keys);
for (Map.Entry<K, V> entry : loadedValues.entrySet()) {
V v = entry.getValue();
if (v != null) {
K k = entry.getKey();
map.put(k, v);
result.put(k, v);
}
}
}
}
return result;
}
/** {@inheritDoc} */
@Override
public boolean containsKey(K key) {
return map.containsKey(key);
}
/** {@inheritDoc} */
@Override
public void loadAll(
Set<? extends K> keys, boolean replaceExistingValues, CompletionListener completionListener) {
if (cacheLoader != null) {
try {
synchronized (map) {
Map<K, V> loadedValues = cacheLoader.loadAll(keys);
for (Map.Entry<K, V> entry : loadedValues.entrySet()) {
V value = entry.getValue();
K key = entry.getKey();
if (value != null) {
boolean existsCurrently = map.containsKey(key);
if (!existsCurrently || replaceExistingValues) {
map.put(key, value);
keys.remove(key);
}
}
}
}
} catch (Exception e) {
if (completionListener != null) {
completionListener.onException(e);
}
}
}
if (completionListener != null) {
if (keys.isEmpty()) {
completionListener.onCompletion();
}
}
}
/** {@inheritDoc} */
@Override
public void put(K key, V value) {
map.put(key, value);
}
/** {@inheritDoc} */
@Override
public V getAndPut(K key, V value) {
V result = null;
synchronized (map) {
result = map.get(key);
if (result == null && isReadThrough && cacheLoader != null) {
V loadedValue = cacheLoader.load(key);
if (loadedValue != null) {
result = loadedValue;
}
}
map.put(key, value);
}
return result;
}
/** {@inheritDoc} */
@Override
public void putAll(Map<? extends K, ? extends V> map) {
synchronized (map) {
for (Map.Entry<? extends K, ? extends V> entry : map.entrySet()) {
this.map.put(entry.getKey(), entry.getValue());
}
}
}
/** {@inheritDoc} */
@Override
public boolean putIfAbsent(K key, V value) {
synchronized (map) {
if (!map.containsKey(key)) {
map.put(key, value);
return true;
} else {
return false;
}
}
}
/** {@inheritDoc} */
@Override
public boolean remove(K key) {
boolean removed = false;
synchronized (map) {
removed = map.remove(key) != null;
notifyRemovedListeners(key);
}
return removed;
}
/** {@inheritDoc} */
@Override
public boolean remove(K key, V oldValue) {
synchronized (map) {
V value = map.get(key);
if (value != null && oldValue.equals(value)) {
map.remove(key);
notifyRemovedListeners(key);
return true;
}
}
return false;
}
/** {@inheritDoc} */
@Override
public V getAndRemove(K key) {
synchronized (map) {
V oldValue = null;
oldValue = map.get(key);
map.remove(key);
notifyRemovedListeners(key);
return oldValue;
}
}
/** {@inheritDoc} */
@Override
public boolean replace(K key, V oldValue, V newValue) {
synchronized (map) {
V value = map.get(key);
if (value != null && oldValue.equals(value)) {
map.put(key, newValue);
return true;
}
}
return false;
}
/** {@inheritDoc} */
@Override
public boolean replace(K key, V value) {
synchronized (map) {
if (map.containsKey(key)) {
map.put(key, value);
return true;
}
}
return false;
}
/** {@inheritDoc} */
@Override
public V getAndReplace(K key, V value) {
synchronized (map) {
V oldValue = map.get(key);
if (value != null && value.equals(oldValue)) {
map.put(key, value);
return oldValue;
}
}
return null;
}
/** {@inheritDoc} */
@Override
public void removeAll(Set<? extends K> keys) {
synchronized (map) {
Iterator<? extends K> it = keys.iterator();
while (it.hasNext()) {
K key = it.next();
if (map.containsKey(key)) {
map.remove(key);
} else {
it.remove();
}
}
}
notifyRemovedListeners(keys);
}
/** {@inheritDoc} */
@Override
public void removeAll() {
synchronized (map) {
Set<K> keys = map.keySet();
map.clear();
notifyRemovedListeners(keys);
}
}
/** {@inheritDoc} */
@Override
public void clear() {
map.clear();
}
/** {@inheritDoc} */
@Override
public <C extends Configuration<K, V>> C getConfiguration(Class<C> clazz) {
if (!MapConfiguration.class.isAssignableFrom(clazz)) {
throw new IllegalArgumentException();
}
return null;
}
/** {@inheritDoc} */
@Override
public <T> T invoke(K key, EntryProcessor<K, V, T> entryProcessor, Object... arguments)
throws EntryProcessorException {
// TODO
return null;
}
/** {@inheritDoc} */
@Override
public <T> Map<K, EntryProcessorResult<T>> invokeAll(
Set<? extends K> keys, EntryProcessor<K, V, T> entryProcessor, Object... arguments) {
synchronized (map) {
for (K key : keys) {
V value = map.get(key);
if (value != null) {
entryProcessor.process(
new MutableEntry<K, V>() {
@Override
public boolean exists() {
return map.containsKey(key);
}
@Override
public void remove() {
synchronized (map) {
V value = map.get(key);
if (value != null) {
map.remove(key);
notifyRemovedListeners(key);
}
}
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return map.get(value);
}
@Override
public <T> T unwrap(Class<T> clazz) {
return null;
}
@Override
public void setValue(V value) {
map.put(key, value);
}
},
arguments);
}
}
}
return null;
}
/** {@inheritDoc} */
@Override
public String getName() {
return name;
}
/** {@inheritDoc} */
@Override
public CacheManager getCacheManager() {
return manager;
}
/** {@inheritDoc} */
@Override
public void close() {}
/** {@inheritDoc} */
@Override
public boolean isClosed() {
return false;
}
/** {@inheritDoc} */
@Override
@SuppressWarnings("unchecked")
public <T> T unwrap(Class<T> clazz) {
if (Map.class.isAssignableFrom(clazz)) {
return (T) map;
}
return null;
}
/** {@inheritDoc} */
@Override
public void registerCacheEntryListener(
CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration) {
//cacheEntryRemovedListeners.add(cacheEntryListenerConfiguration.getCacheEntryListenerFactory().create());
}
/** {@inheritDoc} */
@Override
public void deregisterCacheEntryListener(
CacheEntryListenerConfiguration<K, V> cacheEntryListenerConfiguration) {}
/** {@inheritDoc} */
@Override
public Iterator<Entry<K, V>> iterator() {
synchronized (map) {
return new Iterator<Entry<K, V>>() {
Iterator<Map.Entry<K, V>> entries = map.entrySet().iterator();
@Override
public boolean hasNext() {
return entries.hasNext();
}
@Override
public Entry<K, V> next() {
Map.Entry<K, V> entry = entries.next();
return new Entry<K, V>() {
K key = entry.getKey();
V value = entry.getValue();
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public <T> T unwrap(Class<T> clazz) {
return null;
}
};
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
}
}
private void notifyRemovedListeners(K key) {
// if (cacheEntryRemovedListeners != null) {
// cacheEntryRemovedListeners.forEach(listener -> listener.onRemoved())
// }
}
private void notifyRemovedListeners(Set<? extends K> keys) {}
}

View file

@ -1,41 +0,0 @@
/*
* Copyright (C) 2015 The Helenus 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 net.helenus.core.cache;
public class MemcacheDbCache<K, V> implements SessionCache<K, V> {
//final Cache<K, V> cache;
MemcacheDbCache() {
//this.cache = cache;
}
@Override
public void invalidate(K key) {
//cache.invalidate(key);
}
@Override
public V get(K key) {
return null;
}
@Override
public void put(K key, V value) {
//cache.put(key, value);
}
}

View file

@ -1,41 +0,0 @@
/*
* Copyright (C) 2015 The Helenus 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 net.helenus.core.cache;
public class RedisCache<K, V> implements SessionCache<K, V> {
//final Cache<K, V> cache;
RedisCache() {
//this.cache = cache;
}
@Override
public void invalidate(K key) {
//cache.invalidate(key);
}
@Override
public V get(K key) {
return null;
}
@Override
public void put(K key, V value) {
//cache.put(key, value);
}
}

View file

@ -1,36 +0,0 @@
/*
* Copyright (C) 2015 The Helenus 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 net.helenus.core.cache;
import com.google.common.cache.CacheBuilder;
import java.util.concurrent.TimeUnit;
public interface SessionCache<K, V> {
static <K, V> SessionCache<K, V> defaultCache() {
int MAX_CACHE_SIZE = 10000;
int MAX_CACHE_EXPIRE_SECONDS = 600;
return new GuavaCache<K, V>(CacheBuilder.newBuilder().maximumSize(MAX_CACHE_SIZE)
.expireAfterAccess(MAX_CACHE_EXPIRE_SECONDS, TimeUnit.SECONDS)
.expireAfterWrite(MAX_CACHE_EXPIRE_SECONDS, TimeUnit.SECONDS).recordStats().build());
}
void invalidate(K key);
V get(K key);
void put(K key, V value);
}

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,56 +20,76 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import net.helenus.core.SchemaUtil; import net.helenus.core.SchemaUtil;
import net.helenus.mapping.HelenusProperty; import net.helenus.mapping.HelenusProperty;
public class UnboundFacet extends Facet<String> { public class UnboundFacet extends Facet<String> {
private final List<HelenusProperty> properties; private final List<HelenusProperty> properties;
private final boolean alone;
private final boolean combined;
public UnboundFacet(List<HelenusProperty> properties) { public UnboundFacet(List<HelenusProperty> properties, boolean alone, boolean combined) {
super(SchemaUtil.createPrimaryKeyPhrase(properties)); super(SchemaUtil.createPrimaryKeyPhrase(properties));
this.properties = properties; this.properties = properties;
} this.alone = alone;
this.combined = combined;
}
public UnboundFacet(HelenusProperty property) { public UnboundFacet(List<HelenusProperty> properties) {
super(property.getPropertyName()); this(properties, true, true);
properties = new ArrayList<HelenusProperty>(); }
properties.add(property);
}
public List<HelenusProperty> getProperties() { public UnboundFacet(HelenusProperty property, boolean alone, boolean combined) {
return properties; super(property.getPropertyName());
} properties = new ArrayList<HelenusProperty>();
properties.add(property);
this.alone = alone;
this.combined = combined;
}
public Binder binder() { public UnboundFacet(HelenusProperty property) {
return new Binder(name(), properties); this(property, true, true);
} }
public static class Binder { public List<HelenusProperty> getProperties() {
return properties;
}
private final String name; public Binder binder() {
private final List<HelenusProperty> properties = new ArrayList<HelenusProperty>(); return new Binder(name(), properties, alone, combined);
private Map<HelenusProperty, Object> boundProperties = new HashMap<HelenusProperty, Object>(); }
Binder(String name, List<HelenusProperty> properties) { public static class Binder {
this.name = name;
this.properties.addAll(properties);
}
public Binder setValueForProperty(HelenusProperty prop, Object value) { private final String name;
properties.remove(prop); private final boolean alone;
boundProperties.put(prop, value); private final boolean combined;
return this; private final List<HelenusProperty> properties = new ArrayList<HelenusProperty>();
} private Map<HelenusProperty, Object> boundProperties = new HashMap<HelenusProperty, Object>();
public boolean isBound() { Binder(String name, List<HelenusProperty> properties, boolean alone, boolean combined) {
return properties.isEmpty(); this.name = name;
} this.properties.addAll(properties);
this.alone = alone;
this.combined = combined;
}
public BoundFacet bind() { public Binder setValueForProperty(HelenusProperty prop, Object value) {
return new BoundFacet(name, boundProperties); properties.remove(prop);
} boundProperties.put(prop, value);
} return this;
}
public boolean isBound() {
return properties.isEmpty();
}
public BoundFacet bind() {
BoundFacet facet = new BoundFacet(name, boundProperties);
facet.setUniquelyIdentifyingWhenAlone(alone);
facet.setUniquelyIdentifyingWhenCombined(combined);
return facet;
}
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,97 +16,153 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
import java.util.LinkedList; import java.util.*;
import java.util.List;
import net.helenus.core.*; import net.helenus.core.*;
import net.helenus.core.cache.Facet;
import net.helenus.core.cache.UnboundFacet;
import net.helenus.core.reflect.HelenusPropertyNode;
import net.helenus.mapping.HelenusProperty;
public abstract class AbstractFilterOperation<E, O extends AbstractFilterOperation<E, O>> public abstract class AbstractFilterOperation<E, O extends AbstractFilterOperation<E, O>>
extends extends AbstractOperation<E, O> {
AbstractOperation<E, O> {
protected List<Filter<?>> filters = null; protected List<Filter<?>> filters = null;
protected List<Filter<?>> ifFilters = null; protected List<Filter<?>> ifFilters = null;
public AbstractFilterOperation(AbstractSessionOperations sessionOperations) { public AbstractFilterOperation(AbstractSessionOperations sessionOperations) {
super(sessionOperations); super(sessionOperations);
} }
public <V> O where(Getter<V> getter, Postulate<V> postulate) { public <V> O where(Getter<V> getter, Postulate<V> postulate) {
addFilter(Filter.create(getter, postulate)); addFilter(Filter.create(getter, postulate));
return (O) this; return (O) this;
} }
public <V> O where(Getter<V> getter, Operator operator, V val) { public <V> O where(Getter<V> getter, Operator operator, V val) {
addFilter(Filter.create(getter, operator, val)); addFilter(Filter.create(getter, operator, val));
return (O) this; return (O) this;
} }
public <V> O where(Filter<V> filter) { public <V> O where(Filter<V> filter) {
addFilter(filter); addFilter(filter);
return (O) this; return (O) this;
} }
public <V> O and(Getter<V> getter, Postulate<V> postulate) { public <V> O and(Getter<V> getter, Postulate<V> postulate) {
addFilter(Filter.create(getter, postulate)); addFilter(Filter.create(getter, postulate));
return (O) this; return (O) this;
} }
public <V> O and(Getter<V> getter, Operator operator, V val) { public <V> O and(Getter<V> getter, Operator operator, V val) {
addFilter(Filter.create(getter, operator, val)); addFilter(Filter.create(getter, operator, val));
return (O) this; return (O) this;
} }
public <V> O and(Filter<V> filter) { public <V> O and(Filter<V> filter) {
addFilter(filter); addFilter(filter);
return (O) this; return (O) this;
} }
public <V> O onlyIf(Getter<V> getter, Postulate<V> postulate) { public <V> O onlyIf(Getter<V> getter, Postulate<V> postulate) {
addIfFilter(Filter.create(getter, postulate)); addIfFilter(Filter.create(getter, postulate));
return (O) this; return (O) this;
} }
public <V> O onlyIf(Getter<V> getter, Operator operator, V val) { public <V> O onlyIf(Getter<V> getter, Operator operator, V val) {
addIfFilter(Filter.create(getter, operator, val)); addIfFilter(Filter.create(getter, operator, val));
return (O) this; return (O) this;
} }
public <V> O onlyIf(Filter<V> filter) { public <V> O onlyIf(Filter<V> filter) {
addIfFilter(filter); addIfFilter(filter);
return (O) this; return (O) this;
} }
private void addFilter(Filter<?> filter) { private void addFilter(Filter<?> filter) {
if (filters == null) { if (filters == null) {
filters = new LinkedList<Filter<?>>(); filters = new LinkedList<Filter<?>>();
} }
filters.add(filter); filters.add(filter);
} }
private void addIfFilter(Filter<?> filter) { private void addIfFilter(Filter<?> filter) {
if (ifFilters == null) { if (ifFilters == null) {
ifFilters = new LinkedList<Filter<?>>(); ifFilters = new LinkedList<Filter<?>>();
} }
ifFilters.add(filter); ifFilters.add(filter);
} }
@Override
protected boolean isIdempotentOperation() {
if (filters == null) {
return super.isIdempotentOperation();
}
return filters
.stream()
.anyMatch(
filter -> {
HelenusPropertyNode node = filter.getNode();
if (node != null) {
HelenusProperty prop = node.getProperty();
if (prop != null) {
return prop.isIdempotent();
}
}
return false;
})
|| super.isIdempotentOperation();
}
protected List<Facet> bindFacetValues(List<Facet> facets) {
if (facets == null) {
return new ArrayList<Facet>();
}
List<Facet> boundFacets = new ArrayList<>();
Map<HelenusProperty, Filter> filterMap = new HashMap<>(filters.size());
filters.forEach(f -> filterMap.put(f.getNode().getProperty(), f));
for (Facet facet : facets) {
if (facet instanceof UnboundFacet) {
UnboundFacet unboundFacet = (UnboundFacet) facet;
UnboundFacet.Binder binder = unboundFacet.binder();
if (filters != null) {
for (HelenusProperty prop : unboundFacet.getProperties()) {
Filter filter = filterMap.get(prop);
if (filter != null) {
Object[] postulates = filter.postulateValues();
for (Object p : postulates) {
binder.setValueForProperty(prop, p.toString());
}
}
}
}
if (binder.isBound()) {
boundFacets.add(binder.bind());
}
} else {
boundFacets.add(facet);
}
}
return boundFacets;
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,95 +20,94 @@ import java.util.LinkedHashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import net.helenus.core.*; import net.helenus.core.*;
import net.helenus.mapping.HelenusProperty; import net.helenus.mapping.HelenusProperty;
public abstract class AbstractFilterOptionalOperation<E, O extends AbstractFilterOptionalOperation<E, O>> public abstract class AbstractFilterOptionalOperation<
extends E, O extends AbstractFilterOptionalOperation<E, O>>
AbstractOptionalOperation<E, O> { extends AbstractOptionalOperation<E, O> {
protected Map<HelenusProperty, Filter<?>> filters = null; protected Map<HelenusProperty, Filter<?>> filters = null;
protected List<Filter<?>> ifFilters = null; protected List<Filter<?>> ifFilters = null;
public AbstractFilterOptionalOperation(AbstractSessionOperations sessionOperations) { public AbstractFilterOptionalOperation(AbstractSessionOperations sessionOperations) {
super(sessionOperations); super(sessionOperations);
} }
public <V> O where(Getter<V> getter, Postulate<V> postulate) { public <V> O where(Getter<V> getter, Postulate<V> postulate) {
addFilter(Filter.create(getter, postulate)); addFilter(Filter.create(getter, postulate));
return (O) this; return (O) this;
} }
public <V> O where(Getter<V> getter, Operator operator, V val) { public <V> O where(Getter<V> getter, Operator operator, V val) {
addFilter(Filter.create(getter, operator, val)); addFilter(Filter.create(getter, operator, val));
return (O) this; return (O) this;
} }
public <V> O where(Filter<V> filter) { public <V> O where(Filter<V> filter) {
addFilter(filter); addFilter(filter);
return (O) this; return (O) this;
} }
public <V> O and(Getter<V> getter, Postulate<V> postulate) { public <V> O and(Getter<V> getter, Postulate<V> postulate) {
addFilter(Filter.create(getter, postulate)); addFilter(Filter.create(getter, postulate));
return (O) this; return (O) this;
} }
public <V> O and(Getter<V> getter, Operator operator, V val) { public <V> O and(Getter<V> getter, Operator operator, V val) {
addFilter(Filter.create(getter, operator, val)); addFilter(Filter.create(getter, operator, val));
return (O) this; return (O) this;
} }
public <V> O and(Filter<V> filter) { public <V> O and(Filter<V> filter) {
addFilter(filter); addFilter(filter);
return (O) this; return (O) this;
} }
public <V> O onlyIf(Getter<V> getter, Postulate<V> postulate) { public <V> O onlyIf(Getter<V> getter, Postulate<V> postulate) {
addIfFilter(Filter.create(getter, postulate)); addIfFilter(Filter.create(getter, postulate));
return (O) this; return (O) this;
} }
public <V> O onlyIf(Getter<V> getter, Operator operator, V val) { public <V> O onlyIf(Getter<V> getter, Operator operator, V val) {
addIfFilter(Filter.create(getter, operator, val)); addIfFilter(Filter.create(getter, operator, val));
return (O) this; return (O) this;
} }
public <V> O onlyIf(Filter<V> filter) { public <V> O onlyIf(Filter<V> filter) {
addIfFilter(filter); addIfFilter(filter);
return (O) this; return (O) this;
} }
private void addFilter(Filter<?> filter) { private void addFilter(Filter<?> filter) {
if (filters == null) { if (filters == null) {
filters = new LinkedHashMap<HelenusProperty, Filter<?>>(); filters = new LinkedHashMap<HelenusProperty, Filter<?>>();
} }
filters.put(filter.getNode().getProperty(), filter); filters.put(filter.getNode().getProperty(), filter);
} }
private void addIfFilter(Filter<?> filter) { private void addIfFilter(Filter<?> filter) {
if (ifFilters == null) { if (ifFilters == null) {
ifFilters = new LinkedList<Filter<?>>(); ifFilters = new LinkedList<Filter<?>>();
} }
ifFilters.add(filter); ifFilters.add(filter);
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,95 +20,94 @@ import java.util.LinkedHashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import net.helenus.core.*; import net.helenus.core.*;
import net.helenus.mapping.HelenusProperty; import net.helenus.mapping.HelenusProperty;
public abstract class AbstractFilterStreamOperation<E, O extends AbstractFilterStreamOperation<E, O>> public abstract class AbstractFilterStreamOperation<
extends E, O extends AbstractFilterStreamOperation<E, O>>
AbstractStreamOperation<E, O> { extends AbstractStreamOperation<E, O> {
protected Map<HelenusProperty, Filter<?>> filters = null; protected Map<HelenusProperty, Filter<?>> filters = null;
protected List<Filter<?>> ifFilters = null; protected List<Filter<?>> ifFilters = null;
public AbstractFilterStreamOperation(AbstractSessionOperations sessionOperations) { public AbstractFilterStreamOperation(AbstractSessionOperations sessionOperations) {
super(sessionOperations); super(sessionOperations);
} }
public <V> O where(Getter<V> getter, Postulate<V> postulate) { public <V> O where(Getter<V> getter, Postulate<V> postulate) {
addFilter(Filter.create(getter, postulate)); addFilter(Filter.create(getter, postulate));
return (O) this; return (O) this;
} }
public <V> O where(Getter<V> getter, Operator operator, V val) { public <V> O where(Getter<V> getter, Operator operator, V val) {
addFilter(Filter.create(getter, operator, val)); if (val != null) addFilter(Filter.create(getter, operator, val));
return (O) this; return (O) this;
} }
public <V> O where(Filter<V> filter) { public <V> O where(Filter<V> filter) {
addFilter(filter); addFilter(filter);
return (O) this; return (O) this;
} }
public <V> O and(Getter<V> getter, Postulate<V> postulate) { public <V> O and(Getter<V> getter, Postulate<V> postulate) {
addFilter(Filter.create(getter, postulate)); addFilter(Filter.create(getter, postulate));
return (O) this; return (O) this;
} }
public <V> O and(Getter<V> getter, Operator operator, V val) { public <V> O and(Getter<V> getter, Operator operator, V val) {
addFilter(Filter.create(getter, operator, val)); if (val != null) addFilter(Filter.create(getter, operator, val));
return (O) this; return (O) this;
} }
public <V> O and(Filter<V> filter) { public <V> O and(Filter<V> filter) {
addFilter(filter); addFilter(filter);
return (O) this; return (O) this;
} }
public <V> O onlyIf(Getter<V> getter, Postulate<V> postulate) { public <V> O onlyIf(Getter<V> getter, Postulate<V> postulate) {
addIfFilter(Filter.create(getter, postulate)); addIfFilter(Filter.create(getter, postulate));
return (O) this; return (O) this;
} }
public <V> O onlyIf(Getter<V> getter, Operator operator, V val) { public <V> O onlyIf(Getter<V> getter, Operator operator, V val) {
addIfFilter(Filter.create(getter, operator, val)); if (val != null) addIfFilter(Filter.create(getter, operator, val));
return (O) this; return (O) this;
} }
public <V> O onlyIf(Filter<V> filter) { public <V> O onlyIf(Filter<V> filter) {
addIfFilter(filter); addIfFilter(filter);
return (O) this; return (O) this;
} }
private void addFilter(Filter<?> filter) { private void addFilter(Filter<?> filter) {
if (filters == null) { if (filters == null) {
filters = new LinkedHashMap<HelenusProperty, Filter<?>>(); filters = new LinkedHashMap<HelenusProperty, Filter<?>>();
} }
filters.put(filter.getNode().getProperty(), filter); filters.put(filter.getNode().getProperty(), filter);
} }
private void addIfFilter(Filter<?> filter) { private void addIfFilter(Filter<?> filter) {
if (ifFilters == null) { if (ifFilters == null) {
ifFilters = new LinkedList<Filter<?>>(); ifFilters = new LinkedList<Filter<?>>();
} }
ifFilters.add(filter); ifFilters.add(filter);
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,73 +16,76 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
import com.codahale.metrics.Timer;
import com.datastax.driver.core.ResultSet;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException; import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import com.codahale.metrics.Timer;
import com.datastax.driver.core.ResultSet;
import net.helenus.core.AbstractSessionOperations; import net.helenus.core.AbstractSessionOperations;
import net.helenus.core.UnitOfWork; import net.helenus.core.UnitOfWork;
public abstract class AbstractOperation<E, O extends AbstractOperation<E, O>> extends AbstractStatementOperation<E, O> { public abstract class AbstractOperation<E, O extends AbstractOperation<E, O>>
extends AbstractStatementOperation<E, O> {
public AbstractOperation(AbstractSessionOperations sessionOperations) { public AbstractOperation(AbstractSessionOperations sessionOperations) {
super(sessionOperations); super(sessionOperations);
} }
public abstract E transform(ResultSet resultSet); public abstract E transform(ResultSet resultSet);
public PreparedOperation<E> prepare() { public PreparedOperation<E> prepare() {
return new PreparedOperation<E>(prepareStatement(), this); return new PreparedOperation<E>(prepareStatement(), this);
} }
public E sync() throws TimeoutException { public E sync() throws TimeoutException {
final Timer.Context context = requestLatency.time(); final Timer.Context context = requestLatency.time();
try { try {
ResultSet resultSet = this.execute(sessionOps, null, traceContext, queryExecutionTimeout, queryTimeoutUnits, ResultSet resultSet =
showValues, false); this.execute(
return transform(resultSet); sessionOps, null, queryExecutionTimeout, queryTimeoutUnits, showValues, false);
} finally { return transform(resultSet);
context.stop(); } finally {
} context.stop();
} }
}
public E sync(UnitOfWork uow) throws TimeoutException { public E sync(UnitOfWork uow) throws TimeoutException {
if (uow == null) if (uow == null) return sync();
return sync();
final Timer.Context context = requestLatency.time(); final Timer.Context context = requestLatency.time();
try { try {
ResultSet resultSet = execute(sessionOps, uow, traceContext, queryExecutionTimeout, queryTimeoutUnits, ResultSet resultSet =
showValues, true); execute(sessionOps, uow, queryExecutionTimeout, queryTimeoutUnits, showValues, true);
E result = transform(resultSet); E result = transform(resultSet);
return result; return result;
} finally { } finally {
context.stop(); context.stop();
} }
} }
public CompletableFuture<E> async() { public CompletableFuture<E> async() {
return CompletableFuture.<E>supplyAsync(() -> { return CompletableFuture.<E>supplyAsync(
try { () -> {
return sync(); try {
} catch (TimeoutException ex) { return sync();
throw new CompletionException(ex); } catch (TimeoutException ex) {
} throw new CompletionException(ex);
}); }
} });
}
public CompletableFuture<E> async(UnitOfWork uow) { public CompletableFuture<E> async(UnitOfWork uow) {
if (uow == null) if (uow == null) return async();
return async(); CompletableFuture<E> f =
return CompletableFuture.<E>supplyAsync(() -> { CompletableFuture.<E>supplyAsync(
try { () -> {
return sync(); try {
} catch (TimeoutException ex) { return sync();
throw new CompletionException(ex); } catch (TimeoutException ex) {
} throw new CompletionException(ex);
}); }
} });
uow.addFuture(f);
return f;
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,12 +18,6 @@ package net.helenus.core.operation;
import static net.helenus.core.HelenusSession.deleted; import static net.helenus.core.HelenusSession.deleted;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeoutException;
import com.codahale.metrics.Timer; import com.codahale.metrics.Timer;
import com.datastax.driver.core.PreparedStatement; import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSet;
@ -30,180 +25,231 @@ import com.google.common.base.Function;
import com.google.common.base.Stopwatch; import com.google.common.base.Stopwatch;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import java.io.Serializable;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeoutException;
import net.helenus.core.AbstractSessionOperations; import net.helenus.core.AbstractSessionOperations;
import net.helenus.core.Helenus;
import net.helenus.core.UnitOfWork; import net.helenus.core.UnitOfWork;
import net.helenus.core.cache.CacheUtil; import net.helenus.core.cache.CacheUtil;
import net.helenus.core.cache.Facet; import net.helenus.core.cache.Facet;
import net.helenus.mapping.MappingUtil;
import net.helenus.support.Fun;
import org.apache.commons.lang3.SerializationUtils;
public abstract class AbstractOptionalOperation<E, O extends AbstractOptionalOperation<E, O>> public abstract class AbstractOptionalOperation<E, O extends AbstractOptionalOperation<E, O>>
extends extends AbstractStatementOperation<E, O> {
AbstractStatementOperation<E, O> {
public AbstractOptionalOperation(AbstractSessionOperations sessionOperations) { public AbstractOptionalOperation(AbstractSessionOperations sessionOperations) {
super(sessionOperations); super(sessionOperations);
} }
public abstract Optional<E> transform(ResultSet resultSet); public abstract Optional<E> transform(ResultSet resultSet);
public PreparedOptionalOperation<E> prepare() { public PreparedOptionalOperation<E> prepare() {
return new PreparedOptionalOperation<E>(prepareStatement(), this); return new PreparedOptionalOperation<E>(prepareStatement(), this);
} }
public ListenableFuture<PreparedOptionalOperation<E>> prepareAsync() { public ListenableFuture<PreparedOptionalOperation<E>> prepareAsync() {
final O _this = (O) this; final O _this = (O) this;
return Futures.transform(prepareStatementAsync(), return Futures.transform(
new Function<PreparedStatement, PreparedOptionalOperation<E>>() { prepareStatementAsync(),
@Override new Function<PreparedStatement, PreparedOptionalOperation<E>>() {
public PreparedOptionalOperation<E> apply(PreparedStatement preparedStatement) { @Override
return new PreparedOptionalOperation<E>(preparedStatement, _this); public PreparedOptionalOperation<E> apply(PreparedStatement preparedStatement) {
} return new PreparedOptionalOperation<E>(preparedStatement, _this);
}); }
} });
}
public Optional<E> sync() throws TimeoutException { public Optional<E> sync() throws TimeoutException {
final Timer.Context context = requestLatency.time(); final Timer.Context context = requestLatency.time();
try { try {
Optional<E> result = Optional.empty(); Optional<E> result = Optional.empty();
E cacheResult = null; E cacheResult = null;
boolean updateCache = isSessionCacheable(); boolean updateCache = isSessionCacheable() && !ignoreCache();
if (enableCache && isSessionCacheable()) { if (updateCache) {
List<Facet> facets = bindFacetValues(); List<Facet> facets = bindFacetValues();
String tableName = CacheUtil.schemaName(facets); if (facets != null && facets.size() > 0) {
cacheResult = (E) sessionOps.checkCache(tableName, facets); if (facets.stream().filter(f -> !f.fixed()).distinct().count() > 0) {
if (cacheResult != null) { String tableName = CacheUtil.schemaName(facets);
result = Optional.of(cacheResult); cacheResult = (E) sessionOps.checkCache(tableName, facets);
updateCache = false; if (cacheResult != null) {
sessionCacheHits.mark(); result = Optional.of(cacheResult);
cacheHits.mark(); updateCache = false;
} else { sessionCacheHits.mark();
sessionCacheMiss.mark(); cacheHits.mark();
cacheMiss.mark(); } else {
} sessionCacheMiss.mark();
} cacheMiss.mark();
}
}
} else {
//TODO(gburd): look in statement cache for results
}
}
if (!result.isPresent()) { if (!result.isPresent()) {
// Formulate the query and execute it against the Cassandra cluster. // Formulate the query and execute it against the Cassandra cluster.
ResultSet resultSet = this.execute(sessionOps, null, traceContext, queryExecutionTimeout, ResultSet resultSet =
queryTimeoutUnits, showValues, false); this.execute(
sessionOps,
null,
queryExecutionTimeout,
queryTimeoutUnits,
showValues,
isSessionCacheable());
// Transform the query result set into the desired shape. // Transform the query result set into the desired shape.
result = transform(resultSet); result = transform(resultSet);
} }
if (updateCache && result.isPresent()) { if (updateCache && result.isPresent()) {
List<Facet> facets = getFacets(); E r = result.get();
if (facets != null && facets.size() > 1) { Class<?> resultClass = r.getClass();
sessionOps.updateCache(result.get(), facets); if (!(resultClass.getEnclosingClass() != null
} && resultClass.getEnclosingClass() == Fun.class)) {
} List<Facet> facets = getFacets();
return result; if (facets != null && facets.size() > 1) {
} finally { sessionOps.updateCache(r, facets);
context.stop(); }
} }
} }
return result;
} finally {
context.stop();
}
}
public Optional<E> sync(UnitOfWork<?> uow) throws TimeoutException { public Optional<E> sync(UnitOfWork uow) throws TimeoutException {
if (uow == null) if (uow == null) return sync();
return sync();
final Timer.Context context = requestLatency.time(); final Timer.Context context = requestLatency.time();
try { try {
Optional<E> result = Optional.empty(); Optional<E> result = Optional.empty();
E cachedResult = null; E cachedResult = null;
final boolean updateCache; final boolean updateCache;
if (enableCache) { if (!ignoreCache()) {
Stopwatch timer = Stopwatch.createStarted(); Stopwatch timer = Stopwatch.createStarted();
try { try {
List<Facet> facets = bindFacetValues(); List<Facet> facets = bindFacetValues();
if (facets != null) { if (facets != null && facets.size() > 0) {
cachedResult = checkCache(uow, facets); if (facets.stream().filter(f -> !f.fixed()).distinct().count() > 0) {
if (cachedResult != null) { cachedResult = checkCache(uow, facets);
updateCache = false; if (cachedResult != null) {
result = Optional.of(cachedResult); updateCache = false;
uowCacheHits.mark(); result = Optional.of(cachedResult);
cacheHits.mark(); uowCacheHits.mark();
uow.recordCacheAndDatabaseOperationCount(1, 0); cacheHits.mark();
} else { uow.recordCacheAndDatabaseOperationCount(1, 0);
updateCache = true; } else {
uowCacheMiss.mark(); uowCacheMiss.mark();
if (isSessionCacheable()) { if (isSessionCacheable()) {
String tableName = CacheUtil.schemaName(facets); String tableName = CacheUtil.schemaName(facets);
cachedResult = (E) sessionOps.checkCache(tableName, facets); cachedResult = (E) sessionOps.checkCache(tableName, facets);
if (cachedResult != null) { if (cachedResult != null) {
result = Optional.of(cachedResult); Class<?> iface = MappingUtil.getMappingInterface(cachedResult);
sessionCacheHits.mark(); if (Helenus.entity(iface).isDraftable()) {
cacheHits.mark(); result = Optional.of(cachedResult);
uow.recordCacheAndDatabaseOperationCount(1, 0); } else {
} else { result =
sessionCacheMiss.mark(); Optional.of(
cacheMiss.mark(); (E)
uow.recordCacheAndDatabaseOperationCount(-1, 0); SerializationUtils.<Serializable>clone(
} (Serializable) cachedResult));
} }
} updateCache = false;
} else { sessionCacheHits.mark();
updateCache = false; cacheHits.mark();
} uow.recordCacheAndDatabaseOperationCount(1, 0);
} finally { } else {
timer.stop(); updateCache = true;
uow.addCacheLookupTime(timer); sessionCacheMiss.mark();
} cacheMiss.mark();
} else { uow.recordCacheAndDatabaseOperationCount(-1, 0);
updateCache = false; }
} } else {
updateCache = false;
}
}
} else {
//TODO(gburd): look in statement cache for results
updateCache = false; //true;
cacheMiss.mark();
uow.recordCacheAndDatabaseOperationCount(-1, 0);
}
} else {
updateCache = false;
}
} finally {
timer.stop();
uow.addCacheLookupTime(timer);
}
} else {
updateCache = false;
}
// Check to see if we fetched the object from the cache // Check to see if we fetched the object from the cache
if (result.isPresent()) { if (result.isPresent()) {
// If we fetched the `deleted` object then the result is null (really // If we fetched the `deleted` object then the result is null (really
// Optional.empty()). // Optional.empty()).
if (result.get() == deleted) { if (result.get() == deleted) {
result = Optional.empty(); result = Optional.empty();
} }
} else { } else {
// Formulate the query and execute it against the Cassandra cluster. // Formulate the query and execute it against the Cassandra cluster.
ResultSet resultSet = execute(sessionOps, uow, traceContext, queryExecutionTimeout, queryTimeoutUnits, ResultSet resultSet =
showValues, true); execute(sessionOps, uow, queryExecutionTimeout, queryTimeoutUnits, showValues, true);
// Transform the query result set into the desired shape. // Transform the query result set into the desired shape.
result = transform(resultSet); result = transform(resultSet);
} }
// If we have a result, it wasn't from the UOW cache, and we're caching things // If we have a result, it wasn't from the UOW cache, and we're caching things
// then we need to put this result into the cache for future requests to find. // then we need to put this result into the cache for future requests to find.
if (updateCache && result.isPresent() && result.get() != deleted) { if (updateCache && result.isPresent()) {
cacheUpdate(uow, result.get(), getFacets()); E r = result.get();
} if (!(r instanceof Fun) && r != deleted) {
cacheUpdate(uow, r, getFacets());
}
}
return result; return result;
} finally { } finally {
context.stop(); context.stop();
} }
} }
public CompletableFuture<Optional<E>> async() { public CompletableFuture<Optional<E>> async() {
return CompletableFuture.<Optional<E>>supplyAsync(() -> { return CompletableFuture.<Optional<E>>supplyAsync(
try { () -> {
return sync(); try {
} catch (TimeoutException ex) { return sync();
throw new CompletionException(ex); } catch (TimeoutException ex) {
} throw new CompletionException(ex);
}); }
} });
}
public CompletableFuture<Optional<E>> async(UnitOfWork<?> uow) { public CompletableFuture<Optional<E>> async(UnitOfWork uow) {
if (uow == null) if (uow == null) return async();
return async(); CompletableFuture<Optional<E>> f =
return CompletableFuture.<Optional<E>>supplyAsync(() -> { CompletableFuture.<Optional<E>>supplyAsync(
try { () -> {
return sync(); try {
} catch (TimeoutException ex) { return sync();
throw new CompletionException(ex); } catch (TimeoutException ex) {
} throw new CompletionException(ex);
}); }
} });
uow.addFuture(f);
return f;
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,12 +16,6 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import com.datastax.driver.core.ConsistencyLevel; import com.datastax.driver.core.ConsistencyLevel;
import com.datastax.driver.core.PreparedStatement; import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.RegularStatement; import com.datastax.driver.core.RegularStatement;
@ -31,9 +26,11 @@ import com.datastax.driver.core.policies.FallthroughRetryPolicy;
import com.datastax.driver.core.policies.RetryPolicy; import com.datastax.driver.core.policies.RetryPolicy;
import com.datastax.driver.core.querybuilder.BuiltStatement; import com.datastax.driver.core.querybuilder.BuiltStatement;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import java.util.ArrayList;
import brave.Tracer; import java.util.List;
import brave.propagation.TraceContext; import java.util.Map;
import java.util.Optional;
import java.util.concurrent.TimeUnit;
import net.helenus.core.AbstractSessionOperations; import net.helenus.core.AbstractSessionOperations;
import net.helenus.core.UnitOfWork; import net.helenus.core.UnitOfWork;
import net.helenus.core.cache.Facet; import net.helenus.core.cache.Facet;
@ -43,323 +40,317 @@ import net.helenus.mapping.HelenusProperty;
import net.helenus.mapping.value.BeanColumnValueProvider; import net.helenus.mapping.value.BeanColumnValueProvider;
import net.helenus.support.HelenusException; import net.helenus.support.HelenusException;
public abstract class AbstractStatementOperation<E, O extends AbstractStatementOperation<E, O>> extends Operation<E> { public abstract class AbstractStatementOperation<E, O extends AbstractStatementOperation<E, O>>
extends Operation<E> {
protected boolean enableCache = true; private boolean ignoreCache = false;
protected boolean showValues = true; private ConsistencyLevel consistencyLevel;
protected TraceContext traceContext; private ConsistencyLevel serialConsistencyLevel;
long queryExecutionTimeout = 10; private RetryPolicy retryPolicy;
TimeUnit queryTimeoutUnits = TimeUnit.SECONDS; private boolean enableTracing = false;
private ConsistencyLevel consistencyLevel; private long[] defaultTimestamp = null;
private ConsistencyLevel serialConsistencyLevel; private int[] fetchSize = null;
private RetryPolicy retryPolicy; protected boolean idempotent = false;
private boolean idempotent = false;
private boolean enableTracing = false; public AbstractStatementOperation(AbstractSessionOperations sessionOperations) {
private long[] defaultTimestamp = null; super(sessionOperations);
private int[] fetchSize = null; this.consistencyLevel = sessionOperations.getDefaultConsistencyLevel();
this.idempotent = sessionOperations.getDefaultQueryIdempotency();
public AbstractStatementOperation(AbstractSessionOperations sessionOperations) { }
super(sessionOperations);
this.consistencyLevel = sessionOperations.getDefaultConsistencyLevel(); public abstract Statement buildStatement(boolean cached);
this.idempotent = sessionOperations.getDefaultQueryIdempotency();
} public O uncached(boolean enabled) {
ignoreCache = !enabled;
public abstract Statement buildStatement(boolean cached); return (O) this;
}
public O ignoreCache(boolean enabled) {
enableCache = enabled; public O uncached() {
return (O) this; ignoreCache = true;
} return (O) this;
}
public O ignoreCache() {
enableCache = true; public O showValues(boolean enabled) {
return (O) this; this.showValues = enabled;
} return (O) this;
}
public O showValues(boolean enabled) {
this.showValues = enabled; public O defaultTimestamp(long timestamp) {
return (O) this; this.defaultTimestamp = new long[1];
} this.defaultTimestamp[0] = timestamp;
return (O) this;
public O defaultTimestamp(long timestamp) { }
this.defaultTimestamp = new long[1];
this.defaultTimestamp[0] = timestamp; public O retryPolicy(RetryPolicy retryPolicy) {
return (O) this; this.retryPolicy = retryPolicy;
} return (O) this;
}
public O retryPolicy(RetryPolicy retryPolicy) {
this.retryPolicy = retryPolicy; public O defaultRetryPolicy() {
return (O) this; this.retryPolicy = DefaultRetryPolicy.INSTANCE;
} return (O) this;
}
public O defaultRetryPolicy() {
this.retryPolicy = DefaultRetryPolicy.INSTANCE; public O idempotent() {
return (O) this; this.idempotent = true;
} return (O) this;
}
public O idempotent() {
this.idempotent = true; public O isIdempotent(boolean idempotent) {
return (O) this; this.idempotent = idempotent;
} return (O) this;
}
public O isIdempotent(boolean idempotent) {
this.idempotent = idempotent; public O downgradingConsistencyRetryPolicy() {
return (O) this; this.retryPolicy = DowngradingConsistencyRetryPolicy.INSTANCE;
} return (O) this;
}
public O downgradingConsistencyRetryPolicy() {
this.retryPolicy = DowngradingConsistencyRetryPolicy.INSTANCE; public O fallthroughRetryPolicy() {
return (O) this; this.retryPolicy = FallthroughRetryPolicy.INSTANCE;
} return (O) this;
}
public O fallthroughRetryPolicy() {
this.retryPolicy = FallthroughRetryPolicy.INSTANCE; public O consistency(ConsistencyLevel level) {
return (O) this; this.consistencyLevel = level;
} return (O) this;
}
public O consistency(ConsistencyLevel level) {
this.consistencyLevel = level; public O consistencyAny() {
return (O) this; this.consistencyLevel = ConsistencyLevel.ANY;
} return (O) this;
}
public O consistencyAny() {
this.consistencyLevel = ConsistencyLevel.ANY; public O consistencyOne() {
return (O) this; this.consistencyLevel = ConsistencyLevel.ONE;
} return (O) this;
}
public O consistencyOne() {
this.consistencyLevel = ConsistencyLevel.ONE; public O consistencyQuorum() {
return (O) this; this.consistencyLevel = ConsistencyLevel.QUORUM;
} return (O) this;
}
public O consistencyQuorum() {
this.consistencyLevel = ConsistencyLevel.QUORUM; public O consistencyAll() {
return (O) this; this.consistencyLevel = ConsistencyLevel.ALL;
} return (O) this;
}
public O consistencyAll() {
this.consistencyLevel = ConsistencyLevel.ALL; public O consistencyLocalOne() {
return (O) this; this.consistencyLevel = ConsistencyLevel.LOCAL_ONE;
} return (O) this;
}
public O consistencyLocalOne() {
this.consistencyLevel = ConsistencyLevel.LOCAL_ONE; public O consistencyLocalQuorum() {
return (O) this; this.consistencyLevel = ConsistencyLevel.LOCAL_QUORUM;
} return (O) this;
}
public O consistencyLocalQuorum() {
this.consistencyLevel = ConsistencyLevel.LOCAL_QUORUM; public O consistencyEachQuorum() {
return (O) this; this.consistencyLevel = ConsistencyLevel.EACH_QUORUM;
} return (O) this;
}
public O consistencyEachQuorum() {
this.consistencyLevel = ConsistencyLevel.EACH_QUORUM; public O serialConsistency(ConsistencyLevel level) {
return (O) this; this.serialConsistencyLevel = level;
} return (O) this;
}
public O serialConsistency(ConsistencyLevel level) {
this.serialConsistencyLevel = level; public O serialConsistencyAny() {
return (O) this; this.serialConsistencyLevel = ConsistencyLevel.ANY;
} return (O) this;
}
public O serialConsistencyAny() {
this.serialConsistencyLevel = ConsistencyLevel.ANY; public O serialConsistencyOne() {
return (O) this; this.serialConsistencyLevel = ConsistencyLevel.ONE;
} return (O) this;
}
public O serialConsistencyOne() {
this.serialConsistencyLevel = ConsistencyLevel.ONE; public O serialConsistencyQuorum() {
return (O) this; this.serialConsistencyLevel = ConsistencyLevel.QUORUM;
} return (O) this;
}
public O serialConsistencyQuorum() {
this.serialConsistencyLevel = ConsistencyLevel.QUORUM; public O serialConsistencyAll() {
return (O) this; this.serialConsistencyLevel = ConsistencyLevel.ALL;
} return (O) this;
}
public O serialConsistencyAll() {
this.serialConsistencyLevel = ConsistencyLevel.ALL; public O serialConsistencyLocal() {
return (O) this; this.serialConsistencyLevel = ConsistencyLevel.LOCAL_SERIAL;
} return (O) this;
}
public O serialConsistencyLocal() {
this.serialConsistencyLevel = ConsistencyLevel.LOCAL_SERIAL; public O serialConsistencyLocalQuorum() {
return (O) this; this.serialConsistencyLevel = ConsistencyLevel.LOCAL_QUORUM;
} return (O) this;
}
public O serialConsistencyLocalQuorum() {
this.serialConsistencyLevel = ConsistencyLevel.LOCAL_QUORUM; public O disableTracing() {
return (O) this; this.enableTracing = false;
} return (O) this;
}
public O disableTracing() {
this.enableTracing = false; public O enableTracing() {
return (O) this; this.enableTracing = true;
} return (O) this;
}
public O enableTracing() {
this.enableTracing = true; public O tracing(boolean enable) {
return (O) this; this.enableTracing = enable;
} return (O) this;
}
public O tracing(boolean enable) {
this.enableTracing = enable; public O fetchSize(int fetchSize) {
return (O) this; this.fetchSize = new int[1];
} this.fetchSize[0] = fetchSize;
return (O) this;
public O fetchSize(int fetchSize) { }
this.fetchSize = new int[1];
this.fetchSize[0] = fetchSize; public O queryTimeoutMs(long ms) {
return (O) this; this.queryExecutionTimeout = ms;
} this.queryTimeoutUnits = TimeUnit.MILLISECONDS;
return (O) this;
public O queryTimeoutMs(long ms) { }
this.queryExecutionTimeout = ms;
this.queryTimeoutUnits = TimeUnit.MILLISECONDS; public O queryTimeout(long timeout, TimeUnit units) {
return (O) this; this.queryExecutionTimeout = timeout;
} this.queryTimeoutUnits = units;
return (O) this;
public O queryTimeout(long timeout, TimeUnit units) { }
this.queryExecutionTimeout = timeout;
this.queryTimeoutUnits = units; public Statement options(Statement statement) {
return (O) this;
} if (defaultTimestamp != null) {
statement.setDefaultTimestamp(defaultTimestamp[0]);
public Statement options(Statement statement) { }
if (defaultTimestamp != null) { if (consistencyLevel != null) {
statement.setDefaultTimestamp(defaultTimestamp[0]); statement.setConsistencyLevel(consistencyLevel);
} }
if (consistencyLevel != null) { if (serialConsistencyLevel != null) {
statement.setConsistencyLevel(consistencyLevel); statement.setSerialConsistencyLevel(serialConsistencyLevel);
} }
if (serialConsistencyLevel != null) { if (retryPolicy != null) {
statement.setSerialConsistencyLevel(serialConsistencyLevel); statement.setRetryPolicy(retryPolicy);
} }
if (retryPolicy != null) { if (enableTracing) {
statement.setRetryPolicy(retryPolicy); statement.enableTracing();
} } else {
statement.disableTracing();
if (enableTracing) { }
statement.enableTracing();
} else { if (fetchSize != null) {
statement.disableTracing(); statement.setFetchSize(fetchSize[0]);
} }
if (fetchSize != null) { if (isIdempotentOperation()) {
statement.setFetchSize(fetchSize[0]); statement.setIdempotent(true);
} }
if (idempotent) { return statement;
statement.setIdempotent(true); }
}
@Override
return statement; protected boolean isIdempotentOperation() {
} return idempotent;
}
public O zipkinContext(TraceContext traceContext) {
if (traceContext != null) { public Statement statement() {
Tracer tracer = this.sessionOps.getZipkinTracer(); return buildStatement(false);
if (tracer != null) { }
this.traceContext = traceContext;
} public String cql() {
} Statement statement = buildStatement(false);
if (statement == null) return "";
return (O) this; if (statement instanceof BuiltStatement) {
} BuiltStatement buildStatement = (BuiltStatement) statement;
return buildStatement.setForceNoValues(true).getQueryString();
public Statement statement() { } else {
return buildStatement(false); return statement.toString();
} }
}
public String cql() {
Statement statement = buildStatement(false); public PreparedStatement prepareStatement() {
if (statement == null)
return ""; Statement statement = buildStatement(true);
if (statement instanceof BuiltStatement) {
BuiltStatement buildStatement = (BuiltStatement) statement; if (statement instanceof RegularStatement) {
return buildStatement.setForceNoValues(true).getQueryString();
} else { RegularStatement regularStatement = (RegularStatement) statement;
return statement.toString();
} return sessionOps.prepare(regularStatement);
} }
public PreparedStatement prepareStatement() { throw new HelenusException("only RegularStatements can be prepared");
}
Statement statement = buildStatement(true);
public ListenableFuture<PreparedStatement> prepareStatementAsync() {
if (statement instanceof RegularStatement) {
Statement statement = buildStatement(true);
RegularStatement regularStatement = (RegularStatement) statement;
if (statement instanceof RegularStatement) {
return sessionOps.prepare(regularStatement);
} RegularStatement regularStatement = (RegularStatement) statement;
throw new HelenusException("only RegularStatements can be prepared"); return sessionOps.prepareAsync(regularStatement);
} }
public ListenableFuture<PreparedStatement> prepareStatementAsync() { throw new HelenusException("only RegularStatements can be prepared");
}
Statement statement = buildStatement(true);
protected boolean ignoreCache() {
if (statement instanceof RegularStatement) { return ignoreCache;
}
RegularStatement regularStatement = (RegularStatement) statement;
protected E checkCache(UnitOfWork uow, List<Facet> facets) {
return sessionOps.prepareAsync(regularStatement); E result = null;
} Optional<Object> optionalCachedResult = Optional.empty();
throw new HelenusException("only RegularStatements can be prepared"); if (!facets.isEmpty()) {
} optionalCachedResult = uow.cacheLookup(facets);
if (optionalCachedResult.isPresent()) {
protected E checkCache(UnitOfWork<?> uow, List<Facet> facets) { result = (E) optionalCachedResult.get();
E result = null; }
Optional<Object> optionalCachedResult = Optional.empty(); }
if (!facets.isEmpty()) { return result;
optionalCachedResult = uow.cacheLookup(facets); }
if (optionalCachedResult.isPresent()) {
result = (E) optionalCachedResult.get(); protected Object cacheUpdate(UnitOfWork uow, E pojo, List<Facet> identifyingFacets) {
} List<Facet> facets = new ArrayList<>();
} Map<String, Object> valueMap =
pojo instanceof MapExportable ? ((MapExportable) pojo).toMap() : null;
return result;
} for (Facet facet : identifyingFacets) {
if (facet instanceof UnboundFacet) {
protected void cacheUpdate(UnitOfWork<?> uow, E pojo, List<Facet> identifyingFacets) { UnboundFacet unboundFacet = (UnboundFacet) facet;
List<Facet> facets = new ArrayList<>(); UnboundFacet.Binder binder = unboundFacet.binder();
Map<String, Object> valueMap = pojo instanceof MapExportable ? ((MapExportable) pojo).toMap() : null; for (HelenusProperty prop : unboundFacet.getProperties()) {
Object value;
for (Facet facet : identifyingFacets) { if (valueMap == null) {
if (facet instanceof UnboundFacet) { value = BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false);
UnboundFacet unboundFacet = (UnboundFacet) facet; if (value != null) {
UnboundFacet.Binder binder = unboundFacet.binder(); binder.setValueForProperty(prop, value.toString());
for (HelenusProperty prop : unboundFacet.getProperties()) { }
Object value; } else {
if (valueMap == null) { value = valueMap.get(prop.getPropertyName());
value = BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false); if (value != null) {
if (value != null) { binder.setValueForProperty(prop, value.toString());
binder.setValueForProperty(prop, value.toString()); }
} }
} else { }
value = valueMap.get(prop.getPropertyName()); if (binder.isBound()) {
if (value != null) { facets.add(binder.bind());
binder.setValueForProperty(prop, value.toString()); }
} } else {
} facets.add(facet);
} }
if (binder.isBound()) { }
facets.add(binder.bind());
} // Cache the value (pojo), the statement key, and the fully bound facets.
} else { return uow.cacheUpdate(pojo, facets);
facets.add(facet); }
}
}
// Cache the value (pojo), the statement key, and the fully bound facets.
uow.cacheUpdate(pojo, facets);
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,13 +18,6 @@ package net.helenus.core.operation;
import static net.helenus.core.HelenusSession.deleted; import static net.helenus.core.HelenusSession.deleted;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeoutException;
import java.util.stream.Stream;
import com.codahale.metrics.Timer; import com.codahale.metrics.Timer;
import com.datastax.driver.core.PreparedStatement; import com.datastax.driver.core.PreparedStatement;
import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSet;
@ -31,185 +25,237 @@ import com.google.common.base.Function;
import com.google.common.base.Stopwatch; import com.google.common.base.Stopwatch;
import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.TimeoutException;
import java.util.stream.Stream;
import net.helenus.core.AbstractSessionOperations; import net.helenus.core.AbstractSessionOperations;
import net.helenus.core.Helenus;
import net.helenus.core.UnitOfWork; import net.helenus.core.UnitOfWork;
import net.helenus.core.cache.CacheUtil; import net.helenus.core.cache.CacheUtil;
import net.helenus.core.cache.Facet; import net.helenus.core.cache.Facet;
import net.helenus.mapping.MappingUtil;
import net.helenus.support.Fun;
import org.apache.commons.lang3.SerializationUtils;
public abstract class AbstractStreamOperation<E, O extends AbstractStreamOperation<E, O>> public abstract class AbstractStreamOperation<E, O extends AbstractStreamOperation<E, O>>
extends extends AbstractStatementOperation<E, O> {
AbstractStatementOperation<E, O> {
public AbstractStreamOperation(AbstractSessionOperations sessionOperations) { public AbstractStreamOperation(AbstractSessionOperations sessionOperations) {
super(sessionOperations); super(sessionOperations);
} }
public abstract Stream<E> transform(ResultSet resultSet); public abstract Stream<E> transform(ResultSet resultSet);
public PreparedStreamOperation<E> prepare() { public PreparedStreamOperation<E> prepare() {
return new PreparedStreamOperation<E>(prepareStatement(), this); return new PreparedStreamOperation<E>(prepareStatement(), this);
} }
public ListenableFuture<PreparedStreamOperation<E>> prepareAsync() { public ListenableFuture<PreparedStreamOperation<E>> prepareAsync() {
final O _this = (O) this; final O _this = (O) this;
return Futures.transform(prepareStatementAsync(), return Futures.transform(
new Function<PreparedStatement, PreparedStreamOperation<E>>() { prepareStatementAsync(),
@Override new Function<PreparedStatement, PreparedStreamOperation<E>>() {
public PreparedStreamOperation<E> apply(PreparedStatement preparedStatement) { @Override
return new PreparedStreamOperation<E>(preparedStatement, _this); public PreparedStreamOperation<E> apply(PreparedStatement preparedStatement) {
} return new PreparedStreamOperation<E>(preparedStatement, _this);
}); }
} });
}
public Stream<E> sync() throws TimeoutException { public Stream<E> sync() throws TimeoutException {
final Timer.Context context = requestLatency.time(); final Timer.Context context = requestLatency.time();
try { try {
Stream<E> resultStream = null; Stream<E> resultStream = null;
E cacheResult = null; E cacheResult = null;
boolean updateCache = isSessionCacheable(); boolean updateCache = isSessionCacheable();
if (enableCache && isSessionCacheable()) { if (!ignoreCache() && isSessionCacheable()) {
List<Facet> facets = bindFacetValues(); List<Facet> facets = bindFacetValues();
String tableName = CacheUtil.schemaName(facets); if (facets != null && facets.size() > 0) {
cacheResult = (E) sessionOps.checkCache(tableName, facets); if (facets.stream().filter(f -> !f.fixed()).distinct().count() > 0) {
if (cacheResult != null) { String tableName = CacheUtil.schemaName(facets);
resultStream = Stream.of(cacheResult); cacheResult = (E) sessionOps.checkCache(tableName, facets);
updateCache = false; if (cacheResult != null) {
sessionCacheHits.mark(); resultStream = Stream.of(cacheResult);
cacheHits.mark(); updateCache = false;
} else { sessionCacheHits.mark();
sessionCacheMiss.mark(); cacheHits.mark();
cacheMiss.mark(); } else {
} sessionCacheMiss.mark();
} cacheMiss.mark();
}
} else {
//TODO(gburd): look in statement cache for results
}
}
}
if (resultStream == null) { if (resultStream == null) {
// Formulate the query and execute it against the Cassandra cluster. // Formulate the query and execute it against the Cassandra cluster.
ResultSet resultSet = this.execute(sessionOps, null, traceContext, queryExecutionTimeout, ResultSet resultSet =
queryTimeoutUnits, showValues, false); this.execute(
sessionOps,
null,
queryExecutionTimeout,
queryTimeoutUnits,
showValues,
isSessionCacheable());
// Transform the query result set into the desired shape. // Transform the query result set into the desired shape.
resultStream = transform(resultSet); resultStream = transform(resultSet);
} }
if (updateCache && resultStream != null) { if (updateCache && resultStream != null) {
List<Facet> facets = getFacets(); List<Facet> facets = getFacets();
if (facets != null && facets.size() > 1) { if (facets != null && facets.size() > 1) {
List<E> again = new ArrayList<>(); List<E> again = new ArrayList<>();
resultStream.forEach(result -> { resultStream.forEach(
sessionOps.updateCache(result, facets); result -> {
again.add(result); Class<?> resultClass = result.getClass();
}); if (!(resultClass.getEnclosingClass() != null
resultStream = again.stream(); && resultClass.getEnclosingClass() == Fun.class)) {
} sessionOps.updateCache(result, facets);
} }
return resultStream; again.add(result);
});
resultStream = again.stream();
}
}
return resultStream;
} finally { } finally {
context.stop(); context.stop();
} }
} }
public Stream<E> sync(UnitOfWork uow) throws TimeoutException { public Stream<E> sync(UnitOfWork uow) throws TimeoutException {
if (uow == null) if (uow == null) return sync();
return sync();
final Timer.Context context = requestLatency.time(); final Timer.Context context = requestLatency.time();
try { try {
Stream<E> resultStream = null; Stream<E> resultStream = null;
E cachedResult = null; E cachedResult = null;
final boolean updateCache; final boolean updateCache;
if (enableCache) { if (!ignoreCache()) {
Stopwatch timer = Stopwatch.createStarted(); Stopwatch timer = Stopwatch.createStarted();
try { try {
List<Facet> facets = bindFacetValues(); List<Facet> facets = bindFacetValues();
if (facets != null) { if (facets != null && facets.size() > 0) {
cachedResult = checkCache(uow, facets); if (facets.stream().filter(f -> !f.fixed()).distinct().count() > 0) {
if (cachedResult != null) { cachedResult = checkCache(uow, facets);
updateCache = false; if (cachedResult != null) {
resultStream = Stream.of(cachedResult); updateCache = false;
uowCacheHits.mark(); resultStream = Stream.of(cachedResult);
cacheHits.mark(); uowCacheHits.mark();
uow.recordCacheAndDatabaseOperationCount(1, 0); cacheHits.mark();
} else { uow.recordCacheAndDatabaseOperationCount(1, 0);
updateCache = true; } else {
uowCacheMiss.mark(); uowCacheMiss.mark();
if (isSessionCacheable()) { if (isSessionCacheable()) {
String tableName = CacheUtil.schemaName(facets); String tableName = CacheUtil.schemaName(facets);
cachedResult = (E) sessionOps.checkCache(tableName, facets); cachedResult = (E) sessionOps.checkCache(tableName, facets);
if (cachedResult != null) { if (cachedResult != null) {
resultStream = Stream.of(cachedResult); Class<?> iface = MappingUtil.getMappingInterface(cachedResult);
sessionCacheHits.mark(); E result = null;
cacheHits.mark(); if (Helenus.entity(iface).isDraftable()) {
uow.recordCacheAndDatabaseOperationCount(1, 0); result = cachedResult;
} else { } else {
sessionCacheMiss.mark(); result =
cacheMiss.mark(); (E) SerializationUtils.<Serializable>clone((Serializable) cachedResult);
uow.recordCacheAndDatabaseOperationCount(-1, 0); }
} updateCache = false;
} resultStream = Stream.of(result);
} sessionCacheHits.mark();
} else { cacheHits.mark();
updateCache = false; uow.recordCacheAndDatabaseOperationCount(1, 0);
} } else {
} finally { updateCache = true;
timer.stop(); sessionCacheMiss.mark();
uow.addCacheLookupTime(timer); cacheMiss.mark();
} uow.recordCacheAndDatabaseOperationCount(-1, 0);
} else { }
updateCache = false; } else {
} updateCache = false;
}
}
} else {
//TODO(gburd): look in statement cache for results
updateCache = false; //true;
cacheMiss.mark();
uow.recordCacheAndDatabaseOperationCount(-1, 0);
}
} else {
updateCache = false;
}
} finally {
timer.stop();
uow.addCacheLookupTime(timer);
}
} else {
updateCache = false;
}
// Check to see if we fetched the object from the cache // Check to see if we fetched the object from the cache
if (resultStream == null) { if (resultStream == null) {
ResultSet resultSet = execute(sessionOps, uow, traceContext, queryExecutionTimeout, queryTimeoutUnits, ResultSet resultSet =
showValues, true); execute(sessionOps, uow, queryExecutionTimeout, queryTimeoutUnits, showValues, true);
resultStream = transform(resultSet); resultStream = transform(resultSet);
} }
// If we have a result and we're caching then we need to put it into the cache // If we have a result and we're caching then we need to put it into the cache
// for future requests to find. // for future requests to find.
if (resultStream != null) { if (resultStream != null) {
List<E> again = new ArrayList<>(); if (updateCache) {
List<Facet> facets = getFacets(); List<E> again = new ArrayList<>();
resultStream.forEach(result -> { List<Facet> facets = getFacets();
if (result != deleted) { resultStream.forEach(
if (updateCache) { result -> {
cacheUpdate(uow, result, facets); Class<?> resultClass = result.getClass();
} if (result != deleted
again.add(result); && !(resultClass.getEnclosingClass() != null
} && resultClass.getEnclosingClass() == Fun.class)) {
}); result = (E) cacheUpdate(uow, result, facets);
resultStream = again.stream(); }
} again.add(result);
});
resultStream = again.stream();
}
}
return resultStream; return resultStream;
} finally { } finally {
context.stop(); context.stop();
} }
} }
public CompletableFuture<Stream<E>> async() { public CompletableFuture<Stream<E>> async() {
return CompletableFuture.<Stream<E>>supplyAsync(() -> { return CompletableFuture.<Stream<E>>supplyAsync(
try { () -> {
return sync(); try {
} catch (TimeoutException ex) { return sync();
throw new CompletionException(ex); } catch (TimeoutException ex) {
} throw new CompletionException(ex);
}); }
} });
}
public CompletableFuture<Stream<E>> async(UnitOfWork uow) { public CompletableFuture<Stream<E>> async(UnitOfWork uow) {
if (uow == null) if (uow == null) return async();
return async(); CompletableFuture<Stream<E>> f =
return CompletableFuture.<Stream<E>>supplyAsync(() -> { CompletableFuture.<Stream<E>>supplyAsync(
try { () -> {
return sync(); try {
} catch (TimeoutException ex) { return sync();
throw new CompletionException(ex); } catch (TimeoutException ex) {
} throw new CompletionException(ex);
}); }
} });
uow.addFuture(f);
return f;
}
} }

View file

@ -0,0 +1,140 @@
/*
* Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus 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 net.helenus.core.operation;
import com.codahale.metrics.Timer;
import com.datastax.driver.core.AtomicMonotonicTimestampGenerator;
import com.datastax.driver.core.BatchStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.TimestampGenerator;
import com.google.common.base.Stopwatch;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
import net.helenus.core.AbstractSessionOperations;
import net.helenus.core.UnitOfWork;
import net.helenus.support.HelenusException;
public class BatchOperation extends Operation<Long> {
//TODO(gburd): find the way to get the driver's timestamp generator
private static final TimestampGenerator timestampGenerator =
new AtomicMonotonicTimestampGenerator();
private final BatchStatement batch;
private List<AbstractOperation<?, ?>> operations = new ArrayList<AbstractOperation<?, ?>>();
private boolean logged = true;
public BatchOperation(AbstractSessionOperations sessionOperations) {
super(sessionOperations);
batch = new BatchStatement();
}
public void add(AbstractOperation<?, ?> operation) {
operations.add(operation);
}
@Override
public BatchStatement buildStatement(boolean cached) {
batch.addAll(
operations.stream().map(o -> o.buildStatement(cached)).collect(Collectors.toList()));
batch.setConsistencyLevel(sessionOps.getDefaultConsistencyLevel());
return batch;
}
public BatchOperation logged() {
logged = true;
return this;
}
public BatchOperation setLogged(boolean logStatements) {
logged = logStatements;
return this;
}
public Long sync() {
if (operations.size() == 0) return 0L;
final Timer.Context context = requestLatency.time();
try {
batch.setDefaultTimestamp(timestampGenerator.next());
ResultSet resultSet =
this.execute(
sessionOps, null, queryExecutionTimeout, queryTimeoutUnits, showValues, false);
if (!resultSet.wasApplied()) {
throw new HelenusException("Failed to apply batch.");
}
} catch (TimeoutException e) {
throw new HelenusException(e);
} finally {
context.stop();
}
return batch.getDefaultTimestamp();
}
public Long sync(UnitOfWork uow) {
if (operations.size() == 0) return 0L;
if (uow == null) return sync();
final Timer.Context context = requestLatency.time();
final Stopwatch timer = Stopwatch.createStarted();
try {
uow.recordCacheAndDatabaseOperationCount(0, 1);
batch.setDefaultTimestamp(timestampGenerator.next());
ResultSet resultSet =
this.execute(
sessionOps, uow, queryExecutionTimeout, queryTimeoutUnits, showValues, false);
if (!resultSet.wasApplied()) {
throw new HelenusException("Failed to apply batch.");
}
} catch (TimeoutException e) {
throw new HelenusException(e);
} finally {
context.stop();
timer.stop();
}
uow.addDatabaseTime("Cassandra", timer);
return batch.getDefaultTimestamp();
}
public void addAll(BatchOperation batch) {
batch.operations.forEach(o -> this.operations.add(o));
}
public String toString() {
return toString(true); //TODO(gburd): sessionOps.showQueryValues()
}
public String toString(boolean showValues) {
StringBuilder s = new StringBuilder();
s.append("BEGIN ");
if (!logged) {
s.append("UNLOGGED ");
}
s.append("BATCH ");
if (batch.getDefaultTimestamp() > -9223372036854775808L) {
s.append("USING TIMESTAMP ").append(String.valueOf(batch.getDefaultTimestamp())).append(" ");
}
s.append(
operations
.stream()
.map(o -> Operation.queryString(o.buildStatement(showValues), showValues))
.collect(Collectors.joining(" ")));
s.append(" APPLY BATCH;");
return s.toString();
}
}

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,27 +22,27 @@ import com.datastax.driver.core.Statement;
public final class BoundOperation<E> extends AbstractOperation<E, BoundOperation<E>> { public final class BoundOperation<E> extends AbstractOperation<E, BoundOperation<E>> {
private final BoundStatement boundStatement; private final BoundStatement boundStatement;
private final AbstractOperation<E, ?> delegate; private final AbstractOperation<E, ?> delegate;
public BoundOperation(BoundStatement boundStatement, AbstractOperation<E, ?> operation) { public BoundOperation(BoundStatement boundStatement, AbstractOperation<E, ?> operation) {
super(operation.sessionOps); super(operation.sessionOps);
this.boundStatement = boundStatement; this.boundStatement = boundStatement;
this.delegate = operation; this.delegate = operation;
} }
@Override @Override
public E transform(ResultSet resultSet) { public E transform(ResultSet resultSet) {
return delegate.transform(resultSet); return delegate.transform(resultSet);
} }
@Override @Override
public Statement buildStatement(boolean cached) { public Statement buildStatement(boolean cached) {
return boundStatement; return boundStatement;
} }
@Override @Override
public boolean isSessionCacheable() { public boolean isSessionCacheable() {
return delegate.isSessionCacheable(); return delegate.isSessionCacheable();
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,35 +16,36 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
import java.util.Optional;
import com.datastax.driver.core.BoundStatement; import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Statement; import com.datastax.driver.core.Statement;
import java.util.Optional;
public final class BoundOptionalOperation<E> extends AbstractOptionalOperation<E, BoundOptionalOperation<E>> { public final class BoundOptionalOperation<E>
extends AbstractOptionalOperation<E, BoundOptionalOperation<E>> {
private final BoundStatement boundStatement; private final BoundStatement boundStatement;
private final AbstractOptionalOperation<E, ?> delegate; private final AbstractOptionalOperation<E, ?> delegate;
public BoundOptionalOperation(BoundStatement boundStatement, AbstractOptionalOperation<E, ?> operation) { public BoundOptionalOperation(
super(operation.sessionOps); BoundStatement boundStatement, AbstractOptionalOperation<E, ?> operation) {
this.boundStatement = boundStatement; super(operation.sessionOps);
this.delegate = operation; this.boundStatement = boundStatement;
} this.delegate = operation;
}
@Override @Override
public Optional<E> transform(ResultSet resultSet) { public Optional<E> transform(ResultSet resultSet) {
return delegate.transform(resultSet); return delegate.transform(resultSet);
} }
@Override @Override
public Statement buildStatement(boolean cached) { public Statement buildStatement(boolean cached) {
return boundStatement; return boundStatement;
} }
@Override @Override
public boolean isSessionCacheable() { public boolean isSessionCacheable() {
return delegate.isSessionCacheable(); return delegate.isSessionCacheable();
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,43 +16,43 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
import java.util.List;
import java.util.stream.Stream;
import com.datastax.driver.core.BoundStatement; import com.datastax.driver.core.BoundStatement;
import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Statement; import com.datastax.driver.core.Statement;
import java.util.List;
import java.util.stream.Stream;
import net.helenus.core.cache.Facet; import net.helenus.core.cache.Facet;
public final class BoundStreamOperation<E> extends AbstractStreamOperation<E, BoundStreamOperation<E>> { public final class BoundStreamOperation<E>
extends AbstractStreamOperation<E, BoundStreamOperation<E>> {
private final BoundStatement boundStatement; private final BoundStatement boundStatement;
private final AbstractStreamOperation<E, ?> delegate; private final AbstractStreamOperation<E, ?> delegate;
public BoundStreamOperation(BoundStatement boundStatement, AbstractStreamOperation<E, ?> operation) { public BoundStreamOperation(
super(operation.sessionOps); BoundStatement boundStatement, AbstractStreamOperation<E, ?> operation) {
this.boundStatement = boundStatement; super(operation.sessionOps);
this.delegate = operation; this.boundStatement = boundStatement;
} this.delegate = operation;
}
@Override @Override
public List<Facet> bindFacetValues() { public List<Facet> bindFacetValues() {
return delegate.bindFacetValues(); return delegate.bindFacetValues();
} }
@Override @Override
public Stream<E> transform(ResultSet resultSet) { public Stream<E> transform(ResultSet resultSet) {
return delegate.transform(resultSet); return delegate.transform(resultSet);
} }
@Override @Override
public Statement buildStatement(boolean cached) { public Statement buildStatement(boolean cached) {
return boundStatement; return boundStatement;
} }
@Override @Override
public boolean isSessionCacheable() { public boolean isSessionCacheable() {
return delegate.isSessionCacheable(); return delegate.isSessionCacheable();
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,7 +21,6 @@ import com.datastax.driver.core.querybuilder.BuiltStatement;
import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select; import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Select.Where; import com.datastax.driver.core.querybuilder.Select.Where;
import net.helenus.core.AbstractSessionOperations; import net.helenus.core.AbstractSessionOperations;
import net.helenus.core.Filter; import net.helenus.core.Filter;
import net.helenus.core.reflect.HelenusPropertyNode; import net.helenus.core.reflect.HelenusPropertyNode;
@ -29,53 +29,57 @@ import net.helenus.support.HelenusMappingException;
public final class CountOperation extends AbstractFilterOperation<Long, CountOperation> { public final class CountOperation extends AbstractFilterOperation<Long, CountOperation> {
private HelenusEntity entity; private HelenusEntity entity;
public CountOperation(AbstractSessionOperations sessionOperations) { public CountOperation(AbstractSessionOperations sessionOperations) {
super(sessionOperations); super(sessionOperations);
} }
public CountOperation(AbstractSessionOperations sessionOperations, HelenusEntity entity) { public CountOperation(AbstractSessionOperations sessionOperations, HelenusEntity entity) {
super(sessionOperations); super(sessionOperations);
this.entity = entity; this.entity = entity;
} //TODO(gburd): cache SELECT COUNT results within the scope of a UOW
}
@Override @Override
public BuiltStatement buildStatement(boolean cached) { public BuiltStatement buildStatement(boolean cached) {
if (filters != null && !filters.isEmpty()) { if (filters != null && !filters.isEmpty()) {
filters.forEach(f -> addPropertyNode(f.getNode())); filters.forEach(f -> addPropertyNode(f.getNode()));
} }
if (entity == null) { if (entity == null) {
throw new HelenusMappingException("unknown entity"); throw new HelenusMappingException("unknown entity");
} }
Select select = QueryBuilder.select().countAll().from(entity.getName().toCql()); Select select = QueryBuilder.select().countAll().from(entity.getName().toCql());
if (filters != null && !filters.isEmpty()) { if (filters != null && !filters.isEmpty()) {
Where where = select.where(); Where where = select.where();
for (Filter<?> filter : filters) { for (Filter<?> filter : filters) {
where.and(filter.getClause(sessionOps.getValuePreparer())); where.and(filter.getClause(sessionOps.getValuePreparer()));
} }
} }
return select; return select;
} }
@Override @Override
public Long transform(ResultSet resultSet) { public Long transform(ResultSet resultSet) {
return resultSet.one().getLong(0); return resultSet.one().getLong(0);
} }
private void addPropertyNode(HelenusPropertyNode p) { private void addPropertyNode(HelenusPropertyNode p) {
if (entity == null) { if (entity == null) {
entity = p.getEntity(); entity = p.getEntity();
} else if (entity != p.getEntity()) { } else if (entity != p.getEntity()) {
throw new HelenusMappingException("you can count columns only in single entity " throw new HelenusMappingException(
+ entity.getMappingInterface() + " or " + p.getEntity().getMappingInterface()); "you can count columns only in single entity "
} + entity.getMappingInterface()
} + " or "
+ p.getEntity().getMappingInterface());
}
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,182 +16,160 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeoutException;
import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.querybuilder.BuiltStatement; import com.datastax.driver.core.querybuilder.BuiltStatement;
import com.datastax.driver.core.querybuilder.Delete; import com.datastax.driver.core.querybuilder.Delete;
import com.datastax.driver.core.querybuilder.Delete.Where; import com.datastax.driver.core.querybuilder.Delete.Where;
import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.QueryBuilder;
import java.util.List;
import java.util.concurrent.TimeoutException;
import net.helenus.core.AbstractSessionOperations; import net.helenus.core.AbstractSessionOperations;
import net.helenus.core.Filter; import net.helenus.core.Filter;
import net.helenus.core.UnitOfWork; import net.helenus.core.UnitOfWork;
import net.helenus.core.cache.Facet; import net.helenus.core.cache.Facet;
import net.helenus.core.cache.UnboundFacet;
import net.helenus.core.reflect.HelenusPropertyNode; import net.helenus.core.reflect.HelenusPropertyNode;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
import net.helenus.mapping.HelenusProperty; import net.helenus.support.HelenusException;
import net.helenus.support.HelenusMappingException; import net.helenus.support.HelenusMappingException;
public final class DeleteOperation extends AbstractFilterOperation<ResultSet, DeleteOperation> { public final class DeleteOperation extends AbstractFilterOperation<ResultSet, DeleteOperation> {
private HelenusEntity entity; private HelenusEntity entity;
private boolean ifExists = false; private boolean ifExists = false;
private int[] ttl; private int[] ttl;
private long[] timestamp; private long[] timestamp;
public DeleteOperation(AbstractSessionOperations sessionOperations) { public DeleteOperation(AbstractSessionOperations sessionOperations) {
super(sessionOperations); super(sessionOperations);
} }
public DeleteOperation(AbstractSessionOperations sessionOperations, HelenusEntity entity) { public DeleteOperation(AbstractSessionOperations sessionOperations, HelenusEntity entity) {
super(sessionOperations); super(sessionOperations);
this.entity = entity; this.entity = entity;
} }
@Override @Override
public BuiltStatement buildStatement(boolean cached) { public BuiltStatement buildStatement(boolean cached) {
if (filters != null && !filters.isEmpty()) { if (filters != null && !filters.isEmpty()) {
filters.forEach(f -> addPropertyNode(f.getNode())); filters.forEach(f -> addPropertyNode(f.getNode()));
} }
if (entity == null) { if (entity == null) {
throw new HelenusMappingException("unknown entity"); throw new HelenusMappingException("unknown entity");
} }
if (filters != null && !filters.isEmpty()) { if (filters != null && !filters.isEmpty()) {
Delete delete = QueryBuilder.delete().from(entity.getName().toCql()); Delete delete = QueryBuilder.delete().from(entity.getName().toCql());
if (this.ifExists) { if (this.ifExists) {
delete.ifExists(); delete.ifExists();
} }
Where where = delete.where(); Where where = delete.where();
for (Filter<?> filter : filters) { for (Filter<?> filter : filters) {
where.and(filter.getClause(sessionOps.getValuePreparer())); where.and(filter.getClause(sessionOps.getValuePreparer()));
} }
if (ifFilters != null && !ifFilters.isEmpty()) { if (ifFilters != null && !ifFilters.isEmpty()) {
for (Filter<?> filter : ifFilters) { for (Filter<?> filter : ifFilters) {
delete.onlyIf(filter.getClause(sessionOps.getValuePreparer())); delete.onlyIf(filter.getClause(sessionOps.getValuePreparer()));
} }
} }
if (this.ttl != null) { if (this.ttl != null) {
delete.using(QueryBuilder.ttl(this.ttl[0])); delete.using(QueryBuilder.ttl(this.ttl[0]));
} }
if (this.timestamp != null) { if (this.timestamp != null) {
delete.using(QueryBuilder.timestamp(this.timestamp[0])); delete.using(QueryBuilder.timestamp(this.timestamp[0]));
} }
return delete; return delete;
} else { } else {
return QueryBuilder.truncate(entity.getName().toCql()); return QueryBuilder.truncate(entity.getName().toCql());
} }
} }
@Override @Override
public ResultSet transform(ResultSet resultSet) { public ResultSet transform(ResultSet resultSet) {
return resultSet; return resultSet;
} }
public DeleteOperation ifExists() { public DeleteOperation ifExists() {
this.ifExists = true; this.ifExists = true;
return this; return this;
} }
public DeleteOperation usingTtl(int ttl) { public DeleteOperation usingTtl(int ttl) {
this.ttl = new int[1]; this.ttl = new int[1];
this.ttl[0] = ttl; this.ttl[0] = ttl;
return this; return this;
} }
public DeleteOperation usingTimestamp(long timestamp) { public DeleteOperation usingTimestamp(long timestamp) {
this.timestamp = new long[1]; this.timestamp = new long[1];
this.timestamp[0] = timestamp; this.timestamp[0] = timestamp;
return this; return this;
} }
private void addPropertyNode(HelenusPropertyNode p) { private void addPropertyNode(HelenusPropertyNode p) {
if (entity == null) { if (entity == null) {
entity = p.getEntity(); entity = p.getEntity();
} else if (entity != p.getEntity()) { } else if (entity != p.getEntity()) {
throw new HelenusMappingException("you can delete rows only in single entity " throw new HelenusMappingException(
+ entity.getMappingInterface() + " or " + p.getEntity().getMappingInterface()); "you can delete rows only in single entity "
} + entity.getMappingInterface()
} + " or "
+ p.getEntity().getMappingInterface());
}
}
public List<Facet> bindFacetValues(List<Facet> facets) { public List<Facet> bindFacetValues() {
if (facets == null) { return bindFacetValues(getFacets());
return new ArrayList<Facet>(); }
}
List<Facet> boundFacets = new ArrayList<>();
Map<HelenusProperty, Filter> filterMap = new HashMap<>(filters.size());
filters.forEach(f -> filterMap.put(f.getNode().getProperty(), f));
for (Facet facet : facets) { protected boolean isIdempotentOperation() {
if (facet instanceof UnboundFacet) { return true;
UnboundFacet unboundFacet = (UnboundFacet) facet; }
UnboundFacet.Binder binder = unboundFacet.binder();
if (filters != null) {
for (HelenusProperty prop : unboundFacet.getProperties()) {
Filter filter = filterMap.get(prop); @Override
if (filter != null) { public ResultSet sync() throws TimeoutException {
Object[] postulates = filter.postulateValues(); ResultSet result = super.sync();
for (Object p : postulates) { if (entity.isCacheable()) {
binder.setValueForProperty(prop, p.toString()); sessionOps.cacheEvict(bindFacetValues());
} }
} return result;
} }
} @Override
if (binder.isBound()) { public ResultSet sync(UnitOfWork uow) throws TimeoutException {
boundFacets.add(binder.bind()); if (uow == null) {
} return sync();
} else { }
boundFacets.add(facet); ResultSet result = super.sync(uow);
} uow.cacheEvict(bindFacetValues());
} return result;
return boundFacets; }
}
@Override public ResultSet batch(UnitOfWork uow) throws TimeoutException {
public ResultSet sync() throws TimeoutException { if (uow == null) {
ResultSet result = super.sync(); throw new HelenusException("UnitOfWork cannot be null when batching operations.");
if (entity.isCacheable()) { }
sessionOps.cacheEvict(bindFacetValues());
}
return result;
}
@Override uow.cacheEvict(bindFacetValues());
public List<Facet> getFacets() { uow.batch(this);
return entity.getFacets(); return null;
} }
@Override
public ResultSet sync(UnitOfWork uow) throws TimeoutException {
if (uow == null) {
return sync();
}
ResultSet result = super.sync(uow);
List<Facet> facets = getFacets();
uow.cacheEvict(bindFacetValues(facets));
return result;
}
@Override
public List<Facet> getFacets() {
return entity.getFacets();
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,23 +16,24 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
import java.util.*;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.querybuilder.BuiltStatement; import com.datastax.driver.core.querybuilder.BuiltStatement;
import com.datastax.driver.core.querybuilder.Insert; import com.datastax.driver.core.querybuilder.Insert;
import com.datastax.driver.core.querybuilder.QueryBuilder; import com.datastax.driver.core.querybuilder.QueryBuilder;
import java.util.*;
import java.util.concurrent.TimeoutException;
import java.util.function.Function;
import java.util.stream.Collectors;
import net.helenus.core.AbstractSessionOperations; import net.helenus.core.AbstractSessionOperations;
import net.helenus.core.Getter; import net.helenus.core.Getter;
import net.helenus.core.Helenus; import net.helenus.core.Helenus;
import net.helenus.core.UnitOfWork; import net.helenus.core.UnitOfWork;
import net.helenus.core.cache.CacheUtil;
import net.helenus.core.cache.Facet; import net.helenus.core.cache.Facet;
import net.helenus.core.cache.UnboundFacet;
import net.helenus.core.reflect.DefaultPrimitiveTypes; import net.helenus.core.reflect.DefaultPrimitiveTypes;
import net.helenus.core.reflect.Drafted;
import net.helenus.core.reflect.HelenusPropertyNode; import net.helenus.core.reflect.HelenusPropertyNode;
import net.helenus.core.reflect.MapExportable;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
import net.helenus.mapping.HelenusProperty; import net.helenus.mapping.HelenusProperty;
import net.helenus.mapping.MappingUtil; import net.helenus.mapping.MappingUtil;
@ -42,229 +44,381 @@ import net.helenus.support.HelenusMappingException;
public final class InsertOperation<T> extends AbstractOperation<T, InsertOperation<T>> { public final class InsertOperation<T> extends AbstractOperation<T, InsertOperation<T>> {
private final List<Fun.Tuple2<HelenusPropertyNode, Object>> values = new ArrayList<Fun.Tuple2<HelenusPropertyNode, Object>>(); private final List<Fun.Tuple2<HelenusPropertyNode, Object>> values =
private final T pojo; new ArrayList<Fun.Tuple2<HelenusPropertyNode, Object>>();
private final Class<?> resultType; private final T pojo;
private HelenusEntity entity; private final Class<?> resultType;
private boolean ifNotExists; private final Set<String> readSet;
private HelenusEntity entity;
private boolean ifNotExists;
private int[] ttl; private int[] ttl;
private long[] timestamp; private long[] timestamp;
private long writeTime = 0L;
public InsertOperation(AbstractSessionOperations sessionOperations, boolean ifNotExists) { public InsertOperation(AbstractSessionOperations sessionOperations, boolean ifNotExists) {
super(sessionOperations); super(sessionOperations);
this.ifNotExists = ifNotExists; this.pojo = null;
this.pojo = null; this.readSet = null;
this.resultType = ResultSet.class; this.ifNotExists = ifNotExists;
} this.resultType = ResultSet.class;
}
public InsertOperation(AbstractSessionOperations sessionOperations, Class<?> resultType, boolean ifNotExists) { public InsertOperation(
super(sessionOperations); AbstractSessionOperations sessionOperations,
HelenusEntity entity,
Class<?> resultType,
boolean ifNotExists) {
super(sessionOperations);
this.ifNotExists = ifNotExists; this.pojo = null;
this.pojo = null; this.readSet = null;
this.resultType = resultType; this.ifNotExists = ifNotExists;
} this.resultType = resultType;
this.entity = entity;
}
public InsertOperation(AbstractSessionOperations sessionOperations, HelenusEntity entity, T pojo, public InsertOperation(
Set<String> mutations, boolean ifNotExists) { AbstractSessionOperations sessionOperations, Class<?> resultType, boolean ifNotExists) {
super(sessionOperations); super(sessionOperations);
this.entity = entity; this.pojo = null;
this.pojo = pojo; this.readSet = null;
this.ifNotExists = ifNotExists; this.ifNotExists = ifNotExists;
this.resultType = entity.getMappingInterface(); this.resultType = resultType;
}
Collection<HelenusProperty> properties = entity.getOrderedProperties(); public InsertOperation(
Set<String> keys = (mutations == null) ? null : mutations; AbstractSessionOperations sessionOperations,
HelenusEntity entity,
T pojo,
Set<String> mutations,
Set<String> read,
boolean ifNotExists) {
super(sessionOperations);
for (HelenusProperty prop : properties) { this.pojo = pojo;
boolean addProp = false; this.readSet = read;
this.entity = entity;
this.ifNotExists = ifNotExists;
this.resultType = entity.getMappingInterface();
switch (prop.getColumnType()) { Collection<HelenusProperty> properties = entity.getOrderedProperties();
case PARTITION_KEY : Set<String> keys = (mutations == null) ? null : mutations;
case CLUSTERING_COLUMN :
addProp = true;
break;
default :
addProp = (keys == null || keys.contains(prop.getPropertyName()));
}
if (addProp) { for (HelenusProperty prop : properties) {
Object value = BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop); boolean addProp = false;
value = sessionOps.getValuePreparer().prepareColumnValue(value, prop);
if (value != null) { switch (prop.getColumnType()) {
HelenusPropertyNode node = new HelenusPropertyNode(prop, Optional.empty()); case PARTITION_KEY:
values.add(Fun.Tuple2.of(node, value)); case CLUSTERING_COLUMN:
} addProp = true;
} break;
} default:
} addProp = (keys == null || keys.contains(prop.getPropertyName()));
}
public InsertOperation<T> ifNotExists() { if (addProp) {
this.ifNotExists = true; Object value = BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop);
return this; value = sessionOps.getValuePreparer().prepareColumnValue(value, prop);
}
public InsertOperation<T> ifNotExists(boolean enable) { if (value != null) {
this.ifNotExists = enable; HelenusPropertyNode node = new HelenusPropertyNode(prop, Optional.empty());
return this; values.add(Fun.Tuple2.of(node, value));
} }
}
}
}
public <V> InsertOperation<T> value(Getter<V> getter, V val) { public InsertOperation<T> ifNotExists() {
this.ifNotExists = true;
return this;
}
Objects.requireNonNull(getter, "getter is empty"); public InsertOperation<T> ifNotExists(boolean enable) {
this.ifNotExists = enable;
return this;
}
if (val != null) { public <V> InsertOperation<T> value(Getter<V> getter, V val) {
HelenusPropertyNode node = MappingUtil.resolveMappingProperty(getter);
Object value = sessionOps.getValuePreparer().prepareColumnValue(val, node.getProperty());
if (value != null) { Objects.requireNonNull(getter, "getter is empty");
values.add(Fun.Tuple2.of(node, value));
}
}
return this; if (val != null) {
} HelenusPropertyNode node = MappingUtil.resolveMappingProperty(getter);
Object value = sessionOps.getValuePreparer().prepareColumnValue(val, node.getProperty());
@Override if (value != null) {
public BuiltStatement buildStatement(boolean cached) { values.add(Fun.Tuple2.of(node, value));
}
}
values.forEach(t -> addPropertyNode(t._1)); return this;
}
if (values.isEmpty()) @Override
return null; public BuiltStatement buildStatement(boolean cached) {
List<HelenusEntity> entities =
values
.stream()
.map(t -> t._1.getProperty().getEntity())
.distinct()
.collect(Collectors.toList());
if (entities.size() != 1) {
throw new HelenusMappingException(
"you can insert only single entity at a time, found: "
+ entities
.stream()
.map(e -> e.getMappingInterface().toString())
.collect(Collectors.joining(", ")));
}
HelenusEntity entity = entities.get(0);
if (this.entity != null) {
if (this.entity != entity) {
throw new HelenusMappingException(
"you can insert only single entity at a time, found: "
+ this.entity.getMappingInterface().toString()
+ ", "
+ entity.getMappingInterface().toString());
}
} else {
this.entity = entity;
}
if (entity == null) { if (values.isEmpty()) return null;
throw new HelenusMappingException("unknown entity");
}
Insert insert = QueryBuilder.insertInto(entity.getName().toCql()); if (entity == null) {
throw new HelenusMappingException("unknown entity");
}
if (ifNotExists) { Insert insert = QueryBuilder.insertInto(entity.getName().toCql());
insert.ifNotExists();
}
values.forEach(t -> { if (ifNotExists) {
insert.value(t._1.getColumnName(), t._2); insert.ifNotExists();
}); }
if (this.ttl != null) { values.forEach(
insert.using(QueryBuilder.ttl(this.ttl[0])); t -> {
} insert.value(t._1.getColumnName(), t._2);
if (this.timestamp != null) { });
insert.using(QueryBuilder.timestamp(this.timestamp[0]));
}
return insert; //TODO(gburd): IF NOT EXISTS when @Constraints.Relationship is 1:1 or 1:m
}
@Override if (this.ttl != null) {
public T transform(ResultSet resultSet) { insert.using(QueryBuilder.ttl(this.ttl[0]));
Class<?> iface = entity.getMappingInterface(); }
if (resultType == iface) { if (this.timestamp != null) {
if (values.size() > 0) { insert.using(QueryBuilder.timestamp(this.timestamp[0]));
boolean immutable = iface.isAssignableFrom(Drafted.class); }
Collection<HelenusProperty> properties = entity.getOrderedProperties();
Map<String, Object> backingMap = new HashMap<String, Object>(properties.size());
// First, add all the inserted values into our new map. return insert;
values.forEach(t -> backingMap.put(t._1.getProperty().getPropertyName(), t._2)); }
// Then, fill in all the rest of the properties. private T newInstance(Class<?> iface) {
for (HelenusProperty prop : properties) { if (values.size() > 0) {
String key = prop.getPropertyName(); boolean immutable = entity.isDraftable();
if (backingMap.containsKey(key)) { Collection<HelenusProperty> properties = entity.getOrderedProperties();
// Some values man need to be converted (e.g. from String to Enum). This is done Map<String, Object> backingMap = new HashMap<String, Object>(properties.size());
// within the BeanColumnValueProvider below.
Optional<Function<Object, Object>> converter = prop
.getReadConverter(sessionOps.getSessionRepository());
if (converter.isPresent()) {
backingMap.put(key, converter.get().apply(backingMap.get(key)));
}
} else {
// If we started this operation with an instance of this type, use values from
// that.
if (pojo != null) {
backingMap.put(key,
BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, immutable));
} else {
// Otherwise we'll use default values for the property type if available.
Class<?> propType = prop.getJavaType();
if (propType.isPrimitive()) {
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(propType);
if (type == null) {
throw new HelenusException("unknown primitive type " + propType);
}
backingMap.put(key, type.getDefaultValue());
}
}
}
}
// Lastly, create a new proxy object for the entity and return the new instance. // First, add all the inserted values into our new map.
return (T) Helenus.map(iface, backingMap); values.forEach(t -> backingMap.put(t._1.getProperty().getPropertyName(), t._2));
}
// Oddly, this insert didn't change any value so simply return the pojo.
// TODO(gburd): this pojo is the result of a Draft.build() call which will not
// preserve object identity (o1 == o2), ... fix me.
return (T) pojo;
}
return (T) resultSet;
}
public InsertOperation<T> usingTtl(int ttl) { // Then, fill in all the rest of the properties.
this.ttl = new int[1]; for (HelenusProperty prop : properties) {
this.ttl[0] = ttl; String key = prop.getPropertyName();
return this; if (backingMap.containsKey(key)) {
} // Some values man need to be converted (e.g. from String to Enum). This is done
// within the BeanColumnValueProvider below.
Optional<Function<Object, Object>> converter =
prop.getReadConverter(sessionOps.getSessionRepository());
if (converter.isPresent()) {
backingMap.put(key, converter.get().apply(backingMap.get(key)));
}
} else {
// If we started this operation with an instance of this type, use values from
// that.
if (pojo != null) {
backingMap.put(
key, BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, immutable));
} else {
// Otherwise we'll use default values for the property type if available.
Class<?> propType = prop.getJavaType();
if (propType.isPrimitive()) {
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(propType);
if (type == null) {
throw new HelenusException("unknown primitive type " + propType);
}
backingMap.put(key, type.getDefaultValue());
}
}
}
}
public InsertOperation<T> usingTimestamp(long timestamp) { // Lastly, create a new proxy object for the entity and return the new instance.
this.timestamp = new long[1]; return (T) Helenus.map(iface, backingMap);
this.timestamp[0] = timestamp; }
return this; return null;
} }
private void addPropertyNode(HelenusPropertyNode p) { @Override
if (entity == null) { public T transform(ResultSet resultSet) {
entity = p.getEntity(); if ((ifNotExists == true) && (resultSet.wasApplied() == false)) {
} else if (entity != p.getEntity()) { throw new HelenusException("Statement was not applied due to consistency constraints");
throw new HelenusMappingException("you can insert only single entity " + entity.getMappingInterface() }
+ " or " + p.getEntity().getMappingInterface());
}
}
@Override Class<?> iface = entity.getMappingInterface();
public T sync() throws TimeoutException { if (resultType == iface) {
T result = super.sync(); T o = newInstance(iface);
if (entity.isCacheable() && result != null) { if (o == null) {
sessionOps.updateCache(result, entity.getFacets()); // Oddly, this insert didn't change anything so simply return the pojo.
} return (T) pojo;
return result; }
} return o;
}
return (T) resultSet;
}
@Override public InsertOperation<T> usingTtl(int ttl) {
public T sync(UnitOfWork uow) throws TimeoutException { this.ttl = new int[1];
if (uow == null) { this.ttl[0] = ttl;
return sync(); return this;
} }
T result = super.sync(uow);
Class<?> iface = entity.getMappingInterface();
if (resultType == iface) {
cacheUpdate(uow, result, entity.getFacets());
}
return result;
}
@Override public InsertOperation<T> usingTimestamp(long timestamp) {
public List<Facet> getFacets() { this.timestamp = new long[1];
if (entity != null) { this.timestamp[0] = timestamp;
return entity.getFacets(); return this;
} else { }
return new ArrayList<Facet>();
}
}
protected void adjustTtlAndWriteTime(MapExportable pojo) {
if (ttl != null || writeTime != 0L) {
List<String> columnNames =
values
.stream()
.map(t -> t._1.getProperty())
.filter(
prop -> {
switch (prop.getColumnType()) {
case PARTITION_KEY:
case CLUSTERING_COLUMN:
return false;
default:
return true;
}
})
.map(prop -> prop.getColumnName().toCql(false))
.collect(Collectors.toList());
if (columnNames.size() > 0) {
if (ttl != null) {
columnNames.forEach(name -> pojo.put(CacheUtil.ttlKey(name), ttl));
}
if (writeTime != 0L) {
columnNames.forEach(name -> pojo.put(CacheUtil.writeTimeKey(name), writeTime));
}
}
}
}
@Override
protected boolean isIdempotentOperation() {
return values.stream().map(v -> v._1.getProperty()).allMatch(prop -> prop.isIdempotent())
|| super.isIdempotentOperation();
}
@Override
public T sync() throws TimeoutException {
T result = super.sync();
if (entity.isCacheable() && result != null) {
adjustTtlAndWriteTime((MapExportable) result);
sessionOps.updateCache(result, bindFacetValues());
}
return result;
}
@Override
public T sync(UnitOfWork uow) throws TimeoutException {
if (uow == null) {
return sync();
}
T result = super.sync(uow);
if (result != null && pojo != null && !(pojo == result) && pojo.equals(result)) {
// To preserve object identity we need to find this object in cache
// because it was unchanged by the INSERT but pojo in this case was
// the result of a draft.build().
T cachedValue = (T) uow.cacheLookup(bindFacetValues());
if (cachedValue != null) {
result = cachedValue;
}
}
Class<?> iface = entity.getMappingInterface();
if (resultType == iface) {
if (entity != null && MapExportable.class.isAssignableFrom(entity.getMappingInterface())) {
adjustTtlAndWriteTime((MapExportable) result);
}
cacheUpdate(uow, result, bindFacetValues());
}
return result;
}
public T batch(UnitOfWork uow) throws TimeoutException {
if (uow == null) {
throw new HelenusException("UnitOfWork cannot be null when batching operations.");
}
if (this.entity != null) {
Class<?> iface = this.entity.getMappingInterface();
if (resultType == iface) {
final T result = (pojo == null) ? newInstance(iface) : pojo;
if (result != null) {
adjustTtlAndWriteTime((MapExportable) result);
cacheUpdate(uow, result, bindFacetValues());
}
uow.batch(this);
return (T) result;
}
}
return sync(uow);
}
@Override
public List<Facet> bindFacetValues() {
List<Facet> facets = getFacets();
if (facets == null || facets.size() == 0) {
return new ArrayList<Facet>();
}
List<Facet> boundFacets = new ArrayList<>();
Map<HelenusProperty, Object> valuesMap = new HashMap<>(values.size());
values.forEach(t -> valuesMap.put(t._1.getProperty(), t._2));
for (Facet facet : facets) {
if (facet instanceof UnboundFacet) {
UnboundFacet unboundFacet = (UnboundFacet) facet;
UnboundFacet.Binder binder = unboundFacet.binder();
for (HelenusProperty prop : unboundFacet.getProperties()) {
Object value = valuesMap.get(prop);
if (value != null) {
binder.setValueForProperty(prop, value.toString());
}
}
if (binder.isBound()) {
boundFacets.add(binder.bind());
}
} else {
boundFacets.add(facet);
}
}
return boundFacets;
}
@Override
public List<Facet> getFacets() {
if (entity != null) {
return entity.getFacets();
} else {
return new ArrayList<Facet>();
}
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,153 +16,196 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.datastax.driver.core.*;
import com.datastax.driver.core.querybuilder.BuiltStatement;
import com.google.common.base.Stopwatch;
import java.net.InetAddress;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.codahale.metrics.Meter;
import com.codahale.metrics.MetricRegistry;
import com.codahale.metrics.Timer;
import com.datastax.driver.core.RegularStatement;
import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.ResultSetFuture;
import com.datastax.driver.core.Statement;
import com.datastax.driver.core.querybuilder.BuiltStatement;
import com.google.common.base.Stopwatch;
import brave.Span;
import brave.Tracer;
import brave.propagation.TraceContext;
import net.helenus.core.AbstractSessionOperations; import net.helenus.core.AbstractSessionOperations;
import net.helenus.core.UnitOfWork; import net.helenus.core.UnitOfWork;
import net.helenus.core.cache.Facet; import net.helenus.core.cache.Facet;
import net.helenus.support.HelenusException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class Operation<E> { public abstract class Operation<E> {
private static final Logger LOG = LoggerFactory.getLogger(Operation.class); private static final Logger LOG = LoggerFactory.getLogger(Operation.class);
protected final AbstractSessionOperations sessionOps; protected final AbstractSessionOperations sessionOps;
protected final Meter uowCacheHits; protected boolean showValues;
protected final Meter uowCacheMiss; protected long queryExecutionTimeout = 10;
protected final Meter sessionCacheHits; protected TimeUnit queryTimeoutUnits = TimeUnit.SECONDS;
protected final Meter sessionCacheMiss; protected final Meter uowCacheHits;
protected final Meter cacheHits; protected final Meter uowCacheMiss;
protected final Meter cacheMiss; protected final Meter sessionCacheHits;
protected final Timer requestLatency; protected final Meter sessionCacheMiss;
protected final Meter cacheHits;
protected final Meter cacheMiss;
protected final Timer requestLatency;
Operation(AbstractSessionOperations sessionOperations) { Operation(AbstractSessionOperations sessionOperations) {
this.sessionOps = sessionOperations; this.sessionOps = sessionOperations;
MetricRegistry metrics = sessionOperations.getMetricRegistry(); this.showValues = sessionOps.showValues();
if (metrics == null) { MetricRegistry metrics = sessionOperations.getMetricRegistry();
metrics = new MetricRegistry(); if (metrics == null) {
} metrics = new MetricRegistry();
this.uowCacheHits = metrics.meter("net.helenus.UOW-cache-hits"); }
this.uowCacheMiss = metrics.meter("net.helenus.UOW-cache-miss"); this.uowCacheHits = metrics.meter("net.helenus.UOW-cache-hits");
this.sessionCacheHits = metrics.meter("net.helenus.session-cache-hits"); this.uowCacheMiss = metrics.meter("net.helenus.UOW-cache-miss");
this.sessionCacheMiss = metrics.meter("net.helenus.session-cache-miss"); this.sessionCacheHits = metrics.meter("net.helenus.session-cache-hits");
this.cacheHits = metrics.meter("net.helenus.cache-hits"); this.sessionCacheMiss = metrics.meter("net.helenus.session-cache-miss");
this.cacheMiss = metrics.meter("net.helenus.cache-miss"); this.cacheHits = metrics.meter("net.helenus.cache-hits");
this.requestLatency = metrics.timer("net.helenus.request-latency"); this.cacheMiss = metrics.meter("net.helenus.cache-miss");
} this.requestLatency = metrics.timer("net.helenus.request-latency");
}
public static String queryString(Statement statement, boolean includeValues) { public static String queryString(BatchOperation operation, boolean includeValues) {
String query = null; return operation.toString(includeValues);
if (statement instanceof BuiltStatement) { }
BuiltStatement builtStatement = (BuiltStatement) statement;
if (includeValues) {
RegularStatement regularStatement = builtStatement.setForceNoValues(true);
query = regularStatement.getQueryString();
} else {
query = builtStatement.getQueryString();
}
} else if (statement instanceof RegularStatement) {
RegularStatement regularStatement = (RegularStatement) statement;
query = regularStatement.getQueryString();
} else {
query = statement.toString();
} public static String queryString(Statement statement, boolean includeValues) {
return query; String query = null;
} if (statement instanceof BuiltStatement) {
BuiltStatement builtStatement = (BuiltStatement) statement;
if (includeValues) {
RegularStatement regularStatement = builtStatement.setForceNoValues(true);
query = regularStatement.getQueryString();
} else {
query = builtStatement.getQueryString();
}
} else if (statement instanceof RegularStatement) {
RegularStatement regularStatement = (RegularStatement) statement;
query = regularStatement.getQueryString();
} else {
query = statement.toString();
}
return query;
}
public ResultSet execute(AbstractSessionOperations session, UnitOfWork uow, TraceContext traceContext, long timeout, public ResultSet execute(
TimeUnit units, boolean showValues, boolean cached) throws TimeoutException { AbstractSessionOperations session,
UnitOfWork uow,
long timeout,
TimeUnit units,
boolean showValues,
boolean cached)
throws TimeoutException {
// Start recording in a Zipkin sub-span our execution time to perform this Statement statement = options(buildStatement(cached));
// operation.
Tracer tracer = session.getZipkinTracer();
Span span = null;
if (tracer != null && traceContext != null) {
span = tracer.newChild(traceContext);
}
try { if (session.isShowCql()) {
String stmt =
(this instanceof BatchOperation)
? queryString((BatchOperation) this, showValues)
: queryString(statement, showValues);
session.getPrintStream().println(stmt);
} else if (LOG.isDebugEnabled()) {
String stmt =
(this instanceof BatchOperation)
? queryString((BatchOperation) this, showValues)
: queryString(statement, showValues);
LOG.info("CQL> " + stmt);
}
if (span != null) { Stopwatch timer = Stopwatch.createStarted();
span.name("cassandra"); try {
span.start(); ResultSetFuture futureResultSet = session.executeAsync(statement, uow, timer);
} if (uow != null) uow.recordCacheAndDatabaseOperationCount(0, 1);
ResultSet resultSet = futureResultSet.getUninterruptibly(timeout, units);
ColumnDefinitions columnDefinitions = resultSet.getColumnDefinitions();
if (LOG.isDebugEnabled()) {
ExecutionInfo ei = resultSet.getExecutionInfo();
Host qh = ei.getQueriedHost();
String oh =
ei.getTriedHosts()
.stream()
.map(Host::getAddress)
.map(InetAddress::toString)
.collect(Collectors.joining(", "));
ConsistencyLevel cl = ei.getAchievedConsistencyLevel();
if (cl == null) {
cl = statement.getConsistencyLevel();
}
int se = ei.getSpeculativeExecutions();
String warn = ei.getWarnings().stream().collect(Collectors.joining(", "));
String ri =
String.format(
"%s %s ~%s %s %s%s%sspec-retries: %d",
"server v" + qh.getCassandraVersion(),
qh.getAddress().toString(),
(oh != null && !oh.equals("")) ? " [tried: " + oh + "]" : "",
qh.getDatacenter(),
qh.getRack(),
(cl != null)
? (" consistency: "
+ cl.name()
+ " "
+ (cl.isDCLocal() ? " DC " : "")
+ (cl.isSerial() ? " SC " : ""))
: "",
(warn != null && !warn.equals("")) ? ": " + warn : "",
se);
if (uow != null) uow.setInfo(ri);
else LOG.debug(ri);
}
if (!resultSet.wasApplied()
&& !(columnDefinitions.size() > 1 || !columnDefinitions.contains("[applied]"))) {
throw new HelenusException("Operation Failed");
}
return resultSet;
Statement statement = options(buildStatement(cached)); } finally {
Stopwatch timer = Stopwatch.createStarted(); timer.stop();
try { if (uow != null) uow.addDatabaseTime("Cassandra", timer);
ResultSetFuture futureResultSet = session.executeAsync(statement, uow, timer, showValues); log(statement, uow, timer, showValues);
if (uow != null) }
uow.recordCacheAndDatabaseOperationCount(0, 1); }
ResultSet resultSet = futureResultSet.getUninterruptibly(timeout, units);
return resultSet;
} finally { void log(Statement statement, UnitOfWork uow, Stopwatch timer, boolean showValues) {
timer.stop(); if (LOG.isInfoEnabled()) {
if (uow != null) String uowString = "";
uow.addDatabaseTime("Cassandra", timer); if (uow != null) {
log(statement, uow, timer, showValues); uowString = "UOW(" + uow.hashCode() + ")";
} }
String timerString = "";
if (timer != null) {
timerString = String.format(" %s ", timer.toString());
}
LOG.info(
String.format(
"%s%s%s", uowString, timerString, Operation.queryString(statement, showValues)));
}
}
} finally { protected boolean isIdempotentOperation() {
return false;
}
if (span != null) { public Statement options(Statement statement) {
span.finish(); return statement;
} }
}
}
void log(Statement statement, UnitOfWork uow, Stopwatch timer, boolean showValues) { public Statement buildStatement(boolean cached) {
if (LOG.isInfoEnabled()) { return null;
String uowString = ""; }
if (uow != null) {
uowString = "UOW(" + uow.hashCode() + ")";
}
String timerString = "";
if (timer != null) {
timerString = String.format(" %s ", timer.toString());
}
LOG.info(String.format("%s%s%s", uowString, timerString, Operation.queryString(statement, false)));
}
}
public Statement options(Statement statement) { public List<Facet> getFacets() {
return statement; return new ArrayList<Facet>();
} }
public Statement buildStatement(boolean cached) { public List<Facet> bindFacetValues() {
return null; return null;
} }
public List<Facet> getFacets() {
return new ArrayList<Facet>();
}
public List<Facet> bindFacetValues() {
return null;
}
public boolean isSessionCacheable() {
return false;
}
public boolean isSessionCacheable() {
return false;
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,28 +21,27 @@ import com.datastax.driver.core.PreparedStatement;
public final class PreparedOperation<E> { public final class PreparedOperation<E> {
private final PreparedStatement preparedStatement; private final PreparedStatement preparedStatement;
private final AbstractOperation<E, ?> operation; private final AbstractOperation<E, ?> operation;
public PreparedOperation(PreparedStatement statement, AbstractOperation<E, ?> operation) { public PreparedOperation(PreparedStatement statement, AbstractOperation<E, ?> operation) {
this.preparedStatement = statement; this.preparedStatement = statement;
this.operation = operation; this.operation = operation;
} }
public PreparedStatement getPreparedStatement() { public PreparedStatement getPreparedStatement() {
return preparedStatement; return preparedStatement;
} }
public BoundOperation<E> bind(Object... params) { public BoundOperation<E> bind(Object... params) {
BoundStatement boundStatement = preparedStatement.bind(params); BoundStatement boundStatement = preparedStatement.bind(params);
return new BoundOperation<E>(boundStatement, operation); return new BoundOperation<E>(boundStatement, operation);
} }
@Override
public String toString() {
return preparedStatement.getQueryString();
}
@Override
public String toString() {
return preparedStatement.getQueryString();
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,27 +21,28 @@ import com.datastax.driver.core.PreparedStatement;
public final class PreparedOptionalOperation<E> { public final class PreparedOptionalOperation<E> {
private final PreparedStatement preparedStatement; private final PreparedStatement preparedStatement;
private final AbstractOptionalOperation<E, ?> operation; private final AbstractOptionalOperation<E, ?> operation;
public PreparedOptionalOperation(PreparedStatement statement, AbstractOptionalOperation<E, ?> operation) { public PreparedOptionalOperation(
this.preparedStatement = statement; PreparedStatement statement, AbstractOptionalOperation<E, ?> operation) {
this.operation = operation; this.preparedStatement = statement;
} this.operation = operation;
}
public PreparedStatement getPreparedStatement() { public PreparedStatement getPreparedStatement() {
return preparedStatement; return preparedStatement;
} }
public BoundOptionalOperation<E> bind(Object... params) { public BoundOptionalOperation<E> bind(Object... params) {
BoundStatement boundStatement = preparedStatement.bind(params); BoundStatement boundStatement = preparedStatement.bind(params);
return new BoundOptionalOperation<E>(boundStatement, operation); return new BoundOptionalOperation<E>(boundStatement, operation);
} }
@Override @Override
public String toString() { public String toString() {
return preparedStatement.getQueryString(); return preparedStatement.getQueryString();
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -20,25 +21,26 @@ import com.datastax.driver.core.PreparedStatement;
public final class PreparedStreamOperation<E> { public final class PreparedStreamOperation<E> {
private final PreparedStatement preparedStatement; private final PreparedStatement preparedStatement;
private final AbstractStreamOperation<E, ?> operation; private final AbstractStreamOperation<E, ?> operation;
public PreparedStreamOperation(PreparedStatement statement, AbstractStreamOperation<E, ?> operation) { public PreparedStreamOperation(
this.preparedStatement = statement; PreparedStatement statement, AbstractStreamOperation<E, ?> operation) {
this.operation = operation; this.preparedStatement = statement;
} this.operation = operation;
}
public PreparedStatement getPreparedStatement() { public PreparedStatement getPreparedStatement() {
return preparedStatement; return preparedStatement;
} }
public BoundStreamOperation<E> bind(Object... params) { public BoundStreamOperation<E> bind(Object... params) {
BoundStatement boundStatement = preparedStatement.bind(params); BoundStatement boundStatement = preparedStatement.bind(params);
return new BoundStreamOperation<E>(boundStatement, operation); return new BoundStreamOperation<E>(boundStatement, operation);
} }
@Override @Override
public String toString() { public String toString() {
return preparedStatement.getQueryString(); return preparedStatement.getQueryString();
} }
} }

View file

@ -1,67 +1,72 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* * Copyright (C) 2015-2018 The Helenus Authors
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. * Licensed under the Apache License, Version 2.0 (the "License");
* You may obtain a copy of the License at * 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 *
* * 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, * Unless required by applicable law or agreed to in writing, software
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * distributed under the License is distributed on an "AS IS" BASIS,
* See the License for the specific language governing permissions and * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* limitations under the License. * See the License for the specific language governing permissions and
*/ * limitations under the License.
package net.helenus.core.operation; */
package net.helenus.core.operation;
import java.util.List;
import java.util.Optional; import com.datastax.driver.core.ResultSet;
import java.util.function.Function; import com.datastax.driver.core.querybuilder.BuiltStatement;
import java.util.List;
import com.datastax.driver.core.ResultSet; import java.util.Optional;
import com.datastax.driver.core.querybuilder.BuiltStatement; import java.util.function.Function;
import net.helenus.core.cache.Facet;
import net.helenus.core.cache.Facet;
public final class SelectFirstOperation<E>
public final class SelectFirstOperation<E> extends AbstractFilterOptionalOperation<E, SelectFirstOperation<E>> { extends AbstractFilterOptionalOperation<E, SelectFirstOperation<E>> {
private final SelectOperation<E> delegate; private final SelectOperation<E> delegate;
public SelectFirstOperation(SelectOperation<E> delegate) { public SelectFirstOperation(SelectOperation<E> delegate) {
super(delegate.sessionOps); super(delegate.sessionOps);
this.delegate = delegate; this.delegate = delegate;
this.filters = delegate.filters; this.filters = delegate.filters;
this.ifFilters = delegate.ifFilters; this.ifFilters = delegate.ifFilters;
} }
public <R> SelectFirstTransformingOperation<R, E> map(Function<E, R> fn) { public <R> SelectFirstTransformingOperation<R, E> map(Function<E, R> fn) {
return new SelectFirstTransformingOperation<R, E>(delegate, fn); return new SelectFirstTransformingOperation<R, E>(delegate, fn);
} }
@Override @Override
public BuiltStatement buildStatement(boolean cached) { public BuiltStatement buildStatement(boolean cached) {
return delegate.buildStatement(cached); return delegate.buildStatement(cached);
} }
@Override @Override
public List<Facet> getFacets() { public List<Facet> getFacets() {
return delegate.getFacets(); return delegate.getFacets();
} }
@Override @Override
public List<Facet> bindFacetValues() { public List<Facet> bindFacetValues() {
return delegate.bindFacetValues(); return delegate.bindFacetValues();
} }
@Override @Override
public Optional<E> transform(ResultSet resultSet) { public Optional<E> transform(ResultSet resultSet) {
return delegate.transform(resultSet).findFirst(); return delegate.transform(resultSet).findFirst();
} }
@Override @Override
public boolean isSessionCacheable() { public boolean isSessionCacheable() {
return delegate.isSessionCacheable(); return delegate.isSessionCacheable();
} }
}
@Override
public boolean ignoreCache() {
return delegate.ignoreCache();
}
}

View file

@ -1,62 +1,65 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* * Copyright (C) 2015-2018 The Helenus Authors
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. * Licensed under the Apache License, Version 2.0 (the "License");
* You may obtain a copy of the License at * 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 *
* * 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, * Unless required by applicable law or agreed to in writing, software
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * distributed under the License is distributed on an "AS IS" BASIS,
* See the License for the specific language governing permissions and * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* limitations under the License. * See the License for the specific language governing permissions and
*/ * limitations under the License.
package net.helenus.core.operation; */
package net.helenus.core.operation;
import java.util.List;
import java.util.Optional; import com.datastax.driver.core.ResultSet;
import java.util.function.Function; import com.datastax.driver.core.querybuilder.BuiltStatement;
import java.util.List;
import com.datastax.driver.core.ResultSet; import java.util.Optional;
import com.datastax.driver.core.querybuilder.BuiltStatement; import java.util.function.Function;
import net.helenus.core.cache.Facet;
import net.helenus.core.cache.Facet;
public final class SelectFirstTransformingOperation<R, E>
public final class SelectFirstTransformingOperation<R, E> extends AbstractFilterOptionalOperation<R, SelectFirstTransformingOperation<R, E>> {
extends
AbstractFilterOptionalOperation<R, SelectFirstTransformingOperation<R, E>> { private final SelectOperation<E> delegate;
private final Function<E, R> fn;
private final SelectOperation<E> delegate;
private final Function<E, R> fn; public SelectFirstTransformingOperation(SelectOperation<E> delegate, Function<E, R> fn) {
super(delegate.sessionOps);
public SelectFirstTransformingOperation(SelectOperation<E> delegate, Function<E, R> fn) {
super(delegate.sessionOps); this.delegate = delegate;
this.fn = fn;
this.delegate = delegate; this.filters = delegate.filters;
this.fn = fn; this.ifFilters = delegate.ifFilters;
this.filters = delegate.filters; }
this.ifFilters = delegate.ifFilters;
} @Override
public List<Facet> bindFacetValues() {
@Override return delegate.bindFacetValues();
public List<Facet> bindFacetValues() { }
return delegate.bindFacetValues();
} @Override
public BuiltStatement buildStatement(boolean cached) {
@Override return delegate.buildStatement(cached);
public BuiltStatement buildStatement(boolean cached) { }
return delegate.buildStatement(cached);
} @Override
public Optional<R> transform(ResultSet resultSet) {
@Override return delegate.transform(resultSet).findFirst().map(fn);
public Optional<R> transform(ResultSet resultSet) { }
return delegate.transform(resultSet).findFirst().map(fn);
} @Override
public boolean isSessionCacheable() {
@Override return delegate.isSessionCacheable();
public boolean isSessionCacheable() { }
return delegate.isSessionCacheable();
} @Override
} public boolean ignoreCache() {
return delegate.ignoreCache();
}
}

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,14 +16,6 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.Row; import com.datastax.driver.core.Row;
import com.datastax.driver.core.querybuilder.BuiltStatement; import com.datastax.driver.core.querybuilder.BuiltStatement;
@ -31,11 +24,15 @@ import com.datastax.driver.core.querybuilder.QueryBuilder;
import com.datastax.driver.core.querybuilder.Select; import com.datastax.driver.core.querybuilder.Select;
import com.datastax.driver.core.querybuilder.Select.Selection; import com.datastax.driver.core.querybuilder.Select.Selection;
import com.datastax.driver.core.querybuilder.Select.Where; import com.datastax.driver.core.querybuilder.Select.Where;
import com.google.common.collect.Iterables; import java.util.*;
import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import net.helenus.core.*; import net.helenus.core.*;
import net.helenus.core.cache.CacheUtil;
import net.helenus.core.cache.Facet; import net.helenus.core.cache.Facet;
import net.helenus.core.cache.UnboundFacet; import net.helenus.core.cache.UnboundFacet;
import net.helenus.core.reflect.Entity;
import net.helenus.core.reflect.HelenusPropertyNode; import net.helenus.core.reflect.HelenusPropertyNode;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
import net.helenus.mapping.HelenusProperty; import net.helenus.mapping.HelenusProperty;
@ -45,285 +42,337 @@ import net.helenus.mapping.value.ColumnValueProvider;
import net.helenus.mapping.value.ValueProviderMap; import net.helenus.mapping.value.ValueProviderMap;
import net.helenus.support.Fun; import net.helenus.support.Fun;
import net.helenus.support.HelenusMappingException; import net.helenus.support.HelenusMappingException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, SelectOperation<E>> { public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, SelectOperation<E>> {
private static final Logger LOG = LoggerFactory.getLogger(SelectOperation.class); private static final Logger LOG = LoggerFactory.getLogger(SelectOperation.class);
protected final List<HelenusPropertyNode> props = new ArrayList<HelenusPropertyNode>(); protected final List<HelenusPropertyNode> props = new ArrayList<HelenusPropertyNode>();
protected Function<Row, E> rowMapper = null; protected Function<Row, E> rowMapper = null;
protected List<Ordering> ordering = null; protected List<Ordering> ordering = null;
protected Integer limit = null; protected Integer limit = null;
protected boolean allowFiltering = false; protected boolean allowFiltering = false;
protected String alternateTableName = null;
protected boolean isCacheable = false;
@SuppressWarnings("unchecked") protected String alternateTableName = null;
public SelectOperation(AbstractSessionOperations sessionOperations) { protected boolean isCacheable = false;
super(sessionOperations); protected boolean implementsEntityType = false;
this.rowMapper = new Function<Row, E>() { @SuppressWarnings("unchecked")
public SelectOperation(AbstractSessionOperations sessionOperations) {
super(sessionOperations);
@Override this.rowMapper =
public E apply(Row source) { new Function<Row, E>() {
ColumnValueProvider valueProvider = sessionOps.getValueProvider(); @Override
Object[] arr = new Object[props.size()]; public E apply(Row source) {
int i = 0; ColumnValueProvider valueProvider = sessionOps.getValueProvider();
for (HelenusPropertyNode p : props) { Object[] arr = new Object[props.size()];
Object value = valueProvider.getColumnValue(source, -1, p.getProperty());
arr[i++] = value;
}
return (E) Fun.ArrayTuple.of(arr); int i = 0;
} for (HelenusPropertyNode p : props) {
}; Object value = valueProvider.getColumnValue(source, -1, p.getProperty());
} arr[i++] = value;
}
public SelectOperation(AbstractSessionOperations sessionOperations, HelenusEntity entity) { return (E) Fun.ArrayTuple.of(arr);
}
};
}
super(sessionOperations); public SelectOperation(AbstractSessionOperations sessionOperations, HelenusEntity entity) {
entity.getOrderedProperties().stream().map(p -> new HelenusPropertyNode(p, Optional.empty())) super(sessionOperations);
.forEach(p -> this.props.add(p));
isCacheable = entity.isCacheable(); entity
} .getOrderedProperties()
.stream()
.map(p -> new HelenusPropertyNode(p, Optional.empty()))
.forEach(p -> this.props.add(p));
public SelectOperation(AbstractSessionOperations sessionOperations, HelenusEntity entity, this.isCacheable = entity.isCacheable();
Function<Row, E> rowMapper) { this.implementsEntityType = Entity.class.isAssignableFrom(entity.getMappingInterface());
}
super(sessionOperations); public SelectOperation(
this.rowMapper = rowMapper; AbstractSessionOperations sessionOperations,
HelenusEntity entity,
Function<Row, E> rowMapper) {
entity.getOrderedProperties().stream().map(p -> new HelenusPropertyNode(p, Optional.empty())) super(sessionOperations);
.forEach(p -> this.props.add(p)); this.rowMapper = rowMapper;
isCacheable = entity.isCacheable(); entity
} .getOrderedProperties()
.stream()
.map(p -> new HelenusPropertyNode(p, Optional.empty()))
.forEach(p -> this.props.add(p));
public SelectOperation(AbstractSessionOperations sessionOperations, Function<Row, E> rowMapper, this.isCacheable = entity.isCacheable();
HelenusPropertyNode... props) { this.implementsEntityType = Entity.class.isAssignableFrom(entity.getMappingInterface());
}
super(sessionOperations); public SelectOperation(
AbstractSessionOperations sessionOperations,
Function<Row, E> rowMapper,
HelenusPropertyNode... props) {
this.rowMapper = rowMapper; super(sessionOperations);
Collections.addAll(this.props, props);
}
public CountOperation count() { this.rowMapper = rowMapper;
Collections.addAll(this.props, props);
HelenusEntity entity = null; HelenusEntity entity = props[0].getEntity();
for (HelenusPropertyNode prop : props) { this.isCacheable = entity.isCacheable();
this.implementsEntityType = Entity.class.isAssignableFrom(entity.getMappingInterface());
}
if (entity == null) { public CountOperation count() {
entity = prop.getEntity();
} else if (entity != prop.getEntity()) {
throw new HelenusMappingException("you can count records only from a single entity "
+ entity.getMappingInterface() + " or " + prop.getEntity().getMappingInterface());
}
}
return new CountOperation(sessionOps, entity); HelenusEntity entity = null;
} for (HelenusPropertyNode prop : props) {
public <V extends E> SelectOperation<E> from(Class<V> materializedViewClass) { if (entity == null) {
Objects.requireNonNull(materializedViewClass); entity = prop.getEntity();
HelenusEntity entity = Helenus.entity(materializedViewClass); } else if (entity != prop.getEntity()) {
this.alternateTableName = entity.getName().toCql(); throw new HelenusMappingException(
this.props.clear(); "you can count records only from a single entity "
entity.getOrderedProperties().stream().map(p -> new HelenusPropertyNode(p, Optional.empty())) + entity.getMappingInterface()
.forEach(p -> this.props.add(p)); + " or "
return this; + prop.getEntity().getMappingInterface());
} }
}
public SelectFirstOperation<E> single() { return new CountOperation(sessionOps, entity);
limit(1); }
return new SelectFirstOperation<E>(this);
}
public <R> SelectTransformingOperation<R, E> mapTo(Class<R> entityClass) { public <V extends E> SelectOperation<E> from(Class<V> materializedViewClass) {
Objects.requireNonNull(materializedViewClass);
HelenusEntity entity = Helenus.entity(materializedViewClass);
this.alternateTableName = entity.getName().toCql();
this.props.clear();
entity
.getOrderedProperties()
.stream()
.map(p -> new HelenusPropertyNode(p, Optional.empty()))
.forEach(p -> this.props.add(p));
return this;
}
Objects.requireNonNull(entityClass, "entityClass is null"); public SelectFirstOperation<E> single() {
limit(1);
return new SelectFirstOperation<E>(this);
}
HelenusEntity entity = Helenus.entity(entityClass); public <R> SelectTransformingOperation<R, E> mapTo(Class<R> entityClass) {
this.rowMapper = null; Objects.requireNonNull(entityClass, "entityClass is null");
return new SelectTransformingOperation<R, E>(this, (r) -> { HelenusEntity entity = Helenus.entity(entityClass);
Map<String, Object> map = new ValueProviderMap(r, sessionOps.getValueProvider(), entity);
return (R) Helenus.map(entityClass, map);
});
}
public <R> SelectTransformingOperation<R, E> map(Function<E, R> fn) { this.rowMapper = null;
return new SelectTransformingOperation<R, E>(this, fn);
}
public SelectOperation<E> column(Getter<?> getter) { return new SelectTransformingOperation<R, E>(
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(getter); this,
this.props.add(p); (r) -> {
return this; Map<String, Object> map = new ValueProviderMap(r, sessionOps.getValueProvider(), entity);
} return (R) Helenus.map(entityClass, map);
});
}
public SelectOperation<E> orderBy(Getter<?> getter, OrderingDirection direction) { public <R> SelectTransformingOperation<R, E> map(Function<E, R> fn) {
getOrCreateOrdering().add(new Ordered(getter, direction).getOrdering()); return new SelectTransformingOperation<R, E>(this, fn);
return this; }
}
public SelectOperation<E> orderBy(Ordered ordered) { public SelectOperation<E> column(Getter<?> getter) {
getOrCreateOrdering().add(ordered.getOrdering()); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(getter);
return this; this.props.add(p);
} return this;
}
public SelectOperation<E> limit(Integer limit) { public SelectOperation<E> orderBy(Getter<?> getter, OrderingDirection direction) {
this.limit = limit; getOrCreateOrdering().add(new Ordered(getter, direction).getOrdering());
return this; return this;
} }
public SelectOperation<E> allowFiltering() { public SelectOperation<E> orderBy(Ordered ordered) {
this.allowFiltering = true; getOrCreateOrdering().add(ordered.getOrdering());
return this; return this;
} }
@Override public SelectOperation<E> limit(Integer limit) {
public boolean isSessionCacheable() { this.limit = limit;
return isCacheable; return this;
} }
@Override public SelectOperation<E> allowFiltering() {
public List<Facet> getFacets() { this.allowFiltering = true;
HelenusEntity entity = props.get(0).getEntity(); return this;
return entity.getFacets(); }
}
@Override @Override
public List<Facet> bindFacetValues() { public boolean isSessionCacheable() {
HelenusEntity entity = props.get(0).getEntity(); return isCacheable;
List<Facet> boundFacets = new ArrayList<>(); }
for (Facet facet : entity.getFacets()) { @Override
if (facet instanceof UnboundFacet) { public List<Facet> getFacets() {
UnboundFacet unboundFacet = (UnboundFacet) facet; HelenusEntity entity = props.get(0).getEntity();
UnboundFacet.Binder binder = unboundFacet.binder(); return entity.getFacets();
for (HelenusProperty prop : unboundFacet.getProperties()) { }
if (filters != null) {
Filter filter = filters.get(prop);
if (filter != null) {
Object[] postulates = filter.postulateValues();
for (Object p : postulates) {
binder.setValueForProperty(prop, p.toString());
}
}
}
} @Override
if (binder.isBound()) { public List<Facet> bindFacetValues() {
boundFacets.add(binder.bind()); HelenusEntity entity = props.get(0).getEntity();
} List<Facet> boundFacets = new ArrayList<>();
} else {
boundFacets.add(facet);
}
}
return boundFacets;
}
@Override for (Facet facet : entity.getFacets()) {
public BuiltStatement buildStatement(boolean cached) { if (facet instanceof UnboundFacet) {
UnboundFacet unboundFacet = (UnboundFacet) facet;
UnboundFacet.Binder binder = unboundFacet.binder();
for (HelenusProperty prop : unboundFacet.getProperties()) {
if (filters != null) {
Filter filter = filters.get(prop);
if (filter != null) {
Object[] postulates = filter.postulateValues();
for (Object p : postulates) {
binder.setValueForProperty(prop, p.toString());
}
}
}
}
if (binder.isBound()) {
boundFacets.add(binder.bind());
}
} else {
boundFacets.add(facet);
}
}
return boundFacets;
}
HelenusEntity entity = null; @Override
Selection selection = QueryBuilder.select(); public BuiltStatement buildStatement(boolean cached) {
for (HelenusPropertyNode prop : props) { HelenusEntity entity = null;
String columnName = prop.getColumnName(); Selection selection = QueryBuilder.select();
selection = selection.column(columnName);
if (prop.getProperty().caseSensitiveIndex()) { for (HelenusPropertyNode prop : props) {
allowFiltering = true; String columnName = prop.getColumnName();
} selection = selection.column(columnName);
if (entity == null) { if (entity == null) {
entity = prop.getEntity(); entity = prop.getEntity();
} else if (entity != prop.getEntity()) { } else if (entity != prop.getEntity()) {
throw new HelenusMappingException("you can select columns only from a single entity " throw new HelenusMappingException(
+ entity.getMappingInterface() + " or " + prop.getEntity().getMappingInterface()); "you can select columns only from a single entity "
} + entity.getMappingInterface()
+ " or "
+ prop.getEntity().getMappingInterface());
}
// TODO(gburd): writeTime and ttl will be useful on merge() but cause object if (cached && implementsEntityType) {
// identity to fail. switch (prop.getProperty().getColumnType()) {
if (false && cached) { case PARTITION_KEY:
switch (prop.getProperty().getColumnType()) { case CLUSTERING_COLUMN:
case PARTITION_KEY : break;
case CLUSTERING_COLUMN : default:
break; if (entity.equals(prop.getEntity())) {
default : if (!prop.getProperty().getDataType().isCollectionType()) {
if (entity.equals(prop.getEntity())) { columnName = prop.getProperty().getColumnName().toCql(false);
if (prop.getNext().isPresent()) { selection.ttl(columnName).as('"' + CacheUtil.ttlKey(columnName) + '"');
columnName = Iterables.getLast(prop).getColumnName().toCql(true); selection.writeTime(columnName).as('"' + CacheUtil.writeTimeKey(columnName) + '"');
} }
if (!prop.getProperty().getDataType().isCollectionType()) { }
selection.writeTime(columnName).as(columnName + "_writeTime"); break;
selection.ttl(columnName).as(columnName + "_ttl"); }
} }
} }
break;
}
}
}
if (entity == null) { if (entity == null) {
throw new HelenusMappingException("no entity or table to select data"); throw new HelenusMappingException("no entity or table to select data");
} }
String tableName = alternateTableName == null ? entity.getName().toCql() : alternateTableName; String tableName = alternateTableName == null ? entity.getName().toCql() : alternateTableName;
Select select = selection.from(tableName); Select select = selection.from(tableName);
if (ordering != null && !ordering.isEmpty()) { if (ordering != null && !ordering.isEmpty()) {
select.orderBy(ordering.toArray(new Ordering[ordering.size()])); select.orderBy(ordering.toArray(new Ordering[ordering.size()]));
} }
if (limit != null) { if (limit != null) {
select.limit(limit); select.limit(limit);
} }
if (filters != null && !filters.isEmpty()) { if (filters != null && !filters.isEmpty()) {
Where where = select.where(); Where where = select.where();
for (Filter<?> filter : filters.values()) { boolean isFirstIndex = true;
where.and(filter.getClause(sessionOps.getValuePreparer())); for (Filter<?> filter : filters.values()) {
} where.and(filter.getClause(sessionOps.getValuePreparer()));
} HelenusProperty filterProp = filter.getNode().getProperty();
HelenusProperty prop =
props
.stream()
.map(HelenusPropertyNode::getProperty)
.filter(thisProp -> thisProp.getPropertyName().equals(filterProp.getPropertyName()))
.findFirst()
.orElse(null);
if (allowFiltering == false && prop != null) {
switch (prop.getColumnType()) {
case PARTITION_KEY:
break;
case CLUSTERING_COLUMN:
default:
// When using non-Cassandra-standard 2i types or when using more than one
// indexed column or non-indexed columns the query must include ALLOW FILTERING.
if (prop.caseSensitiveIndex() == false) {
allowFiltering = true;
} else if (prop.getIndexName() != null) {
allowFiltering |= !isFirstIndex;
isFirstIndex = false;
} else {
allowFiltering = true;
}
}
}
}
}
if (ifFilters != null && !ifFilters.isEmpty()) { if (ifFilters != null && !ifFilters.isEmpty()) {
LOG.error("onlyIf conditions " + ifFilters + " would be ignored in the statement " + select); LOG.error("onlyIf conditions " + ifFilters + " would be ignored in the statement " + select);
} }
if (allowFiltering) { if (allowFiltering) {
select.allowFiltering(); select.allowFiltering();
} }
return select; return select;
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public Stream<E> transform(ResultSet resultSet) { public Stream<E> transform(ResultSet resultSet) {
if (rowMapper != null) { if (rowMapper != null) {
return StreamSupport return StreamSupport.stream(
.stream(Spliterators.spliteratorUnknownSize(resultSet.iterator(), Spliterator.ORDERED), false) Spliterators.spliteratorUnknownSize(resultSet.iterator(), Spliterator.ORDERED), false)
.map(rowMapper); .map(rowMapper);
} else { } else {
return (Stream<E>) StreamSupport return (Stream<E>)
.stream(Spliterators.spliteratorUnknownSize(resultSet.iterator(), Spliterator.ORDERED), false); StreamSupport.stream(
} Spliterators.spliteratorUnknownSize(resultSet.iterator(), Spliterator.ORDERED),
} false);
}
}
private List<Ordering> getOrCreateOrdering() { private List<Ordering> getOrCreateOrdering() {
if (ordering == null) { if (ordering == null) {
ordering = new ArrayList<Ordering>(); ordering = new ArrayList<Ordering>();
} }
return ordering; return ordering;
} }
} }

View file

@ -1,62 +1,70 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* * Copyright (C) 2015-2018 The Helenus Authors
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. * Licensed under the Apache License, Version 2.0 (the "License");
* You may obtain a copy of the License at * 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 *
* * 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, * Unless required by applicable law or agreed to in writing, software
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * distributed under the License is distributed on an "AS IS" BASIS,
* See the License for the specific language governing permissions and * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* limitations under the License. * See the License for the specific language governing permissions and
*/ * limitations under the License.
package net.helenus.core.operation; */
package net.helenus.core.operation;
import java.util.List;
import java.util.function.Function; import com.datastax.driver.core.ResultSet;
import java.util.stream.Stream; import com.datastax.driver.core.querybuilder.BuiltStatement;
import java.util.List;
import com.datastax.driver.core.ResultSet; import java.util.function.Function;
import com.datastax.driver.core.querybuilder.BuiltStatement; import java.util.stream.Stream;
import net.helenus.core.cache.Facet;
import net.helenus.core.cache.Facet;
public final class SelectTransformingOperation<R, E>
public final class SelectTransformingOperation<R, E> extends AbstractFilterStreamOperation<R, SelectTransformingOperation<R, E>> {
extends
AbstractFilterStreamOperation<R, SelectTransformingOperation<R, E>> { private final SelectOperation<E> delegate;
private final Function<E, R> fn;
private final SelectOperation<E> delegate;
private final Function<E, R> fn; public SelectTransformingOperation(SelectOperation<E> delegate, Function<E, R> fn) {
super(delegate.sessionOps);
public SelectTransformingOperation(SelectOperation<E> delegate, Function<E, R> fn) {
super(delegate.sessionOps); this.delegate = delegate;
this.fn = fn;
this.delegate = delegate; this.filters = delegate.filters;
this.fn = fn; this.ifFilters = delegate.ifFilters;
this.filters = delegate.filters; }
this.ifFilters = delegate.ifFilters;
} @Override
public List<Facet> bindFacetValues() {
@Override return delegate.bindFacetValues();
public List<Facet> bindFacetValues() { }
return delegate.bindFacetValues();
} @Override
public List<Facet> getFacets() {
@Override return delegate.getFacets();
public List<Facet> getFacets() { }
return delegate.getFacets();
} @Override
public BuiltStatement buildStatement(boolean cached) {
@Override return delegate.buildStatement(cached);
public BuiltStatement buildStatement(boolean cached) { }
return delegate.buildStatement(cached);
} @Override
public Stream<R> transform(ResultSet resultSet) {
@Override return delegate.transform(resultSet).map(fn);
public Stream<R> transform(ResultSet resultSet) { }
return delegate.transform(resultSet).map(fn);
} @Override
} public boolean isSessionCacheable() {
return delegate.isSessionCacheable();
}
@Override
public boolean ignoreCache() {
return delegate.ignoreCache();
}
}

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,34 +20,41 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
public enum DefaultPrimitiveTypes { public enum DefaultPrimitiveTypes {
BOOLEAN(boolean.class, false), BYTE(byte.class, (byte) 0x0), CHAR(char.class, (char) 0x0), SHORT(short.class, BOOLEAN(boolean.class, false),
(short) 0), INT(int.class, 0), LONG(long.class, 0L), FLOAT(float.class, 0.0f), DOUBLE(double.class, 0.0); BYTE(byte.class, (byte) 0x0),
CHAR(char.class, (char) 0x0),
SHORT(short.class, (short) 0),
INT(int.class, 0),
LONG(long.class, 0L),
FLOAT(float.class, 0.0f),
DOUBLE(double.class, 0.0);
private static final Map<Class<?>, DefaultPrimitiveTypes> map = new HashMap<Class<?>, DefaultPrimitiveTypes>(); private static final Map<Class<?>, DefaultPrimitiveTypes> map =
new HashMap<Class<?>, DefaultPrimitiveTypes>();
static { static {
for (DefaultPrimitiveTypes type : DefaultPrimitiveTypes.values()) { for (DefaultPrimitiveTypes type : DefaultPrimitiveTypes.values()) {
map.put(type.getPrimitiveClass(), type); map.put(type.getPrimitiveClass(), type);
} }
} }
private final Class<?> primitiveClass; private final Class<?> primitiveClass;
private final Object defaultValue; private final Object defaultValue;
private DefaultPrimitiveTypes(Class<?> primitiveClass, Object defaultValue) { private DefaultPrimitiveTypes(Class<?> primitiveClass, Object defaultValue) {
this.primitiveClass = primitiveClass; this.primitiveClass = primitiveClass;
this.defaultValue = defaultValue; this.defaultValue = defaultValue;
} }
public static DefaultPrimitiveTypes lookup(Class<?> primitiveClass) { public static DefaultPrimitiveTypes lookup(Class<?> primitiveClass) {
return map.get(primitiveClass); return map.get(primitiveClass);
} }
public Class<?> getPrimitiveClass() { public Class<?> getPrimitiveClass() {
return primitiveClass; return primitiveClass;
} }
public Object getDefaultValue() { public Object getDefaultValue() {
return defaultValue; return defaultValue;
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,7 +20,9 @@ import java.util.Set;
public interface Drafted<T> extends MapExportable { public interface Drafted<T> extends MapExportable {
Set<String> mutated(); Set<String> mutated();
T build(); T build();
Set<String> read();
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,18 +17,17 @@
package net.helenus.core.reflect; package net.helenus.core.reflect;
import com.datastax.driver.core.Metadata; import com.datastax.driver.core.Metadata;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
public interface DslExportable { public interface DslExportable {
String GET_ENTITY_METHOD = "getHelenusMappingEntity"; String GET_ENTITY_METHOD = "getHelenusMappingEntity";
String GET_PARENT_METHOD = "getParentDslHelenusPropertyNode"; String GET_PARENT_METHOD = "getParentDslHelenusPropertyNode";
String SET_METADATA_METHOD = "setCassandraMetadataForHelenusSession"; String SET_METADATA_METHOD = "setCassandraMetadataForHelenusSession";
HelenusEntity getHelenusMappingEntity(); HelenusEntity getHelenusMappingEntity();
HelenusPropertyNode getParentDslHelenusPropertyNode(); HelenusPropertyNode getParentDslHelenusPropertyNode();
void setCassandraMetadataForHelenusSession(Metadata metadata); void setCassandraMetadataForHelenusSession(Metadata metadata);
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,6 +16,7 @@
*/ */
package net.helenus.core.reflect; package net.helenus.core.reflect;
import com.datastax.driver.core.*;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
@ -22,9 +24,6 @@ import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import com.datastax.driver.core.*;
import net.helenus.core.Helenus; import net.helenus.core.Helenus;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
import net.helenus.mapping.HelenusMappingEntity; import net.helenus.mapping.HelenusMappingEntity;
@ -37,164 +36,178 @@ import net.helenus.support.HelenusException;
public class DslInvocationHandler<E> implements InvocationHandler { public class DslInvocationHandler<E> implements InvocationHandler {
private final Class<E> iface; private final Class<E> iface;
private final ClassLoader classLoader; private final ClassLoader classLoader;
private final Optional<HelenusPropertyNode> parent; private final Optional<HelenusPropertyNode> parent;
private final Map<Method, HelenusProperty> map = new HashMap<Method, HelenusProperty>(); private final Map<Method, HelenusProperty> map = new HashMap<Method, HelenusProperty>();
private final Map<Method, Object> udtMap = new HashMap<Method, Object>(); private final Map<Method, Object> udtMap = new HashMap<Method, Object>();
private final Map<Method, Object> tupleMap = new HashMap<Method, Object>(); private final Map<Method, Object> tupleMap = new HashMap<Method, Object>();
private HelenusEntity entity = null; private HelenusEntity entity = null;
private Metadata metadata = null; private Metadata metadata = null;
public DslInvocationHandler(Class<E> iface, ClassLoader classLoader, Optional<HelenusPropertyNode> parent, public DslInvocationHandler(
Metadata metadata) { Class<E> iface,
ClassLoader classLoader,
Optional<HelenusPropertyNode> parent,
Metadata metadata) {
this.metadata = metadata; this.metadata = metadata;
this.parent = parent; this.parent = parent;
this.iface = iface; this.iface = iface;
this.classLoader = classLoader; this.classLoader = classLoader;
} }
public void setCassandraMetadataForHelenusSession(Metadata metadata) { public void setCassandraMetadataForHelenusSession(Metadata metadata) {
if (metadata != null) { if (metadata != null) {
this.metadata = metadata; this.metadata = metadata;
entity = init(metadata); entity = init(metadata);
} }
} }
private HelenusEntity init(Metadata metadata) { private HelenusEntity init(Metadata metadata) {
HelenusEntity entity = new HelenusMappingEntity(iface, metadata); HelenusEntity entity = new HelenusMappingEntity(iface, metadata);
Collection<HelenusProperty> properties = entity.getOrderedProperties(); Collection<HelenusProperty> properties = entity.getOrderedProperties();
if (properties != null) { if (properties != null) {
for (HelenusProperty prop : properties) { for (HelenusProperty prop : properties) {
map.put(prop.getGetterMethod(), prop); map.put(prop.getGetterMethod(), prop);
AbstractDataType type = prop.getDataType(); AbstractDataType type = prop.getDataType();
Class<?> javaType = prop.getJavaType(); Class<?> javaType = prop.getJavaType();
if (type instanceof UDTDataType && !UDTValue.class.isAssignableFrom(javaType)) { if (type instanceof UDTDataType && !UDTValue.class.isAssignableFrom(javaType)) {
Object childDsl = Helenus.dsl(javaType, classLoader, Object childDsl =
Optional.of(new HelenusPropertyNode(prop, parent)), metadata); Helenus.dsl(
javaType,
classLoader,
Optional.of(new HelenusPropertyNode(prop, parent)),
metadata);
udtMap.put(prop.getGetterMethod(), childDsl); udtMap.put(prop.getGetterMethod(), childDsl);
} }
if (type instanceof DTDataType) { if (type instanceof DTDataType) {
DTDataType dataType = (DTDataType) type; DTDataType dataType = (DTDataType) type;
if (dataType.getDataType() instanceof TupleType && !TupleValue.class.isAssignableFrom(javaType)) { if (dataType.getDataType() instanceof TupleType
&& !TupleValue.class.isAssignableFrom(javaType)) {
Object childDsl = Helenus.dsl(javaType, classLoader, Object childDsl =
Optional.of(new HelenusPropertyNode(prop, parent)), metadata); Helenus.dsl(
javaType,
classLoader,
Optional.of(new HelenusPropertyNode(prop, parent)),
metadata);
tupleMap.put(prop.getGetterMethod(), childDsl); tupleMap.put(prop.getGetterMethod(), childDsl);
} }
} }
} }
} }
return entity; return entity;
} }
@Override @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
HelenusEntity entity = this.entity; HelenusEntity entity = this.entity;
String methodName = method.getName(); String methodName = method.getName();
if ("equals".equals(methodName) && method.getParameterCount() == 1) { if ("equals".equals(methodName) && method.getParameterCount() == 1) {
Object otherObj = args[0]; Object otherObj = args[0];
if (otherObj == null) { if (otherObj == null) {
return false; return false;
} }
if (Proxy.isProxyClass(otherObj.getClass())) { if (Proxy.isProxyClass(otherObj.getClass())) {
return this == Proxy.getInvocationHandler(otherObj); return this == Proxy.getInvocationHandler(otherObj);
} }
return false; return false;
} }
if (DslExportable.SET_METADATA_METHOD.equals(methodName) && args.length == 1 && args[0] instanceof Metadata) { if (DslExportable.SET_METADATA_METHOD.equals(methodName)
if (metadata == null) { && args.length == 1
this.setCassandraMetadataForHelenusSession((Metadata) args[0]); && args[0] instanceof Metadata) {
} if (metadata == null) {
return null; this.setCassandraMetadataForHelenusSession((Metadata) args[0]);
} }
return null;
}
if (method.getParameterCount() != 0 || method.getReturnType() == void.class) { if (method.getParameterCount() != 0 || method.getReturnType() == void.class) {
throw new HelenusException("invalid getter method " + method); throw new HelenusException("invalid getter method " + method);
} }
if ("hashCode".equals(methodName)) { if ("hashCode".equals(methodName)) {
return hashCode(); return hashCode();
} }
if (DslExportable.GET_PARENT_METHOD.equals(methodName)) { if (DslExportable.GET_PARENT_METHOD.equals(methodName)) {
return parent.get(); return parent.get();
} }
if (entity == null) { if (entity == null) {
entity = init(metadata); entity = init(metadata);
} }
if ("toString".equals(methodName)) { if ("toString".equals(methodName)) {
return entity.toString(); return entity.toString();
} }
if (DslExportable.GET_ENTITY_METHOD.equals(methodName)) { if (DslExportable.GET_ENTITY_METHOD.equals(methodName)) {
return entity; return entity;
} }
HelenusProperty prop = map.get(method); HelenusProperty prop = map.get(method);
if (prop == null) { if (prop == null) {
prop = entity.getProperty(methodName); prop = entity.getProperty(methodName);
} }
if (prop != null) { if (prop != null) {
AbstractDataType type = prop.getDataType(); AbstractDataType type = prop.getDataType();
if (type instanceof UDTDataType) { if (type instanceof UDTDataType) {
Object childDsl = udtMap.get(method); Object childDsl = udtMap.get(method);
if (childDsl != null) { if (childDsl != null) {
return childDsl; return childDsl;
} }
} }
if (type instanceof DTDataType) { if (type instanceof DTDataType) {
DTDataType dataType = (DTDataType) type; DTDataType dataType = (DTDataType) type;
DataType dt = dataType.getDataType(); DataType dt = dataType.getDataType();
switch (dt.getName()) { switch (dt.getName()) {
case TUPLE : case TUPLE:
Object childDsl = tupleMap.get(method); Object childDsl = tupleMap.get(method);
if (childDsl != null) { if (childDsl != null) {
return childDsl; return childDsl;
} }
break; break;
case SET : case SET:
return new SetDsl(new HelenusPropertyNode(prop, parent)); return new SetDsl(new HelenusPropertyNode(prop, parent));
case LIST : case LIST:
return new ListDsl(new HelenusPropertyNode(prop, parent)); return new ListDsl(new HelenusPropertyNode(prop, parent));
case MAP : case MAP:
return new MapDsl(new HelenusPropertyNode(prop, parent)); return new MapDsl(new HelenusPropertyNode(prop, parent));
default : default:
break; break;
} }
} }
throw new DslPropertyException(new HelenusPropertyNode(prop, parent)); throw new DslPropertyException(new HelenusPropertyNode(prop, parent));
} }
throw new HelenusException("invalid method call " + method); throw new HelenusException("invalid method call " + method);
} }
} }

View file

@ -0,0 +1,73 @@
/*
* Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus 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 net.helenus.core.reflect;
import net.helenus.core.Getter;
public interface Entity {
String WRITTEN_AT_METHOD = "writtenAt";
String TTL_OF_METHOD = "ttlOf";
String TOKEN_OF_METHOD = "tokenOf";
/**
* The write time for the property in question referenced by the getter.
*
* @param getter the property getter
* @return the timestamp associated with the property identified by the getter
*/
default Long writtenAt(Getter getter) {
return 0L;
}
/**
* The write time for the property in question referenced by the property name.
*
* @param prop the name of a property in this entity
* @return the timestamp associated with the property identified by the property name if it exists
*/
default Long writtenAt(String prop) {
return 0L;
};
/**
* The time-to-live for the property in question referenced by the getter.
*
* @param getter the property getter
* @return the time-to-live in seconds associated with the property identified by the getter
*/
default Integer ttlOf(Getter getter) {
return 0;
};
/**
* The time-to-live for the property in question referenced by the property name.
*
* @param prop the name of a property in this entity
* @return the time-to-live in seconds associated with the property identified by the property name if it exists
*/
default Integer ttlOf(String prop) {
return 0;
};
/**
* The token (partition identifier) for this entity which can change over time if
* the cluster grows or shrinks but should be stable otherwise.
*
* @return the token for the entity
*/
default Long tokenOf() { return 0L; }
}

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,9 +20,7 @@ import java.lang.annotation.Annotation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Optional; import java.util.Optional;
import java.util.function.Function; import java.util.function.Function;
import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidator;
import net.helenus.core.SessionRepository; import net.helenus.core.SessionRepository;
import net.helenus.mapping.*; import net.helenus.mapping.*;
import net.helenus.mapping.type.AbstractDataType; import net.helenus.mapping.type.AbstractDataType;
@ -29,79 +28,84 @@ import net.helenus.support.HelenusMappingException;
public final class HelenusNamedProperty implements HelenusProperty { public final class HelenusNamedProperty implements HelenusProperty {
private final String name; private final String name;
public HelenusNamedProperty(String name) { public HelenusNamedProperty(String name) {
this.name = name; this.name = name;
} }
@Override @Override
public HelenusEntity getEntity() { public HelenusEntity getEntity() {
throw new HelenusMappingException("will never called"); throw new HelenusMappingException("will never called");
} }
@Override @Override
public String getPropertyName() { public String getPropertyName() {
return name; return name;
} }
@Override @Override
public Method getGetterMethod() { public Method getGetterMethod() {
throw new HelenusMappingException("will never called"); throw new HelenusMappingException("will never called");
} }
@Override @Override
public IdentityName getColumnName() { public IdentityName getColumnName() {
return IdentityName.of(name, false); return IdentityName.of(name, false);
} }
@Override @Override
public Optional<IdentityName> getIndexName() { public Optional<IdentityName> getIndexName() {
return Optional.empty(); return Optional.empty();
} }
@Override @Override
public boolean caseSensitiveIndex() { public boolean caseSensitiveIndex() {
return false; return false;
} }
@Override @Override
public Class<?> getJavaType() { public boolean isIdempotent() {
throw new HelenusMappingException("will never called"); return false;
} }
@Override @Override
public AbstractDataType getDataType() { public Class<?> getJavaType() {
throw new HelenusMappingException("will never called"); throw new HelenusMappingException("will never called");
} }
@Override @Override
public ColumnType getColumnType() { public AbstractDataType getDataType() {
return ColumnType.COLUMN; throw new HelenusMappingException("will never called");
} }
@Override @Override
public int getOrdinal() { public ColumnType getColumnType() {
return 0; return ColumnType.COLUMN;
} }
@Override @Override
public OrderingDirection getOrdering() { public int getOrdinal() {
return OrderingDirection.ASC; return 0;
} }
@Override @Override
public Optional<Function<Object, Object>> getReadConverter(SessionRepository repository) { public OrderingDirection getOrdering() {
return Optional.empty(); return OrderingDirection.ASC;
} }
@Override @Override
public Optional<Function<Object, Object>> getWriteConverter(SessionRepository repository) { public Optional<Function<Object, Object>> getReadConverter(SessionRepository repository) {
return Optional.empty(); return Optional.empty();
} }
@Override @Override
public ConstraintValidator<? extends Annotation, ?>[] getValidators() { public Optional<Function<Object, Object>> getWriteConverter(SessionRepository repository) {
return MappingUtil.EMPTY_VALIDATORS; return Optional.empty();
} }
@Override
public ConstraintValidator<? extends Annotation, ?>[] getValidators() {
return MappingUtil.EMPTY_VALIDATORS;
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,90 +18,89 @@ package net.helenus.core.reflect;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
import net.helenus.mapping.HelenusProperty; import net.helenus.mapping.HelenusProperty;
public final class HelenusPropertyNode implements Iterable<HelenusProperty> { public final class HelenusPropertyNode implements Iterable<HelenusProperty> {
private final HelenusProperty prop; private final HelenusProperty prop;
private final Optional<HelenusPropertyNode> next; private final Optional<HelenusPropertyNode> next;
public HelenusPropertyNode(HelenusProperty prop, Optional<HelenusPropertyNode> next) { public HelenusPropertyNode(HelenusProperty prop, Optional<HelenusPropertyNode> next) {
this.prop = prop; this.prop = prop;
this.next = next; this.next = next;
} }
public String getColumnName() { public String getColumnName() {
if (next.isPresent()) { if (next.isPresent()) {
List<String> columnNames = new ArrayList<String>(); List<String> columnNames = new ArrayList<String>();
for (HelenusProperty p : this) { for (HelenusProperty p : this) {
columnNames.add(p.getColumnName().toCql(true)); columnNames.add(p.getColumnName().toCql(true));
} }
Collections.reverse(columnNames); Collections.reverse(columnNames);
if (prop instanceof HelenusNamedProperty) { if (prop instanceof HelenusNamedProperty) {
int size = columnNames.size(); int size = columnNames.size();
StringBuilder str = new StringBuilder(); StringBuilder str = new StringBuilder();
for (int i = 0; i != size - 1; ++i) { for (int i = 0; i != size - 1; ++i) {
if (str.length() != 0) { if (str.length() != 0) {
str.append("."); str.append(".");
} }
str.append(columnNames.get(i)); str.append(columnNames.get(i));
} }
str.append("[").append(columnNames.get(size - 1)).append("]"); str.append("[").append(columnNames.get(size - 1)).append("]");
return str.toString(); return str.toString();
} else { } else {
return columnNames.stream().collect(Collectors.joining(".")); return columnNames.stream().collect(Collectors.joining("."));
} }
} else { } else {
return prop.getColumnName().toCql(); return prop.getColumnName().toCql();
} }
} }
public HelenusEntity getEntity() { public HelenusEntity getEntity() {
if (next.isPresent()) { if (next.isPresent()) {
HelenusProperty last = prop; HelenusProperty last = prop;
for (HelenusProperty p : this) { for (HelenusProperty p : this) {
last = p; last = p;
} }
return last.getEntity(); return last.getEntity();
} else { } else {
return prop.getEntity(); return prop.getEntity();
} }
} }
public HelenusProperty getProperty() { public HelenusProperty getProperty() {
return prop; return prop;
} }
public Optional<HelenusPropertyNode> getNext() { public Optional<HelenusPropertyNode> getNext() {
return next; return next;
} }
public Iterator<HelenusProperty> iterator() { public Iterator<HelenusProperty> iterator() {
return new PropertyNodeIterator(Optional.of(this)); return new PropertyNodeIterator(Optional.of(this));
} }
private static class PropertyNodeIterator implements Iterator<HelenusProperty> { private static class PropertyNodeIterator implements Iterator<HelenusProperty> {
private Optional<HelenusPropertyNode> next; private Optional<HelenusPropertyNode> next;
public PropertyNodeIterator(Optional<HelenusPropertyNode> next) { public PropertyNodeIterator(Optional<HelenusPropertyNode> next) {
this.next = next; this.next = next;
} }
@Override @Override
public boolean hasNext() { public boolean hasNext() {
return next.isPresent(); return next.isPresent();
} }
@Override @Override
public HelenusProperty next() { public HelenusProperty next() {
HelenusPropertyNode node = next.get(); HelenusPropertyNode node = next.get();
next = node.next; next = node.next;
return node.prop; return node.prop;
} }
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,165 +17,164 @@
package net.helenus.core.reflect; package net.helenus.core.reflect;
import java.util.*; import java.util.*;
import net.helenus.mapping.HelenusProperty; import net.helenus.mapping.HelenusProperty;
import net.helenus.support.DslPropertyException; import net.helenus.support.DslPropertyException;
import net.helenus.support.HelenusMappingException; import net.helenus.support.HelenusMappingException;
public final class ListDsl<V> implements List<V> { public final class ListDsl<V> implements List<V> {
private final HelenusPropertyNode parent; private final HelenusPropertyNode parent;
public ListDsl(HelenusPropertyNode parent) { public ListDsl(HelenusPropertyNode parent) {
this.parent = parent; this.parent = parent;
} }
public HelenusPropertyNode getParent() { public HelenusPropertyNode getParent() {
return parent; return parent;
} }
@Override @Override
public V get(int index) { public V get(int index) {
HelenusProperty prop = new HelenusNamedProperty(Integer.toString(index)); HelenusProperty prop = new HelenusNamedProperty(Integer.toString(index));
throw new DslPropertyException(new HelenusPropertyNode(prop, Optional.of(parent))); throw new DslPropertyException(new HelenusPropertyNode(prop, Optional.of(parent)));
} }
@Override @Override
public int size() { public int size() {
throwShouldNeverCall(); throwShouldNeverCall();
return 0; return 0;
} }
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
throwShouldNeverCall(); throwShouldNeverCall();
return false; return false;
} }
@Override @Override
public boolean contains(Object o) { public boolean contains(Object o) {
throwShouldNeverCall(); throwShouldNeverCall();
return false; return false;
} }
@Override @Override
public Iterator<V> iterator() { public Iterator<V> iterator() {
throwShouldNeverCall(); throwShouldNeverCall();
return null; return null;
} }
@Override @Override
public Object[] toArray() { public Object[] toArray() {
throwShouldNeverCall(); throwShouldNeverCall();
return null; return null;
} }
@Override @Override
public <T> T[] toArray(T[] a) { public <T> T[] toArray(T[] a) {
throwShouldNeverCall(); throwShouldNeverCall();
return null; return null;
} }
@Override @Override
public boolean add(V e) { public boolean add(V e) {
throwShouldNeverCall(); throwShouldNeverCall();
return false; return false;
} }
@Override @Override
public boolean remove(Object o) { public boolean remove(Object o) {
throwShouldNeverCall(); throwShouldNeverCall();
return false; return false;
} }
@Override @Override
public boolean containsAll(Collection<?> c) { public boolean containsAll(Collection<?> c) {
throwShouldNeverCall(); throwShouldNeverCall();
return false; return false;
} }
@Override @Override
public boolean addAll(Collection<? extends V> c) { public boolean addAll(Collection<? extends V> c) {
throwShouldNeverCall(); throwShouldNeverCall();
return false; return false;
} }
@Override @Override
public boolean addAll(int index, Collection<? extends V> c) { public boolean addAll(int index, Collection<? extends V> c) {
throwShouldNeverCall(); throwShouldNeverCall();
return false; return false;
} }
@Override @Override
public boolean removeAll(Collection<?> c) { public boolean removeAll(Collection<?> c) {
throwShouldNeverCall(); throwShouldNeverCall();
return false; return false;
} }
@Override @Override
public boolean retainAll(Collection<?> c) { public boolean retainAll(Collection<?> c) {
throwShouldNeverCall(); throwShouldNeverCall();
return false; return false;
} }
@Override @Override
public void clear() { public void clear() {
throwShouldNeverCall(); throwShouldNeverCall();
} }
@Override @Override
public V set(int index, V element) { public V set(int index, V element) {
throwShouldNeverCall(); throwShouldNeverCall();
return null; return null;
} }
@Override @Override
public void add(int index, V element) { public void add(int index, V element) {
throwShouldNeverCall(); throwShouldNeverCall();
} }
@Override @Override
public V remove(int index) { public V remove(int index) {
throwShouldNeverCall(); throwShouldNeverCall();
return null; return null;
} }
@Override @Override
public int indexOf(Object o) { public int indexOf(Object o) {
throwShouldNeverCall(); throwShouldNeverCall();
return 0; return 0;
} }
@Override @Override
public int lastIndexOf(Object o) { public int lastIndexOf(Object o) {
throwShouldNeverCall(); throwShouldNeverCall();
return 0; return 0;
} }
@Override @Override
public ListIterator<V> listIterator() { public ListIterator<V> listIterator() {
throwShouldNeverCall(); throwShouldNeverCall();
return null; return null;
} }
@Override @Override
public ListIterator<V> listIterator(int index) { public ListIterator<V> listIterator(int index) {
throwShouldNeverCall(); throwShouldNeverCall();
return null; return null;
} }
@Override @Override
public List<V> subList(int fromIndex, int toIndex) { public List<V> subList(int fromIndex, int toIndex) {
throwShouldNeverCall(); throwShouldNeverCall();
return null; return null;
} }
private void throwShouldNeverCall() { private void throwShouldNeverCall() {
throw new HelenusMappingException("should be never called"); throw new HelenusMappingException("should be never called");
} }
@Override @Override
public String toString() { public String toString() {
return "ListDsl"; return "ListDsl";
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,99 +20,98 @@ import java.util.Collection;
import java.util.Map; import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.Set; import java.util.Set;
import net.helenus.mapping.HelenusProperty; import net.helenus.mapping.HelenusProperty;
import net.helenus.support.DslPropertyException; import net.helenus.support.DslPropertyException;
import net.helenus.support.HelenusMappingException; import net.helenus.support.HelenusMappingException;
public final class MapDsl<K, V> implements Map<K, V> { public final class MapDsl<K, V> implements Map<K, V> {
private final HelenusPropertyNode parent; private final HelenusPropertyNode parent;
public MapDsl(HelenusPropertyNode parent) { public MapDsl(HelenusPropertyNode parent) {
this.parent = parent; this.parent = parent;
} }
public HelenusPropertyNode getParent() { public HelenusPropertyNode getParent() {
return parent; return parent;
} }
@Override @Override
public V get(Object key) { public V get(Object key) {
HelenusProperty prop = new HelenusNamedProperty(key.toString()); HelenusProperty prop = new HelenusNamedProperty(key.toString());
throw new DslPropertyException(new HelenusPropertyNode(prop, Optional.of(parent))); throw new DslPropertyException(new HelenusPropertyNode(prop, Optional.of(parent)));
} }
@Override @Override
public int size() { public int size() {
throwShouldNeverCall(); throwShouldNeverCall();
return 0; return 0;
} }
@Override @Override
public boolean isEmpty() { public boolean isEmpty() {
throwShouldNeverCall(); throwShouldNeverCall();
return false; return false;
} }
@Override @Override
public boolean containsKey(Object key) { public boolean containsKey(Object key) {
throwShouldNeverCall(); throwShouldNeverCall();
return false; return false;
} }
@Override @Override
public boolean containsValue(Object value) { public boolean containsValue(Object value) {
throwShouldNeverCall(); throwShouldNeverCall();
return false; return false;
} }
@Override @Override
public V put(K key, V value) { public V put(K key, V value) {
throwShouldNeverCall(); throwShouldNeverCall();
return null; return null;
} }
@Override @Override
public V remove(Object key) { public V remove(Object key) {
throwShouldNeverCall(); throwShouldNeverCall();
return null; return null;
} }
@Override @Override
public void putAll(Map<? extends K, ? extends V> m) { public void putAll(Map<? extends K, ? extends V> m) {
throwShouldNeverCall(); throwShouldNeverCall();
} }
@Override @Override
public void clear() { public void clear() {
throwShouldNeverCall(); throwShouldNeverCall();
} }
@Override @Override
public Set<K> keySet() { public Set<K> keySet() {
throwShouldNeverCall(); throwShouldNeverCall();
return null; return null;
} }
@Override @Override
public Collection<V> values() { public Collection<V> values() {
throwShouldNeverCall(); throwShouldNeverCall();
return null; return null;
} }
@Override @Override
public Set<java.util.Map.Entry<K, V>> entrySet() { public Set<java.util.Map.Entry<K, V>> entrySet() {
throwShouldNeverCall(); throwShouldNeverCall();
return null; return null;
} }
private void throwShouldNeverCall() { private void throwShouldNeverCall() {
throw new HelenusMappingException("should be never called"); throw new HelenusMappingException("should be never called");
} }
@Override @Override
public String toString() { public String toString() {
return "MapDsl"; return "MapDsl";
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -16,10 +17,25 @@
package net.helenus.core.reflect; package net.helenus.core.reflect;
import java.util.Map; import java.util.Map;
import java.util.Set;
import net.helenus.core.Getter;
public interface MapExportable { public interface MapExportable {
String TO_MAP_METHOD = "toMap";
String TO_READ_SET_METHOD = "toReadSet";
String PUT_METHOD = "put";
public static final String TO_MAP_METHOD = "toMap"; Map<String, Object> toMap();
Map<String, Object> toMap(); default Map<String, Object> toMap(boolean mutable) {
return null;
}
default Set<String> toReadSet() {
return null;
}
default void put(String key, Object value) {}
default <T> void put(Getter<T> getter, T value) {}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,121 +16,279 @@
*/ */
package net.helenus.core.reflect; package net.helenus.core.reflect;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable; import java.io.Serializable;
import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor; import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.util.Collections; import java.util.*;
import java.util.Map; import net.helenus.core.Getter;
import net.helenus.core.Helenus; import net.helenus.core.Helenus;
import net.helenus.core.cache.CacheUtil;
import net.helenus.mapping.MappingUtil;
import net.helenus.mapping.annotation.Transient; import net.helenus.mapping.annotation.Transient;
import net.helenus.mapping.value.ValueProviderMap;
import net.helenus.support.HelenusException; import net.helenus.support.HelenusException;
public class MapperInvocationHandler<E> implements InvocationHandler, Serializable { public class MapperInvocationHandler<E> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -7044209982830584984L; private static final long serialVersionUID = -7044209982830584984L;
private final Map<String, Object> src; private Map<String, Object> src;
private final Class<E> iface; private final Set<String> read = new HashSet<String>();
private final Class<E> iface;
public MapperInvocationHandler(Class<E> iface, Map<String, Object> src) { public MapperInvocationHandler(Class<E> iface, Map<String, Object> src) {
this.src = src; this.src = src;
this.iface = iface; this.iface = iface;
} }
private Object invokeDefault(Object proxy, Method method, Object[] args) throws Throwable { private Object invokeDefault(Object proxy, Method method, Object[] args) throws Throwable {
// NOTE: This is reflection magic to invoke (non-recursively) a default method // NOTE: This is reflection magic to invoke (non-recursively) a default method
// implemented on an interface // implemented on an interface
// that we've proxied (in ReflectionDslInstantiator). I found the answer in this // that we've proxied (in ReflectionDslInstantiator). I found the answer in this
// article. // article.
// https://zeroturnaround.com/rebellabs/recognize-and-conquer-java-proxies-default-methods-and-method-handles/ // https://zeroturnaround.com/rebellabs/recognize-and-conquer-java-proxies-default-methods-and-method-handles/
// First, we need an instance of a private inner-class found in MethodHandles. // First, we need an instance of a private inner-class found in MethodHandles.
Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, Constructor<MethodHandles.Lookup> constructor =
int.class); MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
constructor.setAccessible(true); constructor.setAccessible(true);
// Now we need to lookup and invoke special the default method on the interface // Now we need to lookup and invoke special the default method on the interface
// class. // class.
final Class<?> declaringClass = method.getDeclaringClass(); final Class<?> declaringClass = method.getDeclaringClass();
Object result = constructor.newInstance(declaringClass, MethodHandles.Lookup.PRIVATE) Object result =
.unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args); constructor
return result; .newInstance(declaringClass, MethodHandles.Lookup.PRIVATE)
} .unreflectSpecial(method, declaringClass)
.bindTo(proxy)
.invokeWithArguments(args);
return result;
}
@Override private Object writeReplace() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { return new SerializationProxy<E>(this);
}
// Transient, default methods should simply be invoked as-is. private void readObject(ObjectInputStream stream) throws InvalidObjectException {
if (method.isDefault() && method.getDeclaredAnnotation(Transient.class) != null) { throw new InvalidObjectException("Proxy required.");
return invokeDefault(proxy, method, args); }
}
String methodName = method.getName(); @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("equals".equals(methodName) && method.getParameterCount() == 1) { // Transient, default methods should simply be invoked as-is.
Object otherObj = args[0]; if (method.isDefault() && method.getDeclaredAnnotation(Transient.class) != null) {
if (otherObj == null) { return invokeDefault(proxy, method, args);
return false; }
}
if (Proxy.isProxyClass(otherObj.getClass())) {
if (this == Proxy.getInvocationHandler(otherObj)) {
return true;
}
}
if (otherObj instanceof MapExportable && src.equals(((MapExportable) otherObj).toMap())) {
return true;
}
return false;
}
if (method.getParameterCount() != 0 || method.getReturnType() == void.class) { String methodName = method.getName();
throw new HelenusException("invalid getter method " + method);
}
if ("hashCode".equals(methodName)) { if ("equals".equals(methodName) && method.getParameterCount() == 1) {
return hashCode(); Object otherObj = args[0];
} if (otherObj == null) {
return false;
}
if (Proxy.isProxyClass(otherObj.getClass())) {
if (this == Proxy.getInvocationHandler(otherObj)) {
return true;
}
}
if (otherObj instanceof MapExportable) {
return MappingUtil.compareMaps((MapExportable) otherObj, src);
}
return false;
}
if ("toString".equals(methodName)) { if (MapExportable.PUT_METHOD.equals(methodName) && method.getParameterCount() == 2) {
return iface.getSimpleName() + ": " + src.toString(); final String key;
} if (args[0] instanceof String) {
key = (String) args[0];
} else if (args[0] instanceof Getter) {
key = MappingUtil.resolveMappingProperty((Getter) args[0]).getProperty().getPropertyName();
} else {
key = null;
}
if (key != null) {
final Object value = (Object) args[1];
if (src instanceof ValueProviderMap) {
this.src = fromValueProviderMap(src);
}
src.put(key, value);
}
return null;
}
if ("dsl".equals(methodName)) { if (Entity.WRITTEN_AT_METHOD.equals(methodName) && method.getParameterCount() == 1) {
return Helenus.dsl(iface); final String key;
} if (args[0] instanceof String) {
key = CacheUtil.writeTimeKey((String) args[0]);
} else if (args[0] instanceof Getter) {
Getter getter = (Getter) args[0];
key =
CacheUtil.writeTimeKey(
MappingUtil.resolveMappingProperty(getter)
.getProperty()
.getColumnName()
.toCql(false));
} else {
return 0L;
}
Long v = (Long) src.get(key);
if (v != null) {
return v;
}
return 0L;
}
if (MapExportable.TO_MAP_METHOD.equals(methodName)) { if (Entity.TOKEN_OF_METHOD.equals(methodName) && method.getParameterCount() == 0) {
return Collections.unmodifiableMap(src); Long v = (Long) src.get("");
} if (v != null) {
return v;
}
return 0L;
}
Object value = src.get(methodName); if (Entity.TTL_OF_METHOD.equals(methodName) && method.getParameterCount() == 1) {
final String key;
if (args[0] instanceof String) {
key = CacheUtil.ttlKey((String) args[0]);
} else if (args[0] instanceof Getter) {
Getter getter = (Getter) args[0];
key =
CacheUtil.ttlKey(
MappingUtil.resolveMappingProperty(getter)
.getProperty()
.getColumnName()
.toCql(false));
} else {
return 0;
}
int v[] = (int[]) src.get(key);
if (v != null) {
return v[0];
}
return 0;
}
Class<?> returnType = method.getReturnType(); if (MapExportable.TO_MAP_METHOD.equals(methodName)) {
if (method.getParameterCount() == 1 && args[0] instanceof Boolean) {
if ((boolean) args[0] == true) {
return fromValueProviderMap(src, true);
}
}
return Collections.unmodifiableMap(src);
}
if (value == null) { if (MapExportable.TO_READ_SET_METHOD.equals(methodName)) {
return read;
}
// Default implementations of non-Transient methods in entities are the default if (method.getParameterCount() != 0 || method.getReturnType() == void.class) {
// value when the throw new HelenusException("invalid getter method " + method);
// map contains 'null'. }
if (method.isDefault()) {
return invokeDefault(proxy, method, args);
}
// Otherwise, if the return type of the method is a primitive Java type then if ("hashCode".equals(methodName)) {
// we'll return the standard return hashCode();
// default values to avoid a NPE in user code. }
if (returnType.isPrimitive()) {
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType);
if (type == null) {
throw new HelenusException("unknown primitive type " + returnType);
}
return type.getDefaultValue();
}
}
return value; if ("toString".equals(methodName)) {
} return iface.getSimpleName() + ": " + src.toString();
}
if ("writeReplace".equals(methodName)) {
return new SerializationProxy(this);
}
if ("readObject".equals(methodName)) {
throw new InvalidObjectException("Proxy required.");
}
if ("dsl".equals(methodName)) {
return Helenus.dsl(iface);
}
final Object value = src.get(methodName);
read.add(methodName);
if (value == null) {
Class<?> returnType = method.getReturnType();
// Default implementations of non-Transient methods in entities are the default
// value when the map contains 'null'.
if (method.isDefault()) {
return invokeDefault(proxy, method, args);
}
// Otherwise, if the return type of the method is a primitive Java type then
// we'll return the standard default values to avoid a NPE in user code.
if (returnType.isPrimitive()) {
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType);
if (type == null) {
throw new HelenusException("unknown primitive type " + returnType);
}
return type.getDefaultValue();
}
}
return value;
}
static Map<String, Object> fromValueProviderMap(Map v) {
return fromValueProviderMap(v, false);
}
static Map<String, Object> fromValueProviderMap(Map v, boolean mutable) {
if (v instanceof ValueProviderMap) {
Map<String, Object> m = new HashMap<String, Object>(v.size());
Set<String> keys = v.keySet();
for (String key : keys) {
Object value = v.get(key);
if (value != null && mutable) {
if (ImmutableList.class.isAssignableFrom(value.getClass())) {
m.put(key, new ArrayList((List) value));
} else if (ImmutableMap.class.isAssignableFrom(value.getClass())) {
m.put(key, new HashMap((Map) value));
} else if (ImmutableSet.class.isAssignableFrom(value.getClass())) {
m.put(key, new HashSet((Set) value));
} else {
m.put(key, value);
}
} else {
m.put(key, value);
}
}
return m;
}
return v;
}
static class SerializationProxy<E> implements Serializable {
private static final long serialVersionUID = -5617583940055969353L;
private final Class<E> iface;
private final Map<String, Object> src;
public SerializationProxy(MapperInvocationHandler mapper) {
this.iface = mapper.iface;
if (mapper.src instanceof ValueProviderMap) {
this.src = fromValueProviderMap(mapper.src);
} else {
this.src = mapper.src;
}
}
Object readResolve() throws ObjectStreamException {
return new MapperInvocationHandler(iface, src);
}
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,22 +16,25 @@
*/ */
package net.helenus.core.reflect; package net.helenus.core.reflect;
import com.datastax.driver.core.Metadata;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.util.Optional; import java.util.Optional;
import com.datastax.driver.core.Metadata;
import net.helenus.core.DslInstantiator; import net.helenus.core.DslInstantiator;
public enum ReflectionDslInstantiator implements DslInstantiator { public enum ReflectionDslInstantiator implements DslInstantiator {
INSTANCE; INSTANCE;
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <E> E instantiate(Class<E> iface, ClassLoader classLoader, Optional<HelenusPropertyNode> parent, public <E> E instantiate(
Metadata metadata) { Class<E> iface,
DslInvocationHandler<E> handler = new DslInvocationHandler<E>(iface, classLoader, parent, metadata); ClassLoader classLoader,
E proxy = (E) Proxy.newProxyInstance(classLoader, new Class[]{iface, DslExportable.class}, handler); Optional<HelenusPropertyNode> parent,
return proxy; Metadata metadata) {
} DslInvocationHandler<E> handler =
new DslInvocationHandler<E>(iface, classLoader, parent, metadata);
E proxy =
(E) Proxy.newProxyInstance(classLoader, new Class[] {iface, DslExportable.class}, handler);
return proxy;
}
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,15 +20,14 @@ import net.helenus.support.HelenusMappingException;
public final class ReflectionInstantiator { public final class ReflectionInstantiator {
private ReflectionInstantiator() { private ReflectionInstantiator() {}
}
public static <T> T instantiateClass(Class<T> clazz) { public static <T> T instantiateClass(Class<T> clazz) {
try { try {
return clazz.newInstance(); return clazz.newInstance();
} catch (InstantiationException | IllegalAccessException e) { } catch (InstantiationException | IllegalAccessException e) {
throw new HelenusMappingException("invalid class " + clazz, e); throw new HelenusMappingException("invalid class " + clazz, e);
} }
} }
} }

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2015 The Helenus Authors * Copyright (C) 2015 The Casser Authors
* Copyright (C) 2015-2018 The Helenus Authors
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -15,20 +16,23 @@
*/ */
package net.helenus.core.reflect; package net.helenus.core.reflect;
import java.io.Serializable;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.util.Map; import java.util.Map;
import net.helenus.core.MapperInstantiator; import net.helenus.core.MapperInstantiator;
public enum ReflectionMapperInstantiator implements MapperInstantiator { public enum ReflectionMapperInstantiator implements MapperInstantiator {
INSTANCE; INSTANCE;
@Override @Override
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <E> E instantiate(Class<E> iface, Map<String, Object> src, ClassLoader classLoader) { public <E> E instantiate(Class<E> iface, Map<String, Object> src, ClassLoader classLoader) {
MapperInvocationHandler<E> handler = new MapperInvocationHandler<E>(iface, src); MapperInvocationHandler<E> handler = new MapperInvocationHandler<E>(iface, src);
E proxy = (E) Proxy.newProxyInstance(classLoader, new Class[]{iface, MapExportable.class}, handler); E proxy =
return proxy; (E)
} Proxy.newProxyInstance(
classLoader, new Class[] {iface, MapExportable.class, Serializable.class}, handler);
return proxy;
}
} }

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