diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..76497e9 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,7 @@ +language: java +sudo: false # faster builds + +script: "mvn cobertura:cobertura" + +after_success: + - bash <(curl -s https://codecov.io/bash) \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..7d2c190 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,166 @@ +# act-ebean-java7 CHANGE LOG + +1.8.1 +* Allow app to define customised ebean IdGenerator #22 + +1.8.0 +* update to act-1.8.29 +* update EbeanDao - add `processLikeValue` method + +1.7.9 - 30/Sep/2019 +* update to act-1.8.28 +* update to act-sql-common-1.5.1 + +1.7.8 - 03/Jul/2019 +* update to act-1.8.25 +* update to act-sql-common-1.5.0 + +1.7.7 - 16/Jun/2019 +* update to act-1.8.23 +* update to act-sql-common-1.4.6 + +1.7.6 - 20/Apr/2019 +* Update to act-1.8.20 + +1.7.5 - 20/Nov/2018 +* Catch up Ebean2 fix on GH28 +* update act to 1.8.12 + +1.7.4 - 04/Nov/2018 +* update to act-1.8.9 +* update to act-sql-common to 1.4.4 + +1.7.3 - 30/Oct/2018 +* update to act-1.8.8 +* update to act-sql-common 1.4.3 + +1.7.2 - 19/Jun/2018 +* update to act-1.8.8-RC10 +* update act-sql-common to 1.4.2 + +1.7.1 - 7/Jun/2018 +* update act to 1.8.8-RC9 +* update act-sql-common to 1.4.1 +* catch up act-ebean-1.7.1 updates + +1.7.0 - 29/May/2018 +* update act to 1.8.8-RC8 +* update act-sql-common to 1.4.0 + +1.6.2 - 19/May/2018 +* catch up act-ebean 1.6.5 changes + +1.6.1 - 14/May/2018 +* catch act-ebean 1.6.4 changes + +1.6.0 - 13/May/2018 +* update act to 1.8.8-RC4 +* Disable Ebean classpath search #21 +* Register global mapping filter to avoid copying ebean enhanced fields #20 + + +1.5.3 - 02/Apr/2018 +* update act to 1.8.5 +* update act-sql-common to 1.3.3 + +1.5.2 - 25/Mar/2018 +* rename to act-ebean-java7 +* update act to 1.8.2 +* update act-sql-common to 1.3.2 + +------------------------------------ + +Previous named act-ebean + +1.5.1 - 11/Mar/2018 +* update to act-1.8.1 +* update to act-sql-common-1.3.1 + +1.5.0 - 4/Mar/2018 +* update to act-1.8.0 +* catch ebean2 change: Support act timestamp annotation #19 + +1.4.0 - 19/Feb/2018 +* update to act-1.7.0 +* update to act-sql-common-1.3.0 + +1.3.2 - 23/Jan/2018 +* update to act-1.6.5 +* update act-sql-common to 1.2.1 + +1.3.1 - 11/Jan/2018 +* update to act-1.6.2 +* add `@BindOn` annotation to `EbeanConfigLoaded` event class to allow early bind of event listeners + +1.3.0 - 28/Dec/2017 +* update to act-1.6.0 + +1.2.3 +* update to act-1.4.11, act-sql-common-1.1.1 +* apply oslg-bootstrap version mechanism +* improve maven build process + +1.2.2 +* catch up to sql-common-1.1.1 + +1.2.1 +* catch up to ebean2-1.1.1 + +1.2.0 +* catch up to act-1.4.0 +* catch up to act-sql-common to 1.1.0 + +1.1.5 +- NPE when no third party datasource configured #16 +- update sql-common to 1.0.2 + +1.1.4 +- update to act-sql-common-1.0.1 +- Ebean Agent loaded twice if there are two ebean db services #14 +- The datasource created in sql-common not used when creating ebean server #15 + +1.1.3 +- It should use ebean's naming convention by default #13 + +1.1.2 +- Migrate to act-1.1.0 new DB architecture #12 + +1.1.1 +- HikariDataSourceProvider.confKeyMapping error #10 +- DruidDataSourceProvider.confKeyMapping() error #11 + +1.1.0 +- Support plugin different datasource solution #9 +- change mysql jdbc driver class name #8 +- Support Druid database #6 + +1.0.5 +- It reports XXX table not found when start app in dev mode #7 + +1.0.4 +- take out version range from pom.xml. See https://issues.apache.org/jira/browse/MNG-3092 + +1.0.3 +- version number of mistake + +1.0.2 +- use HikariCP for connection pool #4 +- Make it easy to do low level JDBC logic #5 + +1.0.1 +- EbeanDao.drop() method cause JdbcSQLException #1 +- EbeanInjectionListener not effect on User defined Dao #3 + +1.0.0 +- the first formal release + +0.7.0 - update act to 0.7.0 +0.6.0 - update act to 0.6.0 +0.5.0 - upgrade to act 0.5.0 (to reserve 0.4.0 for techempower test) +0.4.0 - upgrade to act 0.4.0 +0.3.1 - upgrade to act 0.3.1 +0.3.0 - upgrade to act 0.3.0 +0.2.0 - upgrade to act 0.2.0 + - upgrade to ebean-8 +0.1.2 - upgrade to act 0.1.2 +0.1.1 - baseline version diff --git a/README.md b/README.md index 782b4dd..c1596e7 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,35 @@ # act-ebean -Ebean plugin for ACT Framework +[![APL v2](https://img.shields.io/badge/license-Apache%202-blue.svg)](http://www.apache.org/licenses/LICENSE-2.0.html) +[![Maven Central](https://img.shields.io/maven-central/v/org.actframework/act-ebean.svg)](http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22act-ebean%22) +[![Build Status](https://travis-ci.org/actframework/act-ebean.svg?branch=master)](https://travis-ci.org/actframework/act-ebean) +[![codecov](https://codecov.io/gh/actframework/act-ebean/branch/master/graph/badge.svg)](https://codecov.io/gh/actframework/act-ebean) +[![Javadocs](http://www.javadoc.io/badge/org.actframework/act-ebean.svg?color=blue)](http://www.javadoc.io/doc/org.actframework/act-ebean) + + +Ebean plugin for ACT Framework. + +## act-ebean vs act-ebean2 + +* act-ebean: support JDK7 and JDK8 +* act-ebean2: uses latest ebean version but can only run on JDK8 + +## Versions + +| ActFramework | act-ebean | +| ------------ | -------- | +| 1.0.x | 1.0.x, 1.1.0, 1.1.1 | +| 1.1.x | 1.1.2+ | + +## Configuration + +For configuration items, please refer to [act-sql-common](https://github.com/actframework/act-sql-common) + +If application needs to manipulate the loaded configuration before `EbeanServer` is created, try add the following method in any Class: + +```java +@OnEvent(beforeAppStart = true) +public static void configureEbean(ServerConfig config) { + // do whatever required on ebean's ServerConfig +} +``` diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 0000000..5bd34d5 --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,209 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml old mode 100755 new mode 100644 index e560f00..f5c1956 --- a/pom.xml +++ b/pom.xml @@ -1,6 +1,6 @@ - 4.0.0 - org.actframework - act-ebean + act-ebean-java7 jar - 1.0.5-SNAPSHOT + 1.8.1-SNAPSHOT ACT Ebean - The Ebean Plugin for Actframework SQL database access + Provides SQL database access through Ebean ORM library (java 7+) http://actframework.org/plugin/ebean + 2015 + + + ActFramework + http://actframework.org + + + + org.actframework + parent + 1.8.29 + - UTF-8 - UTF-8 - git@github.com:actframework/act-ebean.git - 1.0.5 + git@github.com:actframework/act-ebean.git + 8.8.1 8.1.1 2.1.2 - 2.7.0 - 2.6.1 - - - org.sonatype.oss - oss-parent - 7 - + 1.6.0 + + - scm:git:${git.url} - scm:git:${git.url} - ${git.url} + scm:git:${scm.url} + scm:git:${scm.url} + ${scm.url} - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - repo - - - - - - - src/main/resources - true - - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.3 - - 1.7 - 1.7 - - - - org.apache.maven.plugins - maven-source-plugin - 2.1.2 - - true - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9 - - src/etc/javadoc.css - - - - - gen-javadoc - prepare-package - - javadoc - - - - attach-javadocs - - jar - - - - - - - - - - junit - junit - 4.10 - test - - - - org.mockito - mockito-core - ${mockito-core.version} - test - - org.avaje.ebean ebean @@ -177,72 +77,17 @@ org.actframework - act - ${act.version} - provided + act-sql-common + ${act-sql-common.version} - com.zaxxer - HikariCP - ${HikariCP.version} - compile + org.actframework + act + ${act.version} + provided - - - dist - - - - org.apache.maven.plugins - maven-assembly-plugin - 2.2 - - - ${basedir}/assembly-dist.xml - - gnu - - - - make-assembly - package - - single - - - - ${basedir}/assembly-dist.xml - - - - - - - org.apache.maven.plugins - maven-source-plugin - - - attach-sources - - jar - - - - - - com.mycila.maven-license-plugin - maven-license-plugin - -
src/etc/header.txt
-
-
-
-
-
-
-
diff --git a/src/etc/javadoc.css b/src/etc/javadoc.css new file mode 100644 index 0000000..e951d01 --- /dev/null +++ b/src/etc/javadoc.css @@ -0,0 +1,622 @@ +@import url(highlight.css); + +body { + background-color:#222222; + color:#D2DECA; + font-family:'Roboto', 'San Francisco Text', 'Noto Sans', 'Segoe UI', 'Helvetica Neue', Arial, sans-serif; + font-size:16px; + margin:0; +} +a:link, a:visited { + text-decoration:none; + color:#999; +} +a:hover, a:focus { + text-decoration:underline; +} +a:active { + text-decoration:none; + color:#999; +} +a[name] { + color:#D2DECA; +} +a[name]:hover { + text-decoration:none; + color:#D2DECA; +} +/*[MOD] +pre { + font-family:'Roboto Mono', 'Envy Code R', Consolas, Menlo, Monaco,monospace; + font-size:14px; +}*/ +h1 { + font-size:20px; +} +h2 { + font-size:18px; +} +h3 { + font-size:16px; + font-style:italic; +} +h4 { + font-size:13px; +} +h5 { + font-size:12px; +} +h6 { + font-size:11px; +} +ul { + list-style-type:disc; +} + +code, tt { + font-family:'Roboto Mono', 'Envy Code R', Consolas, Menlo, Monaco,monospace; + font-size:14px; + padding-top:4px; + margin-top:8px; + line-height:1.4em; +} +dt code { + font-family:'Roboto Mono', 'Envy Code R', Consolas, Menlo, Monaco,monospace; + font-size:14px; + padding-top:4px; +} +table tr td dt code { + font-family:'Roboto Mono', 'Envy Code R', Consolas, Menlo, Monaco,monospace; + font-size:14px; + vertical-align:top; + padding-top:4px; +} +sup { + font-size:8px; +} +/* +Document title and Copyright styles +*/ +.clear { + clear:both; + height:0px; + overflow:hidden; +} +.aboutLanguage { + float:right; + padding:0px 21px; + font-size:.8em; + z-index:200; + margin-top:-7px; +} +.legalCopy { + margin-left:.5em; +} +.bar a, .bar a:link, .bar a:visited, .bar a:active { + color:#222222; + text-decoration:none; +} +.bar a:hover, .bar a:focus { + color:#999; +} +.tab { + background-color:#004F66; + color:#D0D0D0; + padding:8px; + width:5em; + font-weight:bold; +} +/* +Navigation bar styles +*/ +.bar { + background-color:#818487; + color:#080707; + padding:.8em .5em .4em .8em; + height:auto;/*height:1.8em;*/ + font-size:11px; + margin:0; +} +.topNav { + background-color:#818487; + color:#222222; + float:left; + padding:0; + width:100%; + clear:right; + height:2.8em; + padding-top:10px; + overflow:hidden; + font-size:12px; +} +.bottomNav { + margin-top:10px; + background-color:#818487; + color:#222222; + float:left; + padding:0; + width:100%; + clear:right; + height:2.8em; + padding-top:10px; + overflow:hidden; + font-size:12px; +} +.subNav { + background-color:#47494C; + float:left; + width:100%; + overflow:hidden; + font-size:12px; +} +.subNav div { + clear:left; + float:left; + padding:0 0 5px 6px; + text-transform:uppercase;/*???*/ +} +ul.navList, ul.subNavList { + float:left; + margin:0 25px 0 0; + padding:0; +} +ul.navList li{ + list-style:none; + float:left; + padding: 5px 6px; + text-transform:uppercase;/*???*/ +} +ul.subNavList li{ + list-style:none; + float:left; + font-size:90%;/*???*/ +} +.topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited { + color:#222222; + text-decoration:none; + text-transform:uppercase;/*???*/ +} +.topNav a:hover, .bottomNav a:hover { + text-decoration:none; + color:#999; + text-transform:uppercase;/*???*/ +} +.navBarCell1Rev { + background-color:#C2C2C2; + color:#253441; + margin: auto 5px; +} +.skipNav { + position:absolute; + top:auto; + left:-9999px; + overflow:hidden; +} +/* +Page header and footer styles +*/ +.header, .footer { + clear:both; + margin:0 20px; + padding:5px 0 0 0; +} +.indexHeader { + margin:10px; + position:relative; +} +.indexHeader span{ + margin-right:15px; +} +.indexHeader h1 { + font-size:13px; +} +.title { + margin:10px 0; +} +.subTitle { + margin:5px 0 0 0; +} +.header ul { + margin:0 0 15px 0; + padding:0; +} +.footer ul { + margin:20px 0 5px 0; +} +.header ul li, .footer ul li { + list-style:none; + font-size:13px; +} +/* +Heading styles +*/ +div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 { + background-color:#47494C; + border:none; + margin:0 0 6px -8px; + padding:7px 5px; +} +ul.blockList ul.blockList ul.blockList li.blockList h3 { + background-color:#47494C; + border:none; + margin:0 0 6px -8px; + padding:7px 5px; +} +ul.blockList ul.blockList li.blockList h3 { + padding:0; + margin:15px 0; +} +ul.blockList li.blockList h2 { + padding:10px 0 10px 0; +} +/* +Page layout container styles +*/ +.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer { + clear:both; + padding:10px 20px; + position:relative; +} +.indexContainer { + margin:10px; + position:relative; + font-size:14px; +} +.indexContainer h2 { + font-size:15px; + padding:0 0 3px 0; +} +.indexContainer ul { + margin:0; + padding:0; +} +.indexContainer ul li { + list-style:none; + padding-top:2px; +} +.contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt { + font-size:14px; + font-weight:bold; + margin:10px 0 0 0; + color:#e4e4e4; +} +.contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd { + margin:5px 0 10px 0px; + font-size:14px; + font-family:'DejaVu Sans Mono',monospace; +} +.serializedFormContainer dl.nameValue dt { + margin-left:1px; + font-size:1.1em; + display:inline; + font-weight:bold; +} +.serializedFormContainer dl.nameValue dd { + margin:0 0 0 1px; + font-size:1.1em; + display:inline; +} +/* +List styles +*/ +ul.horizontal li { + display:inline; + font-size:0.9em; +} +ul.inheritance { + margin:0; + padding:0; +} +ul.inheritance li { + display:inline; + list-style:none; +} +ul.inheritance li ul.inheritance { + margin-left:15px; + padding-left:15px; + padding-top:1px; +} +ul.blockList, ul.blockListLast { + margin:10px 0 10px 0; + padding:0; +} +ul.blockList li.blockList, ul.blockListLast li.blockList { + list-style:none; + margin-bottom:15px; + line-height:1.4; +} +ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList { + padding:0px 20px 5px 10px; + border:1px solid #545454; + background-color:#282828; +} +ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList { + padding:0 0 5px 8px; + background-color:#222222; + border:none; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockList { + margin-left:0; + padding-left:0; + padding-bottom:15px; + border:none; +} +ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast { + list-style:none; + border-bottom:none; + padding-bottom:0; +} +table tr td dl, table tr td dl dt, table tr td dl dd { + margin-top:0; + margin-bottom:1px; +} +/* +Table styles +*/ +.overviewSummary, .memberSummary, .typeSummary, .useSummary, .constantsSummary, .deprecatedSummary { + width:100%; + border-left:1px solid #333; + border-right:1px solid #333; + border-bottom:1px solid #333; +} +.overviewSummary, .memberSummary { + padding:0px; +} +.overviewSummary caption, .memberSummary caption, .typeSummary caption, +.useSummary caption, .constantsSummary caption, .deprecatedSummary caption { + position:relative; + text-align:left; + background-repeat:no-repeat; + color:#253441; + font-weight:bold; + clear:none; + overflow:hidden; + padding:0px; + padding-top:10px; + padding-left:1px; + margin:0px; + white-space:pre; +} +.overviewSummary caption a:link, .memberSummary caption a:link, .typeSummary caption a:link, +.useSummary caption a:link, .constantsSummary caption a:link, .deprecatedSummary caption a:link, +.overviewSummary caption a:hover, .memberSummary caption a:hover, .typeSummary caption a:hover, +.useSummary caption a:hover, .constantsSummary caption a:hover, .deprecatedSummary caption a:hover, +.overviewSummary caption a:active, .memberSummary caption a:active, .typeSummary caption a:active, +.useSummary caption a:active, .constantsSummary caption a:active, .deprecatedSummary caption a:active, +.overviewSummary caption a:visited, .memberSummary caption a:visited, .typeSummary caption a:visited, +.useSummary caption a:visited, .constantsSummary caption a:visited, .deprecatedSummary caption a:visited { + color:#222222; +} +.overviewSummary caption span, .memberSummary caption span, .typeSummary caption span, +.useSummary caption span, .constantsSummary caption span, .deprecatedSummary caption span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + padding-bottom:7px; + display:inline-block; + float:left; + background-color:#C2C2C2; + border: none; + height:16px; +} +.memberSummary caption span.activeTableTab span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + margin-right:3px; + display:inline-block; + float:left; + background-color:#C2C2C2; + height:16px; +} +.memberSummary caption span.tableTab span { + white-space:nowrap; + padding-top:5px; + padding-left:12px; + padding-right:12px; + margin-right:3px; + display:inline-block; + float:left; + background-color:#818487; + height:16px; +} +.memberSummary caption span.tableTab, .memberSummary caption span.activeTableTab { + padding-top:0px; + padding-left:0px; + padding-right:0px; + background-image:none; + float:none; + display:inline; +} +.overviewSummary .tabEnd, .memberSummary .tabEnd, .typeSummary .tabEnd, +.useSummary .tabEnd, .constantsSummary .tabEnd, .deprecatedSummary .tabEnd { + display:none; + width:5px; + position:relative; + float:left; + background-color:#C2C2C2; +} +.memberSummary .activeTableTab .tabEnd { + display:none; + width:5px; + margin-right:3px; + position:relative; + float:left; + background-color:#C2C2C2; +} +.memberSummary .tableTab .tabEnd { + display:none; + width:5px; + margin-right:3px; + position:relative; + background-color:#818487; + float:left; + +} +.overviewSummary td, .memberSummary td, .typeSummary td, +.useSummary td, .constantsSummary td, .deprecatedSummary td { + text-align:left; + padding:0px 0px 12px 10px; + width:100%; +} +th.colOne, th.colFirst, th.colLast, .useSummary th, .constantsSummary th, +td.colOne, td.colFirst, td.colLast, .useSummary td, .constantsSummary td{ + vertical-align:top; + padding-right:0px; + padding-top:8px; + padding-bottom:3px; +} +th.colFirst, th.colLast, th.colOne, .constantsSummary th { + background:#47494C; + text-align:left; + padding:8px 3px 3px 7px; +} +td.colFirst, th.colFirst { + white-space:nowrap; + font-size:13px; +} +td.colLast, th.colLast { + font-size:13px; +} +td.colOne, th.colOne { + font-size:13px; +} +.overviewSummary td.colFirst, .overviewSummary th.colFirst, +.overviewSummary td.colOne, .overviewSummary th.colOne, +.memberSummary td.colFirst, .memberSummary th.colFirst, +.memberSummary td.colOne, .memberSummary th.colOne, +.typeSummary td.colFirst{ + width:25%; + vertical-align:top; +} +td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover { + font-weight:bold; +} +.tableSubHeadingColor { + background-color:#2F3131; +} +.altColor { + background-color:#222222; +} +.rowColor { + background-color:#2F3131; +} +/* +Content styles +*/ +.description pre { + margin-top:0; +} +.deprecatedContent { + margin:0; + padding:10px 0; +} +.docSummary { + padding:0; +} + +ul.blockList ul.blockList ul.blockList li.blockList h3 { + font-style:normal; +} + +div.block { + font-size:16px; + font-family:'Roboto', 'San Francisco Text', 'Noto Sans', 'Segoe UI', 'Helvetica Neue', Arial, sans-serif; +} + +td.colLast div { + padding-top:0px; +} + + +td.colLast a { + padding-bottom:3px; +} +/* +Formatting effect styles +*/ +.sourceLineNo { + color:green; + padding:0 30px 0 0; +} +h1.hidden { + visibility:hidden; + overflow:hidden; + font-size:10px; +} +.block { + display:block; + margin:3px 10px 2px 0px; + color:#E9E9e9; +} +.deprecatedLabel, .descfrmTypeLabel, .memberNameLabel, .memberNameLink, +.overrideSpecifyLabel, .packageHierarchyLabel, .paramLabel, .returnLabel, +.seeLabel, .simpleTagLabel, .throwsLabel, .typeNameLabel, .typeNameLink { + font-weight:bold; +} +.deprecationComment, .emphasizedPhrase, .interfaceName { + font-style:italic; +} + +div.block div.block span.deprecationComment, div.block div.block span.emphasizedPhrase, +div.block div.block span.interfaceName { + font-style:normal; +} + +/* code block tweaks */ +code, tt { + font-size: 14px; +} +pre { + /* inspired by GitHub */ + padding: 0 5px; + border: solid 1px #888; + background: #888; + border-radius: none; +} +pre code { + background: none; +} +li.blockList > pre { + font-size: 16px; + padding: 0; + margin: 0; + background: 0; + border: 0; +} +div.summary code { + background: none; + border: 0; + padding: 0; +} + +/* @todo tag */ +div.todo { + display: block; + margin: 1em; + border: solid 1px black; + border-radius: 3px; +} +div.todoTitle { + display: block; + background: #f6f30d; + padding: 5px; + border-bottom: solid 1px black; +} +span.todoTitle { + font-weight: bold; +} +span.todoCounter { + float: right; + font-size: .8em; +} +div.todoText { + display: block; + padding: 5px; +} + +pre .comment, pre .annotation, pre .template_comment, pre .diff .header, pre .chunk, pre .markdown .blockquote { + color: #333 +} \ No newline at end of file diff --git a/src/main/java/act/db/ebean/EbeanAgentLoader.java b/src/main/java/act/db/ebean/EbeanAgentLoader.java index 1d0eade..1894cd8 100644 --- a/src/main/java/act/db/ebean/EbeanAgentLoader.java +++ b/src/main/java/act/db/ebean/EbeanAgentLoader.java @@ -1,38 +1,55 @@ package act.db.ebean; +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2017 ActFramework + * %% + * 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. + * #L% + */ + +import act.Act; +import act.app.event.SysEventId; +import act.sys.Env; import com.sun.tools.attach.AttachNotSupportedException; import com.sun.tools.attach.VirtualMachine; import com.sun.tools.attach.VirtualMachineDescriptor; import com.sun.tools.attach.spi.AttachProvider; import org.avaje.agentloader.AgentLoader; +import org.osgl.logging.LogManager; +import org.osgl.logging.Logger; +import org.osgl.util.S; import sun.tools.attach.BsdVirtualMachine; import sun.tools.attach.LinuxVirtualMachine; import sun.tools.attach.SolarisVirtualMachine; import sun.tools.attach.WindowsVirtualMachine; -import java.io.File; -import java.io.IOException; -import java.lang.management.ManagementFactory; +import java.io.*; import java.net.URISyntaxException; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; import java.util.List; -import java.util.logging.Logger; public class EbeanAgentLoader extends AgentLoader { - private static final Logger log = Logger.getLogger(AgentLoader.class.getName()); + private static final Logger LOGGER = LogManager.get(EbeanAgentLoader.class.getName()); private static final List loaded = new ArrayList(); - private static final String discoverPid() { - String nameOfRunningVM = ManagementFactory.getRuntimeMXBean().getName(); - int p = nameOfRunningVM.indexOf('@'); - return nameOfRunningVM.substring(0, p); - } - private static final AttachProvider ATTACH_PROVIDER = new AttachProvider() { @Override public String name() { @@ -66,11 +83,9 @@ public static void loadAgent(String jarFilePath) { * Load an agent providing the full file path with parameters. */ public static void loadAgent(String jarFilePath, String params) { - - log.info("dynamically loading javaagent for " + jarFilePath); try { - String pid = discoverPid(); + String pid = Env.PID.get(); VirtualMachine vm; if (AttachProvider.providers().isEmpty()) { @@ -79,7 +94,24 @@ public static void loadAgent(String jarFilePath, String params) { vm = VirtualMachine.attach(pid); } - vm.loadAgent(jarFilePath, params); + final PrintStream ps = System.out; + try { + System.setOut(new PrintStream(new FileOutputStream(".ebean_agent.log"))); + vm.loadAgent(jarFilePath, params); + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("javaagent loaded: " + jarFilePath); + } + } finally { + // ensure ebean2 EnhanceContext logout set to dump output + Act.jobManager().on(SysEventId.CLASS_LOADER_INITIALIZED, + S.buffer("EbeanAgentLoader - clean up for ").append(jarFilePath).toString(), + new Runnable() { + @Override + public void run() { + System.setOut(ps); + } + }); + } vm.detach(); } catch (Exception e) { @@ -98,8 +130,10 @@ public static void loadAgentFromClasspath(String agentName) { * Load the agent from the classpath using its name and passing params. */ public synchronized static boolean loadAgentFromClasspath(String agentName, String params) { - if (loaded.contains(agentName)) { + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(S.concat("agent already loaded: ", agentName)); + } // the agent is already loaded return true; } @@ -118,14 +152,20 @@ public synchronized static boolean loadAgentFromClasspath(String agentName, Stri if (fullName.startsWith("/") && isWindows()) { fullName = fullName.substring(1); } + if (LOGGER.isTraceEnabled()) { + LOGGER.trace(S.concat("loading agent: ", fullName)); + } loadAgent(fullName, params); - loaded.add(fullName); + loaded.add(agentName); return true; } } } // Agent not found and not loaded + if (LOGGER.isTraceEnabled()) { + LOGGER.trace("agent not found"); + } return false; } catch (URISyntaxException use) { diff --git a/src/main/java/act/db/ebean/EbeanConfigLoaded.java b/src/main/java/act/db/ebean/EbeanConfigLoaded.java new file mode 100644 index 0000000..24d36c2 --- /dev/null +++ b/src/main/java/act/db/ebean/EbeanConfigLoaded.java @@ -0,0 +1,41 @@ +package act.db.ebean; + +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2017 ActFramework + * %% + * 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. + * #L% + */ + +import static act.app.event.SysEventId.DEPENDENCY_INJECTOR_PROVISIONED; + +import act.event.ActEvent; +import act.event.BindOn; +import com.avaje.ebean.config.ServerConfig; + +/** + * The event get triggered when {@link ServerConfig} is loaded and + * before the {@link com.avaje.ebean.EbeanServer} is created. + * + * Application can use this event to do further configuration on + * {@link ServerConfig} + */ +@BindOn(DEPENDENCY_INJECTOR_PROVISIONED) +public class EbeanConfigLoaded extends ActEvent { + public EbeanConfigLoaded(ServerConfig source) { + super(source); + } +} diff --git a/src/main/java/act/db/ebean/EbeanConfigurator.java b/src/main/java/act/db/ebean/EbeanConfigurator.java new file mode 100644 index 0000000..d7b8ef3 --- /dev/null +++ b/src/main/java/act/db/ebean/EbeanConfigurator.java @@ -0,0 +1,36 @@ +package act.db.ebean; + +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2020 ActFramework + * %% + * 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. + * #L% + */ + + +import com.avaje.ebean.config.ServerConfig; + +/** + * Application to implement this interface to do further + * configuration to {@link ServerConfig Ebean ServerConfig}. + */ +public interface EbeanConfigurator { + /** + * Configure the Ebean {@link ServerConfig}. + * @param ebeanConfig the Ebean config instance + */ + void configure(ServerConfig ebeanConfig); +} diff --git a/src/main/java/act/db/ebean/EbeanConfiguratorManager.java b/src/main/java/act/db/ebean/EbeanConfiguratorManager.java new file mode 100644 index 0000000..922aedb --- /dev/null +++ b/src/main/java/act/db/ebean/EbeanConfiguratorManager.java @@ -0,0 +1,39 @@ +package act.db.ebean; + +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2020 ActFramework + * %% + * 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. + * #L% + */ + +import com.avaje.ebean.config.ServerConfig; + +import javax.inject.Inject; +import java.util.List; + +public class EbeanConfiguratorManager { + + @Inject + private List configurators; + + void callConfigurators(ServerConfig config) { + for (EbeanConfigurator configurator : configurators) { + configurator.configure(config); + } + } + +} diff --git a/src/main/java/act/db/ebean/EbeanDao.java b/src/main/java/act/db/ebean/EbeanDao.java index 92acf60..9e0ce5c 100644 --- a/src/main/java/act/db/ebean/EbeanDao.java +++ b/src/main/java/act/db/ebean/EbeanDao.java @@ -1,53 +1,71 @@ package act.db.ebean; +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2017 ActFramework + * %% + * 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. + * #L% + */ + +import static act.Act.app; + import act.app.DbServiceManager; -import act.db.DB; -import act.db.DaoBase; -import act.db.DbService; +import act.db.*; import act.db.Model; -import act.inject.param.NoBind; +import act.db.sql.tx.TxContext; import act.util.General; +import act.util.Stateless; import com.avaje.ebean.*; import com.avaje.ebeaninternal.api.SpiEbeanServer; import org.osgl.$; import org.osgl.logging.L; import org.osgl.logging.Logger; -import org.osgl.util.C; -import org.osgl.util.E; -import org.osgl.util.S; +import org.osgl.util.*; -import javax.persistence.Id; -import javax.sql.DataSource; import java.lang.reflect.Array; import java.lang.reflect.Field; -import java.util.Collection; -import java.util.Iterator; -import java.util.List; - -import static act.Act.app; +import java.util.*; +import javax.persistence.Id; +import javax.sql.DataSource; @General -@NoBind public class EbeanDao extends DaoBase> { private static final Logger logger = L.get(EbeanDao.class); - private volatile EbeanServer ebean; - private volatile DataSource ds; - private String tableName; - private Field idField = null; - private List queryIterators = C.newList(); + @Stateless private volatile EbeanServer ebean; + @Stateless private volatile EbeanServer ebeanReadOnly; + @Stateless private volatile EbeanService dbSvc; + @Stateless private volatile DataSource ds; + @Stateless private volatile DataSource dsReadOnly; + @Stateless private String tableName; + @Stateless private Field idField = null; + @Stateless private List queryIterators = C.newList(); EbeanDao(EbeanService service) { init(modelType()); - this.ebean(service.ebean()); + this.dbService(service); } EbeanDao(Class idType, Class modelType, EbeanService service) { super(idType, modelType); init(modelType); - this.ebean(service.ebean()); - this.ds = service.ds(); + this.dbService(service); + this.ds = service.dataSource(); + this.dsReadOnly = service.dataSourceReadOnly(); } public EbeanDao(Class id_type, Class modelType) { @@ -59,9 +77,14 @@ public EbeanDao() { init(modelType()); } - public void ebean(EbeanServer ebean) { - this.ebean = $.notNull(ebean); - this.tableName = ((SpiEbeanServer) ebean).getBeanDescriptor(modelType()).getBaseTable(); + public void ebean(EbeanServer ebean, boolean readonly) { + setEbean($.requireNotNull(ebean), readonly); + } + + public void dbService(EbeanService service) { + this.dbSvc = service; + this.ebean(service.ebean(true), true); + this.ebean(service.ebean(false), false); } public void modelType(Class type) { @@ -92,9 +115,15 @@ private void init(Class modelType) { break; } } - if (null != ebean) { - this.tableName = ((SpiEbeanServer) ebean).getBeanDescriptor(modelType()).getBaseTable(); + } + + private void setEbean(EbeanServer ebean, boolean readonly) { + if (readonly) { + this.ebeanReadOnly = ebean; + return; } + this.ebean = ebean; + this.tableName = ((SpiEbeanServer) ebean).getBeanDescriptor(modelType()).getBaseTable(); } private EbeanService getService(String dbId, DbServiceManager mgr) { @@ -104,20 +133,27 @@ private EbeanService getService(String dbId, DbServiceManager mgr) { return $.cast(svc); } - public EbeanServer ebean() { - if (null != ebean) { + private EbeanServer ebean_(boolean defaultReadOnly) { + boolean ctxReadOnly = TxContext.readOnly(defaultReadOnly); + E.illegalStateIf(!defaultReadOnly && ctxReadOnly, "Cannot do write operation within readonly transaction"); + return ebean(ctxReadOnly); + } + + public EbeanServer ebean(boolean readonly) { + dbSvc.beginTxIfRequired(null); + if (!readonly && null != ebean) { return ebean; } + if (readonly && null != ebeanReadOnly) { + return ebeanReadOnly; + } synchronized (this) { - if (null == ebean) { - DB db = modelType().getAnnotation(DB.class); - String dbId = null == db ? DbServiceManager.DEFAULT : db.value(); - EbeanService dbService = getService(dbId, app().dbServiceManager()); - E.NPE(dbService); - ebean = dbService.ebean(); - } + DB db = modelType().getAnnotation(DB.class); + String dbId = null == db ? DbServiceManager.DEFAULT : db.value(); + EbeanService dbService = getService(dbId, app().dbServiceManager()); + dbService(dbService); + return readonly ? ebeanReadOnly : ebean; } - return ebean; } public DataSource ds() { @@ -130,7 +166,7 @@ public DataSource ds() { String dbId = null == db ? DbServiceManager.DEFAULT : db.value(); EbeanService dbService = getService(dbId, app().dbServiceManager()); E.NPE(dbService); - ds = dbService.ds(); + ds = dbService.dataSource(); } } return ds; @@ -142,7 +178,17 @@ void registerQueryIterator(QueryIterator i) { @Override public MODEL_TYPE findById(ID_TYPE id) { - return ebean().find(modelType(), id); + return ebean_(true).find(modelType(), id); + } + + @Override + public MODEL_TYPE findLatest() { + throw E.unsupport(); + } + + @Override + public MODEL_TYPE findLastModified() { + throw E.unsupport(); } @Override @@ -176,7 +222,7 @@ public List findAllAsList() { @Override public MODEL_TYPE reload(MODEL_TYPE entity) { - ebean().refresh(entity); + ebean_(true).refresh(entity); return entity; } @@ -208,12 +254,12 @@ public long countBy(String fields, Object... values) throws IllegalArgumentExcep @Override public MODEL_TYPE save(MODEL_TYPE entity) { - ebean().save(entity); + ebean_(false).save(entity); return entity; } public MODEL_TYPE save(Transaction tx, MODEL_TYPE entity) { - ebean().save(entity, tx); + ebean_(false).save(entity, tx); return entity; } @@ -223,61 +269,50 @@ public List save(Iterable iterable) { if (list.isEmpty()) { return list; } - Transaction transaction = ebean().createTransaction(TxIsolation.READ_COMMITED); - transaction.setBatchMode(true); - transaction.setBatchSize(list.size()); - try { - ebean().saveAll(list); - transaction.commit(); - } catch (RuntimeException e) { - transaction.rollback(); - throw e; - } finally { - transaction.end(); - } + ebean_(false).saveAll(list); return list; } public List save(Transaction tx, Iterable iterable) { List list = C.list(iterable); - ebean().saveAll(list, tx); + ebean_(false).saveAll(list, tx); return list; } @Override public void save(MODEL_TYPE entity, String fields, Object... values) throws IllegalArgumentException { - ebean().update(entity); + ebean_(false).update(entity); } public void save(Transaction tx, MODEL_TYPE entity, String fields, Object... values) throws IllegalArgumentException { - ebean().update(entity, tx); + ebean_(false).update(entity, tx); } @Override public void delete(MODEL_TYPE entity) { - ebean().delete(entity); + ebean_(false).delete(entity); } public void delete(Transaction tx, MODEL_TYPE entity) { - ebean().delete(entity, tx); + ebean_(false).delete(entity, tx); } @Override public void delete(EbeanQuery query) { - ebean().delete(query.rawQuery(), null); + ebean_(false).delete(query.rawQuery(), null); } public void delete(Transaction tx, EbeanQuery query) { - ebean().delete(query.rawQuery(), tx); + ebean_(false).delete(query.rawQuery(), tx); } @Override public void deleteById(ID_TYPE id) { - ebean().delete(modelType(), id); + ebean_(false).delete(modelType(), id); } public void deleteById(Transaction tx, ID_TYPE id) { - ebean().delete(modelType(), id, tx); + ebean_(false).delete(modelType(), id, tx); } @Override @@ -301,13 +336,13 @@ public void deleteAll(Transaction tx) { @Override public void drop() { String sql = "DELETE from " + tableName; - SqlUpdate sqlUpdate = ebean().createSqlUpdate(sql); - ebean().execute(sqlUpdate); + SqlUpdate sqlUpdate = ebean_(false).createSqlUpdate(sql); + ebean_(false).execute(sqlUpdate); } @Override public EbeanQuery q() { - return new EbeanQuery(this, modelType()); + return new EbeanQuery<>(this, modelType()); } @Override @@ -315,6 +350,10 @@ public EbeanQuery createQuery() { return q(); } + boolean ebeanServerProvided() { + return null != ebean; + } + private enum R2 { betweenProperties() { @Override @@ -502,10 +541,25 @@ private void buildWhere(ExpressionList where, String key, Object val where.eq(sa[0], val); break; case 2: - R1.valueOf(sa[1]).applyTo(where, sa[0], val); + String op = sa[1]; + if ("!=".equalsIgnoreCase(op)) { + op = "ne"; + } else if ("==".equalsIgnoreCase(op)) { + op = "eq"; + } else if (">".equalsIgnoreCase(op)) { + op = "gt"; + } else if (">=".equals(op)) { + op = "ge"; + } else if ("<".equals(op)) { + op = "lt"; + } else if ("<=".equals(op)) { + op = "le"; + } + R1.valueOf(op).applyTo(where, sa[0], val); break; case 3: R2.valueOf(sa[2]).applyTo(where, sa[0], sa[1], val); + break; default: throw E.unexpected("Unknown where expression: %s", key); } @@ -529,4 +583,9 @@ public EbeanQuery q(String keys, Object... values) { public EbeanQuery createQuery(String s, Object... objects) { return q(s, objects); } + + @Override + public Object processLikeValue(String v) { + return v.contains("%") ? v : "%" + v + "%"; + } } diff --git a/src/main/java/act/db/ebean/EbeanDaoInjectionListener.java b/src/main/java/act/db/ebean/EbeanDaoInjectionListener.java index e246255..9cff1b4 100644 --- a/src/main/java/act/db/ebean/EbeanDaoInjectionListener.java +++ b/src/main/java/act/db/ebean/EbeanDaoInjectionListener.java @@ -1,13 +1,37 @@ package act.db.ebean; +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2017 ActFramework + * %% + * 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. + * #L% + */ + +import act.Act; import act.app.App; +import act.app.event.SysEventId; import act.db.DbService; import act.db.di.DaoInjectionListenerBase; +import act.event.SysEventListenerBase; import org.osgl.$; import org.osgl.inject.BeanSpec; import org.osgl.util.Generics; import java.lang.reflect.Type; +import java.util.EventObject; import java.util.List; public class EbeanDaoInjectionListener extends DaoInjectionListenerBase { @@ -19,6 +43,10 @@ public Class[] listenTo() { @Override public void onInjection(Object injectee, BeanSpec spec) { + final EbeanDao dao = $.cast(injectee); + if (dao.ebeanServerProvided()) { + return; + } List typeParameters = spec.typeParams(); if (typeParameters.isEmpty()) { typeParameters = Generics.typeParamImplementations(spec.rawType(), EbeanDao.class); @@ -27,13 +55,17 @@ public void onInjection(Object injectee, BeanSpec spec) { logger.warn("No type parameter information provided"); return; } - $.T2 resolved = resolve(typeParameters); + final $.T2 resolved = resolve(typeParameters); DbService dbService = App.instance().dbServiceManager().dbService(resolved._2); if (dbService instanceof EbeanService) { - EbeanService service = $.cast(dbService); - EbeanDao dao = $.cast(injectee); - dao.ebean(service.ebean()); - dao.modelType(resolved._1); + final EbeanService service = $.cast(dbService); + Act.eventBus().bind(SysEventId.DB_SVC_LOADED, new SysEventListenerBase() { + @Override + public void on(EventObject eventObject) throws Exception { + dao.dbService(service); + dao.modelType(resolved._1); + } + }); } } } diff --git a/src/main/java/act/db/ebean/EbeanModelBase.java b/src/main/java/act/db/ebean/EbeanModelBase.java index 9c105df..ce61b8b 100644 --- a/src/main/java/act/db/ebean/EbeanModelBase.java +++ b/src/main/java/act/db/ebean/EbeanModelBase.java @@ -1,12 +1,31 @@ package act.db.ebean; +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2017 ActFramework + * %% + * 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. + * #L% + */ + import act.app.App; import act.db.Dao; import act.db.Model; import org.osgl.$; import javax.persistence.MappedSuperclass; - @MappedSuperclass public abstract class EbeanModelBase implements Model { diff --git a/src/main/java/act/db/ebean/EbeanModule.java b/src/main/java/act/db/ebean/EbeanModule.java index 827cf6b..4e0e0ca 100644 --- a/src/main/java/act/db/ebean/EbeanModule.java +++ b/src/main/java/act/db/ebean/EbeanModule.java @@ -1,5 +1,25 @@ package act.db.ebean; +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2017 ActFramework + * %% + * 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. + * #L% + */ + import act.db.ebean.util.EbeanDaoLoader; import org.osgl.inject.Module; diff --git a/src/main/java/act/db/ebean/EbeanPlugin.java b/src/main/java/act/db/ebean/EbeanPlugin.java index 069ab24..916201d 100644 --- a/src/main/java/act/db/ebean/EbeanPlugin.java +++ b/src/main/java/act/db/ebean/EbeanPlugin.java @@ -1,19 +1,56 @@ package act.db.ebean; -import act.ActComponent; +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2017 ActFramework + * %% + * 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. + * #L% + */ + import act.app.App; import act.db.DbPlugin; import act.db.DbService; import act.inject.param.ParamValueLoaderService; +import com.avaje.ebeaninternal.api.ScopeTrans; +import org.osgl.OsglConfig; +import osgl.version.Version; import java.util.Map; -@ActComponent public class EbeanPlugin extends DbPlugin { + + public static final Version VERSION = Version.of(EbeanPlugin.class); + + private static final ThreadLocal txHolder = new ThreadLocal<>(); + + @Override + public void register() { + super.register(); + registerGlobalMappingFilter(); + } + @Override - public DbService initDbService(String id, App app, Map conf) { + public DbService initDbService(String id, App app, Map conf) { ParamValueLoaderService.waiveFields("_ebean_intercept", "_ebean_identity"); return new EbeanService(id, app, conf); } + + private void registerGlobalMappingFilter() { + OsglConfig.addGlobalMappingFilter("starts:_ebean_"); + } + } diff --git a/src/main/java/act/db/ebean/EbeanQuery.java b/src/main/java/act/db/ebean/EbeanQuery.java index 26c0a93..687ec7a 100644 --- a/src/main/java/act/db/ebean/EbeanQuery.java +++ b/src/main/java/act/db/ebean/EbeanQuery.java @@ -1,7 +1,28 @@ package act.db.ebean; +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2017 ActFramework + * %% + * 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. + * #L% + */ + import act.db.Dao; import com.avaje.ebean.*; +import org.jetbrains.annotations.Nullable; import org.osgl.$; import org.osgl.util.C; import org.osgl.util.E; @@ -19,6 +40,7 @@ public class EbeanQuery implements Query, Dao.Query modelType; Query q; + Query qReadOnly; EbeanDao dao; public EbeanQuery() { @@ -34,34 +56,83 @@ public EbeanQuery() { public EbeanQuery(EbeanDao dao, Class modelType) { this.modelType = modelType; - EbeanServer ebean = dao.ebean(); - E.NPE(ebean); - q = ebean.createQuery(modelType); + + q = dao.ebean(false).createQuery(modelType); + qReadOnly = dao.ebean(true).createQuery(modelType); + syncEbeanQueries(); + this.dao = dao; } + private void syncEbeanQueries() { + + _sync("detail"); + + q.orderBy(); + _sync("orderBy"); + + q.text(); + _sync("textExpressions"); + + q.where(); + _sync("whereExpressions"); + + q.having(); + _sync("havingExpressions"); + + } + + private void _sync(String property) { + $.setProperty(qReadOnly, $.getProperty(q, property), property); + } + public Query rawQuery() { return q; } @Override - public Query asOf(Timestamp timestamp) { - return q.asOf(timestamp); + public EbeanQuery asOf(Timestamp timestamp) { + q.asOf(timestamp); + qReadOnly.asOf(timestamp); + return this; } @Override public List> findVersions() { - return q.findVersions(); + return qReadOnly.findVersions(); } @Override public List> findVersionsBetween(Timestamp timestamp, Timestamp timestamp1) { - return q.findVersionsBetween(timestamp, timestamp1); + return qReadOnly.findVersionsBetween(timestamp, timestamp1); + } + + @Override + public RawSql getRawSql() { + return q.getRawSql(); + } + + @Override + public void findEach(QueryEachConsumer consumer) { + qReadOnly.findEach(consumer); + } + + @Override + public void findEachWhile(QueryEachWhileConsumer consumer) { + qReadOnly.findEachWhile(consumer); + } + + @Nullable + @Override + public MODEL_TYPE findUnique() { + return qReadOnly.findUnique(); } @Override - public Query apply(FetchPath fetchPath) { - return q.apply(fetchPath); + public EbeanQuery apply(FetchPath fetchPath) { + q.apply(fetchPath); + qReadOnly.apply(fetchPath); + return this; } @Override @@ -70,8 +141,9 @@ public ExpressionList text() { } @Override - public Query setUseDocStore(boolean b) { + public EbeanQuery setUseDocStore(boolean b) { q.setUseDocStore(b); + qReadOnly.setUseDocStore(b); return this; } @@ -87,28 +159,32 @@ public int delete() { @Override public Object getId() { - return q.getId(); + return qReadOnly.getId(); } @Override public Class getBeanType() { - return q.getBeanType(); + return qReadOnly.getBeanType(); } @Override - public Query setDisableLazyLoading(boolean b) { - return q.setDisableLazyLoading(b); + public EbeanQuery setDisableLazyLoading(boolean b) { + q.setDisableLazyLoading(b); + qReadOnly.setDisableLazyLoading(b); + return this; } @Override public EbeanQuery offset(int pos) { q.setFirstRow(pos); + qReadOnly.setFirstRow(pos); return this; } @Override public EbeanQuery limit(int limit) { q.setMaxRows(limit); + qReadOnly.setMaxRows(limit); return this; } @@ -116,32 +192,42 @@ public EbeanQuery limit(int limit) { public EbeanQuery orderBy(String... fieldList) { E.illegalArgumentIf(fieldList.length == 0); q.order(S.join(" ", fieldList)); + qReadOnly.order(S.join(" ", fieldList)); return this; } @Override public MODEL_TYPE first() { - return q.findUnique(); + List list = qReadOnly.setMaxRows(1).findList(); + return list.isEmpty() ? null : list.get(0); } @Override - public Query fetchQuery(String path, String fetchProperties) { - return q.fetchQuery(path, fetchProperties); + public EbeanQuery fetchQuery(String path, String fetchProperties) { + q.fetchQuery(path, fetchProperties); + qReadOnly.fetchQuery(path, fetchProperties); + return this; } @Override - public Query fetchLazy(String path, String fetchProperties) { - return q.fetchLazy(path, fetchProperties); + public EbeanQuery fetchLazy(String path, String fetchProperties) { + q.fetchLazy(path, fetchProperties); + qReadOnly.fetchLazy(path, fetchProperties); + return this; } @Override - public Query fetchQuery(String path) { - return fetchQuery(path); + public EbeanQuery fetchQuery(String path) { + q.fetchQuery(path); + qReadOnly.fetchQuery(path); + return this; } @Override - public Query fetchLazy(String path) { - return fetchLazy(path); + public EbeanQuery fetchLazy(String path) { + q.fetchLazy(path); + qReadOnly.fetchLazy(path); + return this; } @Override @@ -153,7 +239,7 @@ public Iterable fetch() { } qi.close(); return list; -// we need to close the query iterable right now otherwise it hold the data connection forever +// we need to close the query iterable right now otherwise it holds the data connection forever // return new Iterable() { // @Override // public Iterator iterator() { @@ -164,121 +250,137 @@ public Iterable fetch() { @Override public long count() { - return q.findCount(); + return qReadOnly.findCount(); } // --- Ebean Query methods: delegate to q @Override - public Query setIncludeSoftDeletes() { + public EbeanQuery setIncludeSoftDeletes() { q.setIncludeSoftDeletes(); + qReadOnly.setIncludeSoftDeletes(); return this; } @Override - public Query asDraft() { - return q.asDraft(); + public EbeanQuery asDraft() { + q.asDraft(); + qReadOnly.asDraft(); + return this; } @Override public boolean isAutoTuned() { - return q.isAutoTuned(); - } - - @Override - public Query setAutoTune(boolean b) { - return q.setAutoTune(b); + return qReadOnly.isAutoTuned(); } @Override - public Query setDisableReadAuditing() { - return q.setDisableReadAuditing(); + public EbeanQuery setAutoTune(boolean b) { + q.setAutoTune(b); + qReadOnly.setAutoTune(b); + return this; } @Override - public RawSql getRawSql() { - return q.getRawSql(); + public EbeanQuery setDisableReadAuditing() { + q.setDisableReadAuditing(); + qReadOnly.setDisableReadAuditing(); + return this; } @Override public EbeanQuery setRawSql(RawSql rawSql) { q.setRawSql(rawSql); + qReadOnly.setRawSql(rawSql); return this; } + public void setDefaultRawSqlIfRequired() { + } + @Override public void cancel() { q.cancel(); + qReadOnly.cancel(); } @Override public EbeanQuery copy() { - q.copy(); - return this; + EbeanQuery copy = new EbeanQuery<>(); + copy.q = q.copy(); + copy.qReadOnly = qReadOnly.copy(); + return copy; } @Override public EbeanQuery setPersistenceContextScope(PersistenceContextScope scope) { q.setPersistenceContextScope(scope); + qReadOnly.setPersistenceContextScope(scope); return this; } @Override public ExpressionFactory getExpressionFactory() { - return q.getExpressionFactory(); + return qReadOnly.getExpressionFactory(); } @Override public EbeanQuery setLazyLoadBatchSize(int lazyLoadBatchSize) { q.setLazyLoadBatchSize(lazyLoadBatchSize); + qReadOnly.setLazyLoadBatchSize(lazyLoadBatchSize); return this; } @Override public EbeanQuery select(String fetchProperties) { q.select(fetchProperties); + qReadOnly.select(fetchProperties); return this; } @Override public EbeanQuery fetch(String path, String fetchProperties) { q.fetch(path, fetchProperties); + qReadOnly.fetch(path, fetchProperties); return this; } @Override public EbeanQuery fetch(String assocProperty, String fetchProperties, FetchConfig fetchConfig) { q.fetch(assocProperty, fetchProperties, fetchConfig); + qReadOnly.fetch(assocProperty, fetchProperties, fetchConfig); return this; } @Override public EbeanQuery fetch(String path) { q.fetch(path); + qReadOnly.fetch(path); return this; } @Override public EbeanQuery fetch(String path, FetchConfig joinConfig) { q.fetch(path, joinConfig); + qReadOnly.fetch(path, joinConfig); return this; } @Override - public List findIds() { - return q.findIds(); + public List findIds() { + return qReadOnly.findIds(); } @Override public QueryIterator findIterate() { - QueryIterator i = q.findIterate(); + QueryIterator i = qReadOnly.findIterate(); dao.registerQueryIterator(i); return i; } public void consume($.Visitor visitor) { - QueryIterator i = q.findIterate(); + QueryIterator i = qReadOnly.findIterate(); try { while (i.hasNext()) { MODEL_TYPE entity = i.next(); @@ -289,93 +391,86 @@ public void consume($.Visitor visitor) { } } - @Override - public void findEach(QueryEachConsumer consumer) { - q.findEach(consumer); - } - - @Override - public void findEachWhile(QueryEachWhileConsumer consumer) { - q.findEachWhile(consumer); - } - @Override public List findList() { - return q.findList(); + return qReadOnly.findList(); } @Override public Set findSet() { - return q.findSet(); + return qReadOnly.findSet(); } @Override public Map findMap() { - return q.findMap(); + return qReadOnly.findMap(); } @Override public List findSingleAttributeList() { - return q.findSingleAttributeList(); + return qReadOnly.findSingleAttributeList(); } @Override - public MODEL_TYPE findUnique() { - return q.findUnique(); + public int findCount() { + return qReadOnly.findCount(); } - @Override - public int findCount() { - return q.findCount(); + public MODEL_TYPE findOne() { + return qReadOnly.findUnique(); } @Override public FutureRowCount findFutureCount() { - return q.findFutureCount(); + return qReadOnly.findFutureCount(); } @Override public FutureIds findFutureIds() { - return q.findFutureIds(); + return qReadOnly.findFutureIds(); } @Override public FutureList findFutureList() { - return q.findFutureList(); + return qReadOnly.findFutureList(); } @Override public EbeanQuery setParameter(String name, Object value) { q.setParameter(name, value); + qReadOnly.setParameter(name, value); return this; } @Override public EbeanQuery setParameter(int position, Object value) { q.setParameter(position, value); + qReadOnly.setParameter(position, value); return this; } @Override public EbeanQuery setId(Object id) { q.setId(id); + qReadOnly.setId(id); return this; } @Override public EbeanQuery where(Expression expression) { q.where(expression); + qReadOnly.where(expression); return this; } @Override public ExpressionList where() { - return q.where(); + return qReadOnly.where(); } @Override public ExpressionList filterMany(String propertyName) { - return q.filterMany(propertyName); + return qReadOnly.filterMany(propertyName); } @Override @@ -386,18 +481,24 @@ public ExpressionList having() { @Override public EbeanQuery having(Expression addExpressionToHaving) { q.having(addExpressionToHaving); + qReadOnly.having(addExpressionToHaving); return this; } @Override public EbeanQuery orderBy(String orderByClause) { + if (S.blank(orderByClause)) { + return this; + } q.orderBy(orderByClause); + qReadOnly.orderBy(orderByClause); return this; } @Override public EbeanQuery order(String orderByClause) { q.order(orderByClause); + qReadOnly.order(orderByClause); return this; } @@ -414,18 +515,21 @@ public OrderBy orderBy() { @Override public EbeanQuery setOrder(OrderBy orderBy) { q.setOrder(orderBy); + qReadOnly.setOrder(orderBy); return this; } @Override public EbeanQuery setOrderBy(OrderBy orderBy) { q.setOrderBy(orderBy); + qReadOnly.setOrderBy(orderBy); return this; } @Override public EbeanQuery setDistinct(boolean isDistinct) { q.setDistinct(isDistinct); + qReadOnly.setDistinct(isDistinct); return this; } @@ -437,6 +541,7 @@ public int getFirstRow() { @Override public EbeanQuery setFirstRow(int firstRow) { q.setFirstRow(firstRow); + qReadOnly.setFirstRow(firstRow); return this; } @@ -448,24 +553,28 @@ public int getMaxRows() { @Override public EbeanQuery setMaxRows(int maxRows) { q.setMaxRows(maxRows); + qReadOnly.setMaxRows(maxRows); return this; } @Override public EbeanQuery setMapKey(String mapKey) { q.setMapKey(mapKey); + qReadOnly.setMapKey(mapKey); return this; } @Override public EbeanQuery setUseCache(boolean useBeanCache) { q.setUseCache(useBeanCache); + qReadOnly.setUseCache(useBeanCache); return this; } @Override public EbeanQuery setUseQueryCache(boolean useQueryCache) { q.setUseQueryCache(useQueryCache); + qReadOnly.setUseQueryCache(useQueryCache); return this; } @@ -478,18 +587,21 @@ public EbeanQuery setReadOnly(boolean readOnly) { @Override public EbeanQuery setLoadBeanCache(boolean loadBeanCache) { q.setLoadBeanCache(loadBeanCache); + qReadOnly.setLoadBeanCache(loadBeanCache); return this; } @Override public EbeanQuery setTimeout(int secs) { q.setTimeout(secs); + qReadOnly.setTimeout(secs); return this; } @Override public EbeanQuery setBufferFetchSizeHint(int fetchSize) { q.setBufferFetchSizeHint(fetchSize); + qReadOnly.setBufferFetchSizeHint(fetchSize); return this; } @@ -499,6 +611,7 @@ public String getGeneratedSql() { } @Override + @Deprecated public EbeanQuery setForUpdate(boolean forUpdate) { q.setForUpdate(forUpdate); return this; @@ -512,16 +625,18 @@ public boolean isForUpdate() { @Override public EbeanQuery alias(String alias) { q.alias(alias); + qReadOnly.alias(alias); return this; } @Override public PagedList findPagedList() { - return q.findPagedList(); + return qReadOnly.findPagedList(); } @Override public Set validate() { return q.validate(); } + } diff --git a/src/main/java/act/db/ebean/EbeanService.java b/src/main/java/act/db/ebean/EbeanService.java index 7a02df3..9ddd466 100644 --- a/src/main/java/act/db/ebean/EbeanService.java +++ b/src/main/java/act/db/ebean/EbeanService.java @@ -1,107 +1,147 @@ package act.db.ebean; +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2017 ActFramework + * %% + * 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. + * #L% + */ + +import static act.app.event.SysEventId.PRE_LOAD_CLASSES; + import act.Act; import act.app.App; -import act.app.DbServiceManager; import act.conf.AppConfigKey; import act.db.Dao; -import act.db.DbService; -import act.event.AppEventListenerBase; -import com.avaje.ebean.Ebean; +import act.db.ebean.util.EbeanConfigAdaptor; +import act.db.ebean.util.EbeanDataSourceProvider; +import act.db.ebean.util.EbeanDataSourceWrapper; +import act.db.sql.DataSourceConfig; +import act.db.sql.DataSourceProvider; +import act.db.sql.SqlDbService; +import act.db.sql.tx.TxContext; +import act.event.SysEventListenerBase; import com.avaje.ebean.EbeanServer; import com.avaje.ebean.EbeanServerFactory; +import com.avaje.ebean.Transaction; +import com.avaje.ebean.TxScope; import com.avaje.ebean.config.ServerConfig; -import com.zaxxer.hikari.HikariConfig; -import com.zaxxer.hikari.HikariDataSource; -import org.avaje.datasource.DataSourceConfig; import org.osgl.$; -import org.osgl.logging.LogManager; -import org.osgl.logging.Logger; -import org.osgl.util.C; import org.osgl.util.E; import org.osgl.util.S; +import osgl.version.Version; +import osgl.version.Versioned; -import javax.persistence.Entity; -import javax.persistence.Id; -import javax.persistence.PersistenceException; -import javax.sql.DataSource; -import java.io.File; import java.lang.annotation.Annotation; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.EventObject; import java.util.Map; -import java.util.Properties; -import java.util.Set; - -import static act.app.event.AppEventId.CLASS_LOADED; -import static act.app.event.AppEventId.PRE_LOAD_CLASSES; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.sql.DataSource; -public final class EbeanService extends DbService { +@Versioned +public final class EbeanService extends SqlDbService { - private static Logger logger = LogManager.get(EbeanService.class); + public static final Version VERSION = EbeanPlugin.VERSION; // the ebean service instance private EbeanServer ebean; - private Map conf; - - private ServerConfig serverConfig; + // the ebean service instance for readonly operations + private EbeanServer ebeanReadOnly; - // the datasource for low level JDBC API usage - private DataSource ds; - - private static Set> modelTypes = C.newSet(); - - public EbeanService(final String dbId, final App app, final Map config) { - super(dbId, app); - this.conf = config; - Object o = conf.get("agentPackage"); - final String agentPackage = null == o ? S.string(app().config().get(AppConfigKey.SCAN_PACKAGE)) : S.string(o).trim(); + public EbeanService(final String dbId, final App app, final Map config) { + super(dbId, app, config); + String s = config.get("agentPackage"); + String scanPackage = app().config().get(AppConfigKey.SCAN_PACKAGE, null); + final String agentPackage = null == s ? scanPackage : S.string(s).trim(); E.invalidConfigurationIf(S.blank(agentPackage), "\"agentPackage\" not configured"); - logger.info("\"agentPackage\" configured: %s", agentPackage); - final EbeanService svc = this; - app.eventBus().bind(CLASS_LOADED, new AppEventListenerBase(S.builder(dbId).append("-ebean-prestart")) { - @Override - public void on(EventObject event) { - svc.serverConfig = serverConfig(dbId, conf); - app().eventBus().emit(new PreEbeanCreation(serverConfig)); - try { - ebean = EbeanServerFactory.create(serverConfig); - } catch (PersistenceException e) { - // try disable ddlRun - // see http://stackoverflow.com/questions/35676651/ebean-run-ddl-only-if-the-database-does-not-exist/36253846 - if (Act.isDev()) { - serverConfig.setDdlRun(false); - ebean = EbeanServerFactory.create(serverConfig); - } else { - throw e; - } - } - Ebean.register(ebean, S.eq(DbServiceManager.DEFAULT, dbId)); - } - }).bind(PRE_LOAD_CLASSES, new AppEventListenerBase(S.builder(dbId).append("-ebean-pre-cl")) { + if (isTraceEnabled()) { + trace("\"agentPackage\" configured: %s", agentPackage); + } + app.eventBus().bind(PRE_LOAD_CLASSES, new SysEventListenerBase(S.concat(dbId, "-ebean-pre-cl")) { @Override public void on(EventObject event) { - String s = S.builder("debug=").append(Act.isDev() ? "1" : "0") + String s = S.buffer("debug=").append(Act.isDev() ? "1" : "0") .append(";packages=") - //.append("act.db.ebean.*,") .append(agentPackage) .toString(); if (!EbeanAgentLoader.loadAgentFromClasspath("ebean-agent", s)) { - logger.warn("ebean-agent not found in classpath - not dynamically loaded"); + warn("ebean-agent not found in classpath - not dynamically loaded"); } } }); } + @Override + protected boolean supportDdl() { + return true; + } + + @Override + protected void dataSourceProvided(DataSource dataSource, DataSourceConfig dsConfig, boolean readonly) { + ServerConfig ebeanConfig; + if (dataSource instanceof EbeanDataSourceWrapper) { + EbeanDataSourceWrapper wrapper = (EbeanDataSourceWrapper) dataSource; + ebeanConfig = wrapper.ebeanConfig; + } else { + ebeanConfig = new EbeanConfigAdaptor().adaptFrom(this.config, dsConfig, this); + ebeanConfig.setDataSource(dataSource); + } + IdGeneratorRegister rg = Act.getInstance(IdGeneratorRegister.class); + rg.registerTo(ebeanConfig); + EbeanConfiguratorManager configuratorManager = Act.getInstance(EbeanConfiguratorManager.class); + configuratorManager.callConfigurators(ebeanConfig); + //app().eventBus().trigger(new EbeanConfigLoaded(ebeanConfig)); + if (readonly) { + ebeanConfig.setDdlGenerate(false); + ebeanConfig.setDdlRun(false); + ebeanReadOnly = EbeanServerFactory.create(ebeanConfig); + } else { + ebean = EbeanServerFactory.create(ebeanConfig); + if (null == ebeanReadOnly) { + ebeanReadOnly = ebean; + } + } + } + + @Override + protected DataSourceProvider builtInDataSourceProvider() { + return new EbeanDataSourceProvider(config, this); + } + @Override protected void releaseResources() { if (null != ebean) { ebean.shutdown(true, false); + if (logger.isDebugEnabled()) { + logger.debug("ebean shutdown: %s", id()); + } + ebean = null; + } + if (null != ebeanReadOnly) { + ebeanReadOnly.shutdown(true, false); + if (logger.isDebugEnabled()) { + logger.debug("ebean readonly shutdown: %s", id()); + } + ebeanReadOnly = null; } - modelTypes.clear(); - conf.clear(); + super.releaseResources(); } @Override @@ -110,7 +150,7 @@ public DAO defaultDao(Class modelType) { if (EbeanModelBase.class.isAssignableFrom(modelType)) { Type type = modelType.getGenericSuperclass(); if (type instanceof ParameterizedType) { - return $.cast(new EbeanDao((Class)((ParameterizedType) type).getActualTypeArguments()[0], modelType, this)); + return $.cast(new EbeanDao((Class) ((ParameterizedType) type).getActualTypeArguments()[0], modelType, this)); } } Class idType = findModelIdTypeByAnnotation(modelType, Id.class); @@ -122,7 +162,7 @@ public DAO defaultDao(Class modelType) { public DAO newDaoInstance(Class daoType) { E.illegalArgumentIf(!EbeanDao.class.isAssignableFrom(daoType), "expected EbeanDao, found: %s", daoType); EbeanDao dao = $.cast(app().getInstance(daoType)); - dao.ebean(this.ebean()); + dao.dbService(this); return (DAO) dao; } @@ -131,189 +171,53 @@ public Class entityAnnotationType() { return Entity.class; } - public EbeanServer ebean() { - return ebean; - } - - public DataSource ds() { - return ds; + @Override + protected void doStartTx(Object delegate, boolean readOnly) { + if (readOnly) { + TxScope scope = TxScope.required().setReadOnly(true); + ebeanReadOnly.beginTransaction(scope); + } else { + ebean.beginTransaction(); + } } - private ServerConfig serverConfig(String id, Map conf) { - ServerConfig sc = new ServerConfig(); - sc.setName(id); - Properties properties = new Properties(); - properties.putAll(conf); - sc.loadFromProperties(properties); - - HikariDataSource dataSource = dataSource(conf); - sc.setDataSource(dataSource); - ds = dataSource; - - boolean noddl = false; - String ddlGenerate = (String) conf.get("ddl.generate"); - if (null != ddlGenerate) { - sc.setDdlGenerate(Boolean.parseBoolean(ddlGenerate)); - } else if (Act.isDev()) { - String url = dataSource.getJdbcUrl(); - if (url.startsWith("jdbc:h2:")) { - String file = url.substring("jdbc:h2:".length()) + ".mv.db"; - File _file = new File(file); - if (_file.exists()) { - noddl = true; - } - } - sc.setDdlGenerate(!noddl); + @Override + protected void doRollbackTx(Object delegate, Throwable cause) { + Transaction tx = ebean.currentTransaction(); + if (null == tx) { + return; } - - String ddlRun = (String) conf.get("ddl.run"); - if (null != ddlRun) { - sc.setDdlRun(Boolean.parseBoolean(ddlRun)); - } else if (Act.isDev()) { - sc.setDdlRun(!noddl); + if (TxContext.readOnly()) { + ebeanReadOnly.endTransaction(); + } else { + logger.warn(cause, "Roll back transaction"); + ebean.rollbackTransaction(); } + } - String ddlCreateOnly = (String) conf.get("ddl.createOnly"); - if (null != ddlCreateOnly) { - sc.setDdlCreateOnly(Boolean.parseBoolean(ddlCreateOnly)); - } else if (Act.isDev()) { - sc.setDdlCreateOnly(!noddl); + @Override + protected void doEndTxIfActive(Object delegate) { + Transaction tx = ebean.currentTransaction(); + if (null == tx) { + return; } - - for (Class c : modelTypes) { - sc.addClass(c); + if (TxContext.readOnly()) { + ebeanReadOnly.endTransaction(); + } else { + ebean.commitTransaction(); } - - return sc; } - private HikariDataSource dataSource(Map conf) { - HikariConfig hc = new HikariConfig(); - for (Map.Entry entry : conf.entrySet()) { - String key = entry.getKey(); - Object val = entry.getValue(); - if ("username".equals(key)) { - hc.setUsername(S.string(val)); - } else if ("password".equals(key)) { - hc.setPassword(S.string(val)); - } else if ("url".equals(key) || "jdbcUrl".equals(key)) { - hc.setJdbcUrl(S.string(val)); - } else if ("maximumPoolSize".equals(key)) { - hc.setMaximumPoolSize(Integer.parseInt(S.string(val))); - } else if ("autoCommit".equals(key)) { - hc.setAutoCommit(Boolean.valueOf(S.string(val))); - } else if ("idleTimeout".equals(key)) { - hc.setIdleTimeout(Long.parseLong(S.string(val))); - } else if ("maxLifetime".equals(key)) { - hc.setMaxLifetime(Long.parseLong(S.string(val))); - } else if ("connectionTimeout".equals(key)) { - hc.setConnectionTimeout(Long.parseLong(S.string(val))); - } else if ("minimumIdle".equals(key)) { - hc.setMinimumIdle(Integer.parseInt(S.string(val))); - } else if ("poolName".equals(key)) { - hc.setPoolName(S.string(val)); - } else if ("driverClassName".equals(key)) { - hc.setDriverClassName(S.string(val)); - } else { - hc.addDataSourceProperty(entry.getKey(), entry.getValue()); - } - } - ensureDefaultDatasourceConfig(hc); - return new HikariDataSource(hc); + public EbeanServer ebean(boolean readOnly) { + return readOnly ? ebeanReadOnly : ebean; } - private void ensureDefaultDatasourceConfig(HikariConfig dsc) { - String username = dsc.getUsername(); - if (null == username) { - logger.warn("No data source user configuration specified. Will use the default 'sa' user"); - username = "sa"; - } - dsc.setUsername(username); - - String password = dsc.getPassword(); - if (null == password) { - password = ""; - } - dsc.setPassword(password); - - String url = dsc.getJdbcUrl(); - if (null == url) { - logger.warn("No database URL configuration specified. Will use the default h2 inmemory test database"); - url = "jdbc:h2:mem:tests"; - } - dsc.setJdbcUrl(url); - - - String driver = dsc.getDriverClassName(); - if (null == driver) { - if (url.contains("mysql")) { - driver = "com.mysql.jdbc.Driver"; - } else if (url.contains("postgresql")) { - driver = "org.postgresql.Driver"; - } else if (url.contains("jdbc:h2:")) { - driver = "org.h2.Driver"; - } else if (url.contains("jdbc:oracle")) { - driver = "oracle.jdbc.OracleDriver"; - } else if (url.contains("sqlserver")) { - driver = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; - } else if (url.contains("jdbc:db2")) { - driver = "com.ibm.db2.jcc.DB2Driver"; - } else { - throw E.invalidConfiguration("JDBC driver needs to be configured for datasource: %s", id()); - } - logger.warn("JDBC driver not configured, system automatically set to: " + driver); - } - dsc.setDriverClassName(driver); + public EbeanServer ebean() { + return ebean; } - private void ensureDefaultDatasourceConfig(DataSourceConfig dsc) { - String username = dsc.getUsername(); - if (null == username) { - logger.warn("No data source user configuration specified. Will use the default 'sa' user"); - username = "sa"; - } - dsc.setUsername(username); - - String password = dsc.getPassword(); - if (null == password) { - password = ""; - } - dsc.setPassword(password); - - String url = dsc.getUrl(); - if (null == url) { - logger.warn("No database URL configuration specified. Will use the default h2 inmemory test database"); - url = "jdbc:h2:mem:tests"; - } - dsc.setUrl(url); - - String driver = dsc.getDriver(); - if (null == driver) { - if (url.contains("mysql")) { - driver = "com.mysql.jdbc.Driver"; - } else if (url.contains("postgresql")) { - driver = "org.postgresql.Driver"; - } else if (url.contains("jdbc:h2:")) { - driver = "org.h2.Driver"; - } else if (url.contains("jdbc:oracle")) { - driver = "oracle.jdbc.OracleDriver"; - } else if (url.contains("sqlserver")) { - driver = "com.microsoft.sqlserver.jdbc.SQLServerDriver"; - } else if (url.contains("jdbc:db2")) { - driver = "com.ibm.db2.jcc.DB2Driver"; - } else { - throw E.invalidConfiguration("JDBC driver needs to be configured for datasource: %s", id()); - } - logger.warn("JDBC driver not configured, system automatically set to: " + driver); - } - dsc.setDriver(driver); + public EbeanServer ebeanReadOnly() { + return ebeanReadOnly; } - public static void registerModelType(Class modelType) { - modelTypes.add(modelType); - Class superType = modelType.getSuperclass(); - if (!Object.class.equals(superType)) { - registerModelType(superType); - } - } } diff --git a/src/main/java/act/db/ebean/EntityFinder.java b/src/main/java/act/db/ebean/EntityFinder.java deleted file mode 100644 index 8e0258e..0000000 --- a/src/main/java/act/db/ebean/EntityFinder.java +++ /dev/null @@ -1,25 +0,0 @@ -package act.db.ebean; - -import act.ActComponent; -import act.app.App; -import act.app.AppByteCodeScanner; -import act.util.AnnotatedTypeFinder; -import org.osgl.$; -import org.osgl.exception.NotAppliedException; - -import javax.persistence.Entity; -import java.util.Map; -import java.util.Set; - -@ActComponent -public class EntityFinder extends AnnotatedTypeFinder { - public EntityFinder() { - super(true, false, Entity.class, new $.F2, Set>>() { - @Override - public Map, Set> apply(App app, String className) throws NotAppliedException, $.Break { - EbeanService.registerModelType($.classForName(className, app.classLoader())); - return null; - } - }); - } -} diff --git a/src/main/java/act/db/ebean/IdGeneratorRegister.java b/src/main/java/act/db/ebean/IdGeneratorRegister.java new file mode 100644 index 0000000..cc6b397 --- /dev/null +++ b/src/main/java/act/db/ebean/IdGeneratorRegister.java @@ -0,0 +1,44 @@ +package act.db.ebean; + +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2020 ActFramework + * %% + * 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. + * #L% + */ + + +import com.avaje.ebean.config.IdGenerator; +import com.avaje.ebean.config.ServerConfig; + +import javax.inject.Inject; +import javax.inject.Singleton; +import java.util.ArrayList; +import java.util.List; + +@Singleton +public class IdGeneratorRegister { + + @Inject + private List idGenerators = new ArrayList<>(); + + void registerTo(ServerConfig config) { + for (IdGenerator generator : idGenerators) { + config.add(generator); + } + } + +} diff --git a/src/main/java/act/db/ebean/ModelBaseEnhancer.java b/src/main/java/act/db/ebean/ModelBaseEnhancer.java index a7b96e1..dceeafc 100644 --- a/src/main/java/act/db/ebean/ModelBaseEnhancer.java +++ b/src/main/java/act/db/ebean/ModelBaseEnhancer.java @@ -1,5 +1,25 @@ package act.db.ebean; +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2017 ActFramework + * %% + * 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. + * #L% + */ + import act.asm.AnnotationVisitor; import act.asm.Type; import act.util.AppByteCodeEnhancer; diff --git a/src/main/java/act/db/ebean/PreEbeanCreation.java b/src/main/java/act/db/ebean/PreEbeanCreation.java index e9b5a3d..5e2dcf3 100644 --- a/src/main/java/act/db/ebean/PreEbeanCreation.java +++ b/src/main/java/act/db/ebean/PreEbeanCreation.java @@ -1,5 +1,25 @@ package act.db.ebean; +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2017 ActFramework + * %% + * 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. + * #L% + */ + import act.event.ActEvent; import com.avaje.ebean.config.ServerConfig; diff --git a/src/main/java/act/db/ebean/util/EbeanConfigAdaptor.java b/src/main/java/act/db/ebean/util/EbeanConfigAdaptor.java new file mode 100644 index 0000000..02e64a0 --- /dev/null +++ b/src/main/java/act/db/ebean/util/EbeanConfigAdaptor.java @@ -0,0 +1,118 @@ +package act.db.ebean.util; + +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2017 ActFramework + * %% + * 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. + * #L% + */ + +import static act.db.sql.util.NamingConvention.Default.MATCHING; + +import act.db.sql.SqlDbService; +import act.db.sql.SqlDbServiceConfig; +import act.util.LogSupport; +import com.avaje.ebean.config.MatchingNamingConvention; +import com.avaje.ebean.config.NamingConvention; +import com.avaje.ebean.config.ServerConfig; +import com.avaje.ebean.config.UnderscoreNamingConvention; +import org.avaje.datasource.DataSourceConfig; +import org.osgl.util.S; + +import java.sql.Connection; +import java.util.Properties; +import java.util.Set; +import javax.inject.Singleton; + +/** + * Adapt {@link act.db.sql.SqlDbServiceConfig} to {@link ServerConfig} + */ +@Singleton +public class EbeanConfigAdaptor extends LogSupport { + + public ServerConfig adaptFrom(SqlDbServiceConfig actConfig, act.db.sql.DataSourceConfig dsConfig, SqlDbService svc) { + ServerConfig config = new ServerConfig(); + + config.setName(svc.id()); + config.setDataSourceConfig(adaptFrom(dsConfig, svc)); + + config.setDdlGenerate(actConfig.ddlGeneratorConfig.create); + config.setDdlRun(actConfig.ddlGeneratorConfig.create); + config.setDdlCreateOnly(!actConfig.ddlGeneratorConfig.drop); + + config.setNamingConvention(namingConvention(actConfig)); + config.setDatabasePlatformName(dsConfig.customProperties.get("databasePlatformName")); + + Set modelClasses = svc.entityClasses(); + if (null != modelClasses && !modelClasses.isEmpty()) { + for (Class modelClass : modelClasses) { + if (isTraceEnabled()) { + trace(S.concat("add model class into Ebean config: ", modelClass.getName())); + } + config.addClass(modelClass); + } + } + + config.setDisableClasspathSearch(true); + return config; + } + + public DataSourceConfig adaptFrom(act.db.sql.DataSourceConfig actConfig, SqlDbService svc) { + Properties properties = new Properties(); + properties.putAll(actConfig.customProperties); + properties.put("isolationLevel", adaptIsolationLevel(actConfig.isolationLevel)); + + DataSourceConfig config = new DataSourceConfig(); + config.loadSettings(properties, svc.id()); + + config.setUrl(actConfig.url); + config.setDriver(actConfig.driver); + config.setUsername(actConfig.username); + config.setPassword(actConfig.password); + config.setAutoCommit(actConfig.autoCommit); + config.setIsolationLevel(actConfig.isolationLevel); + + config.setMinConnections(actConfig.minConnections); + config.setMaxConnections(actConfig.maxConnections); + + return config; + } + + private NamingConvention namingConvention(SqlDbServiceConfig svcConfig) { + if (!svcConfig.rawConf.containsKey("naming.convention")) { + // https://github.com/actframework/act-ebean2/issues/1 + return new UnderscoreNamingConvention(); + } + //TODO provide more actuate naming convention matching logic + if (MATCHING == svcConfig.tableNamingConvention) { + return new MatchingNamingConvention(); + } + return new UnderscoreNamingConvention(); + } + + private String adaptIsolationLevel(int level) { + switch (level) { + case Connection.TRANSACTION_NONE : return "NONE"; + case Connection.TRANSACTION_READ_COMMITTED : return "READ_COMMITTED"; + case Connection.TRANSACTION_READ_UNCOMMITTED : return "READ_UNCOMMITTED"; + case Connection.TRANSACTION_REPEATABLE_READ : return "REPEATABLE_READ"; + case Connection.TRANSACTION_SERIALIZABLE : return "SERIALIZABLE"; + default: + warn("Unknown isolationLevel found: %s; will use default level: REPEATABLE_READ"); + return "REPEATABLE_READ"; + } + } +} diff --git a/src/main/java/act/db/ebean/util/EbeanDaoLoader.java b/src/main/java/act/db/ebean/util/EbeanDaoLoader.java index 1e5575c..11e88e7 100644 --- a/src/main/java/act/db/ebean/util/EbeanDaoLoader.java +++ b/src/main/java/act/db/ebean/util/EbeanDaoLoader.java @@ -1,5 +1,25 @@ package act.db.ebean.util; +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2017 ActFramework + * %% + * 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. + * #L% + */ + import act.app.App; import act.app.DbServiceManager; import act.db.ebean.EbeanDao; diff --git a/src/main/java/act/db/ebean/util/EbeanDataSourceProvider.java b/src/main/java/act/db/ebean/util/EbeanDataSourceProvider.java new file mode 100644 index 0000000..b8d9570 --- /dev/null +++ b/src/main/java/act/db/ebean/util/EbeanDataSourceProvider.java @@ -0,0 +1,77 @@ +package act.db.ebean.util; + +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2018 ActFramework + * %% + * 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. + * #L% + */ + +import act.db.sql.DataSourceConfig; +import act.db.sql.DataSourceProvider; +import act.db.sql.SqlDbService; +import act.db.sql.SqlDbServiceConfig; +import act.db.sql.monitor.DataSourceStatus; +import com.avaje.ebean.config.ServerConfig; +import org.avaje.datasource.DataSourceAlertFactory; +import org.avaje.datasource.DataSourceFactory; + +import java.util.Map; +import javax.sql.DataSource; + +public class EbeanDataSourceProvider extends DataSourceProvider { + + private SqlDbServiceConfig actConfig; + private SqlDbService svc; + + public EbeanDataSourceProvider(SqlDbServiceConfig actConfig, SqlDbService svc) { + this.actConfig = actConfig; + this.svc = svc; + } + + @Override + public DataSource createDataSource(DataSourceConfig conf) { + ServerConfig ebeanConfig = new EbeanConfigAdaptor().adaptFrom(actConfig, conf, svc); + DataSourceFactory factory = ebeanConfig.service(DataSourceFactory.class); + if (factory == null) { + throw new IllegalStateException("No DataSourceFactory service implementation found in class path." + + " Probably missing dependency to avaje-datasource?"); + } + + DataSourceAlertFactory alertFactory = ebeanConfig.service(DataSourceAlertFactory.class); + org.avaje.datasource.DataSourceConfig dsConfig = ebeanConfig.getDataSourceConfig(); + if (alertFactory != null) { + dsConfig.setAlert(alertFactory.createAlert()); + } + + if (conf.readOnly) { + // setup to use AutoCommit such that we skip explicit commit + dsConfig.setAutoCommit(true); + } + String poolName = ebeanConfig.getName() + (conf.readOnly ? "-ro" : ""); + return new EbeanDataSourceWrapper(ebeanConfig, factory.createPool(poolName, dsConfig)); + } + + @Override + public Map confKeyMapping() { + return null; + } + + @Override + public DataSourceStatus getStatus(DataSource ds) { + return null; + } +} diff --git a/src/main/java/act/db/ebean/util/EbeanDataSourceWrapper.java b/src/main/java/act/db/ebean/util/EbeanDataSourceWrapper.java new file mode 100644 index 0000000..efd0c7b --- /dev/null +++ b/src/main/java/act/db/ebean/util/EbeanDataSourceWrapper.java @@ -0,0 +1,88 @@ +package act.db.ebean.util; + +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2018 ActFramework + * %% + * 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. + * #L% + */ + +import com.avaje.ebean.config.ServerConfig; +import org.avaje.datasource.DataSourcePool; + +import java.io.PrintWriter; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.SQLFeatureNotSupportedException; +import java.util.logging.Logger; +import javax.sql.DataSource; + +public class EbeanDataSourceWrapper implements DataSource { + + public ServerConfig ebeanConfig; + public DataSourcePool ebeanDs; + + public EbeanDataSourceWrapper(ServerConfig ebeanConfig, DataSourcePool ebeanDs) { + ebeanConfig.setDataSource(ebeanDs); + this.ebeanConfig = ebeanConfig; + this.ebeanDs = ebeanDs; + } + + @Override + public Connection getConnection() throws SQLException { + return ebeanDs.getConnection(); + } + + @Override + public Connection getConnection(String username, String password) throws SQLException { + return ebeanDs.getConnection(username, password); + } + + @Override + public T unwrap(Class iface) throws SQLException { + return ebeanDs.unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return ebeanDs.isWrapperFor(iface); + } + + @Override + public PrintWriter getLogWriter() throws SQLException { + return ebeanDs.getLogWriter(); + } + + @Override + public void setLogWriter(PrintWriter out) throws SQLException { + ebeanDs.setLogWriter(out); + } + + @Override + public void setLoginTimeout(int seconds) throws SQLException { + ebeanDs.setLoginTimeout(seconds); + } + + @Override + public int getLoginTimeout() throws SQLException { + return ebeanDs.getLoginTimeout(); + } + + @Override + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + return ebeanDs.getParentLogger(); + } +} diff --git a/src/main/java/act/db/ebean/util/TimestampAuditorEnhancer.java b/src/main/java/act/db/ebean/util/TimestampAuditorEnhancer.java new file mode 100644 index 0000000..e18dfc4 --- /dev/null +++ b/src/main/java/act/db/ebean/util/TimestampAuditorEnhancer.java @@ -0,0 +1,99 @@ +package act.db.ebean.util; + +/*- + * #%L + * ACT Ebean2 + * %% + * Copyright (C) 2015 - 2018 ActFramework + * %% + * 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. + * #L% + */ + +import act.app.App; +import act.asm.AnnotationVisitor; +import act.asm.FieldVisitor; +import act.asm.Type; +import act.db.meta.EntityClassMetaInfo; +import act.db.meta.EntityFieldMetaInfo; +import act.db.meta.EntityMetaInfoRepo; +import act.util.AppByteCodeEnhancer; +import org.osgl.util.S; + +// Adapt act timestamp annotation to ebean timestamp annotation +public class TimestampAuditorEnhancer extends AppByteCodeEnhancer { + + private EntityMetaInfoRepo metaInfoRepo; + private String className; + private String createdAt; + private String lastModifiedAt; + + public TimestampAuditorEnhancer() { + super(S.F.startsWith("act.").negate()); + } + + @Override + protected Class subClass() { + return TimestampAuditorEnhancer.class; + } + + @Override + public AppByteCodeEnhancer app(App app) { + metaInfoRepo = app.entityMetaInfoRepo(); + return super.app(app); + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) { + String classDesc = "L" + name + ";"; + className = Type.getType(classDesc).getClassName(); + EntityClassMetaInfo classMetaInfo = metaInfoRepo.classMetaInfo(className); + if (null != classMetaInfo) { + EntityFieldMetaInfo fieldMetaInfo = classMetaInfo.createdAtField(); + if (null != fieldMetaInfo) { + createdAt = fieldMetaInfo.fieldName(); + } + fieldMetaInfo = classMetaInfo.lastModifiedAtField(); + if (null != fieldMetaInfo) { + lastModifiedAt = fieldMetaInfo.fieldName(); + } + } + super.visit(version, access, name, signature, superName, interfaces); + } + + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + FieldVisitor fv = super.visitField(access, name, desc, signature, value); + if (null == createdAt && null == lastModifiedAt) { + return fv; + } + final boolean isCreatedAt = name.equals(createdAt); + final boolean isLastModified = !isCreatedAt && name.equals(lastModifiedAt); + if (!isCreatedAt && !isLastModified) { + return fv; + } + return new FieldVisitor(ASM5, fv) { + @Override + public void visitEnd() { + AnnotationVisitor av; + if (isCreatedAt) { + av = fv.visitAnnotation("Lcom/avaje/ebean/annotation/WhenCreated;", true); + } else { + av = fv.visitAnnotation("Lcom/avaje/ebean/annotation/WhenModified;", true); + } + av.visitEnd(); + super.visitEnd(); + } + }; + } +} diff --git a/src/main/resources/act.scan.list b/src/main/resources/act.scan.list index 35bfda6..698965d 100644 --- a/src/main/resources/act.scan.list +++ b/src/main/resources/act.scan.list @@ -1 +1,3 @@ -act.db.ebean.EbeanModule \ No newline at end of file +act.db.ebean.EbeanModule +act.db.ebean.IdGeneratorRegister +act.db.ebean.EbeanConfiguratorManager \ No newline at end of file diff --git a/src/main/resources/act/db/ebean/.version b/src/main/resources/act/db/ebean/.version new file mode 100644 index 0000000..ea0c261 --- /dev/null +++ b/src/main/resources/act/db/ebean/.version @@ -0,0 +1,3 @@ +artifact=${project.artifactId} +version=${project.version} +build=${buildNumber} \ No newline at end of file diff --git a/src/test/java/act/db/ebean/VersionTest.java b/src/test/java/act/db/ebean/VersionTest.java new file mode 100644 index 0000000..6902457 --- /dev/null +++ b/src/test/java/act/db/ebean/VersionTest.java @@ -0,0 +1,33 @@ +package act.db.ebean; + +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2017 ActFramework + * %% + * 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. + * #L% + */ + +import org.junit.Test; +import osgl.ut.TestBase; + +public class VersionTest extends TestBase { + + @Test + public void versionShallContainsEbean() { + yes(EbeanPlugin.VERSION.toString().contains("ebean")); + } + +} diff --git a/src/test/java/playground/Contact.java b/src/test/java/playground/Contact.java index 8486ccd..16b065f 100644 --- a/src/test/java/playground/Contact.java +++ b/src/test/java/playground/Contact.java @@ -1,5 +1,25 @@ package playground; +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2017 ActFramework + * %% + * 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. + * #L% + */ + import javax.persistence.Entity; import javax.persistence.Id; diff --git a/src/test/java/playground/ContactService.java b/src/test/java/playground/ContactService.java index 33a4fa2..d221d6f 100644 --- a/src/test/java/playground/ContactService.java +++ b/src/test/java/playground/ContactService.java @@ -1,5 +1,25 @@ package playground; +/*- + * #%L + * ACT Ebean + * %% + * Copyright (C) 2015 - 2017 ActFramework + * %% + * 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. + * #L% + */ + import act.db.ebean.EbeanDao; import java.lang.reflect.Field;