Compare commits

...

216 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
Greg Burd
92f74e41e1 WIP: support other caches 2017-10-27 08:48:42 -04:00
Greg Burd
b04e033bf4 Re-enable TimeoutException. Revert changes to add type to UnitOfWork as it breaks the use of subclasses. 2017-10-26 10:37:08 -04:00
Greg Burd
c7e37acc5a Add generic type where overlooked. Fix some cache logic. 2017-10-25 21:59:12 -04:00
Greg Burd
e5918cd1e8 Formatting. 2017-10-25 20:53:58 -04:00
Greg Burd
7535e9ade7 Improved logging output. Fixed caching for insert, update, delete cases. 2017-10-25 20:53:12 -04:00
Greg Burd
8a7dbfdec1 Implement missing getFacets() methods. Clean up some logic. 2017-10-24 21:07:47 -04:00
Greg Burd
18f2a057de Refining logging. 2017-10-24 16:01:48 -04:00
Greg Burd
1642f09ce9 Fix NPE, replace forEach() with for() /because Java. Formatting. 2017-10-24 14:24:23 -04:00
Greg Burd
c6b95f12b4 Fix up logging a bit. Fix bug where updateCache wasn't properly adding facets that were bound. 2017-10-24 13:40:35 -04:00
Greg Burd
a1e29c62c8 Improve UOW logging. 2017-10-24 11:05:44 -04:00
Greg Burd
25eb81219d WIP: caching 2017-10-23 16:29:23 -04:00
Greg Burd
a3b9ff9af3 Formatting 2017-10-23 14:47:56 -04:00
Greg Burd
7c683acb56 Formatting. 2017-10-23 14:41:20 -04:00
Greg Burd
d25061366b Ensure that the session cache is only used on entity objects marked @Cacheable. 2017-10-23 14:40:00 -04:00
Greg Burd
852ee59da2 Only use session cache for entites and only when requested. 2017-10-23 13:37:27 -04:00
Greg Burd
682a1a304e WIP: more progress toward session cache. 2017-10-23 12:45:03 -04:00
Greg Burd
fc83b8032c WIP: Session cache. 2017-10-23 12:17:20 -04:00
Greg Burd
ecd3d71e47 WIP: session cache populated with UOW data on commit. Also added logging for UOW time. 2017-10-23 11:10:55 -04:00
Greg Burd
dc9c228e4a WIP: flattenFacets 2017-10-22 22:04:20 -04:00
Greg Burd
e36dded9d2 WIP: combinations for flattening facets to keys. 2017-10-22 21:36:10 -04:00
Greg Burd
63c558581b WIP: time work in a UOW, track total and database time. Begin working on session cache. Also need to finish up merge() logic and consider ttl and writeTime. 2017-10-20 15:37:13 -04:00
Greg Burd
9bbb6d2371 Neuter the TimeoutException propogation for now. 2017-10-20 14:30:25 -04:00
Greg Burd
9d91436cb5 Merge branch 'develop' into 2.1.x-SNAPSHOT 2017-10-20 12:44:17 -04:00
Greg Burd
eb44912679 Minor fixups. 2017-10-20 12:43:38 -04:00
Greg Burd
e3ec76e6da Merge branch 'gburd/wip-facet-cache' into develop 2017-10-20 12:43:20 -04:00
Greg Burd
b40dc37704 Formatting. 2017-10-20 12:34:36 -04:00
Greg Burd
1746691826 Facet cache is working, able to fetch by non-primary key within UOW. 2017-10-20 12:33:42 -04:00
Greg Burd
6e687a7e90 Formatting 2017-10-20 08:22:35 -04:00
Greg Burd
75f32eb542 more progress. 2017-10-19 13:08:58 -04:00
Greg Burd
f64d5edd7c wip: good progress toward new multi-key index for cache 2017-10-18 12:17:00 -04:00
Greg Burd
32b06e1494 Merge branch 'develop' into gburd/wip-facet-cache 2017-10-16 16:19:43 -04:00
Greg Burd
b82bd8ed75 Merge branch 'develop' into 2.1.x-SNAPSHOT 2017-10-16 15:37:14 -04:00
Greg Burd
0dba5d6c53 Query execution via sync and async can timeout. Formatting. 2017-10-16 15:36:15 -04:00
Greg Burd
896748a2ec Formatting. 2017-10-13 10:41:21 -04:00
Greg Burd
d369a5b862 Merge branch 'develop' into gburd/wip-facet-cache 2017-10-12 16:29:11 -04:00
Greg Burd
74832a32dd Formatting 2017-10-12 16:18:17 -04:00
Greg Burd
2302b958e7 Merge branch 'develop' into 2.1.x-SNAPSHOT 2017-10-12 09:23:18 -04:00
Greg Burd
3ef4ddd5a0 Selections from materialized views my have different entity properties that impact generated queries. Remove noisy/expensive selection of ttl and write time until we need/use it for cache correctness properties (if ever). 2017-10-12 09:23:00 -04:00
Greg Burd
237adb40bf Merge branch 'develop' into 2.1.x-SNAPSHOT 2017-10-11 15:16:15 -04:00
Greg Burd
4e461c1be0 Insert should include both mutated and primary key components (partition keys and clustering columns) otherwise it's invalid CQL. 2017-10-11 15:15:59 -04:00
Greg Burd
646b2fa57d Merge branch 'develop' into 2.1.x-SNAPSHOT 2017-10-11 14:24:27 -04:00
Greg Burd
605529979d Create views if they don't already exist. 2017-10-11 14:24:01 -04:00
Greg Burd
635e2256b0 Merge branch 'develop' into 2.1.x-SNAPSHOT 2017-10-10 15:30:10 -04:00
Greg Burd
7156d733fa Also support ordering in clustering columns for materialized views (an oversight). 2017-10-10 15:29:48 -04:00
Greg Burd
265c35c4c6 Merge branch 'develop' into 2.1.x-SNAPSHOT 2017-10-10 13:55:44 -04:00
Greg Burd
953b47036c Formatting. 2017-10-10 13:55:08 -04:00
Greg Burd
1618405c83 Add the formatting helper shell script. 2017-10-10 13:45:44 -04:00
Greg Burd
828c456814 Add MaterialziedView entity support and the beginnings of a CoveringIndex (which is another name for MaterializedView). 2017-10-10 13:40:41 -04:00
Greg Burd
01102e2299 Enabled Helenus.dsl() to return a valid object even when the cluster metadata isn't known in advance and later learn more when it is. Changed the AbstractEntityDraft to have both by-string and by-getter mutation/accessor methods. These two combine to make Draft objects more type safe. 2017-10-10 13:40:41 -04:00
Greg Burd
0e9d1086ed Use backingMap for the mutated key set (and save some space/overhead). 2017-10-10 13:40:41 -04:00
Greg Burd
1c37d817b6 Add code formatting standard to project. 2017-10-06 11:40:19 -04:00
Greg Burd
306c9e68ef Merge branch 'develop' into gburd/wip-facet-cache 2017-10-05 09:42:32 -04:00
Greg Burd
25c5c6b969 Enabled Helenus.dsl() to return a valid object even when the cluster metadata isn't known in advance and later learn more when it is. Changed the AbstractEntityDraft to have both by-string and by-getter mutation/accessor methods. These two combine to make Draft objects more type safe. 2017-10-04 15:46:01 -04:00
Greg Burd
be8d1bf029 Use backingMap for the mutated key set (and save some space/overhead). 2017-09-29 10:48:37 -04:00
Greg Burd
0c6cda1db6 Switching version so that the build number is the third part of the version number 2017-09-29 10:21:48 -04:00
Greg Burd
bf1d5f217f First fetch from the backingMap. 2017-09-29 09:47:15 -04:00
Greg Burd
7e6863fc84 Merge branch 'develop' into gburd/wip-facet-cache 2017-09-28 13:43:03 -04:00
Greg Burd
265947586c Distinguish this project from 'https://github.com/helenusdriver/helenus' (which is 'helenus.org' where-as we're 'helenus.net'). Both projects aspire to address the same missing pieces from the DataStax Java Cassandra driver, a more friendly POJO mapping layer and domain-specific language/API, and share the same name (naming is hard) but are completely different implementations. 2017-09-26 11:32:23 -04:00
Greg Burd
b370272c3a No change. 2017-09-26 11:20:17 -04:00
Greg Burd
efa87b2d4f WIP: working toward a faceted cache. 2017-09-26 10:37:08 -04:00
Greg Burd
16af6ea175 When items are reset, they may also be null which would incorrectly return false. 2017-09-26 10:18:55 -04:00
Greg Burd
5c8a0360a2 Reset of a value in AbstractEntityDraft should update with the current value is either null or not equal to desired value, fixed logic. 2017-09-26 09:26:06 -04:00
Greg Burd
d3c24b70bf Be sure to pick up all inherited interfaces and scan those with the @InheritedTable annotation for methods. When doing that, if we encounter the same method name twice prefer the least generic implementation. 2017-09-25 15:50:29 -04:00
Greg Burd
844ebd9155 Add support for marking queries as idempotent so that the C* driver will apply the retry policy and in some cases perform speculative execution. (see: https://docs.datastax.com/en/developer/java-driver/3.1/manual/retries/ and https://docs.datastax.com/en/developer/java-driver/3.1/manual/speculative_execution/). 2017-09-22 09:46:59 -04:00
Greg Burd
a7094abdfa Re-work API for UnitOfWork so as to be able to instantiate custom sub-classes if need be. 2017-09-21 14:38:19 -04:00
Greg Burd
ac7db5f243 Alias writeTime and ttl on SELECT/caching. Shorten UOW log to avoid leaking information. 2017-09-20 10:48:47 -04:00
Greg Burd
67dbb36194 Cache keys must contain all primary key values. 2017-09-18 13:40:06 -04:00
Greg Burd
f9ab9f91a1 Fix cache lookup. 2017-09-18 11:07:09 -04:00
Greg Burd
895921c598 Another spot I missed allowing null UOW in {a}sync(). 2017-09-14 13:16:06 -04:00
Greg Burd
97f74776d5 Allow UOW to be null in {a}sync(UnitOfWork uow). 2017-09-14 09:34:12 -04:00
Greg Burd
8c165a689b Another oversight. 2017-09-13 15:08:50 -04:00
Greg Burd
9e3fabb2f9 Forgot to add type to begin() call in HelenusSession. 2017-09-13 14:27:08 -04:00
Greg Burd
90e1aafd94 Need to parameterize the exception type. 2017-09-13 13:27:34 -04:00
Greg Burd
efca86783c Allow configuration of the exception class used when conflicts arise at UnitOfWork commit time to enable clients to provide custom/advanced/specific exceptions without having to do a bunch of encapsulation. 2017-09-13 12:28:40 -04:00
Greg Burd
66578d77d5 Allow subclasses of UnitOfWork (remove 'final'). 2017-09-13 10:55:23 -04:00
Greg Burd
75aff52312 Honor the ignoreCache() when selecting from UOW cache. 2017-09-13 08:33:48 -04:00
Greg Burd
83ef8d7b0c Adjust how entity draft auditing works. 2017-09-12 15:51:49 -04:00
Greg Burd
58b29ad181 Caching entity results within the context of a UOW (even nested) seems to be working. 2017-09-12 14:09:51 -04:00
Greg Burd
1b46ee0ed1 WIP: More work toward entity cache, very broken at this point. 2017-09-01 15:38:58 -04:00
Greg Burd
cb09324ac6 Commented out the UOW cache (as it's not yet finished/working) and updated the version number. 2017-08-31 14:10:26 -04:00
Greg Burd
5089be2c22 Adding more support for the Draft/Builder pattern for entity objects. Fixed a number of issues and added more tests. 2017-08-31 14:06:44 -04:00
Greg Burd
3cd889ea61 WIP: More work toward UOW caching but mainly a lot of work to cleanup the API. 2017-08-30 13:41:15 -04:00
Greg Burd
09b06f4ca4 WIP: Fixed the ordering of commit.andThen(() -> {}) to match the nesting and fire iff the outter most UOW has committed. Made UnitOfWork Closable which will abort the work iff it has not previously been committed or aborted. Started to add tests to exercise Helenus UnitOfWork (transactions). 2017-08-29 10:13:29 -04:00
Greg Burd
c35d6d19d1 WIP: toward caching at session and unit of work. 2017-08-25 16:13:31 -04:00
Greg Burd
7ac9288eb8 Added update(draft). 2017-08-25 16:09:06 -04:00
Greg Burd
84bb6b5fae WIP: moving maven build to gradle 2017-08-18 16:50:05 -04:00
Greg Burd
933526b05b WIP: still working toward a solid caching scheme, progress but far from done. 2017-08-18 16:44:30 -04:00
Greg Burd
28aa3b1bae WIP: Working toward a solution that can cache result sets and/or transformed entity instances when annotated with @Cacheable in the Session cache. 2017-08-17 16:00:19 -04:00
Greg Burd
0fd9ff828c Update version to 2.0.21 2017-08-17 12:15:48 -04:00
Greg Burd
94ce778451 formatting 2017-08-17 12:14:00 -04:00
Greg Burd
142688a215 Funnel all paths to the database through one single method implementation (the 'Executioner') so as to ensure all calls into Cassandra are wrapped in a common way (traced, measured, cached, etc.) in a single place. 2017-08-17 12:05:50 -04:00
Greg Burd
6ad99fc459 formatting 2017-08-17 10:22:03 -04:00
Greg Burd
602e3521b4 Values return to the client in a proxy object should be converted to the proper type. 2017-08-16 13:43:46 -04:00
Greg Burd
007b6a51ae Merge branch 'develop' into 2.0.19-SNAPSHOT 2017-08-15 15:14:45 -04:00
Greg Burd
9c4d14cc93 Enable serialization of proxied entity objects. Enable the use of default implementation for getter methods to supply default values when the map contains a 'null' (or doesn't contain that key). 2017-08-15 14:58:07 -04:00
Greg Burd
1c68ebdcb8 Enable serialization of proxied entity objects. Enable the use of default implementation for getter methods to supply default values when the map contains a 'null' (or doesn't contain that key). 2017-08-15 14:05:22 -04:00
Greg Burd
89303f9179 Fix the InsertOperation transform method to properly hydrate the pojo with all property values or, in some cases, their default values before returning it to the caller. 2017-08-11 12:19:53 -04:00
Greg Burd
ec55c67d17 Merge branch '2.0.16-SNAPSHOT' into develop 2017-08-10 16:16:15 -04:00
Greg Burd
7f5cc4add6 Merge branch '2.0.15-SNAPSHOT' into develop 2017-08-10 16:15:26 -04:00
Greg Burd
90e19907d2 Merge branch '2.0.15-SNAPSHOT' into develop 2017-08-10 16:10:31 -04:00
Greg Burd
cf2740337e Add 'Drafted' interface and extend the InsertOperation to recognize that so as to be able to persist the mutated set of keys only yet return a pojo with values matching the draft, not just the mutations.
Changed {in,up}sert() to return the instance of the object.
2017-08-10 16:06:31 -04:00
Greg Burd
8569eaa76f Add 'Drafted' interface and extend the InsertOperation to recognize that so as to be able to persist the mutated set of keys only yet return a pojo with values matching the draft, not just the mutations. 2017-08-10 13:09:34 -04:00
Greg Burd
a126607c09 Added check for mutated set so that the backingMap could contain all object values. The other option was to auto-refresh the instance's backing map when a getter is invoked for a valid property that isn't included in the backing map (which still may be handy/required for caching to work). 2017-08-09 16:20:38 -04:00
Greg Burd
871c8d0c90 Fix minor error. 2017-08-09 15:12:24 -04:00
Greg Burd
5b0311c88a Merge branch '2.0.12-SNAPSHOT' into develop 2017-08-09 15:03:59 -04:00
Greg Burd
fd99b60913 Remove Integer/Enum conversion, it wasn't necessary. 2017-08-09 15:03:26 -04:00
Greg Burd
62e2e75dfc Merge branch '2.0.12-SNAPSHOT' into develop 2017-08-09 15:00:46 -04:00
Greg Burd
fa39908f4c Remove Integer/Enum conversion, it wasn't necessary. 2017-08-09 15:00:19 -04:00
Greg Burd
253816bd8c Remove Integer/Enum conversion, it wasn't necessary. 2017-08-09 11:49:38 -04:00
Greg Burd
512cb0f608 Fix for mapping integers to enum in proxy. 2017-08-09 10:42:28 -04:00
Greg Burd
c6cadf345b Merge branch '2.0.10-SNAPSHOT' into develop 2017-08-09 10:23:19 -04:00
Greg Burd
f008a33e53 Changed Zipkin API a bit, now requires a TraceContext rather than a Span before sync/async calls. 2017-08-09 10:22:59 -04:00
Greg Burd
2ebae2aaef Changed Zipkin API a bit, now requires a TraceContext rather than a Span before sync/async calls. 2017-08-09 09:35:14 -04:00
Greg Burd
cbc246f1c0 Merge branch '2.0.8-SNAPSHOT' into develop 2017-08-08 15:30:08 -04:00
Greg Burd
b9600ac931 Fix Zipkin tracing logic. 2017-08-08 15:28:51 -04:00
Greg Burd
efb26b8618 --amend . 2017-08-08 14:28:31 -04:00
Greg Burd
71e84da3bd Start plumbing for Dropwizard metrics and Zipkin tracing. Ensure proper bean value when working with enums. 2017-08-08 12:02:32 -04:00
Greg Burd
f4dbf34920 Remove Scala support and trim Future support at some point I'll re-introduce using Java 8 classes rather than Guava's or Scala's 2017-08-04 11:18:54 -04:00
Greg Burd
c42803b964 Return a Function on successful UnitOfWork.commit() so as to allow users to execute code post-commit iff that commit was successful. 2017-08-04 09:26:58 -04:00
Greg Burd
7497cf5a18 Add compatibility with Java 8 CompletableFuture converting our Guava-supplied ListenableFuture. 2017-08-03 15:38:13 -04:00
Greg Burd
b44c898682 Add the LOCAL_* consistency levels. Add a way to provide a default consistency level for all session operations. 2017-08-02 12:54:01 -04:00
313 changed files with 20491 additions and 13030 deletions

5
.gitignore vendored
View file

@ -1,3 +1,8 @@
.gradle/
build/
gradle/
gradlew
gradlew.bat
*.iml *.iml
.idea .idea
infer-out infer-out

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>

202
NOTES Normal file
View file

@ -0,0 +1,202 @@
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
----
// TODO(gburd): create a statement that matches one that wasn't prepared
//String key =
// "use " + preparedStatement.getQueryKeyspace() + "; " + preparedStatement.getQueryString();
//for (Object param : params) {
// key = key.replaceFirst(Pattern.quote("?"), param.toString());
//}
primitive types have default values, (e.g. boolean, int, ...) but primative wrapper classes do not and can be null (e.g. Boolean, Integer, ...)
create table wal {
id timeuuid,
follows timeuuid,
read <Counting Quotient Filter, Set<{keyspace, col, schema generation, timestamp}>>
write <Counting Quotient Filter, Set<{keyspace, col, schema generation, timestamp}>>
primary key (id, follows)
}
begin:
- insert into wal (timeuuid, parent timeuuid,
// NOTE: Update operations have no meaning when they only contain primary key components, so
// given that `properties` is ordered with the keys first if we find that the last element
// is either a partition key or clustering column then we know we should just skip this operation.
ColumnType ct = ((HelenusProperty) properties.toArray()[properties.size() - 1]).getColumnType();
if (ct != ColumnType.PARTITION_KEY && ct != ColumnType.CLUSTERING_COLUMN) {
return;
}
public Stream<E> sync() {
ListenableFuture<Stream<E>> future = async();
Futures.addCallback(future, new FutureCallback<String>() {
@Override
public void onSuccess(String contents) {
//...process web site contents
}
@Override
public void onFailure(Throwable throwable) {
log.error("Exception in task", throwable);
}
});
}
-------
private mergeCache(Map<String, Set<Object>>
private static <E> Iterable<E> concat(
Iterable<? extends E> i1,
Iterable<? extends E> i2) {
return new Iterable<E>() {
public Iterator<E> iterator() {
return new Iterator<E>() {
Iterator<? extends E> listIterator = i1.iterator();
Boolean checkedHasNext;
E nextValue;
private boolean startTheSecond;
void theNext() {
if (listIterator.hasNext()) {
checkedHasNext = true;
nextValue = listIterator.next();
} else if (startTheSecond)
checkedHasNext = false;
else {
startTheSecond = true;
listIterator = i2.iterator();
theNext();
}
}
public boolean hasNext() {
if (checkedHasNext == null)
theNext();
return checkedHasNext;
}
public E next() {
if (!hasNext())
throw new NoSuchElementException();
checkedHasNext = null;
return nextValue;
}
public void remove() {
listIterator.remove();
}
};
}
};
}
----------------------------------
if ("ttl".equals(methodName) && method.getParameterCount() == 1 && method.getReturnType() == int.class) {
Getter getter = (Getter) args[0];
if (getter == null) {
return false;
}
HelenusProperty prop = MappingUtil.resolveMappingProperty(getter).getProperty();
String getterName = prop.getPropertyName();
String ttlKeyForProperty = prop.getColumnName().toCql() + "_ttl";
if (src.containsKey(ttlKeyForProperty)) {
return src.get(ttlKeyForProperty);
} else {
return 0;
}
}
if ("written".equals(methodName) && method.getParameterCount() == 1 && method.getReturnType() == int.class) {
Getter getter = (Getter) args[0];
if (getter == null) {
return false;
}
HelenusProperty prop = MappingUtil.resolveMappingProperty(getter).getProperty();
String getterName = prop.getPropertyName();
String ttlKeyForProperty = prop.getColumnName().toCql() + "_ttl";
if (src.containsKey(ttlKeyForProperty)) {
return src.get(ttlKeyForProperty);
} else {
return 0;
}
}
-----------------
/*else {
Cache<String, Object> cache = session.getSessionCache();
Map<String, Object> rowMap = this.cache.rowMap();
for (String rowKey : rowMap.keySet()) {
String keys = flattenFacets(facets);
for (String key : keys) {
Object value = cache.getIfPresent(key);
if (value != null) {
result = Optional.of(value);
break;
}
}
}
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,5 +1,5 @@
# Helenus # Helenus
Fast and easy, functional style cutting edge Java 8 and Scala 2.11 Cassandra client for C* 3.x Fast and easy, functional style cutting edge Java 8 Cassandra client for C* 3.x
### Features ### Features
@ -9,14 +9,13 @@ Fast and easy, functional style cutting edge Java 8 and Scala 2.11 Cassandra cli
* Reactive asynchronous and synchronous API * Reactive asynchronous and synchronous API
* Provides Java mapping for Tables, Tuples, UDTs (User Defined Type), Collections, UDT Collections, Tuple Collections * Provides Java mapping for Tables, Tuples, UDTs (User Defined Type), Collections, UDT Collections, Tuple Collections
* Uses lazy mapping in all cases where possible * Uses lazy mapping in all cases where possible
* Supports Guava ListenableFuture and Scala Future * Supports Java 8 Futures and Guava ListenableFuture
### Requirements ### Requirements
* JVM 8 * JVM 8
* Datastax Driver 3.x * Datastax Driver 3.x
* Cassandra 3.x * Cassandra 3.x
* Scala 2.11+
* Maven * Maven
### Maven ### Maven
@ -32,27 +31,6 @@ Latest release dependency:
</dependencies> </dependencies>
``` ```
Active development dependency for Scala 2.11:
```
<dependencies>
<dependency>
<groupId>net.helenus</groupId>
<artifactId>helenus-core</artifactId>
<version>1.2.0_2.11-SNAPSHOT</version>
</dependency>
</dependencies>
<repositories>
<repository>
<id>oss-sonatype</id>
<name>oss-sonatype</name>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
```
### Simple Example ### Simple Example
@ -132,7 +110,6 @@ public interface AbstractRepository {
Account repository: Account repository:
``` ```
import scala.concurrent.Future;
public interface AccountRepository extends AbstractRepository { public interface AccountRepository extends AbstractRepository {

3
bin/deploy.sh Executable file
View file

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

14
bin/format.sh Executable file
View file

@ -0,0 +1,14 @@
#!/usr/bin/env bash
if [ "X$1" == "Xall" ]; then
for f in $(find ./src -name \*.java); do
echo Formatting $f
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,3 +0,0 @@
#!/bin/bash
mvn clean jar:jar javadoc:jar source:jar deploy -Prelease

View file

@ -11,16 +11,12 @@
</content> </content>
<orderEntry type="inheritedJdk" /> <orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: io.dropwizard.metrics:metrics-core:3.2.2" level="project" /> <orderEntry type="library" name="Maven: com.datastax.cassandra:cassandra-driver-core:3.3.2" level="project" />
<orderEntry type="library" scope="TEST" name="Maven: com.github.ben-manes.caffeine:caffeine:2.2.6" level="project" />
<orderEntry type="library" name="Maven: org.scala-lang:scala-library:2.13.0-M1" level="project" />
<orderEntry type="library" name="Maven: com.datastax.cassandra:cassandra-driver-core:3.3.0" 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" />
<orderEntry type="library" name="Maven: io.netty:netty-transport:4.0.47.Final" level="project" /> <orderEntry type="library" name="Maven: io.netty:netty-transport:4.0.47.Final" level="project" />
<orderEntry type="library" name="Maven: io.netty:netty-codec:4.0.47.Final" level="project" /> <orderEntry type="library" name="Maven: io.netty:netty-codec:4.0.47.Final" level="project" />
<orderEntry type="library" name="Maven: io.dropwizard.metrics:metrics-core:3.2.2" level="project" />
<orderEntry type="library" name="Maven: com.github.jnr:jnr-ffi:2.0.7" level="project" /> <orderEntry type="library" name="Maven: com.github.jnr:jnr-ffi:2.0.7" level="project" />
<orderEntry type="library" name="Maven: com.github.jnr:jffi:1.2.10" level="project" /> <orderEntry type="library" name="Maven: com.github.jnr:jffi:1.2.10" level="project" />
<orderEntry type="library" scope="RUNTIME" name="Maven: com.github.jnr:jffi:native:1.2.10" level="project" /> <orderEntry type="library" scope="RUNTIME" name="Maven: com.github.jnr:jffi:native:1.2.10" level="project" />
@ -32,12 +28,15 @@
<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: org.aspectj:aspectjrt:1.8.10" 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: 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.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" />
<orderEntry type="library" scope="TEST" name="Maven: com.anthemengineering.mojo:infer-maven-plugin:0.1.0" level="project" /> <orderEntry type="library" scope="TEST" name="Maven: com.anthemengineering.mojo:infer-maven-plugin:0.1.0" level="project" />
@ -115,9 +114,9 @@
<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" />

Binary file not shown.

62
pom.xml
View file

@ -4,8 +4,8 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>net.helenus</groupId> <groupId>net.helenus</groupId>
<artifactId>helenus-core</artifactId> <artifactId>helenus-net-core</artifactId>
<version>2.0.5-SNAPSHOT</version> <version>2.1</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>helenus</name> <name>helenus</name>
@ -40,6 +40,9 @@
<properties> <properties>
<dist.id>helenus</dist.id> <dist.id>helenus</dist.id>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
</properties> </properties>
<repositories> <repositories>
@ -104,21 +107,21 @@
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>org.scala-lang</groupId> <groupId>com.datastax.cassandra</groupId>
<artifactId>scala-library</artifactId> <artifactId>cassandra-driver-core</artifactId>
<version>2.13.0-M1</version> <version>3.3.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.datastax.cassandra</groupId> <groupId>com.datastax.cassandra</groupId>
<artifactId>cassandra-driver-core</artifactId> <artifactId>cassandra-driver-extras</artifactId>
<version>3.3.0</version> <version>3.3.2</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.aspectj</groupId> <groupId>com.diffplug.durian</groupId>
<artifactId>aspectjrt</artifactId> <artifactId>durian</artifactId>
<version>1.8.10</version> <version>3.4.0</version>
</dependency> </dependency>
<dependency> <dependency>
@ -139,12 +142,25 @@
<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 -->
<dependency>
<groupId>io.dropwizard.metrics</groupId>
<artifactId>metrics-core</artifactId>
<version>3.2.2</version>
</dependency>
<!-- Validation --> <!-- Validation -->
<dependency> <dependency>
<groupId>javax.validation</groupId> <groupId>javax.validation</groupId>
@ -195,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>
@ -209,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>
@ -250,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

@ -5,7 +5,9 @@ import java.util.List;
public class DefaultMetadata extends Metadata { public class DefaultMetadata extends Metadata {
public DefaultMetadata() { super(null); } public DefaultMetadata() {
super(null);
}
private DefaultMetadata(Cluster.Manager cluster) { private DefaultMetadata(Cluster.Manager cluster) {
super(cluster); super(cluster);
@ -18,5 +20,4 @@ public class DefaultMetadata extends Metadata {
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

@ -0,0 +1,49 @@
/*
* 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 com.datastax.driver.core.querybuilder;
import com.datastax.driver.core.CodecRegistry;
import java.util.List;
public class IsNotNullClause extends Clause {
final String name;
public IsNotNullClause(String name) {
this.name = name;
}
@Override
String name() {
return name;
}
@Override
Object firstValue() {
return null;
}
@Override
void appendTo(StringBuilder sb, List<Object> variables, CodecRegistry codecRegistry) {
Utils.appendName(name, sb).append(" IS NOT NULL");
}
@Override
boolean containsBindMarker() {
return false;
}
}

View file

@ -1,10 +1,8 @@
package com.datastax.driver.core.schemabuilder; package com.datastax.driver.core.schemabuilder;
import com.google.common.base.Optional; import static com.datastax.driver.core.schemabuilder.SchemaStatement.*;
import static com.datastax.driver.core.schemabuilder.SchemaStatement.STATEMENT_START; import com.google.common.base.Optional;
import static com.datastax.driver.core.schemabuilder.SchemaStatement.validateNotEmpty;
import static com.datastax.driver.core.schemabuilder.SchemaStatement.validateNotKeyWord;
public class CreateCustomIndex extends CreateIndex { public class CreateCustomIndex extends CreateIndex {
@ -18,7 +16,10 @@ public class CreateCustomIndex extends CreateIndex {
CreateCustomIndex(String indexName) { CreateCustomIndex(String indexName) {
super(indexName); super(indexName);
validateNotEmpty(indexName, "Index name"); validateNotEmpty(indexName, "Index name");
validateNotKeyWord(indexName, String.format("The index name '%s' is not allowed because it is a reserved keyword", indexName)); validateNotKeyWord(
indexName,
String.format(
"The index name '%s' is not allowed because it is a reserved keyword", indexName));
this.indexName = indexName; this.indexName = indexName;
} }
@ -42,8 +43,15 @@ public class CreateCustomIndex extends CreateIndex {
public CreateIndex.CreateIndexOn onTable(String keyspaceName, String tableName) { public CreateIndex.CreateIndexOn onTable(String keyspaceName, String tableName) {
validateNotEmpty(keyspaceName, "Keyspace name"); validateNotEmpty(keyspaceName, "Keyspace name");
validateNotEmpty(tableName, "Table name"); validateNotEmpty(tableName, "Table name");
validateNotKeyWord(keyspaceName, String.format("The keyspace name '%s' is not allowed because it is a reserved keyword", keyspaceName)); validateNotKeyWord(
validateNotKeyWord(tableName, String.format("The table name '%s' is not allowed because it is a reserved keyword", tableName)); keyspaceName,
String.format(
"The keyspace name '%s' is not allowed because it is a reserved keyword",
keyspaceName));
validateNotKeyWord(
tableName,
String.format(
"The table name '%s' is not allowed because it is a reserved keyword", tableName));
this.keyspaceName = Optional.fromNullable(keyspaceName); this.keyspaceName = Optional.fromNullable(keyspaceName);
this.tableName = tableName; this.tableName = tableName;
return new CreateCustomIndex.CreateIndexOn(); return new CreateCustomIndex.CreateIndexOn();
@ -57,46 +65,26 @@ public class CreateCustomIndex extends CreateIndex {
*/ */
public CreateIndex.CreateIndexOn onTable(String tableName) { public CreateIndex.CreateIndexOn onTable(String tableName) {
validateNotEmpty(tableName, "Table name"); validateNotEmpty(tableName, "Table name");
validateNotKeyWord(tableName, String.format("The table name '%s' is not allowed because it is a reserved keyword", tableName)); validateNotKeyWord(
tableName,
String.format(
"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();
} }
public class CreateIndexOn extends CreateIndex.CreateIndexOn { String getCustomClassName() {
/** return "";
* Specify the column to create the index on.
*
* @param columnName the column name.
* @return the final CREATE INDEX statement.
*/
public SchemaStatement andColumn(String columnName) {
validateNotEmpty(columnName, "Column name");
validateNotKeyWord(columnName, String.format("The column name '%s' is not allowed because it is a reserved keyword", columnName));
CreateCustomIndex.this.columnName = columnName;
return SchemaStatement.fromQueryString(buildInternal());
} }
/** String getOptions() {
* Create an index on the keys of the given map column. return "";
*
* @param columnName the column name.
* @return the final CREATE INDEX statement.
*/
public SchemaStatement andKeysOfColumn(String columnName) {
validateNotEmpty(columnName, "Column name");
validateNotKeyWord(columnName, String.format("The column name '%s' is not allowed because it is a reserved keyword", columnName));
CreateCustomIndex.this.columnName = columnName;
CreateCustomIndex.this.keys = true;
return SchemaStatement.fromQueryString(buildInternal());
} }
}
String getCustomClassName() { return ""; }
String getOptions() { 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 ");
@ -129,4 +117,39 @@ public class CreateCustomIndex extends CreateIndex {
return createStatement.toString(); return createStatement.toString();
} }
public class CreateIndexOn extends CreateIndex.CreateIndexOn {
/**
* Specify the column to create the index on.
*
* @param columnName the column name.
* @return the final CREATE INDEX statement.
*/
public SchemaStatement andColumn(String columnName) {
validateNotEmpty(columnName, "Column name");
validateNotKeyWord(
columnName,
String.format(
"The column name '%s' is not allowed because it is a reserved keyword", columnName));
CreateCustomIndex.this.columnName = columnName;
return SchemaStatement.fromQueryString(buildInternal());
}
/**
* Create an index on the keys of the given map column.
*
* @param columnName the column name.
* @return the final CREATE INDEX statement.
*/
public SchemaStatement andKeysOfColumn(String columnName) {
validateNotEmpty(columnName, "Column name");
validateNotKeyWord(
columnName,
String.format(
"The column name '%s' is not allowed because it is a reserved keyword", columnName));
CreateCustomIndex.this.columnName = columnName;
CreateCustomIndex.this.keys = true;
return SchemaStatement.fromQueryString(buildInternal());
}
}
} }

View file

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

View file

@ -17,9 +17,7 @@ package com.datastax.driver.core.schemabuilder;
import com.datastax.driver.core.CodecRegistry; 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) {

View file

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

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,7 +18,6 @@ package net.helenus.config;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.function.Function; 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;
@ -45,5 +45,4 @@ public class DefaultHelenusSettings implements HelenusSettings {
public MapperInstantiator getMapperInstantiator() { public MapperInstantiator getMapperInstantiator() {
return ReflectionMapperInstantiator.INSTANCE; return ReflectionMapperInstantiator.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,13 +16,12 @@
*/ */
package net.helenus.config; package net.helenus.config;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.function.Function;
import net.helenus.mapping.annotation.Transient; import net.helenus.mapping.annotation.Transient;
import java.lang.reflect.Method;
import java.util.function.Function;
public enum GetterMethodDetector implements Function<Method, Boolean> { public enum GetterMethodDetector implements Function<Method, Boolean> {
INSTANCE; INSTANCE;
@Override @Override
@ -35,13 +35,15 @@ public enum GetterMethodDetector implements Function<Method, Boolean> {
return false; return false;
} }
if (Modifier.isStatic(method.getModifiers())) {
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,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,7 +18,6 @@ package net.helenus.config;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.function.Function; 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;
@ -30,5 +30,4 @@ public interface HelenusSettings {
DslInstantiator getDslInstantiator(); DslInstantiator getDslInstantiator();
MapperInstantiator getMapperInstantiator(); MapperInstantiator getMapperInstantiator();
} }

View file

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

View file

@ -0,0 +1,202 @@
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.HashSet;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import net.helenus.core.reflect.DefaultPrimitiveTypes;
import net.helenus.core.reflect.Drafted;
import net.helenus.core.reflect.MapExportable;
import net.helenus.mapping.HelenusProperty;
import net.helenus.mapping.MappingUtil;
import org.apache.commons.lang3.SerializationUtils;
public abstract class AbstractEntityDraft<E> implements Drafted<E> {
private final MapExportable entity;
private final Map<String, Object> valuesMap;
private final Set<String> readSet;
private final Map<String, Object> mutationsMap = new HashMap<String, Object>();
public AbstractEntityDraft(MapExportable entity) {
this.entity = entity;
// 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 E build() {
return Helenus.map(getEntityClass(), toMap());
}
@SuppressWarnings("unchecked")
public <T> T get(Getter<T> getter, Class<?> returnType) {
return (T) get(this.<T>methodNameFor(getter), returnType);
}
@SuppressWarnings("unchecked")
public <T> T get(String key, Class<?> returnType) {
readSet.add(key);
T value = (T) mutationsMap.get(key);
if (value == null) {
value = (T) valuesMap.get(key);
if (value == null) {
if (Primitives.allPrimitiveTypes().contains(returnType)) {
DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType);
if (type == null) {
throw new RuntimeException("unknown primitive type " + returnType);
}
return (T) type.getDefaultValue();
}
} else {
// Collections fetched from the valuesMap
if (value instanceof Collection) {
value = (T) SerializationUtils.<Serializable>clone((Serializable) value);
}
}
}
return value;
}
public <T> Object set(Getter<T> getter, Object value) {
HelenusProperty prop = MappingUtil.resolveMappingProperty(getter).getProperty();
String key = prop.getPropertyName();
HelenusValidator.INSTANCE.validate(prop, value);
if (key == null || value == null) {
return null;
}
mutationsMap.put(key, value);
return value;
}
public Object set(String key, Object value) {
if (key == null || value == null) {
return null;
}
mutationsMap.put(key, value);
return value;
}
public void put(String key, Object value) {
mutationsMap.put(key, value);
}
@SuppressWarnings("unchecked")
public <T> T mutate(Getter<T> getter, T value) {
return (T) mutate(this.<T>methodNameFor(getter), value);
}
public <T> T mutate(String key, T value) {
Objects.requireNonNull(key);
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) {
return MappingUtil.resolveMappingProperty(getter).getProperty().getPropertyName();
}
public <T> Object unset(Getter<T> getter) {
return unset(methodNameFor(getter));
}
public Object unset(String key) {
if (key != null) {
Object value = mutationsMap.get(key);
mutationsMap.put(key, null);
return value;
}
return null;
}
public <T> boolean reset(Getter<T> getter, T desiredValue) {
return this.<T>reset(this.<T>methodNameFor(getter), desiredValue);
}
private <T> T fetch(String key) {
T value = (T) mutationsMap.get(key);
if (value == null) {
value = (T) valuesMap.get(key);
}
return value;
}
public <T> boolean reset(String key, T desiredValue) {
if (key != null && desiredValue != null) {
@SuppressWarnings("unchecked")
T currentValue = (T) this.<T>fetch(key);
if (currentValue == null || !currentValue.equals(desiredValue)) {
set(key, desiredValue);
return true;
}
}
return false;
}
@Override
public Map<String, Object> toMap() {
return toMap(valuesMap);
}
public Map<String, Object> toMap(Map<String, Object> entityMap) {
Map<String, Object> combined;
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
public Set<String> mutated() {
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,134 +16,118 @@
*/ */
package net.helenus.core; package net.helenus.core;
import com.codahale.metrics.MetricRegistry;
import com.datastax.driver.core.*;
import com.google.common.base.Stopwatch;
import com.google.common.collect.Table;
import com.google.common.util.concurrent.ListenableFuture;
import java.io.PrintStream; import java.io.PrintStream;
import java.util.List;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import net.helenus.core.cache.Facet;
import com.datastax.driver.core.schemabuilder.SchemaStatement; import net.helenus.mapping.value.ColumnValuePreparer;
import net.helenus.mapping.value.ColumnValueProvider;
import net.helenus.support.Either;
import net.helenus.support.HelenusException;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import com.datastax.driver.core.*;
import com.datastax.driver.core.querybuilder.BuiltStatement;
import com.google.common.util.concurrent.ListenableFuture;
import net.helenus.mapping.value.ColumnValuePreparer;
import net.helenus.mapping.value.ColumnValueProvider;
import net.helenus.support.HelenusException;
import javax.xml.validation.Schema;
public abstract class AbstractSessionOperations { public abstract class AbstractSessionOperations {
final Logger logger = LoggerFactory.getLogger(getClass()); private static final Logger LOG = LoggerFactory.getLogger(AbstractSessionOperations.class);
abstract public Session currentSession(); public abstract Session currentSession();
abstract public String usingKeyspace(); public abstract String usingKeyspace();
abstract public boolean isShowCql(); public abstract boolean isShowCql();
abstract public PrintStream getPrintStream(); public abstract boolean showValues();
abstract public Executor getExecutor(); public abstract PrintStream getPrintStream();
abstract public SessionRepository getSessionRepository(); public abstract Executor getExecutor();
abstract public ColumnValueProvider getValueProvider(); public abstract SessionRepository getSessionRepository();
abstract public ColumnValuePreparer getValuePreparer(); public abstract ColumnValueProvider getValueProvider();
public abstract ColumnValuePreparer getValuePreparer();
public abstract ConsistencyLevel getDefaultConsistencyLevel();
public abstract boolean getDefaultQueryIdempotency();
public PreparedStatement prepare(RegularStatement statement) { public PreparedStatement prepare(RegularStatement statement) {
try { try {
log(statement, false);
return currentSession().prepare(statement); return currentSession().prepare(statement);
} catch (RuntimeException e) { } catch (RuntimeException e) {
throw translateException(e); throw translateException(e);
} }
} }
public ListenableFuture<PreparedStatement> prepareAsync(RegularStatement statement) { public ListenableFuture<PreparedStatement> prepareAsync(RegularStatement statement) {
try { try {
log(statement, false);
return currentSession().prepareAsync(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 ResultSet execute(Statement statement) {
return execute(statement, null, null);
return executeAsync(statement, showValues).getUninterruptibly();
} }
public ResultSetFuture executeAsync(Statement statement, boolean showValues) { public ResultSet execute(Statement statement, Stopwatch timer) {
return execute(statement, null, timer);
}
public ResultSet execute(Statement statement, UnitOfWork uow) {
return execute(statement, uow, null);
}
public ResultSet execute(Statement statement, UnitOfWork uow, Stopwatch timer) {
return executeAsync(statement, uow, timer).getUninterruptibly();
}
public ResultSetFuture executeAsync(Statement statement) {
return executeAsync(statement, null, null);
}
public ResultSetFuture executeAsync(Statement statement, Stopwatch timer) {
return executeAsync(statement, null, timer);
}
public ResultSetFuture executeAsync(Statement statement, UnitOfWork uow) {
return executeAsync(statement, uow, null);
}
public ResultSetFuture executeAsync(Statement statement, UnitOfWork uow, Stopwatch timer) {
try { try {
log(statement, showValues);
return currentSession().executeAsync(statement); return currentSession().executeAsync(statement);
} catch (RuntimeException e) { } catch (RuntimeException e) {
throw translateException(e); throw translateException(e);
} }
} }
void log(Statement statement, boolean showValues) { public MetricRegistry getMetricRegistry() {
return null;
if (logger.isInfoEnabled()) {
logger.info("Execute statement " + statement);
} }
if (isShowCql()) { public void mergeCache(Table<String, String, Either<Object, List<Facet>>> uowCache) {}
if (statement instanceof BuiltStatement) {
BuiltStatement builtStatement = (BuiltStatement) statement;
if (showValues) {
RegularStatement regularStatement = builtStatement.setForceNoValues(true);
printCql(regularStatement.getQueryString());
} else {
printCql(builtStatement.getQueryString());
}
} else if (statement instanceof RegularStatement) {
RegularStatement regularStatement = (RegularStatement) statement;
printCql(regularStatement.getQueryString());
} else {
printCql(statement.toString());
}
}
}
public void cache(String key, Object value) {
}
RuntimeException translateException(RuntimeException e) { RuntimeException translateException(RuntimeException e) {
if (e instanceof HelenusException) { if (e instanceof HelenusException) {
return e; return e;
} }
throw new HelenusException(e); throw new HelenusException(e);
} }
void printCql(String cql) { public Object checkCache(String tableName, List<Facet> facets) {
getPrintStream().println(cql); return null;
} }
public void updateCache(Object pojo, List<Facet> facets) {}
public void cacheEvict(List<Facet> 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.
@ -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,3 +1,20 @@
/*
* 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; package net.helenus.core;
public class ConflictingUnitOfWorkException extends Exception { public class ConflictingUnitOfWorkException extends Exception {
@ -7,5 +24,4 @@ public class ConflictingUnitOfWorkException extends Exception {
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,10 +16,8 @@
*/ */
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;
@ -33,14 +32,6 @@ public final class Filter<V> {
this.postulate = postulate; this.postulate = postulate;
} }
public HelenusPropertyNode getNode() {
return node;
}
public Clause getClause(ColumnValuePreparer valuePreparer) {
return postulate.getClause(node, valuePreparer);
}
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);
} }
@ -89,13 +80,21 @@ public final class Filter<V> {
return new Filter<V>(node, postulate); return new Filter<V>(node, postulate);
} }
public static <V> Filter<V> create(
Getter<V> getter, HelenusPropertyNode node, Postulate<V> postulate) {
Objects.requireNonNull(getter, "empty getter");
Objects.requireNonNull(postulate, "empty operator");
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(Getter<V> getter, Operator op, V val) {
Objects.requireNonNull(getter, "empty getter"); Objects.requireNonNull(getter, "empty getter");
Objects.requireNonNull(op, "empty op"); Objects.requireNonNull(op, "empty op");
Objects.requireNonNull(val, "empty value"); Objects.requireNonNull(val, "empty value");
if (op == Operator.IN) { if (op == Operator.IN) {
throw new IllegalArgumentException("invalid usage of the 'in' operator, use Filter.in() static method"); throw new IllegalArgumentException(
"invalid usage of the 'in' operator, use Filter.in() static method");
} }
HelenusPropertyNode node = MappingUtil.resolveMappingProperty(getter); HelenusPropertyNode node = MappingUtil.resolveMappingProperty(getter);
@ -105,9 +104,20 @@ public final class Filter<V> {
return new Filter<V>(node, postulate); return new Filter<V>(node, postulate);
} }
public HelenusPropertyNode getNode() {
return node;
}
public Clause getClause(ColumnValuePreparer valuePreparer) {
return postulate.getClause(node, valuePreparer);
}
public V[] postulateValues() {
return postulate.values();
}
@Override @Override
public String toString() { public String toString() {
return node.getColumnName() + postulate.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.
@ -18,5 +19,4 @@ 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,14 +16,15 @@
*/ */
package net.helenus.core; package net.helenus.core;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import com.datastax.driver.core.Cluster; import com.datastax.driver.core.Cluster;
import com.datastax.driver.core.Metadata; import com.datastax.driver.core.Metadata;
import com.datastax.driver.core.Session; import com.datastax.driver.core.Session;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
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;
@ -32,15 +34,15 @@ import net.helenus.support.HelenusMappingException;
public final class Helenus { public final class Helenus {
private static volatile HelenusSettings settings = new DefaultHelenusSettings(); private static final ConcurrentMap<Class<?>, Object> dslCache =
private static final ConcurrentMap<Class<?>, Object> dslCache = new ConcurrentHashMap<Class<?>, Object>(); new ConcurrentHashMap<Class<?>, Object>();
private static final ConcurrentMap<Class<?>, Metadata> metadataForEntity = new ConcurrentHashMap<Class<?>, Metadata>(); private static final ConcurrentMap<Class<?>, Metadata> metadataForEntity =
new ConcurrentHashMap<Class<?>, Metadata>();
private static final Set<HelenusSession> sessions = new HashSet<HelenusSession>(); private static final Set<HelenusSession> sessions = new HashSet<HelenusSession>();
private static volatile HelenusSettings settings = new DefaultHelenusSettings();
private static volatile HelenusSession singleton; 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);
@ -52,7 +54,8 @@ public final class Helenus {
} }
public static void shutdown() { public static void shutdown() {
sessions.forEach((session) -> { sessions.forEach(
(session) -> {
session.close(); session.close();
sessions.remove(session); sessions.remove(session);
}); });
@ -79,6 +82,10 @@ public final class Helenus {
return new SessionInitializer(session); return new SessionInitializer(session);
} }
public static SessionInitializer init(Session session, String keyspace) {
return new SessionInitializer(session, keyspace);
}
public static SessionInitializer init(Session session) { public static SessionInitializer init(Session session) {
if (session == null) { if (session == null) {
@ -104,7 +111,10 @@ public final class Helenus {
return dsl(iface, classLoader, Optional.empty(), metadata); return dsl(iface, classLoader, 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,
Optional<HelenusPropertyNode> parent,
Metadata metadata) { Metadata metadata) {
Object instance = null; Object instance = null;
@ -123,7 +133,6 @@ public final class Helenus {
if (c != null) { if (c != null) {
instance = c; instance = c;
} }
} }
} }
@ -139,7 +148,14 @@ public final class Helenus {
} }
public static HelenusEntity entity(Class<?> iface) { public static HelenusEntity entity(Class<?> iface) {
return entity(iface, metadataForEntity.get(iface)); Metadata metadata = metadataForEntity.get(iface);
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) {
@ -176,12 +192,12 @@ public final class Helenus {
throw new HelenusMappingException("class is not an interface " + iface); throw new HelenusMappingException("class is not an interface " + iface);
} }
if (metadata != null) {
metadataForEntity.putIfAbsent(iface, metadata); metadataForEntity.putIfAbsent(iface, metadata);
}
return entity(iface, metadata); return entity(iface, metadata);
} }
throw new HelenusMappingException("unknown dsl object or mapping interface " + ifaceOrDsl); throw new HelenusMappingException("unknown dsl object or mapping interface " + ifaceOrDsl);
} }
} }

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,67 +16,97 @@
*/ */
package net.helenus.core; package net.helenus.core;
import static net.helenus.core.Query.eq;
import com.codahale.metrics.MetricRegistry;
import com.datastax.driver.core.*;
import com.google.common.collect.Table;
import java.io.Closeable; import java.io.Closeable;
import java.io.PrintStream; import java.io.PrintStream;
import java.util.Map; import java.util.*;
import java.util.Objects;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;
import java.util.function.Function; import java.util.function.Function;
import java.util.stream.Collectors;
import com.datastax.driver.core.*; import javax.cache.Cache;
import com.google.common.cache.Cache; import javax.cache.CacheManager;
import com.google.common.cache.CacheBuilder; import net.helenus.core.cache.CacheUtil;
import net.helenus.core.cache.Facet;
import net.helenus.core.cache.UnboundFacet;
import net.helenus.core.operation.*; import net.helenus.core.operation.*;
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.MappingUtil; import net.helenus.mapping.MappingUtil;
import net.helenus.mapping.value.*; import net.helenus.mapping.value.*;
import net.helenus.support.Fun; import net.helenus.support.*;
import net.helenus.support.Fun.Tuple1; import net.helenus.support.Fun.Tuple1;
import net.helenus.support.Fun.Tuple2; import net.helenus.support.Fun.Tuple2;
import net.helenus.support.Fun.Tuple6; import net.helenus.support.Fun.Tuple6;
public final class HelenusSession extends AbstractSessionOperations implements Closeable { public class HelenusSession extends AbstractSessionOperations implements Closeable {
public static final Object deleted = new Object();
private final int MAX_CACHE_SIZE = 10000;
private final int MAX_CACHE_EXPIRE_SECONDS = 600;
private final Session session; private final Session session;
private final CodecRegistry registry; private final CodecRegistry registry;
private volatile String usingKeyspace; private final ConsistencyLevel defaultConsistencyLevel;
private volatile boolean showCql; private final boolean defaultQueryIdempotency;
private final MetricRegistry metricRegistry;
private final PrintStream printStream; private final PrintStream printStream;
private final SessionRepository sessionRepository; private final SessionRepository sessionRepository;
private final Executor executor; private final Executor executor;
private final boolean dropSchemaOnClose; private final boolean dropSchemaOnClose;
private final CacheManager cacheManager;
private final RowColumnValueProvider valueProvider; private final RowColumnValueProvider valueProvider;
private final StatementColumnValuePreparer valuePreparer; private final StatementColumnValuePreparer valuePreparer;
private final Metadata metadata; private final Metadata metadata;
private final Cache<String, Object> sessionCache; private volatile String usingKeyspace;
private UnitOfWork currentUnitOfWork; private volatile boolean showCql;
private volatile boolean showValues;
HelenusSession(Session session, String usingKeyspace, CodecRegistry registry, boolean showCql, HelenusSession(
PrintStream printStream, SessionRepositoryBuilder sessionRepositoryBuilder, Executor executor, Session session,
boolean dropSchemaOnClose) { String usingKeyspace,
CodecRegistry registry,
boolean showCql,
boolean showValues,
PrintStream printStream,
SessionRepositoryBuilder sessionRepositoryBuilder,
Executor executor,
boolean dropSchemaOnClose,
ConsistencyLevel consistencyLevel,
boolean defaultQueryIdempotency,
CacheManager cacheManager,
MetricRegistry metricRegistry) {
this.session = session; this.session = session;
this.registry = registry == null ? CodecRegistry.DEFAULT_INSTANCE : registry; this.registry = registry == null ? CodecRegistry.DEFAULT_INSTANCE : registry;
this.usingKeyspace = Objects.requireNonNull(usingKeyspace, this.usingKeyspace =
"keyspace needs to be selected before creating session"); Objects.requireNonNull(
usingKeyspace, "keyspace needs to be selected before creating session");
this.showCql = showCql; this.showCql = showCql;
this.showValues = showValues;
this.printStream = printStream; this.printStream = printStream;
this.sessionRepository = sessionRepositoryBuilder.build(); this.sessionRepository =
sessionRepositoryBuilder == null ? null : sessionRepositoryBuilder.build();
this.executor = executor; this.executor = executor;
this.dropSchemaOnClose = dropSchemaOnClose; this.dropSchemaOnClose = dropSchemaOnClose;
this.defaultConsistencyLevel = consistencyLevel;
this.defaultQueryIdempotency = defaultQueryIdempotency;
this.metricRegistry = metricRegistry;
this.cacheManager = cacheManager;
this.valueProvider = new RowColumnValueProvider(this.sessionRepository); this.valueProvider = new RowColumnValueProvider(this.sessionRepository);
this.valuePreparer = new StatementColumnValuePreparer(this.sessionRepository); this.valuePreparer = new StatementColumnValuePreparer(this.sessionRepository);
this.metadata = session.getCluster().getMetadata(); this.metadata = session == null ? null : session.getCluster().getMetadata();
this.sessionCache = CacheBuilder.newBuilder().maximumSize(MAX_CACHE_SIZE) }
.expireAfterAccess(MAX_CACHE_EXPIRE_SECONDS, TimeUnit.SECONDS).recordStats().build();
this.currentUnitOfWork = null; public UnitOfWork begin() {
return new UnitOfWork(this).begin();
}
public UnitOfWork begin(UnitOfWork parent) {
return new UnitOfWork(this, parent).begin();
} }
@Override @Override
@ -114,6 +145,20 @@ public final class HelenusSession extends AbstractSessionOperations implements C
return this; return this;
} }
public HelenusSession showQueryValuesInLog(boolean showValues) {
this.showValues = showValues;
return this;
}
public HelenusSession showQueryValuesInLog() {
this.showValues = true;
return this;
}
public boolean showValues() {
return showValues;
}
@Override @Override
public Executor getExecutor() { public Executor getExecutor() {
return executor; return executor;
@ -134,46 +179,201 @@ public final class HelenusSession extends AbstractSessionOperations implements C
return valuePreparer; return valuePreparer;
} }
public Metadata getMetadata() { return metadata; } @Override
public MetricRegistry getMetricRegistry() {
return metricRegistry;
}
public synchronized UnitOfWork begin() { @Override
if (currentUnitOfWork == null) { public ConsistencyLevel getDefaultConsistencyLevel() {
currentUnitOfWork = new UnitOfWork(this); return defaultConsistencyLevel;
return currentUnitOfWork; }
@Override
public boolean getDefaultQueryIdempotency() {
return defaultQueryIdempotency;
}
@Override
public Object checkCache(String tableName, List<Facet> facets) {
Object result = null;
if (cacheManager != null) {
Cache<String, Object> cache = cacheManager.getCache(tableName);
if (cache != null) {
for (String key : CacheUtil.flatKeys(tableName, facets)) {
result = cache.get(key);
if (result != null) {
return result;
}
}
}
}
return null;
}
@Override
public void cacheEvict(List<Facet> facets) {
if (cacheManager != null) {
String tableName = CacheUtil.schemaName(facets);
Cache<String, Object> cache = cacheManager.getCache(tableName);
if (cache != null) {
CacheUtil.flatKeys(tableName, facets).forEach(key -> cache.remove(key));
}
}
}
@Override
public void updateCache(Object pojo, List<Facet> facets) {
Map<String, Object> valueMap =
pojo instanceof MapExportable ? ((MapExportable) pojo).toMap() : null;
List<Facet> boundFacets = new ArrayList<>();
for (Facet facet : facets) {
if (facet instanceof UnboundFacet) {
UnboundFacet unboundFacet = (UnboundFacet) facet;
UnboundFacet.Binder binder = unboundFacet.binder();
for (HelenusProperty prop : unboundFacet.getProperties()) {
Object value;
if (valueMap == null) {
value = BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false);
if (value != null) {
binder.setValueForProperty(prop, value.toString());
}
} else { } else {
return currentUnitOfWork.begin(); value = valueMap.get(prop.getPropertyName());
if (value != null) binder.setValueForProperty(prop, value.toString());
}
}
if (binder.isBound()) {
boundFacets.add(binder.bind());
}
} else {
boundFacets.add(facet);
}
}
String tableName = CacheUtil.schemaName(facets);
List<String[]> facetCombinations = CacheUtil.flattenFacets(boundFacets);
replaceCachedFacetValues(pojo, tableName, facetCombinations);
}
@Override
public void mergeCache(Table<String, String, Either<Object, List<Facet>>> uowCache) {
if (cacheManager != null) {
List<Object> items =
uowCache
.values()
.stream()
.filter(Either::isLeft)
.map(Either::getLeft)
.distinct()
.collect(Collectors.toList());
for (Object pojo : items) {
HelenusEntity entity = Helenus.resolve(MappingUtil.getMappingInterface(pojo));
Map<String, Object> valueMap =
pojo instanceof MapExportable ? ((MapExportable) pojo).toMap() : null;
if (entity.isCacheable()) {
List<Facet> boundFacets = new ArrayList<>();
for (Facet facet : entity.getFacets()) {
if (facet instanceof UnboundFacet) {
UnboundFacet unboundFacet = (UnboundFacet) facet;
UnboundFacet.Binder binder = unboundFacet.binder();
unboundFacet
.getProperties()
.forEach(
prop -> {
if (valueMap == null) {
Object value =
BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop);
binder.setValueForProperty(prop, value.toString());
} else {
Object v = valueMap.get(prop.getPropertyName());
if (v != null) {
binder.setValueForProperty(prop, v.toString());
}
}
});
if (binder.isBound()) {
boundFacets.add(binder.bind());
}
} else {
boundFacets.add(facet);
}
}
List<String[]> facetCombinations = CacheUtil.flattenFacets(boundFacets);
String tableName = CacheUtil.schemaName(boundFacets);
replaceCachedFacetValues(pojo, tableName, facetCombinations);
} }
} }
public synchronized void commit() throws ConflictingUnitOfWorkException { List<List<Facet>> deletedFacetSets =
if (currentUnitOfWork != null) { uowCache
currentUnitOfWork.commit(); .values()
currentUnitOfWork = null; .stream()
.filter(Either::isRight)
.map(Either::getRight)
.collect(Collectors.toList());
for (List<Facet> facets : deletedFacetSets) {
String tableName = CacheUtil.schemaName(facets);
Cache<String, Object> cache = cacheManager.getCache(tableName);
if (cache != null) {
List<String> keys = CacheUtil.flatKeys(tableName, facets);
keys.forEach(key -> cache.remove(key));
}
}
} }
} }
public synchronized void abort() { private void replaceCachedFacetValues(
if (currentUnitOfWork != null) { Object pojo, String tableName, List<String[]> facetCombinations) {
currentUnitOfWork.abort(); if (cacheManager != null) {
currentUnitOfWork = null; for (String[] combination : facetCombinations) {
String cacheKey = tableName + "." + Arrays.toString(combination);
Cache<String, Object> cache = cacheManager.getCache(tableName);
if (cache != null) {
if (pojo == null || pojo == HelenusSession.deleted) {
cache.remove(cacheKey);
} else {
cache.put(cacheKey, pojo);
}
}
}
} }
} }
public void cache(String key, Object value) { public CacheManager getCacheManager() {
sessionCache.put(key, value); // ttl return cacheManager;
}
public Metadata getMetadata() {
return metadata;
}
public <E> SelectOperation<E> select(E pojo) {
Objects.requireNonNull(
pojo, "supplied object must be a dsl for a registered entity but cannot be null");
ColumnValueProvider valueProvider = getValueProvider();
HelenusEntity entity = Helenus.resolve(pojo);
Class<?> entityClass = entity.getMappingInterface();
return new SelectOperation<E>(
this,
entity,
(r) -> {
Map<String, Object> map = new ValueProviderMap(r, valueProvider, entity);
return (E) Helenus.map(entityClass, map);
});
} }
public <E> SelectOperation<E> select(Class<E> entityClass) { public <E> SelectOperation<E> select(Class<E> entityClass) {
Objects.requireNonNull(entityClass, "entityClass is empty"); Objects.requireNonNull(entityClass, "entityClass is empty");
ColumnValueProvider valueProvider = getValueProvider(); ColumnValueProvider valueProvider = getValueProvider();
HelenusEntity entity = Helenus.entity(entityClass); HelenusEntity entity = Helenus.entity(entityClass);
return new SelectOperation<E>(this, entity, (r) -> { return new SelectOperation<E>(
this,
entity,
(r) -> {
Map<String, Object> map = new ValueProviderMap(r, valueProvider, entity); Map<String, Object> map = new ValueProviderMap(r, valueProvider, entity);
return (E) Helenus.map(entityClass, map); return (E) Helenus.map(entityClass, map);
}); });
} }
@ -181,9 +381,24 @@ public final class HelenusSession extends AbstractSessionOperations implements C
return new SelectOperation<Fun.ArrayTuple>(this); return new SelectOperation<Fun.ArrayTuple>(this);
} }
public SelectOperation<Row> selectAll(Class<?> entityClass) { public <E> SelectOperation<E> selectAll(Class<E> entityClass) {
Objects.requireNonNull(entityClass, "entityClass is empty"); Objects.requireNonNull(entityClass, "entityClass is empty");
return new SelectOperation<Row>(this, Helenus.entity(entityClass)); HelenusEntity entity = Helenus.entity(entityClass);
return new SelectOperation<E>(
this,
entity,
(r) -> {
Map<String, Object> map = new ValueProviderMap(r, valueProvider, entity);
return (E) Helenus.map(entityClass, map);
});
}
public <E> SelectOperation<Row> selectAll(E pojo) {
Objects.requireNonNull(
pojo, "supplied object must be a dsl for a registered entity but cannot be null");
HelenusEntity entity = Helenus.resolve(pojo);
return new SelectOperation<Row>(this, entity);
} }
public <E> SelectOperation<E> selectAll(Class<E> entityClass, Function<Row, E> rowMapper) { public <E> SelectOperation<E> selectAll(Class<E> entityClass, Function<Row, E> rowMapper) {
@ -196,7 +411,8 @@ public final class HelenusSession extends AbstractSessionOperations implements C
Objects.requireNonNull(getter1, "field 1 is empty"); Objects.requireNonNull(getter1, "field 1 is empty");
HelenusPropertyNode p1 = MappingUtil.resolveMappingProperty(getter1); HelenusPropertyNode p1 = MappingUtil.resolveMappingProperty(getter1);
return new SelectOperation<Tuple1<V1>>(this, new Mappers.Mapper1<V1>(getValueProvider(), p1), p1); return new SelectOperation<Tuple1<V1>>(
this, new Mappers.Mapper1<V1>(getValueProvider(), p1), p1);
} }
public <V1, V2> SelectOperation<Tuple2<V1, V2>> select(Getter<V1> getter1, Getter<V2> getter2) { public <V1, V2> SelectOperation<Tuple2<V1, V2>> select(Getter<V1> getter1, Getter<V2> getter2) {
@ -205,12 +421,12 @@ public final class HelenusSession extends AbstractSessionOperations implements C
HelenusPropertyNode p1 = MappingUtil.resolveMappingProperty(getter1); HelenusPropertyNode p1 = MappingUtil.resolveMappingProperty(getter1);
HelenusPropertyNode p2 = MappingUtil.resolveMappingProperty(getter2); HelenusPropertyNode p2 = MappingUtil.resolveMappingProperty(getter2);
return new SelectOperation<Fun.Tuple2<V1, V2>>(this, new Mappers.Mapper2<V1, V2>(getValueProvider(), p1, p2), return new SelectOperation<Fun.Tuple2<V1, V2>>(
p1, p2); this, new Mappers.Mapper2<V1, V2>(getValueProvider(), p1, p2), p1, p2);
} }
public <V1, V2, V3> SelectOperation<Fun.Tuple3<V1, V2, V3>> select(Getter<V1> getter1, Getter<V2> getter2, public <V1, V2, V3> SelectOperation<Fun.Tuple3<V1, V2, V3>> select(
Getter<V3> getter3) { Getter<V1> getter1, Getter<V2> getter2, Getter<V3> getter3) {
Objects.requireNonNull(getter1, "field 1 is empty"); Objects.requireNonNull(getter1, "field 1 is empty");
Objects.requireNonNull(getter2, "field 2 is empty"); Objects.requireNonNull(getter2, "field 2 is empty");
Objects.requireNonNull(getter3, "field 3 is empty"); Objects.requireNonNull(getter3, "field 3 is empty");
@ -218,12 +434,12 @@ public final class HelenusSession extends AbstractSessionOperations implements C
HelenusPropertyNode p1 = MappingUtil.resolveMappingProperty(getter1); HelenusPropertyNode p1 = MappingUtil.resolveMappingProperty(getter1);
HelenusPropertyNode p2 = MappingUtil.resolveMappingProperty(getter2); HelenusPropertyNode p2 = MappingUtil.resolveMappingProperty(getter2);
HelenusPropertyNode p3 = MappingUtil.resolveMappingProperty(getter3); HelenusPropertyNode p3 = MappingUtil.resolveMappingProperty(getter3);
return new SelectOperation<Fun.Tuple3<V1, V2, V3>>(this, return new SelectOperation<Fun.Tuple3<V1, V2, V3>>(
new Mappers.Mapper3<V1, V2, V3>(getValueProvider(), p1, p2, p3), p1, p2, p3); this, new Mappers.Mapper3<V1, V2, V3>(getValueProvider(), p1, p2, p3), p1, p2, p3);
} }
public <V1, V2, V3, V4> SelectOperation<Fun.Tuple4<V1, V2, V3, V4>> select(Getter<V1> getter1, Getter<V2> getter2, public <V1, V2, V3, V4> SelectOperation<Fun.Tuple4<V1, V2, V3, V4>> select(
Getter<V3> getter3, Getter<V4> getter4) { Getter<V1> getter1, Getter<V2> getter2, Getter<V3> getter3, Getter<V4> getter4) {
Objects.requireNonNull(getter1, "field 1 is empty"); Objects.requireNonNull(getter1, "field 1 is empty");
Objects.requireNonNull(getter2, "field 2 is empty"); Objects.requireNonNull(getter2, "field 2 is empty");
Objects.requireNonNull(getter3, "field 3 is empty"); Objects.requireNonNull(getter3, "field 3 is empty");
@ -233,12 +449,21 @@ public final class HelenusSession extends AbstractSessionOperations implements C
HelenusPropertyNode p2 = MappingUtil.resolveMappingProperty(getter2); HelenusPropertyNode p2 = MappingUtil.resolveMappingProperty(getter2);
HelenusPropertyNode p3 = MappingUtil.resolveMappingProperty(getter3); HelenusPropertyNode p3 = MappingUtil.resolveMappingProperty(getter3);
HelenusPropertyNode p4 = MappingUtil.resolveMappingProperty(getter4); HelenusPropertyNode p4 = MappingUtil.resolveMappingProperty(getter4);
return new SelectOperation<Fun.Tuple4<V1, V2, V3, V4>>(this, return new SelectOperation<Fun.Tuple4<V1, V2, V3, V4>>(
new Mappers.Mapper4<V1, V2, V3, V4>(getValueProvider(), p1, p2, p3, p4), p1, p2, p3, p4); this,
new Mappers.Mapper4<V1, V2, V3, V4>(getValueProvider(), p1, p2, p3, p4),
p1,
p2,
p3,
p4);
} }
public <V1, V2, V3, V4, V5> SelectOperation<Fun.Tuple5<V1, V2, V3, V4, V5>> select(Getter<V1> getter1, public <V1, V2, V3, V4, V5> SelectOperation<Fun.Tuple5<V1, V2, V3, V4, V5>> select(
Getter<V2> getter2, Getter<V3> getter3, Getter<V4> getter4, Getter<V5> getter5) { Getter<V1> getter1,
Getter<V2> getter2,
Getter<V3> getter3,
Getter<V4> getter4,
Getter<V5> getter5) {
Objects.requireNonNull(getter1, "field 1 is empty"); Objects.requireNonNull(getter1, "field 1 is empty");
Objects.requireNonNull(getter2, "field 2 is empty"); Objects.requireNonNull(getter2, "field 2 is empty");
Objects.requireNonNull(getter3, "field 3 is empty"); Objects.requireNonNull(getter3, "field 3 is empty");
@ -250,12 +475,23 @@ public final class HelenusSession extends AbstractSessionOperations implements C
HelenusPropertyNode p3 = MappingUtil.resolveMappingProperty(getter3); HelenusPropertyNode p3 = MappingUtil.resolveMappingProperty(getter3);
HelenusPropertyNode p4 = MappingUtil.resolveMappingProperty(getter4); HelenusPropertyNode p4 = MappingUtil.resolveMappingProperty(getter4);
HelenusPropertyNode p5 = MappingUtil.resolveMappingProperty(getter5); HelenusPropertyNode p5 = MappingUtil.resolveMappingProperty(getter5);
return new SelectOperation<Fun.Tuple5<V1, V2, V3, V4, V5>>(this, return new SelectOperation<Fun.Tuple5<V1, V2, V3, V4, V5>>(
new Mappers.Mapper5<V1, V2, V3, V4, V5>(getValueProvider(), p1, p2, p3, p4, p5), p1, p2, p3, p4, p5); this,
new Mappers.Mapper5<V1, V2, V3, V4, V5>(getValueProvider(), p1, p2, p3, p4, p5),
p1,
p2,
p3,
p4,
p5);
} }
public <V1, V2, V3, V4, V5, V6> SelectOperation<Fun.Tuple6<V1, V2, V3, V4, V5, V6>> select(Getter<V1> getter1, public <V1, V2, V3, V4, V5, V6> SelectOperation<Fun.Tuple6<V1, V2, V3, V4, V5, V6>> select(
Getter<V2> getter2, Getter<V3> getter3, Getter<V4> getter4, Getter<V5> getter5, Getter<V6> getter6) { Getter<V1> getter1,
Getter<V2> getter2,
Getter<V3> getter3,
Getter<V4> getter4,
Getter<V5> getter5,
Getter<V6> getter6) {
Objects.requireNonNull(getter1, "field 1 is empty"); Objects.requireNonNull(getter1, "field 1 is empty");
Objects.requireNonNull(getter2, "field 2 is empty"); Objects.requireNonNull(getter2, "field 2 is empty");
Objects.requireNonNull(getter3, "field 3 is empty"); Objects.requireNonNull(getter3, "field 3 is empty");
@ -269,14 +505,26 @@ public final class HelenusSession extends AbstractSessionOperations implements C
HelenusPropertyNode p4 = MappingUtil.resolveMappingProperty(getter4); HelenusPropertyNode p4 = MappingUtil.resolveMappingProperty(getter4);
HelenusPropertyNode p5 = MappingUtil.resolveMappingProperty(getter5); HelenusPropertyNode p5 = MappingUtil.resolveMappingProperty(getter5);
HelenusPropertyNode p6 = MappingUtil.resolveMappingProperty(getter6); HelenusPropertyNode p6 = MappingUtil.resolveMappingProperty(getter6);
return new SelectOperation<Tuple6<V1, V2, V3, V4, V5, V6>>(this, return new SelectOperation<Tuple6<V1, V2, V3, V4, V5, V6>>(
new Mappers.Mapper6<V1, V2, V3, V4, V5, V6>(getValueProvider(), p1, p2, p3, p4, p5, p6), p1, p2, p3, p4, this,
p5, p6); new Mappers.Mapper6<V1, V2, V3, V4, V5, V6>(getValueProvider(), p1, p2, p3, p4, p5, p6),
p1,
p2,
p3,
p4,
p5,
p6);
} }
public <V1, V2, V3, V4, V5, V6, V7> SelectOperation<Fun.Tuple7<V1, V2, V3, V4, V5, V6, V7>> select( public <V1, V2, V3, V4, V5, V6, V7>
Getter<V1> getter1, Getter<V2> getter2, Getter<V3> getter3, Getter<V4> getter4, Getter<V5> getter5, SelectOperation<Fun.Tuple7<V1, V2, V3, V4, V5, V6, V7>> select(
Getter<V6> getter6, Getter<V7> getter7) { Getter<V1> getter1,
Getter<V2> getter2,
Getter<V3> getter3,
Getter<V4> getter4,
Getter<V5> getter5,
Getter<V6> getter6,
Getter<V7> getter7) {
Objects.requireNonNull(getter1, "field 1 is empty"); Objects.requireNonNull(getter1, "field 1 is empty");
Objects.requireNonNull(getter2, "field 2 is empty"); Objects.requireNonNull(getter2, "field 2 is empty");
Objects.requireNonNull(getter3, "field 3 is empty"); Objects.requireNonNull(getter3, "field 3 is empty");
@ -292,9 +540,17 @@ public final class HelenusSession extends AbstractSessionOperations implements C
HelenusPropertyNode p5 = MappingUtil.resolveMappingProperty(getter5); HelenusPropertyNode p5 = MappingUtil.resolveMappingProperty(getter5);
HelenusPropertyNode p6 = MappingUtil.resolveMappingProperty(getter6); HelenusPropertyNode p6 = MappingUtil.resolveMappingProperty(getter6);
HelenusPropertyNode p7 = MappingUtil.resolveMappingProperty(getter7); HelenusPropertyNode p7 = MappingUtil.resolveMappingProperty(getter7);
return new SelectOperation<Fun.Tuple7<V1, V2, V3, V4, V5, V6, V7>>(this, return new SelectOperation<Fun.Tuple7<V1, V2, V3, V4, V5, V6, V7>>(
new Mappers.Mapper7<V1, V2, V3, V4, V5, V6, V7>(getValueProvider(), p1, p2, p3, p4, p5, p6, p7), p1, p2, this,
p3, p4, p5, p6, p7); new Mappers.Mapper7<V1, V2, V3, V4, V5, V6, V7>(
getValueProvider(), p1, p2, p3, p4, p5, p6, p7),
p1,
p2,
p3,
p4,
p5,
p6,
p7);
} }
public CountOperation count() { public CountOperation count() {
@ -306,43 +562,162 @@ public final class HelenusSession extends AbstractSessionOperations implements C
return new CountOperation(this, Helenus.resolve(dsl)); return new CountOperation(this, Helenus.resolve(dsl));
} }
public <V> UpdateOperation update() { public UpdateOperation<ResultSet> update() {
return new UpdateOperation(this); return new UpdateOperation<ResultSet>(this);
} }
public <V> UpdateOperation update(Getter<V> getter, V v) { public <E> UpdateOperation<E> update(Object pojo) {
if (pojo instanceof MapExportable == false) {
throw new HelenusMappingException(
"update of objects that don't implement MapExportable is not yet supported");
}
return new UpdateOperation<E>(this, pojo);
}
public <E> UpdateOperation<E> update(Drafted<E> drafted) {
if (drafted instanceof AbstractEntityDraft == false) {
throw new HelenusMappingException(
"update of draft objects that don't inherit from AbstractEntityDraft is not yet supported");
}
AbstractEntityDraft<E> draft = (AbstractEntityDraft<E>) drafted;
UpdateOperation update = new UpdateOperation<E>(this, draft);
Map<String, Object> map = draft.toMap();
Set<String> mutatedProperties = draft.mutated();
HelenusEntity entity = Helenus.entity(draft.getEntityClass());
// Add all the mutated values contained in the draft.
entity
.getOrderedProperties()
.forEach(
property -> {
switch (property.getColumnType()) {
case PARTITION_KEY:
case CLUSTERING_COLUMN:
break;
default:
String propertyName = property.getPropertyName();
if (mutatedProperties.contains(propertyName)) {
Object value = map.get(propertyName);
Getter<Object> getter =
new Getter<Object>() {
@Override
public Object get() {
throw new DslPropertyException(
new HelenusPropertyNode(property, Optional.empty()));
}
};
update.set(getter, value);
}
}
});
// Add the partition and clustering keys if they were in the draft (normally the
// case).
entity
.getOrderedProperties()
.forEach(
property -> {
switch (property.getColumnType()) {
case PARTITION_KEY:
case CLUSTERING_COLUMN:
String propertyName = property.getPropertyName();
Object value = map.get(propertyName);
Getter<Object> getter =
new Getter<Object>() {
@Override
public Object get() {
throw new DslPropertyException(
new HelenusPropertyNode(property, Optional.empty()));
}
};
update.where(getter, eq(value));
}
});
return update;
}
public <V> UpdateOperation<ResultSet> update(Getter<V> getter, V v) {
Objects.requireNonNull(getter, "field is empty"); Objects.requireNonNull(getter, "field is empty");
Objects.requireNonNull(v, "value is empty"); Objects.requireNonNull(v, "value is empty");
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(getter); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(getter);
return new UpdateOperation(this, p, v); return new UpdateOperation<ResultSet>(this, p, v);
} }
public InsertOperation insert() { public InsertOperation<ResultSet> insert() {
return new InsertOperation(this, true); return new InsertOperation<ResultSet>(this, true);
} }
public InsertOperation insert(Object pojo) { public <T> InsertOperation<T> insert(Class<?> resultType) {
return new InsertOperation<T>(this, resultType, true);
}
public <T> InsertOperation<T> insert(T pojo) {
Objects.requireNonNull(
pojo,
"supplied object must be either an instance of the entity class or a dsl for it, but cannot be null");
HelenusEntity entity = null;
try {
entity = Helenus.resolve(pojo);
} catch (HelenusMappingException e) {
}
if (entity != null) {
return new InsertOperation<T>(this, entity, entity.getMappingInterface(), true);
} else {
return this.<T>insert(pojo, null, null);
}
}
public <T> InsertOperation<T> insert(Drafted draft) {
return insert(draft.build(), draft.mutated(), draft.read());
}
private <T> InsertOperation<T> insert(T pojo, Set<String> mutations, Set<String> read) {
Objects.requireNonNull(pojo, "pojo is empty"); Objects.requireNonNull(pojo, "pojo is empty");
Class<?> iface = MappingUtil.getMappingInterface(pojo); Class<?> iface = MappingUtil.getMappingInterface(pojo);
HelenusEntity entity = Helenus.entity(iface); HelenusEntity entity = Helenus.entity(iface);
return new InsertOperation(this, entity, pojo, true); return new InsertOperation<T>(this, entity, pojo, mutations, read, true);
} }
public InsertOperation upsert() { public InsertOperation<ResultSet> upsert() {
return new InsertOperation(this, false); return new InsertOperation<ResultSet>(this, false);
} }
public InsertOperation upsert(Object pojo) { public <T> InsertOperation<T> upsert(Class<?> resultType) {
return new InsertOperation<T>(this, resultType, false);
}
public <T> InsertOperation<T> upsert(Drafted draft) {
return this.<T>upsert((T) draft.build(), draft.mutated(), draft.read());
}
public <T> InsertOperation<T> upsert(T pojo) {
Objects.requireNonNull(
pojo,
"supplied object must be either an instance of the entity class or a dsl for it, but cannot be null");
HelenusEntity entity = null;
try {
entity = Helenus.resolve(pojo);
} catch (HelenusMappingException e) {
}
if (entity != null) {
return new InsertOperation<T>(this, entity, entity.getMappingInterface(), false);
} else {
return this.<T>upsert(pojo, null, null);
}
}
private <T> InsertOperation<T> upsert(T pojo, Set<String> mutations, Set<String> read) {
Objects.requireNonNull(pojo, "pojo is empty"); Objects.requireNonNull(pojo, "pojo is empty");
Class<?> iface = MappingUtil.getMappingInterface(pojo); Class<?> iface = MappingUtil.getMappingInterface(pojo);
HelenusEntity entity = Helenus.entity(iface); HelenusEntity entity = Helenus.entity(iface);
return new InsertOperation(this, entity, pojo, false); return new InsertOperation<T>(this, entity, pojo, mutations, read, false);
} }
public DeleteOperation delete() { public DeleteOperation delete() {
@ -358,7 +733,14 @@ public final class HelenusSession extends AbstractSessionOperations implements C
return session; return session;
} }
public <E> E dsl(Class<E> iface) {
return Helenus.dsl(iface, getMetadata());
}
public void close() { public void close() {
if (session == null) {
return;
}
if (session.isClosed()) { if (session.isClosed()) {
return; return;
@ -383,23 +765,21 @@ public final class HelenusSession extends AbstractSessionOperations implements C
private void dropSchema() { private void dropSchema() {
sessionRepository.entities().forEach(e -> dropEntity(e)); sessionRepository.entities().forEach(e -> dropEntity(e));
} }
private void dropEntity(HelenusEntity entity) { private void dropEntity(HelenusEntity entity) {
switch (entity.getType()) { switch (entity.getType()) {
case TABLE: case TABLE:
execute(SchemaUtil.dropTable(entity), true); execute(SchemaUtil.dropTable(entity));
break; break;
case UDT: case UDT:
execute(SchemaUtil.dropUserType(entity), true); execute(SchemaUtil.dropUserType(entity));
break; break;
default:
throw new HelenusException("Unknown entity type.");
} }
} }
} }

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,15 +17,12 @@
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) {
@ -38,14 +36,13 @@ public enum HelenusValidator implements PropertyValueValidator {
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.
@ -20,5 +21,4 @@ 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,10 +25,9 @@ import net.helenus.support.Fun;
public final class Mappers { public final class Mappers {
private Mappers() { private Mappers() {}
}
public final static 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;
@ -45,7 +43,7 @@ public final class Mappers {
} }
} }
public final static 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;
@ -59,18 +57,22 @@ public final class Mappers {
@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 final static 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(
ColumnValueProvider provider,
HelenusPropertyNode p1,
HelenusPropertyNode p2,
HelenusPropertyNode p3) { HelenusPropertyNode p3) {
this.provider = provider; this.provider = provider;
this.p1 = p1.getProperty(); this.p1 = p1.getProperty();
@ -80,12 +82,14 @@ public final class Mappers {
@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, 0, p1),
provider.getColumnValue(row, 1, p2),
provider.getColumnValue(row, 2, p3)); provider.getColumnValue(row, 2, p3));
} }
} }
public final static 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;
@ -93,8 +97,12 @@ public final class Mappers {
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,
HelenusPropertyNode p1,
HelenusPropertyNode p2,
HelenusPropertyNode p3,
HelenusPropertyNode p4) {
this.provider = provider; this.provider = provider;
this.p1 = p1.getProperty(); this.p1 = p1.getProperty();
this.p2 = p2.getProperty(); this.p2 = p2.getProperty();
@ -104,18 +112,27 @@ public final class Mappers {
@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 final static 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,
HelenusPropertyNode p1,
HelenusPropertyNode p2,
HelenusPropertyNode p3,
HelenusPropertyNode p4,
HelenusPropertyNode p5) {
this.provider = provider; this.provider = provider;
this.p1 = p1.getProperty(); this.p1 = p1.getProperty();
this.p2 = p2.getProperty(); this.p2 = p2.getProperty();
@ -126,19 +143,29 @@ public final class Mappers {
@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 final static 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,
HelenusPropertyNode p1,
HelenusPropertyNode p2,
HelenusPropertyNode p3,
HelenusPropertyNode p4,
HelenusPropertyNode p5,
HelenusPropertyNode p6) {
this.provider = provider; this.provider = provider;
this.p1 = p1.getProperty(); this.p1 = p1.getProperty();
this.p2 = p2.getProperty(); this.p2 = p2.getProperty();
@ -150,20 +177,30 @@ public final class Mappers {
@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, 2, p3),
provider.getColumnValue(row, 3, p4),
provider.getColumnValue(row, 4, p5),
provider.getColumnValue(row, 5, p6)); provider.getColumnValue(row, 5, p6));
} }
} }
public final static 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 p1,
HelenusPropertyNode p2,
HelenusPropertyNode p3,
HelenusPropertyNode p4,
HelenusPropertyNode p5,
HelenusPropertyNode p6,
HelenusPropertyNode p7) { HelenusPropertyNode p7) {
this.provider = provider; this.provider = provider;
this.p1 = p1.getProperty(); this.p1 = p1.getProperty();
@ -177,11 +214,14 @@ public final class Mappers {
@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,7 +20,6 @@ import java.util.HashMap;
import java.util.Map; import java.util.Map;
public enum Operator { public enum Operator {
EQ("=="), EQ("=="),
IN("in"), IN("in"),
@ -32,9 +32,7 @@ public enum Operator {
LTE("<="); LTE("<=");
private final String name; private static final Map<String, Operator> indexByName = new HashMap<String, Operator>();
private final static Map<String, Operator> indexByName = new HashMap<String, Operator>();
static { static {
for (Operator fo : Operator.values()) { for (Operator fo : Operator.values()) {
@ -42,16 +40,17 @@ public enum Operator {
} }
} }
private final String name;
private Operator(String name) { private Operator(String name) {
this.name = name; this.name = name;
} }
public String getName() {
return name;
}
public static Operator findByOperator(String name) { public static Operator findByOperator(String name) {
return indexByName.get(name); return indexByName.get(name);
} }
public String getName() {
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;
@ -34,16 +32,13 @@ public final class Ordered {
} }
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

@ -0,0 +1,78 @@
package net.helenus.core;
import java.util.List;
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 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 List<CheckedRunnable> commitThunks;
private final List<CheckedRunnable> abortThunks;
private Consumer<? super Throwable> exceptionallyThunk;
private boolean committed;
PostCommitFunction(List<CheckedRunnable> postCommit, List<CheckedRunnable> abortThunks,
Consumer<? super Throwable> exceptionallyThunk,
boolean committed) {
this.commitThunks = postCommit;
this.abortThunks = abortThunks;
this.exceptionallyThunk = exceptionallyThunk;
this.committed = committed;
}
private void apply(CheckedRunnable... fns) {
try {
for (CheckedRunnable fn : fns) {
fn.run();
}
} catch (Throwable t) {
if (exceptionallyThunk != null) {
exceptionallyThunk.accept(t);
}
}
}
public PostCommitFunction<T, R> andThen(CheckedRunnable... after) {
Objects.requireNonNull(after);
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,7 +18,6 @@ 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;
@ -39,10 +39,9 @@ public final class Postulate<V> {
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];
@ -52,25 +51,28 @@ public final class Postulate<V> {
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() {
return values;
} }
@Override @Override
@ -92,11 +94,8 @@ public final class Postulate<V> {
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.
@ -20,5 +21,4 @@ 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,24 +16,17 @@
*/ */
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();
@ -83,7 +77,6 @@ public final class Query {
public V get() { public V get() {
return listGetter.get().get(index); return listGetter.get().get(index);
} }
}; };
} }
@ -97,8 +90,6 @@ public final class Query {
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,24 +16,25 @@
*/ */
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.IndexMetadata; import com.datastax.driver.core.querybuilder.IsNotNullClause;
import com.datastax.driver.core.querybuilder.QueryBuilder;
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.mapping.*; import net.helenus.mapping.*;
import net.helenus.mapping.ColumnType; import net.helenus.mapping.ColumnType;
import net.helenus.mapping.annotation.ClusteringColumn;
import net.helenus.mapping.type.OptionalColumnMetadata; import net.helenus.mapping.type.OptionalColumnMetadata;
import net.helenus.support.CqlUtil; import net.helenus.support.CqlUtil;
import net.helenus.support.HelenusMappingException; 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) {
@ -55,23 +57,31 @@ public final class SchemaUtil {
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 { try {
prop.getDataType().addColumn(create, prop.getColumnName()); prop.getDataType().addColumn(create, prop.getColumnName());
} catch (IllegalArgumentException e) { } catch (IllegalArgumentException e) {
throw new HelenusMappingException("invalid column name '" + prop.getColumnName() + "' in entity '" throw new HelenusMappingException(
+ entity.getName().getName() + "'", e); "invalid column name '"
+ prop.getColumnName()
+ "' in entity '"
+ entity.getName().getName()
+ "'",
e);
} }
} }
return create; return create;
} }
public static List<SchemaStatement> alterUserType(UserType userType, HelenusEntity entity, public static List<SchemaStatement> alterUserType(
boolean dropUnusedColumns) { UserType userType, HelenusEntity entity, boolean dropUnusedColumns) {
if (entity.getType() != HelenusEntityType.UDT) { if (entity.getType() != HelenusEntityType.UDT) {
throw new HelenusMappingException("expected UDT entity " + entity); throw new HelenusMappingException("expected UDT entity " + entity);
@ -80,13 +90,13 @@ public final class SchemaUtil {
List<SchemaStatement> result = new ArrayList<SchemaStatement>(); List<SchemaStatement> result = new ArrayList<SchemaStatement>();
/** /**
* TODO: In future replace SchemaBuilder.alterTable by SchemaBuilder.alterType * TODO: In future replace SchemaBuilder.alterTable by SchemaBuilder.alterType when it will
* when it will exist * exist
*/ */
Alter alter = SchemaBuilder.alterTable(entity.getName().toCql()); Alter alter = SchemaBuilder.alterTable(entity.getName().toCql());
final Set<String> visitedColumns = dropUnusedColumns ? new HashSet<String>() : Collections.<String>emptySet(); final Set<String> visitedColumns =
dropUnusedColumns ? new HashSet<String>() : Collections.<String>emptySet();
for (HelenusProperty prop : entity.getOrderedProperties()) { for (HelenusProperty prop : entity.getOrderedProperties()) {
@ -103,13 +113,13 @@ public final class SchemaUtil {
} }
DataType dataType = userType.getFieldType(columnName); DataType dataType = userType.getFieldType(columnName);
SchemaStatement stmt = prop.getDataType().alterColumn(alter, prop.getColumnName(), SchemaStatement stmt =
optional(columnName, dataType)); prop.getDataType()
.alterColumn(alter, prop.getColumnName(), optional(columnName, dataType));
if (stmt != null) { if (stmt != null) {
result.add(stmt); result.add(stmt);
} }
} }
if (dropUnusedColumns) { if (dropUnusedColumns) {
@ -117,13 +127,11 @@ public final class SchemaUtil {
if (!visitedColumns.contains(field)) { if (!visitedColumns.contains(field)) {
result.add(alter.dropColumn(field)); result.add(alter.dropColumn(field));
} }
} }
} }
return result; return result;
} }
public static SchemaStatement dropUserType(HelenusEntity entity) { public static SchemaStatement dropUserType(HelenusEntity entity) {
@ -133,7 +141,6 @@ public final class SchemaUtil {
} }
return SchemaBuilder.dropType(entity.getName().toCql()).ifExists(); return SchemaBuilder.dropType(entity.getName().toCql()).ifExists();
} }
public static SchemaStatement dropUserType(UserType type) { public static SchemaStatement dropUserType(UserType type) {
@ -141,6 +148,100 @@ public final class SchemaUtil {
return SchemaBuilder.dropType(type.getTypeName()).ifExists(); 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) { public static SchemaStatement createTable(HelenusEntity entity) {
if (entity.getType() != HelenusEntityType.TABLE) { if (entity.getType() != HelenusEntityType.TABLE) {
@ -166,20 +267,19 @@ public final class SchemaUtil {
} }
prop.getDataType().addColumn(create, prop.getColumnName()); prop.getDataType().addColumn(create, prop.getColumnName());
} }
if (!clusteringColumns.isEmpty()) { if (!clusteringColumns.isEmpty()) {
Options options = create.withOptions(); Options options = create.withOptions();
clusteringColumns clusteringColumns.forEach(
.forEach(p -> options.clusteringOrder(p.getColumnName().toCql(), mapDirection(p.getOrdering()))); p -> options.clusteringOrder(p.getColumnName().toCql(), mapDirection(p.getOrdering())));
} }
return create; return create;
} }
public static List<SchemaStatement> alterTable(TableMetadata tmd, HelenusEntity entity, boolean dropUnusedColumns) { public static List<SchemaStatement> alterTable(
TableMetadata tmd, HelenusEntity entity, boolean dropUnusedColumns) {
if (entity.getType() != HelenusEntityType.TABLE) { if (entity.getType() != HelenusEntityType.TABLE) {
throw new HelenusMappingException("expected table entity " + entity); throw new HelenusMappingException("expected table entity " + entity);
@ -189,7 +289,8 @@ public final class SchemaUtil {
Alter alter = SchemaBuilder.alterTable(entity.getName().toCql()); Alter alter = SchemaBuilder.alterTable(entity.getName().toCql());
final Set<String> visitedColumns = dropUnusedColumns ? new HashSet<String>() : Collections.<String>emptySet(); final Set<String> visitedColumns =
dropUnusedColumns ? new HashSet<String>() : Collections.<String>emptySet();
for (HelenusProperty prop : entity.getOrderedProperties()) { for (HelenusProperty prop : entity.getOrderedProperties()) {
@ -206,13 +307,12 @@ public final class SchemaUtil {
} }
ColumnMetadata columnMetadata = tmd.getColumn(columnName); ColumnMetadata columnMetadata = tmd.getColumn(columnName);
SchemaStatement stmt = prop.getDataType().alterColumn(alter, prop.getColumnName(), SchemaStatement stmt =
optional(columnMetadata)); prop.getDataType().alterColumn(alter, prop.getColumnName(), optional(columnMetadata));
if (stmt != null) { if (stmt != null) {
result.add(stmt); result.add(stmt);
} }
} }
if (dropUnusedColumns) { if (dropUnusedColumns) {
@ -220,7 +320,6 @@ public final class SchemaUtil {
if (!visitedColumns.contains(cm.getName())) { if (!visitedColumns.contains(cm.getName())) {
result.add(alter.dropColumn(cm.getName())); result.add(alter.dropColumn(cm.getName()));
} }
} }
} }
@ -235,12 +334,11 @@ public final class SchemaUtil {
} }
return SchemaBuilder.dropTable(entity.getName().toCql()).ifExists(); return SchemaBuilder.dropTable(entity.getName().toCql()).ifExists();
} }
public static SchemaStatement createIndex(HelenusProperty prop) { public static SchemaStatement createIndex(HelenusProperty prop) {
if (prop.caseSensitiveIndex()) { if (prop.caseSensitiveIndex()) {
return SchemaBuilder.createIndex(prop.getIndexName().get().toCql()) return SchemaBuilder.createIndex(indexName(prop))
.ifNotExists() .ifNotExists()
.onTable(prop.getEntity().getName().toCql()) .onTable(prop.getEntity().getName().toCql())
.andColumn(prop.getColumnName().toCql()); .andColumn(prop.getColumnName().toCql());
@ -254,20 +352,28 @@ public final class SchemaUtil {
public static List<SchemaStatement> createIndexes(HelenusEntity entity) { public static List<SchemaStatement> createIndexes(HelenusEntity entity) {
return entity.getOrderedProperties().stream().filter(p -> p.getIndexName().isPresent()) return entity
.map(p -> SchemaUtil.createIndex(p)).collect(Collectors.toList()); .getOrderedProperties()
.stream()
.filter(p -> p.getIndexName().isPresent())
.map(p -> SchemaUtil.createIndex(p))
.collect(Collectors.toList());
} }
public static List<SchemaStatement> alterIndexes(TableMetadata tmd, HelenusEntity entity, public static List<SchemaStatement> alterIndexes(
boolean dropUnusedIndexes) { TableMetadata tmd, HelenusEntity entity, boolean dropUnusedIndexes) {
List<SchemaStatement> list = new ArrayList<SchemaStatement>(); List<SchemaStatement> list = new ArrayList<SchemaStatement>();
final Set<String> visitedColumns = dropUnusedIndexes ? new HashSet<String>() : Collections.<String>emptySet(); final Set<String> visitedColumns =
dropUnusedIndexes ? new HashSet<String>() : Collections.<String>emptySet();
entity.getOrderedProperties().stream().filter(p -> p.getIndexName().isPresent()).forEach(p -> {
entity
.getOrderedProperties()
.stream()
.filter(p -> p.getIndexName().isPresent())
.forEach(
p -> {
String columnName = p.getColumnName().getName(); String columnName = p.getColumnName().getName();
if (dropUnusedIndexes) { if (dropUnusedIndexes) {
@ -284,25 +390,24 @@ public final class SchemaUtil {
} else { } else {
list.add(createIndex(p)); list.add(createIndex(p));
} }
}); });
if (dropUnusedIndexes) { if (dropUnusedIndexes) {
tmd.getColumns().stream().filter(c -> tmd.getIndex(c.getName()) != null && !visitedColumns.contains(c.getName())) tmd.getColumns()
.forEach(c -> { .stream()
.filter(c -> tmd.getIndex(c.getName()) != null && !visitedColumns.contains(c.getName()))
.forEach(
c -> {
list.add(SchemaBuilder.dropIndex(tmd.getIndex(c.getName()).getName()).ifExists()); list.add(SchemaBuilder.dropIndex(tmd.getIndex(c.getName()).getName()).ifExists());
}); });
} }
return list; return list;
} }
public static SchemaStatement dropIndex(HelenusProperty prop) { public static SchemaStatement dropIndex(HelenusProperty prop) {
return SchemaBuilder.dropIndex(prop.getIndexName().get().toCql()).ifExists(); return SchemaBuilder.dropIndex(indexName(prop)).ifExists();
} }
private static SchemaBuilder.Direction mapDirection(OrderingDirection o) { private static SchemaBuilder.Direction mapDirection(OrderingDirection o) {
@ -319,9 +424,11 @@ public final class SchemaUtil {
throw new HelenusMappingException( throw new HelenusMappingException(
"only primitive types and Set,List,Map collections and UserDefinedTypes are allowed, unknown type for property '" "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.getPropertyName()
+ "' type is '"
+ prop.getJavaType()
+ "' in the entity "
+ prop.getEntity()); + prop.getEntity());
} }
private static OptionalColumnMetadata optional(final ColumnMetadata columnMetadata) { private static OptionalColumnMetadata optional(final ColumnMetadata columnMetadata) {
@ -337,7 +444,6 @@ public final class SchemaUtil {
public DataType getType() { public DataType getType() {
return columnMetadata.getType(); return columnMetadata.getType();
} }
}; };
} }
return null; return null;
@ -356,10 +462,13 @@ public final class SchemaUtil {
public DataType getType() { public DataType getType() {
return dataType; return dataType;
} }
}; };
} }
return null; return null;
} }
private static String indexName(HelenusProperty prop) {
return prop.getEntity().getName().toCql() + "_" + prop.getIndexName().get().toCql();
}
} }

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,44 +16,53 @@
*/ */
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 java.util.stream.Collector; import javax.cache.CacheManager;
import java.util.stream.Collectors; import net.helenus.core.reflect.DslExportable;
import java.util.stream.Stream;
import com.datastax.driver.core.*;
import com.google.common.util.concurrent.MoreExecutors;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
import net.helenus.mapping.HelenusEntityType; import net.helenus.mapping.HelenusEntityType;
import net.helenus.mapping.MappingUtil;
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.HelenusException; import net.helenus.support.HelenusException;
import net.helenus.support.PackageUtil; 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 CodecRegistry registry; private CodecRegistry registry;
private String usingKeyspace; private String usingKeyspace;
private boolean showCql = false; private boolean showCql = false;
private boolean showValues = true;
private ConsistencyLevel consistencyLevel;
private boolean idempotent = false;
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 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 final List<Object> initList = new ArrayList<Object>();
private AutoDdl autoDdl = AutoDdl.UPDATE; private AutoDdl autoDdl = AutoDdl.UPDATE;
private CacheManager cacheManager = null;
SessionInitializer(Session session, String keyspace) {
this.session = session;
this.usingKeyspace = keyspace;
if (session != null) {
this.sessionRepository = new SessionRepositoryBuilder(session);
}
}
SessionInitializer(Session session) { SessionInitializer(Session session) {
this.session = Objects.requireNonNull(session, "empty session"); this.session = Objects.requireNonNull(session, "empty session");
@ -100,6 +110,53 @@ public final class SessionInitializer extends AbstractSessionOperations {
return this; return this;
} }
public SessionInitializer showQueryValuesInLog(boolean showValues) {
this.showValues = showValues;
return this;
}
public SessionInitializer showQueryValuesInLog() {
this.showValues = true;
return this;
}
public boolean showValues() {
return showValues;
}
public SessionInitializer metricRegistry(MetricRegistry metricRegistry) {
this.metricRegistry = metricRegistry;
return this;
}
public SessionInitializer consistencyLevel(ConsistencyLevel consistencyLevel) {
this.consistencyLevel = consistencyLevel;
return this;
}
public SessionInitializer setCacheManager(CacheManager cacheManager) {
this.cacheManager = cacheManager;
return this;
}
public ConsistencyLevel getDefaultConsistencyLevel() {
return consistencyLevel;
}
public SessionInitializer setOperationsIdempotentByDefault() {
this.idempotent = true;
return this;
}
public SessionInitializer idempotentQueryExecution(boolean idempotent) {
this.idempotent = idempotent;
return this;
}
public boolean getDefaultQueryIdempotency() {
return idempotent;
}
@Override @Override
public PrintStream getPrintStream() { public PrintStream getPrintStream() {
return printStream; return printStream;
@ -143,8 +200,13 @@ public final class SessionInitializer extends AbstractSessionOperations {
public SessionInitializer addPackage(String packageName) { public SessionInitializer addPackage(String packageName) {
try { try {
PackageUtil.getClasses(packageName).stream().filter(c -> c.isInterface() && !c.isAnnotation()) PackageUtil.getClasses(packageName)
.forEach(initList::add); .stream()
.filter(c -> c.isInterface() && !c.isAnnotation())
.forEach(
clazz -> {
initList.add(Either.right(clazz));
});
} catch (IOException | ClassNotFoundException e) { } catch (IOException | ClassNotFoundException e) {
throw new HelenusException("fail to add package " + packageName, e); throw new HelenusException("fail to add package " + packageName, e);
} }
@ -156,7 +218,7 @@ public final class SessionInitializer extends AbstractSessionOperations {
int len = dsls.length; int len = dsls.length;
for (int i = 0; i != len; ++i) { for (int i = 0; i != len; ++i) {
Object obj = Objects.requireNonNull(dsls[i], "element " + i + " is empty"); Object obj = Objects.requireNonNull(dsls[i], "element " + i + " is empty");
initList.add(obj); initList.add(Either.left(obj));
} }
return this; return this;
} }
@ -187,8 +249,10 @@ public final class SessionInitializer extends AbstractSessionOperations {
} }
public SessionInitializer use(String keyspace) { public SessionInitializer use(String keyspace) {
if (session != null) {
session.execute(SchemaUtil.use(keyspace, false)); session.execute(SchemaUtil.use(keyspace, false));
this.usingKeyspace = keyspace; this.usingKeyspace = keyspace;
}
return this; return this;
} }
@ -199,62 +263,128 @@ public final class SessionInitializer extends AbstractSessionOperations {
} }
public void singleton() { public void singleton() {
Helenus.setSession(get()); Helenus.setSession(get());
} }
public synchronized HelenusSession get() { public synchronized HelenusSession get() {
initialize(); initialize();
return new HelenusSession(session, usingKeyspace, registry, showCql, printStream, sessionRepository, executor, return new HelenusSession(
autoDdl == AutoDdl.CREATE_DROP); session,
usingKeyspace,
registry,
showCql,
showValues,
printStream,
sessionRepository,
executor,
autoDdl == AutoDdl.CREATE_DROP,
consistencyLevel,
idempotent,
cacheManager,
metricRegistry);
} }
private void initialize() { private void initialize() {
Objects.requireNonNull(usingKeyspace, "please define keyspace by 'use' operator"); Objects.requireNonNull(usingKeyspace, "please define keyspace by 'use' operator");
initList.forEach(dsl -> sessionRepository.add(dsl)); initList.forEach(
(either) -> {
Class<?> iface = null;
if (either.isLeft()) {
iface = MappingUtil.getMappingInterface(either.getLeft());
} else {
iface = either.getRight();
}
DslExportable dsl = (DslExportable) Helenus.dsl(iface);
if (session != null) {
dsl.setCassandraMetadataForHelenusSession(session.getCluster().getMetadata());
}
if (sessionRepository != null) {
sessionRepository.add(dsl);
}
});
if (session == null) return;
TableOperations tableOps = new TableOperations(this, dropUnusedColumns, dropUnusedIndexes); TableOperations tableOps = new TableOperations(this, dropUnusedColumns, dropUnusedIndexes);
UserTypeOperations userTypeOps = new UserTypeOperations(this, dropUnusedColumns); UserTypeOperations userTypeOps = new UserTypeOperations(this, dropUnusedColumns);
switch (autoDdl) { switch (autoDdl) {
case CREATE_DROP: case CREATE_DROP:
// Drop tables first, otherwise a `DROP TYPE ...` will fail as the type is still referenced // Drop view first, otherwise a `DROP TABLE ...` will fail as the type is still
// by a table. // referenced
sessionRepository.entities().stream().filter(e -> e.getType() == HelenusEntityType.TABLE) // by a view.
sessionRepository
.entities()
.stream()
.filter(e -> e.getType() == HelenusEntityType.VIEW)
.forEach(e -> tableOps.dropView(e));
// Drop tables second, before DROP TYPE otherwise a `DROP TYPE ...` will fail as
// the type is
// still referenced by a table.
sessionRepository
.entities()
.stream()
.filter(e -> e.getType() == HelenusEntityType.TABLE)
.forEach(e -> tableOps.dropTable(e)); .forEach(e -> tableOps.dropTable(e));
eachUserTypeInReverseOrder(userTypeOps, e -> userTypeOps.dropUserType(e)); eachUserTypeInReverseOrder(userTypeOps, e -> userTypeOps.dropUserType(e));
// FALLTHRU to CREATE case (read: the absence of a `break;` statement here is intentional!) // FALLTHRU to CREATE case (read: the absence of a `break;` statement here is
// intentional!)
case CREATE: case CREATE:
eachUserTypeInOrder(userTypeOps, e -> userTypeOps.createUserType(e)); eachUserTypeInOrder(userTypeOps, e -> userTypeOps.createUserType(e));
sessionRepository.entities().stream().filter(e -> e.getType() == HelenusEntityType.TABLE) sessionRepository
.entities()
.stream()
.filter(e -> e.getType() == HelenusEntityType.TABLE)
.forEach(e -> tableOps.createTable(e)); .forEach(e -> tableOps.createTable(e));
sessionRepository
.entities()
.stream()
.filter(e -> e.getType() == HelenusEntityType.VIEW)
.forEach(e -> tableOps.createView(e));
break; break;
case VALIDATE: case VALIDATE:
eachUserTypeInOrder(userTypeOps, e -> userTypeOps.validateUserType(getUserType(e), e)); eachUserTypeInOrder(userTypeOps, e -> userTypeOps.validateUserType(getUserType(e), e));
sessionRepository.entities().stream().filter(e -> e.getType() == HelenusEntityType.TABLE) sessionRepository
.entities()
.stream()
.filter(e -> e.getType() == HelenusEntityType.TABLE)
.forEach(e -> tableOps.validateTable(getTableMetadata(e), e)); .forEach(e -> tableOps.validateTable(getTableMetadata(e), e));
break; break;
case UPDATE: case UPDATE:
eachUserTypeInOrder(userTypeOps, e -> userTypeOps.updateUserType(getUserType(e), e)); eachUserTypeInOrder(userTypeOps, e -> userTypeOps.updateUserType(getUserType(e), e));
sessionRepository.entities().stream().filter(e -> e.getType() == HelenusEntityType.TABLE) sessionRepository
.forEach(e -> tableOps.updateTable(getTableMetadata(e), e)); .entities()
break; .stream()
.filter(e -> e.getType() == HelenusEntityType.VIEW)
.forEach(e -> tableOps.dropView(e));
sessionRepository
.entities()
.stream()
.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(); KeyspaceMetadata km = getKeyspaceMetadata();
@ -262,42 +392,43 @@ public final class SessionInitializer extends AbstractSessionOperations {
for (UserType userType : km.getUserTypes()) { for (UserType userType : km.getUserTypes()) {
sessionRepository.addUserType(userType.getTypeName(), userType); sessionRepository.addUserType(userType.getTypeName(), userType);
} }
} }
private void eachUserTypeInOrder(UserTypeOperations userTypeOps, Consumer<? super HelenusEntity> action) { private void eachUserTypeInOrder(
UserTypeOperations userTypeOps, Consumer<? super HelenusEntity> action) {
Set<HelenusEntity> processedSet = new HashSet<HelenusEntity>(); Set<HelenusEntity> processedSet = new HashSet<HelenusEntity>();
Set<HelenusEntity> stack = new HashSet<HelenusEntity>(); Set<HelenusEntity> stack = new HashSet<HelenusEntity>();
sessionRepository.entities().stream().filter(e -> e.getType() == HelenusEntityType.UDT).forEach(e -> { sessionRepository
.entities()
.stream()
.filter(e -> e.getType() == HelenusEntityType.UDT)
.forEach(
e -> {
stack.clear(); stack.clear();
eachUserTypeInRecursion(e, processedSet, stack, userTypeOps, action); eachUserTypeInRecursion(e, processedSet, stack, userTypeOps, action);
}); });
} }
private void eachUserTypeInReverseOrder(UserTypeOperations userTypeOps, Consumer<? super HelenusEntity> action) { private void eachUserTypeInReverseOrder(
UserTypeOperations userTypeOps, Consumer<? super HelenusEntity> action) {
ArrayDeque<HelenusEntity> deque = new ArrayDeque<>(); ArrayDeque<HelenusEntity> deque = new ArrayDeque<>();
eachUserTypeInOrder(userTypeOps, e -> deque.addFirst(e)); eachUserTypeInOrder(userTypeOps, e -> deque.addFirst(e));
deque.stream().forEach(e -> {action.accept(e); }); deque
/* .stream()
Set<HelenusEntity> processedSet = new HashSet<HelenusEntity>(); .forEach(
Set<HelenusEntity> stack = new HashSet<HelenusEntity>(); e -> {
action.accept(e);
sessionRepository.entities().stream()
.filter(e -> e.getType() == HelenusEntityType.UDT)
.collect(Collectors.toCollection(ArrayDeque::new))
.descendingIterator()
.forEachRemaining(e -> {
stack.clear();
eachUserTypeInRecursion(e, processedSet, stack, userTypeOps, action);
}); });
*/
} }
private void eachUserTypeInRecursion(HelenusEntity e, Set<HelenusEntity> processedSet, Set<HelenusEntity> stack, private void eachUserTypeInRecursion(
UserTypeOperations userTypeOps, Consumer<? super HelenusEntity> action) { HelenusEntity e,
Set<HelenusEntity> processedSet,
Set<HelenusEntity> stack,
UserTypeOperations userTypeOps,
Consumer<? super HelenusEntity> action) {
stack.add(e); stack.add(e);
@ -314,19 +445,18 @@ public final class SessionInitializer extends AbstractSessionOperations {
action.accept(e); action.accept(e);
processedSet.add(e); processedSet.add(e);
} }
} }
private KeyspaceMetadata getKeyspaceMetadata() { private KeyspaceMetadata getKeyspaceMetadata() {
if (keyspaceMetadata == null) { if (keyspaceMetadata == null) {
keyspaceMetadata = session.getCluster().getMetadata().getKeyspace(usingKeyspace.toLowerCase()); keyspaceMetadata =
session.getCluster().getMetadata().getKeyspace(usingKeyspace.toLowerCase());
} }
return keyspaceMetadata; return keyspaceMetadata;
} }
private TableMetadata getTableMetadata(HelenusEntity entity) { private TableMetadata getTableMetadata(HelenusEntity entity) {
return getKeyspaceMetadata().getTable(entity.getName().getName()); return getKeyspaceMetadata().getTable(entity.getName().getName());
} }
private UserType getUserType(HelenusEntity entity) { private UserType getUserType(HelenusEntity entity) {

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,11 +16,9 @@
*/ */
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 {
@ -32,7 +31,8 @@ public final class SessionRepository {
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) {
@ -42,5 +42,4 @@ public final class SessionRepository {
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,7 +34,8 @@ 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>();
@ -45,7 +45,6 @@ public final class SessionRepositoryBuilder {
private final Session session; private final Session session;
SessionRepositoryBuilder(Session session) { SessionRepositoryBuilder(Session session) {
this.session = session; this.session = session;
} }
@ -85,7 +84,6 @@ public final class SessionRepositoryBuilder {
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) {
@ -101,7 +99,8 @@ public final class SessionRepositoryBuilder {
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);
@ -111,7 +110,6 @@ public final class SessionRepositoryBuilder {
} else { } else {
entity = concurrentEntity; entity = concurrentEntity;
} }
} }
return entity; return entity;
@ -140,13 +138,8 @@ public final class SessionRepositoryBuilder {
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,11 +16,9 @@
*/ */
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;
@ -29,31 +28,30 @@ public final class TableOperations {
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(
AbstractSessionOperations sessionOps, boolean dropUnusedColumns, boolean dropUnusedIndexes) {
this.sessionOps = sessionOps; this.sessionOps = sessionOps;
this.dropUnusedColumns = dropUnusedColumns; this.dropUnusedColumns = dropUnusedColumns;
this.dropUnusedIndexes = dropUnusedIndexes; this.dropUnusedIndexes = dropUnusedIndexes;
} }
public void createTable(HelenusEntity entity) { public void createTable(HelenusEntity entity) {
sessionOps.execute(SchemaUtil.createTable(entity));
sessionOps.execute(SchemaUtil.createTable(entity), true);
executeBatch(SchemaUtil.createIndexes(entity)); executeBatch(SchemaUtil.createIndexes(entity));
} }
public void dropTable(HelenusEntity entity) { public void dropTable(HelenusEntity entity) {
sessionOps.execute(SchemaUtil.dropTable(entity));
sessionOps.execute(SchemaUtil.dropTable(entity), true);
} }
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 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);
@ -62,12 +60,37 @@ public final class TableOperations {
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) {
createTable(entity);
return;
}
executeBatch(SchemaUtil.alterTable(tmd, entity, dropUnusedColumns));
executeBatch(SchemaUtil.alterIndexes(tmd, entity, dropUnusedIndexes));
}
public void createView(HelenusEntity entity) {
sessionOps.execute(
SchemaUtil.createMaterializedView(
sessionOps.usingKeyspace(), entity.getName().toCql(), entity));
// executeBatch(SchemaUtil.createIndexes(entity)); NOTE: Unfortunately C* 3.10 does not yet support 2i on materialized views.
}
public void dropView(HelenusEntity entity) {
sessionOps.execute(
SchemaUtil.dropMaterializedView(
sessionOps.usingKeyspace(), entity.getName().toCql(), entity));
}
public void updateView(TableMetadata tmd, HelenusEntity entity) {
if (tmd == null) { if (tmd == null) {
createTable(entity); createTable(entity);
return; return;
@ -79,10 +102,6 @@ public final class TableOperations {
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,60 +1,816 @@
/*
* 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; package net.helenus.core;
import static net.helenus.core.HelenusSession.deleted;
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.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;
* Encapsulates the concept of a "transaction" as a unit-of-work. import net.helenus.core.cache.Facet;
*/ import net.helenus.core.cache.MapCache;
public class UnitOfWork { 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;
private final HelenusSession session; /** Encapsulates the concept of a "transaction" as a unit-of-work. */
private ArrayList<UnitOfWork> nested; public class UnitOfWork implements AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(UnitOfWork.class);
UnitOfWork(HelenusSession session) { public final UnitOfWork parent;
protected final List<UnitOfWork> nested = new ArrayList<>();
protected final Table<String, String, Either<Object, List<Facet>>> cache = HashBasedTable.create();
protected final EvictTrackingMapCache<String, Object> statementCache;
protected final HelenusSession session;
protected String purpose;
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;
public UnitOfWork(HelenusSession session) {
this(session, null);
}
public UnitOfWork(HelenusSession session, UnitOfWork parent) {
Objects.requireNonNull(session, "containing session cannot be null");
this.parent = parent;
if (parent != null) {
parent.addNestedUnitOfWork(this);
}
this.session = session; this.session = session;
// log.record(txn::start) CacheLoader<String, Object> cacheLoader = null;
if (parent != null) {
cacheLoader =
new CacheLoader<String, Object>() {
Cache<String, Object> cache = parent.getCache();
@Override
public Object load(String key) throws CacheLoaderException {
return cache.get(key);
}
@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);
}
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));
}
}
public void addCacheLookupTime(Stopwatch amount) {
cacheLookupTimeMSecs += amount.elapsed(TimeUnit.MICROSECONDS);
}
public void addNestedUnitOfWork(UnitOfWork uow) {
synchronized (nested) {
nested.add(uow);
}
} }
/** /**
* Marks the beginning of a transactional section of work. Will write a record * Marks the beginning of a transactional section of work. Will write a
* to the shared write-ahead log. * recordCacheAndDatabaseOperationCount to the shared write-ahead log.
* *
* @return the handle used to commit or abort the work. * @return the handle used to commit or abort the work.
*/ */
public UnitOfWork begin() { public synchronized UnitOfWork begin() {
if (nested == null) { elapsedTime.start();
nested = new ArrayList<UnitOfWork>(); // log.record(txn::start)
return this;
} }
UnitOfWork unitOfWork = new UnitOfWork(session);
nested.add(unitOfWork); public String getPurpose() {
return unitOfWork; 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 * Checks to see if the work performed between calling begin and now can be committed or not.
* committed or not.
* *
* @throws ConflictingUnitOfWorkException * @return a function from which to chain work that only happens when commit is successful
* when the work overlaps with other concurrent writers. * @throws HelenusException when the work overlaps with other concurrent writers.
*/ */
public void commit() throws ConflictingUnitOfWorkException { public synchronized PostCommitFunction<Void, Void> commit() throws HelenusException {
// nested.foreach.commit()
// log.record(txn::provisionalCommit) if (isDone()) {
// examine log for conflicts in read-set and write-set between begin and return PostCommitFunction.NULL_ABORT;
// provisional commit }
// if (conflict) { throw new ConflictingUnitOfWorkException(this) }
// 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 discard the work and mark it as as such in the log. * 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 void abort() { 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) // log.record(txn::abort)
// cache.invalidateSince(txn::start time) // 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() { public String describeConflicts() {
return "it's complex..."; 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,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,11 +16,9 @@
*/ */
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;
@ -35,14 +34,12 @@ public final class UserTypeOperations {
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) {
@ -56,9 +53,11 @@ public final class UserTypeOperations {
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) {
@ -69,15 +68,10 @@ public final class UserTypeOperations {
} }
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,3 +1,19 @@
/*
* 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.annotation; package net.helenus.core.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
@ -7,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,17 +1,36 @@
/*
* 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.annotation; package net.helenus.core.annotation;
import java.lang.annotation.ElementType; import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; 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 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; Class<? extends Exception>[] on() default {
ConflictingUnitOfWorkException.class, TimeoutException.class
};
int times() default 3; int times() default 3;
} }

View file

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

View file

@ -0,0 +1,60 @@
/*
* 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.cache;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import net.helenus.mapping.HelenusProperty;
public class BoundFacet extends Facet<String> {
private final Map<HelenusProperty, Object> properties;
public BoundFacet(HelenusProperty property, Object value) {
super(property.getPropertyName(), value == null ? null : value.toString());
this.properties = new HashMap<HelenusProperty, Object>(1);
this.properties.put(property, value);
}
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

@ -0,0 +1,221 @@
package net.helenus.core.cache;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
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 static List<String[]> combinations(List<String> items) {
int n = items.size();
if (n > 20) throw new IllegalArgumentException(n + " is out of range");
long e = Math.round(Math.pow(2, n));
List<String[]> out = new ArrayList<String[]>((int) e - 1);
for (int k = 1; k <= items.size(); k++) {
kCombinations(items, 0, k, new String[k], out);
}
return out;
}
private static void kCombinations(
List<String> items, int n, int k, String[] arr, List<String[]> out) {
if (k == 0) {
out.add(arr.clone());
} else {
for (int i = n; i <= items.size() - k; i++) {
arr[arr.length - k] = items.get(i);
kCombinations(items, i + 1, k - 1, arr, out);
}
}
}
public static List<String> flatKeys(String table, List<Facet> facets) {
return flattenFacets(facets)
.stream()
.map(
combination -> {
return table + "." + Arrays.toString(combination);
})
.collect(Collectors.toList());
}
public static List<String[]> flattenFacets(List<Facet> facets) {
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;
}
/** Merge changed values in the map behind `from` into `to`. */
public static Object merge(Object t, Object f) {
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

@ -0,0 +1,69 @@
/*
* 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.cache;
/** An Entity is identifiable via one or more Facets */
public class Facet<T> {
private final String name;
private T value;
private boolean fixed = false;
private boolean alone = true;
private boolean combined = true;
public Facet(String name) {
this.name = name;
}
public Facet(String name, T value) {
this.name = name;
this.value = value;
}
public String name() {
return name;
}
public T value() {
return value;
}
public Facet setFixed() {
fixed = true;
return this;
}
public boolean 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

@ -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

@ -0,0 +1,95 @@
/*
* 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.cache;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import net.helenus.core.SchemaUtil;
import net.helenus.mapping.HelenusProperty;
public class UnboundFacet extends Facet<String> {
private final List<HelenusProperty> properties;
private final boolean alone;
private final boolean combined;
public UnboundFacet(List<HelenusProperty> properties, boolean alone, boolean combined) {
super(SchemaUtil.createPrimaryKeyPhrase(properties));
this.properties = properties;
this.alone = alone;
this.combined = combined;
}
public UnboundFacet(List<HelenusProperty> properties) {
this(properties, true, true);
}
public UnboundFacet(HelenusProperty property, boolean alone, boolean combined) {
super(property.getPropertyName());
properties = new ArrayList<HelenusProperty>();
properties.add(property);
this.alone = alone;
this.combined = combined;
}
public UnboundFacet(HelenusProperty property) {
this(property, true, true);
}
public List<HelenusProperty> getProperties() {
return properties;
}
public Binder binder() {
return new Binder(name(), properties, alone, combined);
}
public static class Binder {
private final String name;
private final boolean alone;
private final boolean combined;
private final List<HelenusProperty> properties = new ArrayList<HelenusProperty>();
private Map<HelenusProperty, Object> boundProperties = new HashMap<HelenusProperty, Object>();
Binder(String name, List<HelenusProperty> properties, boolean alone, boolean combined) {
this.name = name;
this.properties.addAll(properties);
this.alone = alone;
this.combined = combined;
}
public Binder setValueForProperty(HelenusProperty prop, Object value) {
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,14 +16,15 @@
*/ */
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;
@ -108,4 +110,59 @@ public abstract class AbstractFilterOperation<E, O extends AbstractFilterOperati
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.
@ -15,16 +16,18 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
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 net.helenus.core.*; import net.helenus.core.*;
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 List<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) {
@ -96,9 +99,9 @@ public abstract class AbstractFilterOptionalOperation<E, O extends AbstractFilte
private void addFilter(Filter<?> filter) { private void addFilter(Filter<?> filter) {
if (filters == null) { if (filters == null) {
filters = new LinkedList<Filter<?>>(); filters = new LinkedHashMap<HelenusProperty, Filter<?>>();
} }
filters.add(filter); filters.put(filter.getNode().getProperty(), filter);
} }
private void addIfFilter(Filter<?> filter) { private void addIfFilter(Filter<?> filter) {
@ -107,5 +110,4 @@ public abstract class AbstractFilterOptionalOperation<E, O extends AbstractFilte
} }
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,16 +16,18 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
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 net.helenus.core.*; import net.helenus.core.*;
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 List<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) {
@ -40,7 +43,7 @@ public abstract class AbstractFilterStreamOperation<E, O extends AbstractFilterS
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;
} }
@ -61,7 +64,7 @@ public abstract class AbstractFilterStreamOperation<E, O extends AbstractFilterS
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;
} }
@ -82,7 +85,7 @@ public abstract class AbstractFilterStreamOperation<E, O extends AbstractFilterS
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;
} }
@ -96,9 +99,9 @@ public abstract class AbstractFilterStreamOperation<E, O extends AbstractFilterS
private void addFilter(Filter<?> filter) { private void addFilter(Filter<?> filter) {
if (filters == null) { if (filters == null) {
filters = new LinkedList<Filter<?>>(); filters = new LinkedHashMap<HelenusProperty, Filter<?>>();
} }
filters.add(filter); filters.put(filter.getNode().getProperty(), filter);
} }
private void addIfFilter(Filter<?> filter) { private void addIfFilter(Filter<?> filter) {
@ -107,5 +110,4 @@ public abstract class AbstractFilterStreamOperation<E, O extends AbstractFilterS
} }
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,105 +16,76 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
import com.datastax.driver.core.PreparedStatement; import com.codahale.metrics.Timer;
import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.ResultSetFuture; import java.util.concurrent.CompletableFuture;
import com.google.common.base.Function; import java.util.concurrent.CompletionException;
import com.google.common.util.concurrent.Futures; import java.util.concurrent.TimeoutException;
import com.google.common.util.concurrent.ListenableFuture;
import net.helenus.core.AbstractSessionOperations; import net.helenus.core.AbstractSessionOperations;
import net.helenus.support.Fun; import net.helenus.core.UnitOfWork;
import net.helenus.support.Scala;
import scala.concurrent.Future;
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 abstract E transform(ResultSet resultSet);
public boolean cacheable() {
return false;
}
public String getCacheKey() {
return "";
}
public AbstractOperation(AbstractSessionOperations sessionOperations) { public AbstractOperation(AbstractSessionOperations sessionOperations) {
super(sessionOperations); super(sessionOperations);
} }
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 ListenableFuture<PreparedOperation<E>> prepareAsync() { public E sync() throws TimeoutException {
final Timer.Context context = requestLatency.time();
final O _this = (O) this; try {
ResultSet resultSet =
return Futures.transform(prepareStatementAsync(), new Function<PreparedStatement, PreparedOperation<E>>() { this.execute(
sessionOps, null, queryExecutionTimeout, queryTimeoutUnits, showValues, false);
@Override
public PreparedOperation<E> apply(PreparedStatement preparedStatement) {
return new PreparedOperation<E>(preparedStatement, _this);
}
});
}
public Future<PreparedOperation<E>> prepareFuture() {
return Scala.asFuture(prepareAsync());
}
public E sync() {
ResultSet resultSet = sessionOps.executeAsync(options(buildStatement()), showValues).getUninterruptibly();
E result = transform(resultSet);
if (cacheable()) {
sessionOps.cache(getCacheKey(), result);
}
return result;
}
public ListenableFuture<E> async() {
ResultSetFuture resultSetFuture = sessionOps.executeAsync(options(buildStatement()), showValues);
ListenableFuture<E> future = Futures.transform(resultSetFuture, new Function<ResultSet, E>() {
@Override
public E apply(ResultSet resultSet) {
E result = transform(resultSet);
if (cacheable()) {
sessionOps.cache(getCacheKey(), result);
}
return transform(resultSet); return transform(resultSet);
} finally {
context.stop();
}
} }
}, sessionOps.getExecutor()); public E sync(UnitOfWork uow) throws TimeoutException {
if (uow == null) return sync();
return future; final Timer.Context context = requestLatency.time();
try {
ResultSet resultSet =
execute(sessionOps, uow, queryExecutionTimeout, queryTimeoutUnits, showValues, true);
E result = transform(resultSet);
return result;
} finally {
context.stop();
}
} }
public Future<E> future() { public CompletableFuture<E> async() {
return Scala.asFuture(async()); return CompletableFuture.<E>supplyAsync(
() -> {
try {
return sync();
} catch (TimeoutException ex) {
throw new CompletionException(ex);
}
});
} }
public <A> Future<Fun.Tuple2<E, A>> future(A a) { public CompletableFuture<E> async(UnitOfWork uow) {
return Scala.asFuture(async(), a); if (uow == null) return async();
CompletableFuture<E> f =
CompletableFuture.<E>supplyAsync(
() -> {
try {
return sync();
} catch (TimeoutException ex) {
throw new CompletionException(ex);
} }
});
public <A, B> Future<Fun.Tuple3<E, A, B>> future(A a, B b) { uow.addFuture(f);
return Scala.asFuture(async(), a, b); return f;
} }
public <A, B, C> Future<Fun.Tuple4<E, A, B, C>> future(A a, B b, C c) {
return Scala.asFuture(async(), a, b, c);
}
public <A, B, C, D> Future<Fun.Tuple5<E, A, B, C, D>> future(A a, B b, C c, D d) {
return Scala.asFuture(async(), a, b, c, d);
}
} }

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,25 +16,32 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
import java.util.Optional; import static net.helenus.core.HelenusSession.deleted;
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;
import com.datastax.driver.core.ResultSetFuture;
import com.google.common.base.Function; import com.google.common.base.Function;
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.cache.CacheUtil;
import net.helenus.core.cache.Facet;
import net.helenus.mapping.MappingUtil;
import net.helenus.support.Fun; import net.helenus.support.Fun;
import net.helenus.support.Scala; import org.apache.commons.lang3.SerializationUtils;
import scala.Option;
import scala.Some;
import scala.concurrent.Future;
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);
@ -46,87 +54,202 @@ public abstract class AbstractOptionalOperation<E, O extends AbstractOptionalOpe
} }
public ListenableFuture<PreparedOptionalOperation<E>> prepareAsync() { public ListenableFuture<PreparedOptionalOperation<E>> prepareAsync() {
final O _this = (O) this; final O _this = (O) this;
return Futures.transform(
return Futures.transform(prepareStatementAsync(), prepareStatementAsync(),
new Function<PreparedStatement, PreparedOptionalOperation<E>>() { new Function<PreparedStatement, PreparedOptionalOperation<E>>() {
@Override @Override
public PreparedOptionalOperation<E> apply(PreparedStatement preparedStatement) { public PreparedOptionalOperation<E> apply(PreparedStatement preparedStatement) {
return new PreparedOptionalOperation<E>(preparedStatement, _this); return new PreparedOptionalOperation<E>(preparedStatement, _this);
} }
}); });
} }
public Future<PreparedOptionalOperation<E>> prepareFuture() { public Optional<E> sync() throws TimeoutException {
return Scala.asFuture(prepareAsync()); final Timer.Context context = requestLatency.time();
} try {
Optional<E> result = Optional.empty();
E cacheResult = null;
boolean updateCache = isSessionCacheable() && !ignoreCache();
public Optional<E> sync() { if (updateCache) {
List<Facet> facets = bindFacetValues();
ResultSet resultSet = sessionOps.executeAsync(options(buildStatement()), showValues).getUninterruptibly(); if (facets != null && facets.size() > 0) {
if (facets.stream().filter(f -> !f.fixed()).distinct().count() > 0) {
return transform(resultSet); String tableName = CacheUtil.schemaName(facets);
} cacheResult = (E) sessionOps.checkCache(tableName, facets);
if (cacheResult != null) {
public ListenableFuture<Optional<E>> async() { result = Optional.of(cacheResult);
updateCache = false;
ResultSetFuture resultSetFuture = sessionOps.executeAsync(options(buildStatement()), showValues); sessionCacheHits.mark();
cacheHits.mark();
ListenableFuture<Optional<E>> future = Futures.transform(resultSetFuture,
new Function<ResultSet, Optional<E>>() {
@Override
public Optional<E> apply(ResultSet resultSet) {
return transform(resultSet);
}
}, sessionOps.getExecutor());
return future;
}
public ListenableFuture<Option<E>> asyncForScala() {
ResultSetFuture resultSetFuture = sessionOps.executeAsync(options(buildStatement()), showValues);
ListenableFuture<Option<E>> future = Futures.transform(resultSetFuture, new Function<ResultSet, Option<E>>() {
@Override
public Option<E> apply(ResultSet resultSet) {
Optional<E> optional = transform(resultSet);
if (optional.isPresent()) {
return new Some<E>(optional.get());
} else { } else {
return Option.empty(); sessionCacheMiss.mark();
cacheMiss.mark();
}
}
} else {
//TODO(gburd): look in statement cache for results
} }
} }
}, sessionOps.getExecutor()); if (!result.isPresent()) {
// Formulate the query and execute it against the Cassandra cluster.
ResultSet resultSet =
this.execute(
sessionOps,
null,
queryExecutionTimeout,
queryTimeoutUnits,
showValues,
isSessionCacheable());
return future; // Transform the query result set into the desired shape.
} result = transform(resultSet);
public Future<Option<E>> future() {
return Scala.asFuture(asyncForScala());
} }
public <A> Future<Fun.Tuple2<Option<E>, A>> future(A a) { if (updateCache && result.isPresent()) {
return Scala.asFuture(asyncForScala(), a); E r = result.get();
Class<?> resultClass = r.getClass();
if (!(resultClass.getEnclosingClass() != null
&& resultClass.getEnclosingClass() == Fun.class)) {
List<Facet> facets = getFacets();
if (facets != null && facets.size() > 1) {
sessionOps.updateCache(r, facets);
}
}
}
return result;
} finally {
context.stop();
}
} }
public <A, B> Future<Fun.Tuple3<Option<E>, A, B>> future(A a, B b) { public Optional<E> sync(UnitOfWork uow) throws TimeoutException {
return Scala.asFuture(asyncForScala(), a, b); if (uow == null) return sync();
final Timer.Context context = requestLatency.time();
try {
Optional<E> result = Optional.empty();
E cachedResult = null;
final boolean updateCache;
if (!ignoreCache()) {
Stopwatch timer = Stopwatch.createStarted();
try {
List<Facet> facets = bindFacetValues();
if (facets != null && facets.size() > 0) {
if (facets.stream().filter(f -> !f.fixed()).distinct().count() > 0) {
cachedResult = checkCache(uow, facets);
if (cachedResult != null) {
updateCache = false;
result = Optional.of(cachedResult);
uowCacheHits.mark();
cacheHits.mark();
uow.recordCacheAndDatabaseOperationCount(1, 0);
} else {
uowCacheMiss.mark();
if (isSessionCacheable()) {
String tableName = CacheUtil.schemaName(facets);
cachedResult = (E) sessionOps.checkCache(tableName, facets);
if (cachedResult != null) {
Class<?> iface = MappingUtil.getMappingInterface(cachedResult);
if (Helenus.entity(iface).isDraftable()) {
result = Optional.of(cachedResult);
} else {
result =
Optional.of(
(E)
SerializationUtils.<Serializable>clone(
(Serializable) cachedResult));
}
updateCache = false;
sessionCacheHits.mark();
cacheHits.mark();
uow.recordCacheAndDatabaseOperationCount(1, 0);
} else {
updateCache = true;
sessionCacheMiss.mark();
cacheMiss.mark();
uow.recordCacheAndDatabaseOperationCount(-1, 0);
}
} 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;
} }
public <A, B, C> Future<Fun.Tuple4<Option<E>, A, B, C>> future(A a, B b, C c) { // Check to see if we fetched the object from the cache
return Scala.asFuture(asyncForScala(), a, b, c); if (result.isPresent()) {
// If we fetched the `deleted` object then the result is null (really
// Optional.empty()).
if (result.get() == deleted) {
result = Optional.empty();
}
} else {
// Formulate the query and execute it against the Cassandra cluster.
ResultSet resultSet =
execute(sessionOps, uow, queryExecutionTimeout, queryTimeoutUnits, showValues, true);
// Transform the query result set into the desired shape.
result = transform(resultSet);
} }
public <A, B, C, D> Future<Fun.Tuple5<Option<E>, A, B, C, D>> future(A a, B b, C c, D d) { // If we have a result, it wasn't from the UOW cache, and we're caching things
return Scala.asFuture(asyncForScala(), a, b, c, d); // then we need to put this result into the cache for future requests to find.
if (updateCache && result.isPresent()) {
E r = result.get();
if (!(r instanceof Fun) && r != deleted) {
cacheUpdate(uow, r, getFacets());
}
} }
return result;
} finally {
context.stop();
}
}
public CompletableFuture<Optional<E>> async() {
return CompletableFuture.<Optional<E>>supplyAsync(
() -> {
try {
return sync();
} catch (TimeoutException ex) {
throw new CompletionException(ex);
}
});
}
public CompletableFuture<Optional<E>> async(UnitOfWork uow) {
if (uow == null) return async();
CompletableFuture<Optional<E>> f =
CompletableFuture.<Optional<E>>supplyAsync(
() -> {
try {
return sync();
} 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,9 +16,6 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
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;
@ -28,30 +26,47 @@ 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 java.util.List;
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.cache.Facet;
import net.helenus.core.cache.UnboundFacet;
import net.helenus.core.reflect.MapExportable;
import net.helenus.mapping.HelenusProperty;
import net.helenus.mapping.value.BeanColumnValueProvider;
import net.helenus.support.HelenusException; import net.helenus.support.HelenusException;
import net.helenus.support.Scala;
import scala.concurrent.Future;
public abstract class AbstractStatementOperation<E, O extends AbstractStatementOperation<E, O>> { public abstract class AbstractStatementOperation<E, O extends AbstractStatementOperation<E, O>>
extends Operation<E> {
final Logger logger = LoggerFactory.getLogger(getClass()); private boolean ignoreCache = false;
protected final AbstractSessionOperations sessionOps;
public abstract Statement buildStatement();
protected boolean showValues = true;
private ConsistencyLevel consistencyLevel; private ConsistencyLevel consistencyLevel;
private ConsistencyLevel serialConsistencyLevel; private ConsistencyLevel serialConsistencyLevel;
private RetryPolicy retryPolicy; private RetryPolicy retryPolicy;
private boolean enableTracing = false; private boolean enableTracing = false;
private long[] defaultTimestamp = null; private long[] defaultTimestamp = null;
private int[] fetchSize = null; private int[] fetchSize = null;
protected boolean idempotent = false;
public AbstractStatementOperation(AbstractSessionOperations sessionOperations) { public AbstractStatementOperation(AbstractSessionOperations sessionOperations) {
this.sessionOps = sessionOperations; super(sessionOperations);
this.consistencyLevel = sessionOperations.getDefaultConsistencyLevel();
this.idempotent = sessionOperations.getDefaultQueryIdempotency();
}
public abstract Statement buildStatement(boolean cached);
public O uncached(boolean enabled) {
ignoreCache = !enabled;
return (O) this;
}
public O uncached() {
ignoreCache = true;
return (O) this;
} }
public O showValues(boolean enabled) { public O showValues(boolean enabled) {
@ -75,6 +90,16 @@ public abstract class AbstractStatementOperation<E, O extends AbstractStatementO
return (O) this; return (O) this;
} }
public O idempotent() {
this.idempotent = true;
return (O) this;
}
public O isIdempotent(boolean idempotent) {
this.idempotent = idempotent;
return (O) this;
}
public O downgradingConsistencyRetryPolicy() { public O downgradingConsistencyRetryPolicy() {
this.retryPolicy = DowngradingConsistencyRetryPolicy.INSTANCE; this.retryPolicy = DowngradingConsistencyRetryPolicy.INSTANCE;
return (O) this; return (O) this;
@ -110,6 +135,21 @@ public abstract class AbstractStatementOperation<E, O extends AbstractStatementO
return (O) this; return (O) this;
} }
public O consistencyLocalOne() {
this.consistencyLevel = ConsistencyLevel.LOCAL_ONE;
return (O) this;
}
public O consistencyLocalQuorum() {
this.consistencyLevel = ConsistencyLevel.LOCAL_QUORUM;
return (O) this;
}
public O consistencyEachQuorum() {
this.consistencyLevel = ConsistencyLevel.EACH_QUORUM;
return (O) this;
}
public O serialConsistency(ConsistencyLevel level) { public O serialConsistency(ConsistencyLevel level) {
this.serialConsistencyLevel = level; this.serialConsistencyLevel = level;
return (O) this; return (O) this;
@ -135,6 +175,16 @@ public abstract class AbstractStatementOperation<E, O extends AbstractStatementO
return (O) this; return (O) this;
} }
public O serialConsistencyLocal() {
this.serialConsistencyLevel = ConsistencyLevel.LOCAL_SERIAL;
return (O) this;
}
public O serialConsistencyLocalQuorum() {
this.serialConsistencyLevel = ConsistencyLevel.LOCAL_QUORUM;
return (O) this;
}
public O disableTracing() { public O disableTracing() {
this.enableTracing = false; this.enableTracing = false;
return (O) this; return (O) this;
@ -156,7 +206,19 @@ public abstract class AbstractStatementOperation<E, O extends AbstractStatementO
return (O) this; return (O) this;
} }
protected Statement options(Statement statement) { public O queryTimeoutMs(long ms) {
this.queryExecutionTimeout = ms;
this.queryTimeoutUnits = TimeUnit.MILLISECONDS;
return (O) this;
}
public O queryTimeout(long timeout, TimeUnit units) {
this.queryExecutionTimeout = timeout;
this.queryTimeoutUnits = units;
return (O) this;
}
public Statement options(Statement statement) {
if (defaultTimestamp != null) { if (defaultTimestamp != null) {
statement.setDefaultTimestamp(defaultTimestamp[0]); statement.setDefaultTimestamp(defaultTimestamp[0]);
@ -184,15 +246,24 @@ public abstract class AbstractStatementOperation<E, O extends AbstractStatementO
statement.setFetchSize(fetchSize[0]); statement.setFetchSize(fetchSize[0]);
} }
if (isIdempotentOperation()) {
statement.setIdempotent(true);
}
return statement; return statement;
} }
@Override
protected boolean isIdempotentOperation() {
return idempotent;
}
public Statement statement() { public Statement statement() {
return buildStatement(); return buildStatement(false);
} }
public String cql() { public String cql() {
Statement statement = buildStatement(); Statement statement = buildStatement(false);
if (statement == null) return ""; if (statement == null) return "";
if (statement instanceof BuiltStatement) { if (statement instanceof BuiltStatement) {
BuiltStatement buildStatement = (BuiltStatement) statement; BuiltStatement buildStatement = (BuiltStatement) statement;
@ -204,7 +275,7 @@ public abstract class AbstractStatementOperation<E, O extends AbstractStatementO
public PreparedStatement prepareStatement() { public PreparedStatement prepareStatement() {
Statement statement = buildStatement(); Statement statement = buildStatement(true);
if (statement instanceof RegularStatement) { if (statement instanceof RegularStatement) {
@ -218,21 +289,68 @@ public abstract class AbstractStatementOperation<E, O extends AbstractStatementO
public ListenableFuture<PreparedStatement> prepareStatementAsync() { public ListenableFuture<PreparedStatement> prepareStatementAsync() {
Statement statement = buildStatement(); Statement statement = buildStatement(true);
if (statement instanceof RegularStatement) { if (statement instanceof RegularStatement) {
RegularStatement regularStatement = (RegularStatement) statement; RegularStatement regularStatement = (RegularStatement) statement;
return sessionOps.prepareAsync(regularStatement); return sessionOps.prepareAsync(regularStatement);
} }
throw new HelenusException("only RegularStatements can be prepared"); throw new HelenusException("only RegularStatements can be prepared");
} }
public Future<PreparedStatement> prepareStatementFuture() { protected boolean ignoreCache() {
return Scala.asFuture(prepareStatementAsync()); return ignoreCache;
} }
protected E checkCache(UnitOfWork uow, List<Facet> facets) {
E result = null;
Optional<Object> optionalCachedResult = Optional.empty();
if (!facets.isEmpty()) {
optionalCachedResult = uow.cacheLookup(facets);
if (optionalCachedResult.isPresent()) {
result = (E) optionalCachedResult.get();
}
}
return result;
}
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;
for (Facet facet : identifyingFacets) {
if (facet instanceof UnboundFacet) {
UnboundFacet unboundFacet = (UnboundFacet) facet;
UnboundFacet.Binder binder = unboundFacet.binder();
for (HelenusProperty prop : unboundFacet.getProperties()) {
Object value;
if (valueMap == null) {
value = BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false);
if (value != null) {
binder.setValueForProperty(prop, value.toString());
}
} else {
value = valueMap.get(prop.getPropertyName());
if (value != null) {
binder.setValueForProperty(prop, value.toString());
}
}
}
if (binder.isBound()) {
facets.add(binder.bind());
}
} else {
facets.add(facet);
}
}
// Cache the value (pojo), the statement key, and the fully bound facets.
return 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.
@ -15,23 +16,33 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
import java.util.stream.Stream; import static net.helenus.core.HelenusSession.deleted;
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;
import com.datastax.driver.core.ResultSetFuture;
import com.google.common.base.Function; import com.google.common.base.Function;
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.cache.CacheUtil;
import net.helenus.core.cache.Facet;
import net.helenus.mapping.MappingUtil;
import net.helenus.support.Fun; import net.helenus.support.Fun;
import net.helenus.support.Scala; import org.apache.commons.lang3.SerializationUtils;
import scala.concurrent.Future;
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);
@ -44,85 +55,207 @@ public abstract class AbstractStreamOperation<E, O extends AbstractStreamOperati
} }
public ListenableFuture<PreparedStreamOperation<E>> prepareAsync() { public ListenableFuture<PreparedStreamOperation<E>> prepareAsync() {
final O _this = (O) this; final O _this = (O) this;
return Futures.transform(
return Futures.transform(prepareStatementAsync(), prepareStatementAsync(),
new Function<PreparedStatement, PreparedStreamOperation<E>>() { new Function<PreparedStatement, PreparedStreamOperation<E>>() {
@Override @Override
public PreparedStreamOperation<E> apply(PreparedStatement preparedStatement) { public PreparedStreamOperation<E> apply(PreparedStatement preparedStatement) {
return new PreparedStreamOperation<E>(preparedStatement, _this); return new PreparedStreamOperation<E>(preparedStatement, _this);
} }
}); });
} }
public Future<PreparedStreamOperation<E>> prepareFuture() { public Stream<E> sync() throws TimeoutException {
return Scala.asFuture(prepareAsync()); final Timer.Context context = requestLatency.time();
try {
Stream<E> resultStream = null;
E cacheResult = null;
boolean updateCache = isSessionCacheable();
if (!ignoreCache() && isSessionCacheable()) {
List<Facet> facets = bindFacetValues();
if (facets != null && facets.size() > 0) {
if (facets.stream().filter(f -> !f.fixed()).distinct().count() > 0) {
String tableName = CacheUtil.schemaName(facets);
cacheResult = (E) sessionOps.checkCache(tableName, facets);
if (cacheResult != null) {
resultStream = Stream.of(cacheResult);
updateCache = false;
sessionCacheHits.mark();
cacheHits.mark();
} else {
sessionCacheMiss.mark();
cacheMiss.mark();
}
} else {
//TODO(gburd): look in statement cache for results
}
}
} }
public Stream<E> sync() { if (resultStream == null) {
// Formulate the query and execute it against the Cassandra cluster.
ResultSet resultSet =
this.execute(
sessionOps,
null,
queryExecutionTimeout,
queryTimeoutUnits,
showValues,
isSessionCacheable());
ResultSet resultSet = sessionOps.executeAsync(options(buildStatement()), showValues).getUninterruptibly(); // Transform the query result set into the desired shape.
resultStream = transform(resultSet);
return transform(resultSet);
} }
public ListenableFuture<Stream<E>> async() { if (updateCache && resultStream != null) {
List<Facet> facets = getFacets();
if (facets != null && facets.size() > 1) {
List<E> again = new ArrayList<>();
resultStream.forEach(
result -> {
Class<?> resultClass = result.getClass();
if (!(resultClass.getEnclosingClass() != null
&& resultClass.getEnclosingClass() == Fun.class)) {
sessionOps.updateCache(result, facets);
}
again.add(result);
});
resultStream = again.stream();
}
}
return resultStream;
ResultSetFuture resultSetFuture = sessionOps.executeAsync(options(buildStatement()), showValues); } finally {
context.stop();
ListenableFuture<Stream<E>> future = Futures.transform(resultSetFuture, new Function<ResultSet, Stream<E>>() { }
@Override
public Stream<E> apply(ResultSet resultSet) {
return transform(resultSet);
} }
}, sessionOps.getExecutor()); public Stream<E> sync(UnitOfWork uow) throws TimeoutException {
if (uow == null) return sync();
return future; final Timer.Context context = requestLatency.time();
try {
Stream<E> resultStream = null;
E cachedResult = null;
final boolean updateCache;
if (!ignoreCache()) {
Stopwatch timer = Stopwatch.createStarted();
try {
List<Facet> facets = bindFacetValues();
if (facets != null && facets.size() > 0) {
if (facets.stream().filter(f -> !f.fixed()).distinct().count() > 0) {
cachedResult = checkCache(uow, facets);
if (cachedResult != null) {
updateCache = false;
resultStream = Stream.of(cachedResult);
uowCacheHits.mark();
cacheHits.mark();
uow.recordCacheAndDatabaseOperationCount(1, 0);
} else {
uowCacheMiss.mark();
if (isSessionCacheable()) {
String tableName = CacheUtil.schemaName(facets);
cachedResult = (E) sessionOps.checkCache(tableName, facets);
if (cachedResult != null) {
Class<?> iface = MappingUtil.getMappingInterface(cachedResult);
E result = null;
if (Helenus.entity(iface).isDraftable()) {
result = cachedResult;
} else {
result =
(E) SerializationUtils.<Serializable>clone((Serializable) cachedResult);
}
updateCache = false;
resultStream = Stream.of(result);
sessionCacheHits.mark();
cacheHits.mark();
uow.recordCacheAndDatabaseOperationCount(1, 0);
} else {
updateCache = true;
sessionCacheMiss.mark();
cacheMiss.mark();
uow.recordCacheAndDatabaseOperationCount(-1, 0);
}
} 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;
} }
public ListenableFuture<scala.collection.immutable.Stream<E>> asyncForScala() { // Check to see if we fetched the object from the cache
if (resultStream == null) {
ResultSetFuture resultSetFuture = sessionOps.executeAsync(options(buildStatement()), showValues); ResultSet resultSet =
execute(sessionOps, uow, queryExecutionTimeout, queryTimeoutUnits, showValues, true);
ListenableFuture<scala.collection.immutable.Stream<E>> future = Futures.transform(resultSetFuture, resultStream = transform(resultSet);
new Function<ResultSet, scala.collection.immutable.Stream<E>>() {
@Override
public scala.collection.immutable.Stream<E> apply(ResultSet resultSet) {
Stream<E> stream = transform(resultSet);
return scala.collection.JavaConversions.asScalaIterator(stream.iterator()).toStream();
} }
}, sessionOps.getExecutor()); // If we have a result and we're caching then we need to put it into the cache
// for future requests to find.
return future; if (resultStream != null) {
if (updateCache) {
List<E> again = new ArrayList<>();
List<Facet> facets = getFacets();
resultStream.forEach(
result -> {
Class<?> resultClass = result.getClass();
if (result != deleted
&& !(resultClass.getEnclosingClass() != null
&& resultClass.getEnclosingClass() == Fun.class)) {
result = (E) cacheUpdate(uow, result, facets);
}
again.add(result);
});
resultStream = again.stream();
}
} }
public Future<scala.collection.immutable.Stream<E>> future() { return resultStream;
return Scala.asFuture(asyncForScala()); } finally {
context.stop();
}
} }
public <A> Future<Fun.Tuple2<scala.collection.immutable.Stream<E>, A>> future(A a) { public CompletableFuture<Stream<E>> async() {
return Scala.asFuture(asyncForScala(), a); return CompletableFuture.<Stream<E>>supplyAsync(
() -> {
try {
return sync();
} catch (TimeoutException ex) {
throw new CompletionException(ex);
}
});
} }
public <A, B> Future<Fun.Tuple3<scala.collection.immutable.Stream<E>, A, B>> future(A a, B b) { public CompletableFuture<Stream<E>> async(UnitOfWork uow) {
return Scala.asFuture(asyncForScala(), a, b); if (uow == null) return async();
CompletableFuture<Stream<E>> f =
CompletableFuture.<Stream<E>>supplyAsync(
() -> {
try {
return sync();
} catch (TimeoutException ex) {
throw new CompletionException(ex);
} }
});
public <A, B, C> Future<Fun.Tuple4<scala.collection.immutable.Stream<E>, A, B, C>> future(A a, B b, C c) { uow.addFuture(f);
return Scala.asFuture(asyncForScala(), a, b, c); return f;
} }
public <A, B, C, D> Future<Fun.Tuple5<scala.collection.immutable.Stream<E>, A, B, C, D>> future(A a, B b, C c,
D d) {
return Scala.asFuture(asyncForScala(), a, b, c, d);
}
} }

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.
@ -36,8 +37,12 @@ public final class BoundOperation<E> extends AbstractOperation<E, BoundOperation
} }
@Override @Override
public Statement buildStatement() { public Statement buildStatement(boolean cached) {
return boundStatement; return boundStatement;
} }
@Override
public boolean 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,18 +16,19 @@
*/ */
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(
BoundStatement boundStatement, AbstractOptionalOperation<E, ?> operation) {
super(operation.sessionOps); super(operation.sessionOps);
this.boundStatement = boundStatement; this.boundStatement = boundStatement;
this.delegate = operation; this.delegate = operation;
@ -38,8 +40,12 @@ public final class BoundOptionalOperation<E> extends AbstractOptionalOperation<E
} }
@Override @Override
public Statement buildStatement() { public Statement buildStatement(boolean cached) {
return boundStatement; return boundStatement;
} }
@Override
public boolean 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,31 +16,43 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
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;
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(
BoundStatement boundStatement, AbstractStreamOperation<E, ?> operation) {
super(operation.sessionOps); super(operation.sessionOps);
this.boundStatement = boundStatement; this.boundStatement = boundStatement;
this.delegate = operation; this.delegate = operation;
} }
@Override
public List<Facet> 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() { public Statement buildStatement(boolean cached) {
return boundStatement; return boundStatement;
} }
@Override
public boolean 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;
@ -38,10 +38,11 @@ public final class CountOperation extends AbstractFilterOperation<Long, CountOpe
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() { 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()));
@ -74,9 +75,11 @@ public final class CountOperation extends AbstractFilterOperation<Long, CountOpe
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.
@ -20,11 +21,15 @@ 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.cache.Facet;
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.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> {
@ -47,7 +52,7 @@ public final class DeleteOperation extends AbstractFilterOperation<ResultSet, De
} }
@Override @Override
public BuiltStatement buildStatement() { 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()));
@ -118,8 +123,53 @@ public final class DeleteOperation extends AbstractFilterOperation<ResultSet, De
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() {
return bindFacetValues(getFacets());
}
protected boolean isIdempotentOperation() {
return true;
}
@Override
public ResultSet sync() throws TimeoutException {
ResultSet result = super.sync();
if (entity.isCacheable()) {
sessionOps.cacheEvict(bindFacetValues());
}
return result;
}
@Override
public ResultSet sync(UnitOfWork uow) throws TimeoutException {
if (uow == null) {
return sync();
}
ResultSet result = super.sync(uow);
uow.cacheEvict(bindFacetValues());
return result;
}
public ResultSet batch(UnitOfWork uow) throws TimeoutException {
if (uow == null) {
throw new HelenusException("UnitOfWork cannot be null when batching operations.");
}
uow.cacheEvict(bindFacetValues());
uow.batch(this);
return null;
}
@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,59 +16,110 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
import java.util.*;
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 com.google.common.base.Predicates; import java.util.concurrent.TimeoutException;
import com.google.common.collect.Lists; import java.util.function.Function;
import com.google.common.collect.Maps; 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.UnitOfWork;
import net.helenus.core.cache.CacheUtil;
import net.helenus.core.cache.Facet;
import net.helenus.core.cache.UnboundFacet;
import net.helenus.core.reflect.DefaultPrimitiveTypes;
import net.helenus.core.reflect.HelenusPropertyNode; import net.helenus.core.reflect.HelenusPropertyNode;
import net.helenus.core.reflect.MapExportable; import net.helenus.core.reflect.MapExportable;
import net.helenus.mapping.ColumnType;
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;
import net.helenus.mapping.value.BeanColumnValueProvider; import net.helenus.mapping.value.BeanColumnValueProvider;
import net.helenus.support.Fun; import net.helenus.support.Fun;
import net.helenus.support.HelenusException;
import net.helenus.support.HelenusMappingException; import net.helenus.support.HelenusMappingException;
public final class InsertOperation extends AbstractOperation<ResultSet, InsertOperation> { 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 T pojo;
private final Class<?> resultType;
private final Set<String> readSet;
private HelenusEntity entity; private HelenusEntity entity;
private final List<Fun.Tuple2<HelenusPropertyNode, Object>> values = new ArrayList<Fun.Tuple2<HelenusPropertyNode, Object>>();
private boolean ifNotExists; 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.pojo = null;
this.readSet = null;
this.ifNotExists = ifNotExists; this.ifNotExists = ifNotExists;
this.resultType = ResultSet.class;
} }
public InsertOperation(AbstractSessionOperations sessionOperations, HelenusEntity entity, Object pojo, public InsertOperation(
AbstractSessionOperations sessionOperations,
HelenusEntity entity,
Class<?> resultType,
boolean ifNotExists) { boolean ifNotExists) {
super(sessionOperations); super(sessionOperations);
this.pojo = null;
this.readSet = null;
this.ifNotExists = ifNotExists;
this.resultType = resultType;
this.entity = entity;
}
public InsertOperation(
AbstractSessionOperations sessionOperations, Class<?> resultType, boolean ifNotExists) {
super(sessionOperations);
this.pojo = null;
this.readSet = null;
this.ifNotExists = ifNotExists;
this.resultType = resultType;
}
public InsertOperation(
AbstractSessionOperations sessionOperations,
HelenusEntity entity,
T pojo,
Set<String> mutations,
Set<String> read,
boolean ifNotExists) {
super(sessionOperations);
this.pojo = pojo;
this.readSet = read;
this.entity = entity; this.entity = entity;
this.ifNotExists = ifNotExists; this.ifNotExists = ifNotExists;
Set<String> keys = (pojo instanceof MapExportable) ? ((MapExportable)pojo).toMap().keySet() : null; this.resultType = entity.getMappingInterface();
Collection<HelenusProperty> properties = entity.getOrderedProperties(); Collection<HelenusProperty> properties = entity.getOrderedProperties();
Set<String> keys = (mutations == null) ? null : mutations;
for (HelenusProperty prop : properties) { for (HelenusProperty prop : properties) {
boolean addProp = false;
// Skip properties that are not in the map of the pojo we're storing. This creates a path switch (prop.getColumnType()) {
// for entity instances to be {in,up}serted without including all columns in the INSERT statement. case PARTITION_KEY:
if (keys == null || keys.contains(prop.getPropertyName())) { case CLUSTERING_COLUMN:
addProp = true;
break;
default:
addProp = (keys == null || keys.contains(prop.getPropertyName()));
}
if (addProp) {
Object value = BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop); Object value = BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop);
value = sessionOps.getValuePreparer().prepareColumnValue(value, prop); value = sessionOps.getValuePreparer().prepareColumnValue(value, prop);
@ -75,24 +127,21 @@ public final class InsertOperation extends AbstractOperation<ResultSet, InsertOp
HelenusPropertyNode node = new HelenusPropertyNode(prop, Optional.empty()); HelenusPropertyNode node = new HelenusPropertyNode(prop, Optional.empty());
values.add(Fun.Tuple2.of(node, value)); values.add(Fun.Tuple2.of(node, value));
} }
}
}
} }
} public InsertOperation<T> ifNotExists() {
}
public InsertOperation ifNotExists() {
this.ifNotExists = true; this.ifNotExists = true;
return this; return this;
} }
public InsertOperation ifNotExists(boolean enable) { public InsertOperation<T> ifNotExists(boolean enable) {
this.ifNotExists = enable; this.ifNotExists = enable;
return this; return this;
} }
public <V> InsertOperation value(Getter<V> getter, V val) { public <V> InsertOperation<T> value(Getter<V> getter, V val) {
Objects.requireNonNull(getter, "getter is empty"); Objects.requireNonNull(getter, "getter is empty");
@ -109,12 +158,35 @@ public final class InsertOperation extends AbstractOperation<ResultSet, InsertOp
} }
@Override @Override
public BuiltStatement buildStatement() { 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;
}
values.forEach(t -> addPropertyNode(t._1)); if (values.isEmpty()) return null;
if (values.isEmpty())
return null;
if (entity == null) { if (entity == null) {
throw new HelenusMappingException("unknown entity"); throw new HelenusMappingException("unknown entity");
@ -126,10 +198,13 @@ public final class InsertOperation extends AbstractOperation<ResultSet, InsertOp
insert.ifNotExists(); insert.ifNotExists();
} }
values.forEach(t -> { values.forEach(
t -> {
insert.value(t._1.getColumnName(), t._2); insert.value(t._1.getColumnName(), t._2);
}); });
//TODO(gburd): IF NOT EXISTS when @Constraints.Relationship is 1:1 or 1:m
if (this.ttl != null) { if (this.ttl != null) {
insert.using(QueryBuilder.ttl(this.ttl[0])); insert.using(QueryBuilder.ttl(this.ttl[0]));
} }
@ -140,29 +215,210 @@ public final class InsertOperation extends AbstractOperation<ResultSet, InsertOp
return insert; return insert;
} }
@Override private T newInstance(Class<?> iface) {
public ResultSet transform(ResultSet resultSet) { if (values.size() > 0) {
return resultSet; boolean immutable = entity.isDraftable();
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.
values.forEach(t -> backingMap.put(t._1.getProperty().getPropertyName(), t._2));
// Then, fill in all the rest of the properties.
for (HelenusProperty prop : properties) {
String key = prop.getPropertyName();
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 usingTtl(int ttl) { // Lastly, create a new proxy object for the entity and return the new instance.
return (T) Helenus.map(iface, backingMap);
}
return null;
}
@Override
public T transform(ResultSet resultSet) {
if ((ifNotExists == true) && (resultSet.wasApplied() == false)) {
throw new HelenusException("Statement was not applied due to consistency constraints");
}
Class<?> iface = entity.getMappingInterface();
if (resultType == iface) {
T o = newInstance(iface);
if (o == null) {
// Oddly, this insert didn't change anything so simply return the pojo.
return (T) pojo;
}
return o;
}
return (T) resultSet;
}
public InsertOperation<T> 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 InsertOperation usingTimestamp(long timestamp) { public InsertOperation<T> 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) { protected void adjustTtlAndWriteTime(MapExportable pojo) {
if (entity == null) { if (ttl != null || writeTime != 0L) {
entity = p.getEntity(); List<String> columnNames =
} else if (entity != p.getEntity()) { values
throw new HelenusMappingException("you can insert only single entity " + entity.getMappingInterface() .stream()
+ " or " + p.getEntity().getMappingInterface()); .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

@ -0,0 +1,211 @@
/*
* 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.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.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
import net.helenus.core.AbstractSessionOperations;
import net.helenus.core.UnitOfWork;
import net.helenus.core.cache.Facet;
import net.helenus.support.HelenusException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class Operation<E> {
private static final Logger LOG = LoggerFactory.getLogger(Operation.class);
protected final AbstractSessionOperations sessionOps;
protected boolean showValues;
protected long queryExecutionTimeout = 10;
protected TimeUnit queryTimeoutUnits = TimeUnit.SECONDS;
protected final Meter uowCacheHits;
protected final Meter uowCacheMiss;
protected final Meter sessionCacheHits;
protected final Meter sessionCacheMiss;
protected final Meter cacheHits;
protected final Meter cacheMiss;
protected final Timer requestLatency;
Operation(AbstractSessionOperations sessionOperations) {
this.sessionOps = sessionOperations;
this.showValues = sessionOps.showValues();
MetricRegistry metrics = sessionOperations.getMetricRegistry();
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.sessionCacheHits = metrics.meter("net.helenus.session-cache-hits");
this.sessionCacheMiss = metrics.meter("net.helenus.session-cache-miss");
this.cacheHits = metrics.meter("net.helenus.cache-hits");
this.cacheMiss = metrics.meter("net.helenus.cache-miss");
this.requestLatency = metrics.timer("net.helenus.request-latency");
}
public static String queryString(BatchOperation operation, boolean includeValues) {
return operation.toString(includeValues);
}
public static String queryString(Statement statement, boolean includeValues) {
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,
long timeout,
TimeUnit units,
boolean showValues,
boolean cached)
throws TimeoutException {
Statement statement = options(buildStatement(cached));
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);
}
Stopwatch timer = Stopwatch.createStarted();
try {
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;
} finally {
timer.stop();
if (uow != null) uow.addDatabaseTime("Cassandra", timer);
log(statement, uow, timer, showValues);
}
}
void log(Statement statement, UnitOfWork uow, Stopwatch timer, boolean showValues) {
if (LOG.isInfoEnabled()) {
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, showValues)));
}
}
protected boolean isIdempotentOperation() {
return false;
}
public Statement options(Statement statement) {
return statement;
}
public Statement buildStatement(boolean cached) {
return null;
}
public List<Facet> getFacets() {
return new ArrayList<Facet>();
}
public List<Facet> bindFacetValues() {
return null;
}
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.
@ -43,5 +44,4 @@ public final class PreparedOperation<E> {
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.
@ -23,7 +24,8 @@ 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(
PreparedStatement statement, AbstractOptionalOperation<E, ?> operation) {
this.preparedStatement = statement; this.preparedStatement = statement;
this.operation = operation; this.operation = operation;
} }
@ -43,5 +45,4 @@ public final class PreparedOptionalOperation<E> {
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.
@ -23,7 +24,8 @@ 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(
PreparedStatement statement, AbstractStreamOperation<E, ?> operation) {
this.preparedStatement = statement; this.preparedStatement = statement;
this.operation = operation; this.operation = operation;
} }
@ -33,9 +35,7 @@ public final class PreparedStreamOperation<E> {
} }
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);
} }
@ -43,5 +43,4 @@ public final class PreparedStreamOperation<E> {
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.
@ -15,36 +16,57 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
import java.util.Optional;
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 java.util.List;
import java.util.Optional;
import java.util.function.Function;
import net.helenus.core.cache.Facet;
public final class SelectFirstOperation<E> extends AbstractFilterOptionalOperation<E, SelectFirstOperation<E>> { public final class SelectFirstOperation<E>
extends AbstractFilterOptionalOperation<E, SelectFirstOperation<E>> {
private final SelectOperation<E> src; private final SelectOperation<E> delegate;
public SelectFirstOperation(SelectOperation<E> src) { public SelectFirstOperation(SelectOperation<E> delegate) {
super(src.sessionOps); super(delegate.sessionOps);
this.src = src; this.delegate = delegate;
this.filters = src.filters; this.filters = delegate.filters;
this.ifFilters = src.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>(src, fn); return new SelectFirstTransformingOperation<R, E>(delegate, fn);
} }
@Override @Override
public BuiltStatement buildStatement() { public BuiltStatement buildStatement(boolean cached) {
return src.buildStatement(); return delegate.buildStatement(cached);
}
@Override
public List<Facet> getFacets() {
return delegate.getFacets();
}
@Override
public List<Facet> bindFacetValues() {
return delegate.bindFacetValues();
} }
@Override @Override
public Optional<E> transform(ResultSet resultSet) { public Optional<E> transform(ResultSet resultSet) {
return src.transform(resultSet).findFirst(); return delegate.transform(resultSet).findFirst();
} }
@Override
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,36 +16,50 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
import java.util.Optional;
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 java.util.List;
import java.util.Optional;
import java.util.function.Function;
import net.helenus.core.cache.Facet;
public final class SelectFirstTransformingOperation<R, E> public final class SelectFirstTransformingOperation<R, E>
extends extends AbstractFilterOptionalOperation<R, SelectFirstTransformingOperation<R, E>> {
AbstractFilterOptionalOperation<R, SelectFirstTransformingOperation<R, E>> {
private final SelectOperation<E> src; private final SelectOperation<E> delegate;
private final Function<E, R> fn; private final Function<E, R> fn;
public SelectFirstTransformingOperation(SelectOperation<E> src, Function<E, R> fn) { public SelectFirstTransformingOperation(SelectOperation<E> delegate, Function<E, R> fn) {
super(src.sessionOps); super(delegate.sessionOps);
this.src = src; this.delegate = delegate;
this.fn = fn; this.fn = fn;
this.filters = src.filters; this.filters = delegate.filters;
this.ifFilters = src.ifFilters; this.ifFilters = delegate.ifFilters;
} }
@Override @Override
public BuiltStatement buildStatement() { public List<Facet> bindFacetValues() {
return src.buildStatement(); return delegate.bindFacetValues();
}
@Override
public BuiltStatement buildStatement(boolean cached) {
return delegate.buildStatement(cached);
} }
@Override @Override
public Optional<R> transform(ResultSet resultSet) { public Optional<R> transform(ResultSet resultSet) {
return src.transform(resultSet).findFirst().map(fn); return delegate.transform(resultSet).findFirst().map(fn);
} }
@Override
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,11 +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 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;
@ -28,30 +24,47 @@ 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 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.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.MappingUtil; import net.helenus.mapping.MappingUtil;
import net.helenus.mapping.OrderingDirection; import net.helenus.mapping.OrderingDirection;
import net.helenus.mapping.value.ColumnValueProvider; 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>> {
protected Function<Row, E> rowMapper = null; 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 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;
protected boolean implementsEntityType = false;
@SuppressWarnings("unchecked")
public SelectOperation(AbstractSessionOperations sessionOperations) { public SelectOperation(AbstractSessionOperations sessionOperations) {
super(sessionOperations); super(sessionOperations);
this.rowMapper = new Function<Row, E>() { this.rowMapper =
new Function<Row, E>() {
@Override @Override
public E apply(Row source) { public E apply(Row source) {
@ -67,7 +80,6 @@ public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, S
return (E) Fun.ArrayTuple.of(arr); return (E) Fun.ArrayTuple.of(arr);
} }
}; };
} }
@ -75,28 +87,47 @@ public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, S
super(sessionOperations); super(sessionOperations);
entity.getOrderedProperties().stream().map(p -> new HelenusPropertyNode(p, Optional.empty())) entity
.getOrderedProperties()
.stream()
.map(p -> new HelenusPropertyNode(p, Optional.empty()))
.forEach(p -> this.props.add(p)); .forEach(p -> this.props.add(p));
this.isCacheable = entity.isCacheable();
this.implementsEntityType = Entity.class.isAssignableFrom(entity.getMappingInterface());
} }
public SelectOperation(AbstractSessionOperations sessionOperations, HelenusEntity entity, public SelectOperation(
AbstractSessionOperations sessionOperations,
HelenusEntity entity,
Function<Row, E> rowMapper) { Function<Row, E> rowMapper) {
super(sessionOperations); super(sessionOperations);
this.rowMapper = rowMapper; this.rowMapper = rowMapper;
entity.getOrderedProperties().stream().map(p -> new HelenusPropertyNode(p, Optional.empty())) entity
.getOrderedProperties()
.stream()
.map(p -> new HelenusPropertyNode(p, Optional.empty()))
.forEach(p -> this.props.add(p)); .forEach(p -> this.props.add(p));
this.isCacheable = entity.isCacheable();
this.implementsEntityType = Entity.class.isAssignableFrom(entity.getMappingInterface());
} }
public SelectOperation(AbstractSessionOperations sessionOperations, Function<Row, E> rowMapper, public SelectOperation(
AbstractSessionOperations sessionOperations,
Function<Row, E> rowMapper,
HelenusPropertyNode... props) { HelenusPropertyNode... props) {
super(sessionOperations); super(sessionOperations);
this.rowMapper = rowMapper; this.rowMapper = rowMapper;
Collections.addAll(this.props, props); Collections.addAll(this.props, props);
HelenusEntity entity = props[0].getEntity();
this.isCacheable = entity.isCacheable();
this.implementsEntityType = Entity.class.isAssignableFrom(entity.getMappingInterface());
} }
public CountOperation count() { public CountOperation count() {
@ -107,14 +138,30 @@ public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, S
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 count records only from a single entity " throw new HelenusMappingException(
+ entity.getMappingInterface() + " or " + prop.getEntity().getMappingInterface()); "you can count records only from a single entity "
+ entity.getMappingInterface()
+ " or "
+ prop.getEntity().getMappingInterface());
} }
} }
return new CountOperation(sessionOps, entity); return new CountOperation(sessionOps, entity);
} }
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;
}
public SelectFirstOperation<E> single() { public SelectFirstOperation<E> single() {
limit(1); limit(1);
return new SelectFirstOperation<E>(this); return new SelectFirstOperation<E>(this);
@ -128,11 +175,11 @@ public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, S
this.rowMapper = null; this.rowMapper = null;
return new SelectTransformingOperation<R, E>(this, (r) -> { return new SelectTransformingOperation<R, E>(
this,
(r) -> {
Map<String, Object> map = new ValueProviderMap(r, sessionOps.getValueProvider(), entity); Map<String, Object> map = new ValueProviderMap(r, sessionOps.getValueProvider(), entity);
return (R) Helenus.map(entityClass, map); return (R) Helenus.map(entityClass, map);
}); });
} }
@ -167,23 +214,81 @@ public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, S
} }
@Override @Override
public BuiltStatement buildStatement() { public boolean isSessionCacheable() {
return isCacheable;
}
@Override
public List<Facet> getFacets() {
HelenusEntity entity = props.get(0).getEntity();
return entity.getFacets();
}
@Override
public List<Facet> bindFacetValues() {
HelenusEntity entity = props.get(0).getEntity();
List<Facet> boundFacets = new ArrayList<>();
for (Facet facet : entity.getFacets()) {
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;
}
@Override
public BuiltStatement buildStatement(boolean cached) {
HelenusEntity entity = null; HelenusEntity entity = null;
Selection selection = QueryBuilder.select(); Selection selection = QueryBuilder.select();
for (HelenusPropertyNode prop : props) { for (HelenusPropertyNode prop : props) {
selection = selection.column(prop.getColumnName()); String columnName = prop.getColumnName();
selection = selection.column(columnName);
if (prop.getProperty().caseSensitiveIndex()) {
allowFiltering = true;
}
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());
}
if (cached && implementsEntityType) {
switch (prop.getProperty().getColumnType()) {
case PARTITION_KEY:
case CLUSTERING_COLUMN:
break;
default:
if (entity.equals(prop.getEntity())) {
if (!prop.getProperty().getDataType().isCollectionType()) {
columnName = prop.getProperty().getColumnName().toCql(false);
selection.ttl(columnName).as('"' + CacheUtil.ttlKey(columnName) + '"');
selection.writeTime(columnName).as('"' + CacheUtil.writeTimeKey(columnName) + '"');
}
}
break;
}
} }
} }
@ -191,7 +296,8 @@ public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, S
throw new HelenusMappingException("no entity or table to select data"); throw new HelenusMappingException("no entity or table to select data");
} }
Select select = selection.from(entity.getName().toCql()); String tableName = alternateTableName == null ? entity.getName().toCql() : alternateTableName;
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()]));
@ -205,13 +311,40 @@ public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, S
Where where = select.where(); Where where = select.where();
for (Filter<?> filter : filters) { boolean isFirstIndex = true;
for (Filter<?> filter : filters.values()) {
where.and(filter.getClause(sessionOps.getValuePreparer())); 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()) {
logger.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) {
@ -224,19 +357,15 @@ public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, S
@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.stream(
return StreamSupport Spliterators.spliteratorUnknownSize(resultSet.iterator(), Spliterator.ORDERED), false)
.stream(Spliterators.spliteratorUnknownSize(resultSet.iterator(), Spliterator.ORDERED), false)
.map(rowMapper); .map(rowMapper);
} } else {
return (Stream<E>)
else { StreamSupport.stream(
Spliterators.spliteratorUnknownSize(resultSet.iterator(), Spliterator.ORDERED),
return (Stream<E>) StreamSupport false);
.stream(Spliterators.spliteratorUnknownSize(resultSet.iterator(), Spliterator.ORDERED), false);
} }
} }
@ -246,5 +375,4 @@ public final class SelectOperation<E> extends AbstractFilterStreamOperation<E, S
} }
return ordering; return ordering;
} }
} }

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,36 +16,55 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
import java.util.function.Function;
import java.util.stream.Stream;
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 java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;
import net.helenus.core.cache.Facet;
public final class SelectTransformingOperation<R, E> public final class SelectTransformingOperation<R, E>
extends extends AbstractFilterStreamOperation<R, SelectTransformingOperation<R, E>> {
AbstractFilterStreamOperation<R, SelectTransformingOperation<R, E>> {
private final SelectOperation<E> src; private final SelectOperation<E> delegate;
private final Function<E, R> fn; private final Function<E, R> fn;
public SelectTransformingOperation(SelectOperation<E> src, Function<E, R> fn) { public SelectTransformingOperation(SelectOperation<E> delegate, Function<E, R> fn) {
super(src.sessionOps); super(delegate.sessionOps);
this.src = src; this.delegate = delegate;
this.fn = fn; this.fn = fn;
this.filters = src.filters; this.filters = delegate.filters;
this.ifFilters = src.ifFilters; this.ifFilters = delegate.ifFilters;
} }
@Override @Override
public BuiltStatement buildStatement() { public List<Facet> bindFacetValues() {
return src.buildStatement(); return delegate.bindFacetValues();
}
@Override
public List<Facet> getFacets() {
return delegate.getFacets();
}
@Override
public BuiltStatement buildStatement(boolean cached) {
return delegate.buildStatement(cached);
} }
@Override @Override
public Stream<R> transform(ResultSet resultSet) { public Stream<R> transform(ResultSet resultSet) {
return src.transform(resultSet).map(fn); return delegate.transform(resultSet).map(fn);
} }
@Override
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,54 +16,109 @@
*/ */
package net.helenus.core.operation; package net.helenus.core.operation;
import java.util.*;
import java.util.function.Function;
import com.datastax.driver.core.ResultSet; import com.datastax.driver.core.ResultSet;
import com.datastax.driver.core.querybuilder.Assignment; import com.datastax.driver.core.querybuilder.Assignment;
import com.datastax.driver.core.querybuilder.BuiltStatement; 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.Update; import com.datastax.driver.core.querybuilder.Update;
import java.util.*;
import net.helenus.core.AbstractSessionOperations; import java.util.concurrent.TimeoutException;
import net.helenus.core.Filter; import java.util.function.Function;
import net.helenus.core.Getter; import java.util.stream.Collectors;
import net.helenus.core.*;
import net.helenus.core.cache.BoundFacet;
import net.helenus.core.cache.CacheUtil;
import net.helenus.core.cache.Facet;
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;
import net.helenus.mapping.value.BeanColumnValueProvider;
import net.helenus.support.HelenusException;
import net.helenus.support.HelenusMappingException; import net.helenus.support.HelenusMappingException;
import net.helenus.support.Immutables; import net.helenus.support.Immutables;
public final class UpdateOperation extends AbstractFilterOperation<ResultSet, UpdateOperation> { public final class UpdateOperation<E> extends AbstractFilterOperation<E, UpdateOperation<E>> {
private final Map<Assignment, BoundFacet> assignments = new HashMap<>();
private final AbstractEntityDraft<E> draft;
private final Map<String, Object> draftMap;
private final Set<String> readSet;
private HelenusEntity entity = null; private HelenusEntity entity = null;
private Object pojo;
private final List<Assignment> assignments = new ArrayList<Assignment>();
private int[] ttl; private int[] ttl;
private long[] timestamp; private long[] timestamp;
private long writeTime = 0L;
public UpdateOperation(AbstractSessionOperations sessionOperations) { public UpdateOperation(AbstractSessionOperations sessionOperations) {
super(sessionOperations); super(sessionOperations);
this.draft = null;
this.draftMap = null;
this.readSet = null;
} }
public UpdateOperation(AbstractSessionOperations sessionOperations, HelenusPropertyNode p, Object v) { public UpdateOperation(
AbstractSessionOperations sessionOperations, AbstractEntityDraft<E> draft) {
super(sessionOperations); super(sessionOperations);
this.draft = draft;
this.draftMap = draft.toMap();
this.readSet = draft.read();
}
public UpdateOperation(AbstractSessionOperations sessionOperations, Object pojo) {
super(sessionOperations);
this.draft = null;
this.draftMap = null;
if (pojo != null) {
this.entity = Helenus.resolve(MappingUtil.getMappingInterface(pojo));
if (this.entity != null && entity.isCacheable() && pojo instanceof MapExportable) {
this.pojo = pojo;
this.readSet = ((MapExportable) pojo).toReadSet();
} else {
this.readSet = null;
}
} else {
this.readSet = null;
}
}
public UpdateOperation(
AbstractSessionOperations sessionOperations, HelenusPropertyNode p, Object v) {
super(sessionOperations);
this.draft = null;
this.draftMap = null;
this.readSet = null;
Object value = sessionOps.getValuePreparer().prepareColumnValue(v, p.getProperty()); Object value = sessionOps.getValuePreparer().prepareColumnValue(v, p.getProperty());
assignments.add(QueryBuilder.set(p.getColumnName(), value)); assignments.put(QueryBuilder.set(p.getColumnName(), value), new BoundFacet(p.getProperty(), v));
addPropertyNode(p); addPropertyNode(p);
} }
public <V> UpdateOperation set(Getter<V> getter, V v) { public <V> UpdateOperation<E> set(Getter<V> getter, V v) {
Objects.requireNonNull(getter, "getter is empty"); Objects.requireNonNull(getter, "getter is empty");
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(getter); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(getter);
HelenusProperty prop = p.getProperty();
Object value = sessionOps.getValuePreparer().prepareColumnValue(v, p.getProperty()); Object value = sessionOps.getValuePreparer().prepareColumnValue(v, prop);
assignments.add(QueryBuilder.set(p.getColumnName(), value)); assignments.put(QueryBuilder.set(p.getColumnName(), value), new BoundFacet(prop, value));
if (draft != null) {
String key = prop.getPropertyName();
if (draft.get(key, value.getClass()) != v) {
draft.set(key, v);
}
}
if (pojo != null) {
if (!BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop).equals(v)) {
String key = prop.getPropertyName();
((MapExportable) pojo).put(key, v);
}
}
addPropertyNode(p); addPropertyNode(p);
@ -77,38 +133,60 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
* *
*/ */
public <V> UpdateOperation increment(Getter<V> counterGetter) { public <V> UpdateOperation<E> increment(Getter<V> counterGetter) {
return increment(counterGetter, 1L); return increment(counterGetter, 1L);
} }
public <V> UpdateOperation increment(Getter<V> counterGetter, long delta) { public <V> UpdateOperation<E> increment(Getter<V> counterGetter, long delta) {
Objects.requireNonNull(counterGetter, "counterGetter is empty"); Objects.requireNonNull(counterGetter, "counterGetter is empty");
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(counterGetter); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(counterGetter);
assignments.add(QueryBuilder.incr(p.getColumnName(), delta)); BoundFacet facet = null;
HelenusProperty prop = p.getProperty();
addPropertyNode(p); if (pojo != null) {
return this; Long value = (Long) BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop);
facet = new BoundFacet(prop, value + delta);
} else if (draft != null) {
String key = prop.getPropertyName();
draftMap.put(key, (Long) draftMap.get(key) + delta);
facet = new BoundFacet(prop, draftMap.get(key));
} }
public <V> UpdateOperation decrement(Getter<V> counterGetter) { assignments.put(QueryBuilder.incr(p.getColumnName(), delta), facet);
addPropertyNode(p);
return this;
}
public <V> UpdateOperation<E> decrement(Getter<V> counterGetter) {
return decrement(counterGetter, 1L); return decrement(counterGetter, 1L);
} }
public <V> UpdateOperation decrement(Getter<V> counterGetter, long delta) { public <V> UpdateOperation<E> decrement(Getter<V> counterGetter, long delta) {
Objects.requireNonNull(counterGetter, "counterGetter is empty"); Objects.requireNonNull(counterGetter, "counterGetter is empty");
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(counterGetter); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(counterGetter);
assignments.add(QueryBuilder.decr(p.getColumnName(), delta)); BoundFacet facet = null;
HelenusProperty prop = p.getProperty();
if (pojo != null) {
Long value = (Long) BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop);
facet = new BoundFacet(prop, value - delta);
} else if (draft != null) {
String key = prop.getPropertyName();
draftMap.put(key, (Long) draftMap.get(key) - delta);
facet = new BoundFacet(prop, draftMap.get(key));
}
assignments.put(QueryBuilder.decr(p.getColumnName(), delta), facet);
addPropertyNode(p); addPropertyNode(p);
return this;
return this;
} }
/* /*
@ -118,7 +196,7 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
* *
*/ */
public <V> UpdateOperation prepend(Getter<List<V>> listGetter, V value) { public <V> UpdateOperation<E> prepend(Getter<List<V>> listGetter, V value) {
Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(listGetter, "listGetter is empty");
Objects.requireNonNull(value, "value is empty"); Objects.requireNonNull(value, "value is empty");
@ -126,13 +204,31 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter);
Object valueObj = prepareSingleListValue(p, value); Object valueObj = prepareSingleListValue(p, value);
assignments.add(QueryBuilder.prepend(p.getColumnName(), valueObj)); final List<V> list;
final BoundFacet facet;
HelenusProperty prop = p.getProperty();
if (pojo != null) {
list = (List<V>) BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false);
list.add(0, value);
facet = new BoundFacet(prop, list);
} else if (draft != null) {
String key = prop.getPropertyName();
list = (List<V>) draftMap.get(key);
list.add(0, value);
facet = new BoundFacet(prop, list);
} else {
list = null;
facet = null;
}
assignments.put(QueryBuilder.prepend(p.getColumnName(), valueObj), facet);
addPropertyNode(p); addPropertyNode(p);
return this; return this;
} }
public <V> UpdateOperation prependAll(Getter<List<V>> listGetter, List<V> value) { public <V> UpdateOperation<E> prependAll(Getter<List<V>> listGetter, List<V> value) {
Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(listGetter, "listGetter is empty");
Objects.requireNonNull(value, "value is empty"); Objects.requireNonNull(value, "value is empty");
@ -140,13 +236,31 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter);
List valueObj = prepareListValue(p, value); List valueObj = prepareListValue(p, value);
assignments.add(QueryBuilder.prependAll(p.getColumnName(), valueObj)); final List<V> list;
final BoundFacet facet;
HelenusProperty prop = p.getProperty();
if (pojo != null) {
list = (List<V>) BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false);
list.addAll(0, value);
facet = new BoundFacet(prop, list);
} else if (draft != null && value.size() > 0) {
String key = p.getProperty().getPropertyName();
list = (List<V>) draftMap.get(key);
list.addAll(0, value);
facet = new BoundFacet(prop, list);
} else {
list = null;
facet = null;
}
assignments.put(QueryBuilder.prependAll(p.getColumnName(), valueObj), facet);
addPropertyNode(p); addPropertyNode(p);
return this; return this;
} }
public <V> UpdateOperation setIdx(Getter<List<V>> listGetter, int idx, V value) { public <V> UpdateOperation<E> setIdx(Getter<List<V>> listGetter, int idx, V value) {
Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(listGetter, "listGetter is empty");
Objects.requireNonNull(value, "value is empty"); Objects.requireNonNull(value, "value is empty");
@ -154,13 +268,37 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter);
Object valueObj = prepareSingleListValue(p, value); Object valueObj = prepareSingleListValue(p, value);
assignments.add(QueryBuilder.setIdx(p.getColumnName(), idx, valueObj)); final BoundFacet facet;
HelenusProperty prop = p.getProperty();
if (pojo != null || draft != null) {
final List<V> list;
if (pojo != null) {
list = (List<V>) BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false);
} else {
String key = prop.getPropertyName();
list = (List<V>) draftMap.get(key);
}
if (idx < 0) {
list.add(0, value);
} else if (idx > list.size()) {
list.add(list.size(), value);
} else {
list.add(idx, value);
}
list.add(0, value);
facet = new BoundFacet(prop, list);
} else {
facet = null;
}
assignments.put(QueryBuilder.setIdx(p.getColumnName(), idx, valueObj), facet);
addPropertyNode(p); addPropertyNode(p);
return this; return this;
} }
public <V> UpdateOperation append(Getter<List<V>> listGetter, V value) { public <V> UpdateOperation<E> append(Getter<List<V>> listGetter, V value) {
Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(listGetter, "listGetter is empty");
Objects.requireNonNull(value, "value is empty"); Objects.requireNonNull(value, "value is empty");
@ -168,13 +306,30 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter);
Object valueObj = prepareSingleListValue(p, value); Object valueObj = prepareSingleListValue(p, value);
assignments.add(QueryBuilder.append(p.getColumnName(), valueObj)); final List<V> list;
final BoundFacet facet;
HelenusProperty prop = p.getProperty();
if (pojo != null) {
list = (List<V>) BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false);
list.add(value);
facet = new BoundFacet(prop, list);
} else if (draft != null) {
String key = prop.getPropertyName();
list = (List<V>) draftMap.get(key);
list.add(value);
facet = new BoundFacet(prop, list);
} else {
list = null;
facet = null;
}
assignments.put(QueryBuilder.append(p.getColumnName(), valueObj), facet);
addPropertyNode(p); addPropertyNode(p);
return this; return this;
} }
public <V> UpdateOperation appendAll(Getter<List<V>> listGetter, List<V> value) { public <V> UpdateOperation<E> appendAll(Getter<List<V>> listGetter, List<V> value) {
Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(listGetter, "listGetter is empty");
Objects.requireNonNull(value, "value is empty"); Objects.requireNonNull(value, "value is empty");
@ -182,13 +337,30 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter);
List valueObj = prepareListValue(p, value); List valueObj = prepareListValue(p, value);
assignments.add(QueryBuilder.appendAll(p.getColumnName(), valueObj)); final List<V> list;
final BoundFacet facet;
HelenusProperty prop = p.getProperty();
if (pojo != null) {
list = (List<V>) BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false);
list.addAll(value);
facet = new BoundFacet(prop, list);
} else if (draft != null && value.size() > 0) {
String key = prop.getPropertyName();
list = (List<V>) draftMap.get(key);
list.addAll(value);
facet = new BoundFacet(prop, list);
} else {
list = null;
facet = null;
}
assignments.put(QueryBuilder.appendAll(p.getColumnName(), valueObj), facet);
addPropertyNode(p); addPropertyNode(p);
return this; return this;
} }
public <V> UpdateOperation discard(Getter<List<V>> listGetter, V value) { public <V> UpdateOperation<E> discard(Getter<List<V>> listGetter, V value) {
Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(listGetter, "listGetter is empty");
Objects.requireNonNull(value, "value is empty"); Objects.requireNonNull(value, "value is empty");
@ -196,13 +368,30 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter);
Object valueObj = prepareSingleListValue(p, value); Object valueObj = prepareSingleListValue(p, value);
assignments.add(QueryBuilder.discard(p.getColumnName(), valueObj)); final List<V> list;
final BoundFacet facet;
HelenusProperty prop = p.getProperty();
if (pojo != null) {
list = (List<V>) BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false);
list.remove(value);
facet = new BoundFacet(prop, list);
} else if (draft != null) {
String key = prop.getPropertyName();
list = (List<V>) draftMap.get(key);
list.remove(value);
facet = new BoundFacet(prop, list);
} else {
list = null;
facet = null;
}
assignments.put(QueryBuilder.discard(p.getColumnName(), valueObj), facet);
addPropertyNode(p); addPropertyNode(p);
return this; return this;
} }
public <V> UpdateOperation discardAll(Getter<List<V>> listGetter, List<V> value) { public <V> UpdateOperation<E> discardAll(Getter<List<V>> listGetter, List<V> value) {
Objects.requireNonNull(listGetter, "listGetter is empty"); Objects.requireNonNull(listGetter, "listGetter is empty");
Objects.requireNonNull(value, "value is empty"); Objects.requireNonNull(value, "value is empty");
@ -210,9 +399,26 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(listGetter);
List valueObj = prepareListValue(p, value); List valueObj = prepareListValue(p, value);
assignments.add(QueryBuilder.discardAll(p.getColumnName(), valueObj)); final List<V> list;
final BoundFacet facet;
HelenusProperty prop = p.getProperty();
if (pojo != null) {
list = (List<V>) BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false);
list.removeAll(value);
facet = new BoundFacet(prop, list);
} else if (draft != null) {
String key = prop.getPropertyName();
list = (List<V>) draftMap.get(key);
list.removeAll(value);
facet = new BoundFacet(prop, list);
} else {
list = null;
facet = null;
}
assignments.put(QueryBuilder.discardAll(p.getColumnName(), valueObj), facet);
addPropertyNode(p); addPropertyNode(p);
return this; return this;
} }
@ -221,7 +427,8 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
Object valueObj = value; Object valueObj = value;
Optional<Function<Object, Object>> converter = prop.getWriteConverter(sessionOps.getSessionRepository()); Optional<Function<Object, Object>> converter =
prop.getWriteConverter(sessionOps.getSessionRepository());
if (converter.isPresent()) { if (converter.isPresent()) {
List convertedList = (List) converter.get().apply(Immutables.listOf(value)); List convertedList = (List) converter.get().apply(Immutables.listOf(value));
valueObj = convertedList.get(0); valueObj = convertedList.get(0);
@ -236,7 +443,8 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
List valueObj = value; List valueObj = value;
Optional<Function<Object, Object>> converter = prop.getWriteConverter(sessionOps.getSessionRepository()); Optional<Function<Object, Object>> converter =
prop.getWriteConverter(sessionOps.getSessionRepository());
if (converter.isPresent()) { if (converter.isPresent()) {
valueObj = (List) converter.get().apply(value); valueObj = (List) converter.get().apply(value);
} }
@ -252,7 +460,7 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
* *
*/ */
public <V> UpdateOperation add(Getter<Set<V>> setGetter, V value) { public <V> UpdateOperation<E> add(Getter<Set<V>> setGetter, V value) {
Objects.requireNonNull(setGetter, "setGetter is empty"); Objects.requireNonNull(setGetter, "setGetter is empty");
Objects.requireNonNull(value, "value is empty"); Objects.requireNonNull(value, "value is empty");
@ -260,13 +468,30 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(setGetter); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(setGetter);
Object valueObj = prepareSingleSetValue(p, value); Object valueObj = prepareSingleSetValue(p, value);
assignments.add(QueryBuilder.add(p.getColumnName(), valueObj)); final Set<V> set;
final BoundFacet facet;
HelenusProperty prop = p.getProperty();
if (pojo != null) {
set = (Set<V>) BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false);
set.add(value);
facet = new BoundFacet(prop, set);
} else if (draft != null) {
String key = prop.getPropertyName();
set = (Set<V>) draftMap.get(key);
set.add(value);
facet = new BoundFacet(prop, set);
} else {
set = null;
facet = null;
}
assignments.put(QueryBuilder.add(p.getColumnName(), valueObj), facet);
addPropertyNode(p); addPropertyNode(p);
return this; return this;
} }
public <V> UpdateOperation addAll(Getter<Set<V>> setGetter, Set<V> value) { public <V> UpdateOperation<E> addAll(Getter<Set<V>> setGetter, Set<V> value) {
Objects.requireNonNull(setGetter, "setGetter is empty"); Objects.requireNonNull(setGetter, "setGetter is empty");
Objects.requireNonNull(value, "value is empty"); Objects.requireNonNull(value, "value is empty");
@ -274,13 +499,30 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(setGetter); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(setGetter);
Set valueObj = prepareSetValue(p, value); Set valueObj = prepareSetValue(p, value);
assignments.add(QueryBuilder.addAll(p.getColumnName(), valueObj)); final Set<V> set;
final BoundFacet facet;
HelenusProperty prop = p.getProperty();
if (pojo != null) {
set = (Set<V>) BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false);
set.addAll(value);
facet = new BoundFacet(prop, set);
} else if (draft != null) {
String key = prop.getPropertyName();
set = (Set<V>) draftMap.get(key);
set.addAll(value);
facet = new BoundFacet(prop, set);
} else {
set = null;
facet = null;
}
assignments.put(QueryBuilder.addAll(p.getColumnName(), valueObj), facet);
addPropertyNode(p); addPropertyNode(p);
return this; return this;
} }
public <V> UpdateOperation remove(Getter<Set<V>> setGetter, V value) { public <V> UpdateOperation<E> remove(Getter<Set<V>> setGetter, V value) {
Objects.requireNonNull(setGetter, "setGetter is empty"); Objects.requireNonNull(setGetter, "setGetter is empty");
Objects.requireNonNull(value, "value is empty"); Objects.requireNonNull(value, "value is empty");
@ -288,13 +530,30 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(setGetter); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(setGetter);
Object valueObj = prepareSingleSetValue(p, value); Object valueObj = prepareSingleSetValue(p, value);
assignments.add(QueryBuilder.remove(p.getColumnName(), valueObj)); final Set<V> set;
final BoundFacet facet;
HelenusProperty prop = p.getProperty();
if (pojo != null) {
set = (Set<V>) BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false);
set.remove(value);
facet = new BoundFacet(prop, set);
} else if (draft != null) {
String key = prop.getPropertyName();
set = (Set<V>) draftMap.get(key);
set.remove(value);
facet = new BoundFacet(prop, set);
} else {
set = null;
facet = null;
}
assignments.put(QueryBuilder.remove(p.getColumnName(), valueObj), facet);
addPropertyNode(p); addPropertyNode(p);
return this; return this;
} }
public <V> UpdateOperation removeAll(Getter<Set<V>> setGetter, Set<V> value) { public <V> UpdateOperation<E> removeAll(Getter<Set<V>> setGetter, Set<V> value) {
Objects.requireNonNull(setGetter, "setGetter is empty"); Objects.requireNonNull(setGetter, "setGetter is empty");
Objects.requireNonNull(value, "value is empty"); Objects.requireNonNull(value, "value is empty");
@ -302,9 +561,26 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(setGetter); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(setGetter);
Set valueObj = prepareSetValue(p, value); Set valueObj = prepareSetValue(p, value);
assignments.add(QueryBuilder.removeAll(p.getColumnName(), valueObj)); final Set<V> set;
final BoundFacet facet;
HelenusProperty prop = p.getProperty();
if (pojo != null) {
set = (Set<V>) BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false);
set.removeAll(value);
facet = new BoundFacet(prop, set);
} else if (draft != null) {
String key = prop.getPropertyName();
set = (Set<V>) draftMap.get(key);
set.removeAll(value);
facet = new BoundFacet(prop, set);
} else {
set = null;
facet = null;
}
assignments.put(QueryBuilder.removeAll(p.getColumnName(), valueObj), facet);
addPropertyNode(p); addPropertyNode(p);
return this; return this;
} }
@ -313,7 +589,8 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
HelenusProperty prop = p.getProperty(); HelenusProperty prop = p.getProperty();
Object valueObj = value; Object valueObj = value;
Optional<Function<Object, Object>> converter = prop.getWriteConverter(sessionOps.getSessionRepository()); Optional<Function<Object, Object>> converter =
prop.getWriteConverter(sessionOps.getSessionRepository());
if (converter.isPresent()) { if (converter.isPresent()) {
Set convertedSet = (Set) converter.get().apply(Immutables.setOf(value)); Set convertedSet = (Set) converter.get().apply(Immutables.setOf(value));
valueObj = convertedSet.iterator().next(); valueObj = convertedSet.iterator().next();
@ -327,7 +604,8 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
HelenusProperty prop = p.getProperty(); HelenusProperty prop = p.getProperty();
Set valueObj = value; Set valueObj = value;
Optional<Function<Object, Object>> converter = prop.getWriteConverter(sessionOps.getSessionRepository()); Optional<Function<Object, Object>> converter =
prop.getWriteConverter(sessionOps.getSessionRepository());
if (converter.isPresent()) { if (converter.isPresent()) {
valueObj = (Set) converter.get().apply(value); valueObj = (Set) converter.get().apply(value);
} }
@ -343,7 +621,7 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
* *
*/ */
public <K, V> UpdateOperation put(Getter<Map<K, V>> mapGetter, K key, V value) { public <K, V> UpdateOperation<E> put(Getter<Map<K, V>> mapGetter, K key, V value) {
Objects.requireNonNull(mapGetter, "mapGetter is empty"); Objects.requireNonNull(mapGetter, "mapGetter is empty");
Objects.requireNonNull(key, "key is empty"); Objects.requireNonNull(key, "key is empty");
@ -351,22 +629,39 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(mapGetter); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(mapGetter);
HelenusProperty prop = p.getProperty(); HelenusProperty prop = p.getProperty();
Optional<Function<Object, Object>> converter = prop.getWriteConverter(sessionOps.getSessionRepository()); final Map<K, V> map;
final BoundFacet facet;
if (pojo != null) {
map = (Map<K, V>) BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false);
map.put(key, value);
facet = new BoundFacet(prop, map);
} else if (draft != null) {
map = (Map<K, V>) draftMap.get(prop.getPropertyName());
map.put(key, value);
facet = new BoundFacet(prop, map);
} else {
map = null;
facet = null;
}
Optional<Function<Object, Object>> converter =
prop.getWriteConverter(sessionOps.getSessionRepository());
if (converter.isPresent()) { if (converter.isPresent()) {
Map<Object, Object> convertedMap = (Map<Object, Object>) converter.get() Map<Object, Object> convertedMap =
.apply(Immutables.mapOf(key, value)); (Map<Object, Object>) converter.get().apply(Immutables.mapOf(key, value));
for (Map.Entry<Object, Object> e : convertedMap.entrySet()) { for (Map.Entry<Object, Object> e : convertedMap.entrySet()) {
assignments.add(QueryBuilder.put(p.getColumnName(), e.getKey(), e.getValue())); assignments.put(QueryBuilder.put(p.getColumnName(), e.getKey(), e.getValue()), facet);
} }
} else { } else {
assignments.add(QueryBuilder.put(p.getColumnName(), key, value)); assignments.put(QueryBuilder.put(p.getColumnName(), key, value), facet);
} }
addPropertyNode(p); addPropertyNode(p);
return this; return this;
} }
public <K, V> UpdateOperation putAll(Getter<Map<K, V>> mapGetter, Map<K, V> map) { public <K, V> UpdateOperation<E> putAll(Getter<Map<K, V>> mapGetter, Map<K, V> map) {
Objects.requireNonNull(mapGetter, "mapGetter is empty"); Objects.requireNonNull(mapGetter, "mapGetter is empty");
Objects.requireNonNull(map, "map is empty"); Objects.requireNonNull(map, "map is empty");
@ -374,20 +669,37 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
HelenusPropertyNode p = MappingUtil.resolveMappingProperty(mapGetter); HelenusPropertyNode p = MappingUtil.resolveMappingProperty(mapGetter);
HelenusProperty prop = p.getProperty(); HelenusProperty prop = p.getProperty();
Optional<Function<Object, Object>> converter = prop.getWriteConverter(sessionOps.getSessionRepository()); final Map<K, V> newMap;
final BoundFacet facet;
if (pojo != null) {
newMap = (Map<K, V>) BeanColumnValueProvider.INSTANCE.getColumnValue(pojo, -1, prop, false);
newMap.putAll(map);
facet = new BoundFacet(prop, newMap);
} else if (draft != null) {
newMap = (Map<K, V>) draftMap.get(prop.getPropertyName());
newMap.putAll(map);
facet = new BoundFacet(prop, newMap);
} else {
newMap = null;
facet = null;
}
Optional<Function<Object, Object>> converter =
prop.getWriteConverter(sessionOps.getSessionRepository());
if (converter.isPresent()) { if (converter.isPresent()) {
Map convertedMap = (Map) converter.get().apply(map); Map convertedMap = (Map) converter.get().apply(map);
assignments.add(QueryBuilder.putAll(p.getColumnName(), convertedMap)); assignments.put(QueryBuilder.putAll(p.getColumnName(), convertedMap), facet);
} else { } else {
assignments.add(QueryBuilder.putAll(p.getColumnName(), map)); assignments.put(QueryBuilder.putAll(p.getColumnName(), map), facet);
} }
addPropertyNode(p); addPropertyNode(p);
return this; return this;
} }
@Override @Override
public BuiltStatement buildStatement() { public BuiltStatement buildStatement(boolean cached) {
if (entity == null) { if (entity == null) {
throw new HelenusMappingException("empty update operation"); throw new HelenusMappingException("empty update operation");
@ -395,7 +707,7 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
Update update = QueryBuilder.update(entity.getName().toCql()); Update update = QueryBuilder.update(entity.getName().toCql());
for (Assignment assignment : assignments) { for (Assignment assignment : assignments.keySet()) {
update.with(assignment); update.with(assignment);
} }
@ -416,6 +728,7 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
if (this.ttl != null) { if (this.ttl != null) {
update.using(QueryBuilder.ttl(this.ttl[0])); update.using(QueryBuilder.ttl(this.ttl[0]));
} }
if (this.timestamp != null) { if (this.timestamp != null) {
update.using(QueryBuilder.timestamp(this.timestamp[0])); update.using(QueryBuilder.timestamp(this.timestamp[0]));
} }
@ -424,17 +737,25 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
} }
@Override @Override
public ResultSet transform(ResultSet resultSet) { public E transform(ResultSet resultSet) {
return resultSet; if ((ifFilters != null && !ifFilters.isEmpty()) && (resultSet.wasApplied() == false)) {
throw new HelenusException("Statement was not applied due to consistency constraints");
} }
public UpdateOperation usingTtl(int ttl) { if (draft != null) {
return Helenus.map(draft.getEntityClass(), draft.toMap(draftMap));
} else {
return (E) resultSet;
}
}
public UpdateOperation<E> 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 UpdateOperation usingTimestamp(long timestamp) { public UpdateOperation<E> 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;
@ -444,8 +765,140 @@ public final class UpdateOperation extends AbstractFilterOperation<ResultSet, Up
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 update columns only in single entity " throw new HelenusMappingException(
+ entity.getMappingInterface() + " or " + p.getEntity().getMappingInterface()); "you can update columns only in single entity "
+ entity.getMappingInterface()
+ " or "
+ p.getEntity().getMappingInterface());
}
}
private void adjustTtlAndWriteTime(MapExportable pojo) {
if (ttl != null || writeTime != 0L) {
List<String> names = new ArrayList<String>(assignments.size());
for (BoundFacet facet : assignments.values()) {
for (HelenusProperty prop : facet.getProperties()) {
names.add(prop.getColumnName().toCql(false));
}
}
if (names.size() > 0) {
if (ttl != null) {
names.forEach(name -> pojo.put(CacheUtil.ttlKey(name), ttl));
}
if (writeTime != 0L) {
names.forEach(name -> pojo.put(CacheUtil.writeTimeKey(name), writeTime));
}
}
}
}
@Override
protected boolean isIdempotentOperation() {
return assignments
.values()
.stream()
.allMatch(
facet -> {
if (facet != null) {
Set<HelenusProperty> props = facet.getProperties();
if (props != null && props.size() > 0) {
return props.stream().allMatch(prop -> prop.isIdempotent());
} else {
return true;
}
} else {
// In this case our UPDATE statement made mutations via the List, Set, Map methods only.
return false;
}
})
|| super.isIdempotentOperation();
}
@Override
public E sync() throws TimeoutException {
E result = super.sync();
if (result != null && entity.isCacheable()) {
if (draft != null) {
adjustTtlAndWriteTime(draft);
adjustTtlAndWriteTime((MapExportable) result);
sessionOps.updateCache(result, bindFacetValues());
} else if (pojo != null) {
adjustTtlAndWriteTime((MapExportable) pojo);
sessionOps.updateCache(pojo, bindFacetValues());
} else {
sessionOps.cacheEvict(bindFacetValues());
}
}
return result;
}
@Override
public E sync(UnitOfWork uow) throws TimeoutException {
if (uow == null) {
return sync();
}
E result = super.sync(uow);
if (result != null) {
if (draft != null) {
adjustTtlAndWriteTime(draft);
}
if (entity != null && MapExportable.class.isAssignableFrom(entity.getMappingInterface())) {
adjustTtlAndWriteTime((MapExportable) result);
cacheUpdate(uow, result, bindFacetValues());
} else if (pojo != null) {
adjustTtlAndWriteTime((MapExportable) pojo);
cacheUpdate(uow, (E) pojo, bindFacetValues());
return (E) pojo;
}
}
return result;
}
public E batch(UnitOfWork uow) throws TimeoutException {
if (uow == null) {
throw new HelenusException("UnitOfWork cannot be null when batching operations.");
}
final E result;
if (draft != null) {
result = draft.build();
adjustTtlAndWriteTime(draft);
} else if (pojo != null) {
result = (E) pojo;
adjustTtlAndWriteTime((MapExportable) pojo);
} else {
result = null;
}
if (result != null) {
cacheUpdate(uow, result, bindFacetValues());
uow.batch(this);
return result;
}
return sync(uow);
}
@Override
public List<Facet> bindFacetValues() {
List<Facet> facets = bindFacetValues(entity.getFacets());
facets.addAll(
assignments
.values()
.stream()
.distinct()
.filter(o -> o != null)
.collect(Collectors.toList()));
return facets;
}
@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.
@ -19,14 +20,17 @@ 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, (short) 0),
INT(int.class, 0),
LONG(long.class, 0L),
FLOAT(float.class, 0.0f),
DOUBLE(double.class, 0.0);
BOOLEAN(boolean.class, false), BYTE(byte.class, (byte) 0x0), CHAR(char.class, (char) 0x0), SHORT(short.class, private static final Map<Class<?>, DefaultPrimitiveTypes> map =
(short) 0), INT(int.class, 0), LONG(long.class, 0L), FLOAT(float.class, 0.0f), DOUBLE(double.class, 0.0); new HashMap<Class<?>, DefaultPrimitiveTypes>();
private final Class<?> primitiveClass;
private final Object defaultValue;
private final static Map<Class<?>, DefaultPrimitiveTypes> map = new HashMap<Class<?>, DefaultPrimitiveTypes>();
static { static {
for (DefaultPrimitiveTypes type : DefaultPrimitiveTypes.values()) { for (DefaultPrimitiveTypes type : DefaultPrimitiveTypes.values()) {
@ -34,6 +38,9 @@ public enum DefaultPrimitiveTypes {
} }
} }
private final Class<?> primitiveClass;
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;
@ -50,5 +57,4 @@ public enum DefaultPrimitiveTypes {
public Object getDefaultValue() { public Object getDefaultValue() {
return defaultValue; return defaultValue;
} }
} }

View file

@ -0,0 +1,28 @@
/*
* 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 java.util.Set;
public interface Drafted<T> extends MapExportable {
Set<String> mutated();
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.
@ -15,15 +16,18 @@
*/ */
package net.helenus.core.reflect; package net.helenus.core.reflect;
import com.datastax.driver.core.Metadata;
import net.helenus.mapping.HelenusEntity; import net.helenus.mapping.HelenusEntity;
public interface DslExportable { public interface DslExportable {
public static final String GET_ENTITY_METHOD = "getHelenusMappingEntity"; String GET_ENTITY_METHOD = "getHelenusMappingEntity";
public static final String GET_PARENT_METHOD = "getParentDslHelenusPropertyNode"; String GET_PARENT_METHOD = "getParentDslHelenusPropertyNode";
String SET_METADATA_METHOD = "setCassandraMetadataForHelenusSession";
HelenusEntity getHelenusMappingEntity(); HelenusEntity getHelenusMappingEntity();
HelenusPropertyNode getParentDslHelenusPropertyNode(); HelenusPropertyNode getParentDslHelenusPropertyNode();
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,18 +16,14 @@
*/ */
package net.helenus.core.reflect; package net.helenus.core.reflect;
import java.lang.invoke.MethodHandle; import com.datastax.driver.core.*;
import java.lang.invoke.MethodHandles;
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.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.Objects;
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;
@ -39,21 +36,39 @@ import net.helenus.support.HelenusException;
public class DslInvocationHandler<E> implements InvocationHandler { public class DslInvocationHandler<E> implements InvocationHandler {
private final HelenusEntity entity; private final Class<E> iface;
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 Metadata metadata = null;
public DslInvocationHandler(Class<E> iface, ClassLoader classLoader, Optional<HelenusPropertyNode> parent, Metadata metadata) { public DslInvocationHandler(
Class<E> iface,
ClassLoader classLoader,
Optional<HelenusPropertyNode> parent,
Metadata metadata) {
this.entity = new HelenusMappingEntity(iface, metadata); this.metadata = metadata;
this.parent = parent; this.parent = parent;
this.iface = iface;
this.classLoader = classLoader;
}
if (this.entity != null) { public void setCassandraMetadataForHelenusSession(Metadata metadata) {
for (HelenusProperty prop : entity.getOrderedProperties()) { if (metadata != null) {
this.metadata = metadata;
entity = init(metadata);
}
}
private HelenusEntity init(Metadata metadata) {
HelenusEntity entity = new HelenusMappingEntity(iface, metadata);
Collection<HelenusProperty> properties = entity.getOrderedProperties();
if (properties != null) {
for (HelenusProperty prop : properties) {
map.put(prop.getGetterMethod(), prop); map.put(prop.getGetterMethod(), prop);
@ -62,8 +77,12 @@ public class DslInvocationHandler<E> implements InvocationHandler {
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);
} }
@ -71,23 +90,29 @@ public class DslInvocationHandler<E> implements InvocationHandler {
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;
}
} }
@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;
String methodName = method.getName(); String methodName = method.getName();
if ("equals".equals(methodName) && method.getParameterCount() == 1) { if ("equals".equals(methodName) && method.getParameterCount() == 1) {
@ -101,6 +126,15 @@ public class DslInvocationHandler<E> implements InvocationHandler {
return false; return false;
} }
if (DslExportable.SET_METADATA_METHOD.equals(methodName)
&& args.length == 1
&& args[0] instanceof Metadata) {
if (metadata == 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);
} }
@ -109,6 +143,14 @@ public class DslInvocationHandler<E> implements InvocationHandler {
return hashCode(); return hashCode();
} }
if (DslExportable.GET_PARENT_METHOD.equals(methodName)) {
return parent.get();
}
if (entity == null) {
entity = init(metadata);
}
if ("toString".equals(methodName)) { if ("toString".equals(methodName)) {
return entity.toString(); return entity.toString();
} }
@ -117,11 +159,6 @@ public class DslInvocationHandler<E> implements InvocationHandler {
return entity; return entity;
} }
if (DslExportable.GET_PARENT_METHOD.equals(methodName)) {
return parent.get();
}
HelenusProperty prop = map.get(method); HelenusProperty prop = map.get(method);
if (prop == null) { if (prop == null) {
prop = entity.getProperty(methodName); prop = entity.getProperty(methodName);
@ -138,7 +175,6 @@ public class DslInvocationHandler<E> implements InvocationHandler {
if (childDsl != null) { if (childDsl != null) {
return childDsl; return childDsl;
} }
} }
if (type instanceof DTDataType) { if (type instanceof DTDataType) {
@ -146,9 +182,7 @@ public class DslInvocationHandler<E> implements InvocationHandler {
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) {
@ -168,9 +202,7 @@ public class DslInvocationHandler<E> implements InvocationHandler {
default: default:
break; break;
} }
} }
throw new DslPropertyException(new HelenusPropertyNode(prop, parent)); throw new DslPropertyException(new HelenusPropertyNode(prop, parent));
@ -178,5 +210,4 @@ public class DslInvocationHandler<E> implements InvocationHandler {
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;
@ -61,7 +60,14 @@ public final class HelenusNamedProperty implements HelenusProperty {
} }
@Override @Override
public boolean caseSensitiveIndex() { return false; } public boolean caseSensitiveIndex() {
return false;
}
@Override
public boolean isIdempotent() {
return false;
}
@Override @Override
public Class<?> getJavaType() { public Class<?> getJavaType() {

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,7 +18,6 @@ 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;
@ -102,7 +102,5 @@ public final class HelenusPropertyNode implements Iterable<HelenusProperty> {
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,7 +17,6 @@
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;

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,6 @@ 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;
@ -114,5 +114,4 @@ public final class MapDsl<K, V> implements Map<K, V> {
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,11 +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";
public static final String TO_MAP_METHOD = "toMap"; String TO_READ_SET_METHOD = "toReadSet";
String PUT_METHOD = "put";
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,20 +16,32 @@
*/ */
package net.helenus.core.reflect; package net.helenus.core.reflect;
import java.lang.invoke.MethodHandle; 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.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.cache.CacheUtil;
import net.helenus.mapping.MappingUtil;
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 { public class MapperInvocationHandler<E> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -7044209982830584984L;
private final Map<String, Object> src; private Map<String, Object> src;
private final Set<String> read = new HashSet<String>();
private final Class<E> iface; private final Class<E> iface;
public MapperInvocationHandler(Class<E> iface, Map<String, Object> src) { public MapperInvocationHandler(Class<E> iface, Map<String, Object> src) {
@ -36,27 +49,46 @@ public class MapperInvocationHandler<E> implements InvocationHandler {
this.iface = iface; this.iface = iface;
} }
@Override private Object invokeDefault(Object proxy, Method method, Object[] args) throws Throwable {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // NOTE: This is reflection magic to invoke (non-recursively) a default method
// implemented on an interface
if (method.isDefault()) { // that we've proxied (in ReflectionDslInstantiator). I found the answer in this
// NOTE: This is reflection magic to invoke (non-recursively) a default method implemented on an interface // article.
// that we've proxied (in ReflectionDslInstantiator). I found the answer in this 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, int.class); Constructor<MethodHandles.Lookup> constructor =
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 class. // Now we need to lookup and invoke special the default method on the interface
// class.
final Class<?> declaringClass = method.getDeclaringClass(); final Class<?> declaringClass = method.getDeclaringClass();
Object result = constructor.newInstance(declaringClass, MethodHandles.Lookup.PRIVATE) Object result =
constructor
.newInstance(declaringClass, MethodHandles.Lookup.PRIVATE)
.unreflectSpecial(method, declaringClass) .unreflectSpecial(method, declaringClass)
.bindTo(proxy) .bindTo(proxy)
.invokeWithArguments(args); .invokeWithArguments(args);
return result; return result;
} }
private Object writeReplace() {
return new SerializationProxy<E>(this);
}
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required.");
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Transient, default methods should simply be invoked as-is.
if (method.isDefault() && method.getDeclaredAnnotation(Transient.class) != null) {
return invokeDefault(proxy, method, args);
}
String methodName = method.getName(); String methodName = method.getName();
if ("equals".equals(methodName) && method.getParameterCount() == 1) { if ("equals".equals(methodName) && method.getParameterCount() == 1) {
@ -65,11 +97,100 @@ public class MapperInvocationHandler<E> implements InvocationHandler {
return false; return false;
} }
if (Proxy.isProxyClass(otherObj.getClass())) { if (Proxy.isProxyClass(otherObj.getClass())) {
return this == Proxy.getInvocationHandler(otherObj); if (this == Proxy.getInvocationHandler(otherObj)) {
return true;
}
}
if (otherObj instanceof MapExportable) {
return MappingUtil.compareMaps((MapExportable) otherObj, src);
} }
return false; return false;
} }
if (MapExportable.PUT_METHOD.equals(methodName) && method.getParameterCount() == 2) {
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 (Entity.WRITTEN_AT_METHOD.equals(methodName) && method.getParameterCount() == 1) {
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 (Entity.TOKEN_OF_METHOD.equals(methodName) && method.getParameterCount() == 0) {
Long v = (Long) src.get("");
if (v != null) {
return v;
}
return 0L;
}
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;
}
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 (MapExportable.TO_READ_SET_METHOD.equals(methodName)) {
return read;
}
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);
} }
@ -82,30 +203,92 @@ public class MapperInvocationHandler<E> implements InvocationHandler {
return iface.getSimpleName() + ": " + src.toString(); return iface.getSimpleName() + ": " + src.toString();
} }
if (MapExportable.TO_MAP_METHOD.equals(methodName)) { if ("writeReplace".equals(methodName)) {
return Collections.unmodifiableMap(src); return new SerializationProxy(this);
} }
Object value = src.get(methodName); 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) { if (value == null) {
Class<?> returnType = method.getReturnType(); Class<?> returnType = method.getReturnType();
if (returnType.isPrimitive()) { // 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); DefaultPrimitiveTypes type = DefaultPrimitiveTypes.lookup(returnType);
if (type == null) { if (type == null) {
throw new HelenusException("unknown primitive type " + returnType); throw new HelenusException("unknown primitive type " + returnType);
} }
return type.getDefaultValue(); return type.getDefaultValue();
} }
} }
return value; 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, Metadata metadata) { public <E> E instantiate(
DslInvocationHandler<E> handler = new DslInvocationHandler<E>(iface, classLoader, parent, metadata); Class<E> iface,
E proxy = (E) Proxy.newProxyInstance(classLoader, new Class[]{iface, DslExportable.class}, handler); ClassLoader classLoader,
Optional<HelenusPropertyNode> parent,
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; 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,8 +20,7 @@ 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) {
@ -29,7 +29,5 @@ public final class ReflectionInstantiator {
} 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,13 +16,12 @@
*/ */
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
@ -29,9 +29,10 @@ public enum ReflectionMapperInstantiator implements MapperInstantiator {
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 =
(E)
Proxy.newProxyInstance(
classLoader, new Class[] {iface, MapExportable.class, Serializable.class}, handler);
return proxy; 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.
@ -18,7 +19,6 @@ package net.helenus.core.reflect;
import java.util.Collection; import java.util.Collection;
import java.util.Iterator; import java.util.Iterator;
import java.util.Set; import java.util.Set;
import net.helenus.support.HelenusMappingException; import net.helenus.support.HelenusMappingException;
public final class SetDsl<V> implements Set<V> { public final class SetDsl<V> implements Set<V> {

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,7 +17,6 @@
package net.helenus.mapping; package net.helenus.mapping;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import net.helenus.mapping.annotation.ClusteringColumn; import net.helenus.mapping.annotation.ClusteringColumn;
import net.helenus.mapping.annotation.Column; import net.helenus.mapping.annotation.Column;
import net.helenus.mapping.annotation.PartitionKey; import net.helenus.mapping.annotation.PartitionKey;
@ -103,15 +103,21 @@ public final class ColumnInformation {
private void ensureSingleColumnType(ColumnType columnTypeLocal, Method getter) { private void ensureSingleColumnType(ColumnType columnTypeLocal, Method getter) {
if (columnTypeLocal != ColumnType.COLUMN) { if (columnTypeLocal != ColumnType.COLUMN) {
throw new HelenusMappingException("property can be annotated only by a single column type " + getter); throw new HelenusMappingException(
"property can be annotated only by a single column type " + getter);
} }
} }
@Override @Override
public String toString() { public String toString() {
return "ColumnInformation [columnName=" + columnName + ", columnType=" + columnType + ", ordinal=" + ordinal return "ColumnInformation [columnName="
+ ", ordering=" + ordering + "]"; + columnName
+ ", columnType="
+ columnType
+ ", ordinal="
+ ordinal
+ ", ordering="
+ ordering
+ "]";
} }
} }

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.mapping; package net.helenus.mapping;
public enum ColumnType { public enum ColumnType {
PARTITION_KEY, CLUSTERING_COLUMN, STATIC_COLUMN, COLUMN; PARTITION_KEY,
CLUSTERING_COLUMN,
STATIC_COLUMN,
COLUMN;
} }

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,6 +17,8 @@
package net.helenus.mapping; package net.helenus.mapping;
import java.util.Collection; import java.util.Collection;
import java.util.List;
import net.helenus.core.cache.Facet;
public interface HelenusEntity { public interface HelenusEntity {
@ -31,4 +34,7 @@ public interface HelenusEntity {
HelenusProperty getProperty(String name); HelenusProperty getProperty(String name);
List<Facet> getFacets();
boolean isDraftable();
} }

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.mapping; package net.helenus.mapping;
public enum HelenusEntityType { public enum HelenusEntityType {
TABLE, TUPLE, UDT; TABLE,
VIEW,
TUPLE,
UDT;
} }

View file

@ -1,6 +1,7 @@
/* /*
* *
* 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.
* You may obtain a copy of the License at * You may obtain a copy of the License at
@ -15,20 +16,23 @@
*/ */
package net.helenus.mapping; package net.helenus.mapping;
import java.lang.reflect.Method; import com.datastax.driver.core.DefaultMetadata;
import java.util.*; import com.datastax.driver.core.Metadata;
import com.datastax.driver.core.*;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableMap;
import java.lang.reflect.Method;
import com.google.common.collect.ImmutableSet; import java.util.*;
import com.google.common.reflect.TypeToken; import javax.validation.ConstraintValidator;
import net.helenus.config.HelenusSettings; import net.helenus.config.HelenusSettings;
import net.helenus.core.Helenus; import net.helenus.core.Helenus;
import net.helenus.core.annotation.Cacheable; import net.helenus.core.annotation.Cacheable;
import net.helenus.core.cache.Facet;
import net.helenus.core.cache.UnboundFacet;
import net.helenus.mapping.annotation.*; import net.helenus.mapping.annotation.*;
import net.helenus.mapping.validator.DistinctValidator;
import net.helenus.support.HelenusMappingException; import net.helenus.support.HelenusMappingException;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
public final class HelenusMappingEntity implements HelenusEntity { public final class HelenusMappingEntity implements HelenusEntity {
@ -36,9 +40,11 @@ public final class HelenusMappingEntity implements HelenusEntity {
private final HelenusEntityType type; private final HelenusEntityType type;
private final IdentityName name; private final IdentityName name;
private final boolean cacheable; private final boolean cacheable;
private final boolean draftable;
private final ImmutableMap<String, Method> methods; private final ImmutableMap<String, Method> methods;
private final ImmutableMap<String, HelenusProperty> props; private final ImmutableMap<String, HelenusProperty> props;
private final ImmutableList<HelenusProperty> orderedProps; private final ImmutableList<HelenusProperty> orderedProps;
private final List<Facet> facets;
public HelenusMappingEntity(Class<?> iface, Metadata metadata) { public HelenusMappingEntity(Class<?> iface, Metadata metadata) {
this(iface, autoDetectType(iface), metadata); this(iface, autoDetectType(iface), metadata);
@ -56,19 +62,33 @@ public final class HelenusMappingEntity implements HelenusEntity {
HelenusSettings settings = Helenus.settings(); HelenusSettings settings = Helenus.settings();
Map<String, Method> methods = new HashMap<String, Method>();
for (Method m : iface.getDeclaredMethods()) {
methods.put(m.getName(), m);
}
List<Method> methods = new ArrayList<Method>(); for (Class<?> c : ClassUtils.getAllInterfaces(iface)) {
if (c.getDeclaredAnnotation(Table.class) != null
methods.addAll(Arrays.asList(iface.getDeclaredMethods())); || c.getDeclaredAnnotation(InheritedTable.class) != null) {
for (Class<?> c : iface.getInterfaces()) { for (Method m : c.getDeclaredMethods()) {
methods.addAll(Arrays.asList(c.getDeclaredMethods())); Method o = methods.get(m.getName());
if (o != null) {
// Prefer overridden method implementation.
if (o.getDeclaringClass().isAssignableFrom(m.getDeclaringClass())) {
methods.put(m.getName(), m);
}
} else {
methods.put(m.getName(), m);
}
}
}
} }
List<HelenusProperty> propsLocal = new ArrayList<HelenusProperty>(); List<HelenusProperty> propsLocal = new ArrayList<HelenusProperty>();
ImmutableMap.Builder<String, HelenusProperty> propsBuilder = ImmutableMap.builder(); ImmutableMap.Builder<String, HelenusProperty> propsBuilder = ImmutableMap.builder();
ImmutableMap.Builder<String, Method> methodsBuilder = ImmutableMap.builder(); ImmutableMap.Builder<String, Method> methodsBuilder = ImmutableMap.builder();
for (Method method : methods) { for (Method method : methods.values()) {
if (settings.getGetterMethodDetector().apply(method)) { if (settings.getGetterMethodDetector().apply(method)) {
@ -80,9 +100,7 @@ public final class HelenusMappingEntity implements HelenusEntity {
propsBuilder.put(prop.getPropertyName(), prop); propsBuilder.put(prop.getPropertyName(), prop);
propsLocal.add(prop); propsLocal.add(prop);
} }
} }
} }
this.methods = methodsBuilder.build(); this.methods = methodsBuilder.build();
@ -93,7 +111,107 @@ public final class HelenusMappingEntity implements HelenusEntity {
validateOrdinals(); validateOrdinals();
// Caching
cacheable = (null != iface.getDeclaredAnnotation(Cacheable.class)); cacheable = (null != iface.getDeclaredAnnotation(Cacheable.class));
// Draft
Class<?> draft;
try {
draft = Class.forName(iface.getName() + "$Draft");
} catch (Exception ignored) {
draft = null;
}
draftable = (draft != null);
// Materialized view
List<HelenusProperty> primaryKeyProperties = new ArrayList<>();
ImmutableList.Builder<Facet> facetsBuilder = ImmutableList.builder();
if (iface.getDeclaredAnnotation(MaterializedView.class) == null) {
facetsBuilder.add(new Facet("table", name.toCql()).setFixed());
} else {
facetsBuilder.add(
new Facet("table", Helenus.entity(iface.getInterfaces()[0]).getName().toCql())
.setFixed());
}
for (HelenusProperty prop : orderedProps) {
switch (prop.getColumnType()) {
case PARTITION_KEY:
case CLUSTERING_COLUMN:
primaryKeyProperties.add(prop);
break;
default:
if (primaryKeyProperties != null && primaryKeyProperties.size() > 0) {
facetsBuilder.add(new UnboundFacet(primaryKeyProperties));
primaryKeyProperties = null;
}
for (ConstraintValidator<?, ?> constraint :
MappingUtil.getValidators(prop.getGetterMethod())) {
if (constraint instanceof DistinctValidator) {
DistinctValidator validator = (DistinctValidator) constraint;
String[] values = validator.constraintAnnotation.value();
UnboundFacet facet;
if (values != null && values.length >= 1 && !(StringUtils.isBlank(values[0]))) {
List<HelenusProperty> props = new ArrayList<HelenusProperty>(values.length + 1);
props.add(prop);
for (String value : values) {
for (HelenusProperty p : orderedProps) {
String name = p.getPropertyName();
if (name.equals(value) && !name.equals(prop.getPropertyName())) {
props.add(p);
}
}
}
facet = new UnboundFacet(props, validator.alone(), validator.combined());
} else {
facet = new UnboundFacet(prop, validator.alone(), validator.combined());
}
facetsBuilder.add(facet);
break;
}
}
}
}
if (primaryKeyProperties != null && primaryKeyProperties.size() > 0) {
facetsBuilder.add(new UnboundFacet(primaryKeyProperties));
}
this.facets = facetsBuilder.build();
}
private static IdentityName resolveName(Class<?> iface, HelenusEntityType type) {
switch (type) {
case TABLE:
return MappingUtil.getTableName(iface, true);
case VIEW:
return MappingUtil.getViewName(iface, true);
case TUPLE:
return IdentityName.of(MappingUtil.getDefaultEntityName(iface), false);
case UDT:
return MappingUtil.getUserDefinedTypeName(iface, true);
}
throw new HelenusMappingException("invalid entity type " + type + " in " + type);
}
private static HelenusEntityType autoDetectType(Class<?> iface) {
Objects.requireNonNull(iface, "empty iface");
if (null != iface.getDeclaredAnnotation(Table.class)) {
return HelenusEntityType.TABLE;
} else if (null != iface.getDeclaredAnnotation(MaterializedView.class)) {
return HelenusEntityType.VIEW;
} else if (null != iface.getDeclaredAnnotation(Tuple.class)) {
return HelenusEntityType.TUPLE;
} else if (null != iface.getDeclaredAnnotation(UDT.class)) {
return HelenusEntityType.UDT;
}
throw new HelenusMappingException(
"entity must be annotated by @Table or @Tuple or @UserDefinedType " + iface);
} }
@Override @Override
@ -106,6 +224,11 @@ public final class HelenusMappingEntity implements HelenusEntity {
return cacheable; return cacheable;
} }
@Override
public boolean isDraftable() {
return draftable;
}
@Override @Override
public Class<?> getMappingInterface() { public Class<?> getMappingInterface() {
return iface; return iface;
@ -126,52 +249,19 @@ public final class HelenusMappingEntity implements HelenusEntity {
return props.get(name); return props.get(name);
} }
@Override
public List<Facet> getFacets() {
return facets;
}
@Override @Override
public IdentityName getName() { public IdentityName getName() {
return name; return name;
} }
private static IdentityName resolveName(Class<?> iface, HelenusEntityType type) {
switch (type) {
case TABLE :
return MappingUtil.getTableName(iface, true);
case TUPLE :
return IdentityName.of(MappingUtil.getDefaultEntityName(iface), false);
case UDT :
return MappingUtil.getUserDefinedTypeName(iface, true);
}
throw new HelenusMappingException("invalid entity type " + type + " in " + type);
}
private static HelenusEntityType autoDetectType(Class<?> iface) {
Objects.requireNonNull(iface, "empty iface");
if (null != iface.getDeclaredAnnotation(Table.class)) {
return HelenusEntityType.TABLE;
}
else if (null != iface.getDeclaredAnnotation(Tuple.class)) {
return HelenusEntityType.TUPLE;
}
else if (null != iface.getDeclaredAnnotation(UDT.class)) {
return HelenusEntityType.UDT;
}
throw new HelenusMappingException("entity must be annotated by @Table or @Tuple or @UserDefinedType " + iface);
}
private void validateOrdinals() { private void validateOrdinals() {
switch (getType()) { switch (getType()) {
case TABLE: case TABLE:
validateOrdinalsForTable(); validateOrdinalsForTable();
break; break;
@ -183,7 +273,6 @@ public final class HelenusMappingEntity implements HelenusEntity {
default: default:
break; break;
} }
} }
private void validateOrdinalsForTable() { private void validateOrdinalsForTable() {
@ -198,11 +287,12 @@ public final class HelenusMappingEntity implements HelenusEntity {
int ordinal = prop.getOrdinal(); int ordinal = prop.getOrdinal();
switch (type) { switch (type) {
case PARTITION_KEY: case PARTITION_KEY:
if (partitionKeys.get(ordinal)) { if (partitionKeys.get(ordinal)) {
throw new HelenusMappingException( throw new HelenusMappingException(
"detected two or more partition key columns with the same ordinal " + ordinal + " in " "detected two or more partition key columns with the same ordinal "
+ ordinal
+ " in "
+ prop.getEntity()); + prop.getEntity());
} }
partitionKeys.set(ordinal); partitionKeys.set(ordinal);
@ -210,8 +300,11 @@ public final class HelenusMappingEntity implements HelenusEntity {
case CLUSTERING_COLUMN: case CLUSTERING_COLUMN:
if (clusteringColumns.get(ordinal)) { if (clusteringColumns.get(ordinal)) {
throw new HelenusMappingException("detected two or clustering columns with the same ordinal " throw new HelenusMappingException(
+ ordinal + " in " + prop.getEntity()); "detected two or clustering columns with the same ordinal "
+ ordinal
+ " in "
+ prop.getEntity());
} }
clusteringColumns.set(ordinal); clusteringColumns.set(ordinal);
break; break;
@ -219,30 +312,36 @@ public final class HelenusMappingEntity implements HelenusEntity {
default: default:
break; break;
} }
} }
} }
private void validateOrdinalsInTuple() { private void validateOrdinalsInTuple() {
boolean[] ordinals = new boolean[props.size()]; boolean[] ordinals = new boolean[props.size()];
getOrderedProperties().forEach(p -> { getOrderedProperties()
.forEach(
p -> {
int ordinal = p.getOrdinal(); int ordinal = p.getOrdinal();
if (ordinal < 0 || ordinal >= ordinals.length) { if (ordinal < 0 || ordinal >= ordinals.length) {
throw new HelenusMappingException("invalid ordinal " + ordinal + " found for property " throw new HelenusMappingException(
+ p.getPropertyName() + " in " + p.getEntity()); "invalid ordinal "
+ ordinal
+ " found for property "
+ p.getPropertyName()
+ " in "
+ p.getEntity());
} }
if (ordinals[ordinal]) { if (ordinals[ordinal]) {
throw new HelenusMappingException( throw new HelenusMappingException(
"detected two or more properties with the same ordinal " + ordinal + " in " + p.getEntity()); "detected two or more properties with the same ordinal "
+ ordinal
+ " in "
+ p.getEntity());
} }
ordinals[ordinal] = true; ordinals[ordinal] = true;
}); });
for (int i = 0; i != ordinals.length; ++i) { for (int i = 0; i != ordinals.length; ++i) {
@ -250,15 +349,18 @@ public final class HelenusMappingEntity implements HelenusEntity {
throw new HelenusMappingException("detected absent ordinal " + i + " in " + this); throw new HelenusMappingException("detected absent ordinal " + i + " in " + this);
} }
} }
} }
@Override @Override
public String toString() { public String toString() {
StringBuilder str = new StringBuilder(); StringBuilder str = new StringBuilder();
str.append(iface.getSimpleName()).append("(").append(name.getName()).append(") ") str.append(iface.getSimpleName())
.append(type.name().toLowerCase()).append(":\n"); .append("(")
.append(name.getName())
.append(") ")
.append(type.name().toLowerCase())
.append(":\n");
for (HelenusProperty prop : getOrderedProperties()) { for (HelenusProperty prop : getOrderedProperties()) {
str.append(prop.toString()); str.append(prop.toString());
@ -266,5 +368,4 @@ public final class HelenusMappingEntity implements HelenusEntity {
} }
return str.toString(); return str.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,15 +16,13 @@
*/ */
package net.helenus.mapping; package net.helenus.mapping;
import com.datastax.driver.core.Metadata;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.lang.reflect.Type; import java.lang.reflect.Type;
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 com.datastax.driver.core.Metadata;
import net.helenus.core.SessionRepository; import net.helenus.core.SessionRepository;
import net.helenus.mapping.javatype.AbstractJavaType; import net.helenus.mapping.javatype.AbstractJavaType;
import net.helenus.mapping.javatype.MappingJavaTypes; import net.helenus.mapping.javatype.MappingJavaTypes;
@ -37,6 +36,7 @@ public final class HelenusMappingProperty implements HelenusProperty {
private final String propertyName; private final String propertyName;
private final Optional<IdentityName> indexName; private final Optional<IdentityName> indexName;
private final boolean caseSensitiveIndex; private final boolean caseSensitiveIndex;
private final boolean idempotent;
private final ColumnInformation columnInfo; private final ColumnInformation columnInfo;
@ -44,12 +44,10 @@ public final class HelenusMappingProperty implements HelenusProperty {
private final Class<?> javaType; private final Class<?> javaType;
private final AbstractJavaType abstractJavaType; private final AbstractJavaType abstractJavaType;
private final AbstractDataType dataType; private final AbstractDataType dataType;
private final ConstraintValidator<? extends Annotation, ?>[] validators;
private volatile Optional<Function<Object, Object>> readConverter = null; private volatile Optional<Function<Object, Object>> readConverter = null;
private volatile Optional<Function<Object, Object>> writeConverter = null; private volatile Optional<Function<Object, Object>> writeConverter = null;
private final ConstraintValidator<? extends Annotation, ?>[] validators;
public HelenusMappingProperty(HelenusMappingEntity entity, Method getter, Metadata metadata) { public HelenusMappingProperty(HelenusMappingEntity entity, Method getter, Metadata metadata) {
this.entity = entity; this.entity = entity;
this.getter = getter; this.getter = getter;
@ -60,12 +58,22 @@ public final class HelenusMappingProperty implements HelenusProperty {
this.columnInfo = new ColumnInformation(getter); this.columnInfo = new ColumnInformation(getter);
switch (this.columnInfo.getColumnType()) {
case PARTITION_KEY:
case CLUSTERING_COLUMN:
this.idempotent = true;
break;
default:
this.idempotent = MappingUtil.idempotent(getter);
}
this.genericJavaType = getter.getGenericReturnType(); this.genericJavaType = getter.getGenericReturnType();
this.javaType = getter.getReturnType(); this.javaType = getter.getReturnType();
this.abstractJavaType = MappingJavaTypes.resolveJavaType(this.javaType); this.abstractJavaType = MappingJavaTypes.resolveJavaType(this.javaType);
this.dataType = abstractJavaType.resolveDataType(this.getter, this.genericJavaType, this.dataType =
this.columnInfo.getColumnType(), metadata); abstractJavaType.resolveDataType(
this.getter, this.genericJavaType, this.columnInfo.getColumnType(), metadata);
this.validators = MappingUtil.getValidators(getter); this.validators = MappingUtil.getValidators(getter);
} }
@ -115,6 +123,11 @@ public final class HelenusMappingProperty implements HelenusProperty {
return caseSensitiveIndex; return caseSensitiveIndex;
} }
@Override
public boolean isIdempotent() {
return idempotent;
}
@Override @Override
public String getPropertyName() { public String getPropertyName() {
return propertyName; return propertyName;
@ -169,7 +182,6 @@ public final class HelenusMappingProperty implements HelenusProperty {
ColumnType type = this.getColumnType(); ColumnType type = this.getColumnType();
switch (type) { switch (type) {
case PARTITION_KEY: case PARTITION_KEY:
str.append("partition_key["); str.append("partition_key[");
str.append(this.getOrdinal()); str.append(this.getOrdinal());
@ -192,7 +204,6 @@ public final class HelenusMappingProperty implements HelenusProperty {
case COLUMN: case COLUMN:
break; break;
} }
Optional<IdentityName> idx = this.getIndexName(); Optional<IdentityName> idx = this.getIndexName();
@ -202,5 +213,4 @@ public final class HelenusMappingProperty implements HelenusProperty {
return str.toString(); return str.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.
@ -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.type.AbstractDataType; import net.helenus.mapping.type.AbstractDataType;
@ -39,6 +38,8 @@ public interface HelenusProperty {
boolean caseSensitiveIndex(); boolean caseSensitiveIndex();
boolean isIdempotent();
Class<?> getJavaType(); Class<?> getJavaType();
AbstractDataType getDataType(); AbstractDataType getDataType();
@ -54,5 +55,4 @@ public interface HelenusProperty {
Optional<Function<Object, Object>> getWriteConverter(SessionRepository repository); Optional<Function<Object, Object>> getWriteConverter(SessionRepository repository);
ConstraintValidator<? extends Annotation, ?>[] getValidators(); ConstraintValidator<? extends Annotation, ?>[] getValidators();
} }

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