You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@james.apache.org by bt...@apache.org on 2023/06/14 06:59:31 UTC
[james-project] 09/28: Allow passing openjpa and underlying datasource (DBCP2) properties (#1580)
This is an automated email from the ASF dual-hosted git repository.
btellier pushed a commit to branch 3.8.x
in repository https://gitbox.apache.org/repos/asf/james-project.git
commit 534a7848183c7172d155b52ba0431f5adfc996b5
Author: Wojtek <wo...@users.noreply.github.com>
AuthorDate: Mon Jun 12 04:25:44 2023 -0400
Allow passing openjpa and underlying datasource (DBCP2) properties (#1580)
---
.../james/modules/data/JPAConfiguration.java | 87 ++++++++++++++++++++--
.../james/modules/data/JPAEntityManagerModule.java | 53 +++++++++----
src/site/xdoc/server/config-system.xml | 9 ++-
3 files changed, 126 insertions(+), 23 deletions(-)
diff --git a/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPAConfiguration.java b/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPAConfiguration.java
index bed6c9041a..62419c1bb2 100644
--- a/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPAConfiguration.java
+++ b/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPAConfiguration.java
@@ -19,11 +19,17 @@
package org.apache.james.modules.data;
import static org.apache.james.modules.data.JPAConfiguration.Credential.NO_CREDENTIAL;
+import static org.apache.james.modules.data.JPAConfiguration.ReadyToBuild.CUSTOM_DATASOURCE_PROPERTIES;
+import static org.apache.james.modules.data.JPAConfiguration.ReadyToBuild.CUSTOM_OPENJPA_PROPERTIES;
import static org.apache.james.modules.data.JPAConfiguration.ReadyToBuild.NO_MAX_CONNECTIONS;
+import static org.apache.james.modules.data.JPAConfiguration.ReadyToBuild.NO_MULTITHREADED;
import static org.apache.james.modules.data.JPAConfiguration.ReadyToBuild.NO_TEST_ON_BORROW;
import static org.apache.james.modules.data.JPAConfiguration.ReadyToBuild.NO_VALIDATION_QUERY;
import static org.apache.james.modules.data.JPAConfiguration.ReadyToBuild.NO_VALIDATION_QUERY_TIMEOUT_SEC;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Optional;
import org.apache.commons.lang3.StringUtils;
@@ -34,6 +40,23 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
public class JPAConfiguration {
+ static final String JPA_CONNECTION_DRIVER_NAME = "openjpa.ConnectionDriverName";
+ static final String JPA_CONNECTION_USERNAME = "openjpa.ConnectionUserName";
+ static final String JPA_CONNECTION_PASSWORD = "openjpa.ConnectionPassword";
+ static final String JPA_CONNECTION_PROPERTIES = "openjpa.ConnectionProperties";
+ static final String JPA_CONNECTION_URL = "openjpa.ConnectionURL";
+ static final String JPA_MULTITHREADED = "openjpa.Multithreaded";
+ static List<String> DEFAULT_JPA_PROPERTIES = List.of(JPA_CONNECTION_DRIVER_NAME, JPA_CONNECTION_URL, JPA_MULTITHREADED, JPA_CONNECTION_USERNAME, JPA_CONNECTION_PASSWORD);
+
+ static final String DATASOURCE_TEST_ON_BORROW = "datasource.testOnBorrow";
+ static final String DATASOURCE_VALIDATION_QUERY_TIMEOUT_SEC = "datasource.validationQueryTimeoutSec";
+ static final String DATASOURCE_VALIDATION_QUERY = "datasource.validationQuery";
+ static final String DATASOURCE_MAX_TOTAL = "datasource.maxTotal";
+ static List<String> DEFAULT_DATASOURCE_PROPERTIES = List.of(DATASOURCE_TEST_ON_BORROW, DATASOURCE_VALIDATION_QUERY_TIMEOUT_SEC, DATASOURCE_VALIDATION_QUERY, DATASOURCE_MAX_TOTAL);
+
+ static {
+ }
+
public static class Credential {
private static final Logger LOGGER = LoggerFactory.getLogger(Credential.class);
static final Optional<Credential> NO_CREDENTIAL = Optional.empty();
@@ -78,40 +101,50 @@ public class JPAConfiguration {
public static class ReadyToBuild {
static final Optional<Boolean> NO_TEST_ON_BORROW = Optional.empty();
+ static final Optional<Boolean> NO_MULTITHREADED = Optional.empty();
static final Optional<Integer> NO_VALIDATION_QUERY_TIMEOUT_SEC = Optional.empty();
static final Optional<String> NO_VALIDATION_QUERY = Optional.empty();
static final Optional<Integer> NO_MAX_CONNECTIONS = Optional.empty();
+ static final Map<String,String> CUSTOM_OPENJPA_PROPERTIES = Map.of();
+ static final Map<String,String> CUSTOM_DATASOURCE_PROPERTIES = Map.of();
private final String driverName;
private final String driverURL;
private Optional<Credential> credential;
private Optional<Boolean> testOnBorrow;
+ private Optional<Boolean> multithreaded;
private Optional<Integer> validationQueryTimeoutSec;
private Optional<String> validationQuery;
private Optional<Integer> maxConnections;
+ private Map<String,String> customDatasourceProperties;
+ private Map<String,String> customOpenjpaProperties;
private ReadyToBuild(String driverName, String driverURL, Optional<Credential> credential,
- Optional<Boolean> testOnBorrow, Optional<Integer> validationQueryTimeoutSec,
- Optional<String> validationQuery,Optional<Integer> maxConnections
+ Optional<Boolean> testOnBorrow, Optional<Boolean> multithreaded, Optional<Integer> validationQueryTimeoutSec,
+ Optional<String> validationQuery,Optional<Integer> maxConnections,
+ Map<String,String> customDatasourceProperties, Map<String,String> customOpenjpaProperties
) {
this.driverName = driverName;
this.driverURL = driverURL;
this.credential = credential;
this.testOnBorrow = testOnBorrow;
+ this.multithreaded = multithreaded;
this.validationQueryTimeoutSec = validationQueryTimeoutSec;
this.validationQuery = validationQuery;
this.maxConnections = maxConnections;
+ this.customDatasourceProperties = customDatasourceProperties;
+ this.customOpenjpaProperties = customOpenjpaProperties;
}
public JPAConfiguration build() {
- return new JPAConfiguration(driverName, driverURL, credential, testOnBorrow, validationQueryTimeoutSec, validationQuery, maxConnections);
+ return new JPAConfiguration(driverName, driverURL, credential, testOnBorrow, multithreaded, validationQueryTimeoutSec, validationQuery, maxConnections, customDatasourceProperties, customOpenjpaProperties);
}
public RequirePassword username(String username) {
return password -> new ReadyToBuild(driverName, driverURL, Credential.of(username, password),
- testOnBorrow, validationQueryTimeoutSec, validationQuery, maxConnections);
+ testOnBorrow, multithreaded, validationQueryTimeoutSec, validationQuery, maxConnections, customDatasourceProperties, customOpenjpaProperties);
}
public ReadyToBuild testOnBorrow(Boolean testOnBorrow) {
@@ -119,6 +152,11 @@ public class JPAConfiguration {
return this;
}
+ public ReadyToBuild multithreaded(Boolean multithreaded) {
+ this.multithreaded = Optional.ofNullable(multithreaded);
+ return this;
+ }
+
public ReadyToBuild validationQueryTimeoutSec(Integer validationQueryTimeoutSec) {
this.validationQueryTimeoutSec = Optional.ofNullable(validationQueryTimeoutSec);
return this;
@@ -133,6 +171,19 @@ public class JPAConfiguration {
this.maxConnections = Optional.ofNullable(maxConnections);
return this;
}
+
+ public ReadyToBuild setCustomDatasourceProperties(Map<String, String> customDatasourceProperties) {
+ this.customDatasourceProperties = new HashMap<>(customDatasourceProperties);
+ DEFAULT_DATASOURCE_PROPERTIES.forEach(this.customDatasourceProperties::remove);
+ return this;
+ }
+
+ public ReadyToBuild setCustomOpenjpaProperties(Map<String, String> customOpenjpaProperties) {
+ this.customOpenjpaProperties = customOpenjpaProperties;
+ DEFAULT_JPA_PROPERTIES.forEach(this.customOpenjpaProperties::remove);
+ return this;
+ }
+
}
@FunctionalInterface
@@ -141,21 +192,25 @@ public class JPAConfiguration {
}
public static RequireDriverName builder() {
- return driverName -> driverURL -> new ReadyToBuild(driverName, driverURL, NO_CREDENTIAL, NO_TEST_ON_BORROW,
- NO_VALIDATION_QUERY_TIMEOUT_SEC, NO_VALIDATION_QUERY, NO_MAX_CONNECTIONS);
+ return driverName -> driverURL -> new ReadyToBuild(driverName, driverURL, NO_CREDENTIAL, NO_TEST_ON_BORROW, NO_MULTITHREADED,
+ NO_VALIDATION_QUERY_TIMEOUT_SEC, NO_VALIDATION_QUERY, NO_MAX_CONNECTIONS, CUSTOM_DATASOURCE_PROPERTIES, CUSTOM_OPENJPA_PROPERTIES);
}
private final String driverName;
private final String driverURL;
private final Optional<Boolean> testOnBorrow;
+ private final Optional<Boolean> multithreaded;
private final Optional<Integer> validationQueryTimeoutSec;
private final Optional<Credential> credential;
private final Optional<String> validationQuery;
private final Optional<Integer> maxConnections;
+ private Map<String,String> customDatasourceProperties;
+ private Map<String,String> customOpenjpaProperties;
+
@VisibleForTesting
- JPAConfiguration(String driverName, String driverURL, Optional<Credential> credential, Optional<Boolean> testOnBorrow,
- Optional<Integer> validationQueryTimeoutSec, Optional<String> validationQuery, Optional<Integer> maxConnections) {
+ JPAConfiguration(String driverName, String driverURL, Optional<Credential> credential, Optional<Boolean> testOnBorrow, Optional<Boolean> multithreaded,
+ Optional<Integer> validationQueryTimeoutSec, Optional<String> validationQuery, Optional<Integer> maxConnections, Map<String,String> customDatasourceProperties, Map<String,String> customOpenjpaProperties) {
Preconditions.checkNotNull(driverName, "driverName cannot be null");
Preconditions.checkNotNull(driverURL, "driverURL cannot be null");
validationQueryTimeoutSec.ifPresent(timeoutInSec ->
@@ -167,9 +222,13 @@ public class JPAConfiguration {
this.driverURL = driverURL;
this.credential = credential;
this.testOnBorrow = testOnBorrow;
+ this.multithreaded = multithreaded;
this.validationQueryTimeoutSec = validationQueryTimeoutSec;
this.validationQuery = validationQuery;
this.maxConnections = maxConnections;
+ this.customDatasourceProperties = customDatasourceProperties;
+ this.customOpenjpaProperties = customOpenjpaProperties;
+
}
public String getDriverName() {
@@ -184,6 +243,10 @@ public class JPAConfiguration {
return testOnBorrow;
}
+ public Optional<Boolean> isMultithreaded() {
+ return multithreaded;
+ }
+
public Optional<Integer> getValidationQueryTimeoutSec() {
return validationQueryTimeoutSec;
}
@@ -196,6 +259,14 @@ public class JPAConfiguration {
return credential;
}
+ public Map<String, String> getCustomOpenjpaProperties() {
+ return customOpenjpaProperties;
+ }
+
+ public Map<String, String> getCustomDatasourceProperties() {
+ return customDatasourceProperties;
+ }
+
public Optional<Integer> getMaxConnections() {
return maxConnections;
diff --git a/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPAEntityManagerModule.java b/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPAEntityManagerModule.java
index 8024f4f8b7..efcc300c11 100644
--- a/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPAEntityManagerModule.java
+++ b/server/container/guice/jpa-common/src/main/java/org/apache/james/modules/data/JPAEntityManagerModule.java
@@ -21,7 +21,10 @@ package org.apache.james.modules.data;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Iterator;
import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
import javax.inject.Singleton;
import javax.persistence.EntityManagerFactory;
@@ -41,25 +44,30 @@ public class JPAEntityManagerModule extends AbstractModule {
public EntityManagerFactory provideEntityManagerFactory(JPAConfiguration jpaConfiguration) {
HashMap<String, String> properties = new HashMap<>();
- properties.put("openjpa.ConnectionDriverName", jpaConfiguration.getDriverName());
- properties.put("openjpa.ConnectionURL", jpaConfiguration.getDriverURL());
+ properties.put(JPAConfiguration.JPA_CONNECTION_DRIVER_NAME, jpaConfiguration.getDriverName());
+ properties.put(JPAConfiguration.JPA_CONNECTION_URL, jpaConfiguration.getDriverURL());
jpaConfiguration.getCredential()
.ifPresent(credential -> {
- properties.put("openjpa.ConnectionUserName", credential.getUsername());
- properties.put("openjpa.ConnectionPassword", credential.getPassword());
+ properties.put(JPAConfiguration.JPA_CONNECTION_USERNAME, credential.getUsername());
+ properties.put(JPAConfiguration.JPA_CONNECTION_PASSWORD, credential.getPassword());
});
- List<String> connectionFactoryProperties = new ArrayList<>();
- connectionFactoryProperties.add("TestOnBorrow=" + jpaConfiguration.isTestOnBorrow());
+ List<String> connectionProperties = new ArrayList<>();
+ jpaConfiguration.isTestOnBorrow().ifPresent(testOnBorrow -> connectionProperties.add("TestOnBorrow=" + testOnBorrow));
jpaConfiguration.getValidationQueryTimeoutSec()
- .ifPresent(timeoutSecond -> connectionFactoryProperties.add("ValidationTimeout=" + timeoutSecond * 1000));
+ .ifPresent(timeoutSecond -> connectionProperties.add("ValidationTimeout=" + timeoutSecond * 1000));
jpaConfiguration.getValidationQuery()
- .ifPresent(validationQuery -> connectionFactoryProperties.add("ValidationSQL='" + validationQuery + "'"));
- jpaConfiguration.getMaxConnections().ifPresent(maxConnections ->
- connectionFactoryProperties.add("MaxTotal=" + maxConnections)
- );
+ .ifPresent(validationQuery -> connectionProperties.add("ValidationSQL='" + validationQuery + "'"));
+ jpaConfiguration.getMaxConnections()
+ .ifPresent(maxConnections -> connectionProperties.add("MaxTotal=" + maxConnections));
- properties.put("openjpa.ConnectionFactoryProperties", Joiner.on(", ").join(connectionFactoryProperties));
+ connectionProperties.addAll(jpaConfiguration.getCustomDatasourceProperties().entrySet().stream().map(entry -> entry.getKey() + "=" + entry.getValue()).collect(Collectors.toList()));
+ properties.put(JPAConfiguration.JPA_CONNECTION_PROPERTIES, Joiner.on(",").join(connectionProperties));
+ properties.putAll(jpaConfiguration.getCustomOpenjpaProperties());
+
+ if (jpaConfiguration.isMultithreaded().isPresent()) {
+ properties.put(JPAConfiguration.JPA_MULTITHREADED, jpaConfiguration.isMultithreaded().get().toString());
+ }
return Persistence.createEntityManagerFactory("Global", properties);
}
@@ -68,15 +76,34 @@ public class JPAEntityManagerModule extends AbstractModule {
@Singleton
JPAConfiguration provideConfiguration(PropertiesProvider propertiesProvider) throws FileNotFoundException, ConfigurationException {
Configuration dataSource = propertiesProvider.getConfiguration("james-database");
+
+ Map<String, String> openjpaProperties = getKeysForPrefix(dataSource, "openjpa", false);
+ Map<String, String> datasourceProperties = getKeysForPrefix(dataSource, "datasource", true);
+
+
return JPAConfiguration.builder()
.driverName(dataSource.getString("database.driverClassName"))
.driverURL(dataSource.getString("database.url"))
.testOnBorrow(dataSource.getBoolean("datasource.testOnBorrow", false))
.validationQueryTimeoutSec(dataSource.getInteger("datasource.validationQueryTimeoutSec", null))
.validationQuery(dataSource.getString("datasource.validationQuery", null))
- .maxConnections(dataSource.getInteger("datasource.maxTotal",null))
+ .maxConnections(dataSource.getInteger("datasource.maxTotal", null))
+ .multithreaded(dataSource.getBoolean(JPAConfiguration.JPA_MULTITHREADED, true))
.username(dataSource.getString("database.username"))
.password(dataSource.getString("database.password"))
+ .setCustomOpenjpaProperties(openjpaProperties)
+ .setCustomDatasourceProperties(datasourceProperties)
.build();
}
+
+ private static Map<String, String> getKeysForPrefix(Configuration dataSource, String prefix, boolean stripPrefix) {
+ Iterator<String> keys = dataSource.getKeys(prefix);
+ Map<String, String> properties = new HashMap<>();
+ while (keys.hasNext()) {
+ String key = keys.next();
+ String propertyKey = stripPrefix ? key.replace(prefix + ".", "") : key;
+ properties.put(propertyKey, dataSource.getString(key));
+ }
+ return properties;
+ }
}
diff --git a/src/site/xdoc/server/config-system.xml b/src/site/xdoc/server/config-system.xml
index 9d6602a1b8..55650a198a 100644
--- a/src/site/xdoc/server/config-system.xml
+++ b/src/site/xdoc/server/config-system.xml
@@ -88,14 +88,19 @@
<dd>true or false - Use streaming for Blobs. This is only supported on a limited set of databases atm. You
should check if its supported by your DB before enable it. See <a href="http://openjpa.apache.org/builds/latest/docs/manual/ref_guide_mapping_jpa.html">http://openjpa.apache.org/builds/latest/docs/manual/ref_guide_mapping_jpa.html</a> (#7.11. LOB Streaming).</dd>
</dl>
- <p>The JPA datasource handling is delegated to commons-dbcp, some of dbcp properties can be configured through james-database.properties. The corresponding definitions and default values can be found in the <a href="https://commons.apache.org/proper/commons-dbcp/configuration.html">dbcp documentation</a></p>
+
+ <p>NOTE: all<code>openjpa.*</code> properties will be passed to OpenJPA library as configuration See <a href="https://openjpa.apache.org/builds/3.2.2/apache-openjpa/docs/ref_guide_conf_openjpa.html">https://openjpa.apache.org/builds/3.2.2/apache-openjpa/docs/ref_guide_conf_openjpa.html</a> for a complete list </p>
+
+ <p>The JPA datasource and connection pooling can be delegated to commons-dbcp2 - the library dependency has to be present in classpath for it to be enabled. dbcp2 properties can be configured through james-database.properties - all properties prefixed with <code>datasource.*</code> will be passed to DBCP2 library via <code>openjpa.ConnectionProperties</code> OpenJPA property. The corresponding definitions and default values can be found in the <a href="https://commons.apa [...]
<ul>
<li>datasource.testOnBorrow</li>
<li>datasource.validationQueryTimeoutSec</li>
<li>datasource.validationQuery</li>
+ <li>datasource.initialSize</li>
+ <li>datasource.minIdle</li>
<li>datasource.maxTotal</li>
-
</ul>
+ <p>NOTE: Connection poolling requires that <code>openjpa.Multithreaded</code> property is set to <code>true</code> (default)</p>
<p>Note for postgresql databases: Add <code>standard_conforming_strings=off</code> to your postgresql.xml, otherwise you
will get <code>""Invalid escape string Hint: Escape string must be empty or one character. {prepstmnt 174928937
SELECT t0.mailbox_id, t0.mailbox_highest_modseq, t0.mailbox_last_uid, t0.mailbox_name, t0.mailbox_namespace,
---------------------------------------------------------------------
To unsubscribe, e-mail: notifications-unsubscribe@james.apache.org
For additional commands, e-mail: notifications-help@james.apache.org