From f0cacf559e81f461eacb0a9fad7547ac22609b6a Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Wed, 17 Apr 2019 10:18:27 +0200 Subject: [PATCH 1/3] Fix ReportTest --- .../midpoint/report/AbstractReportIntegrationTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/model/report-impl/src/test/java/com/evolveum/midpoint/report/AbstractReportIntegrationTest.java b/model/report-impl/src/test/java/com/evolveum/midpoint/report/AbstractReportIntegrationTest.java index 9fcbe8d1556..31236257935 100644 --- a/model/report-impl/src/test/java/com/evolveum/midpoint/report/AbstractReportIntegrationTest.java +++ b/model/report-impl/src/test/java/com/evolveum/midpoint/report/AbstractReportIntegrationTest.java @@ -18,6 +18,7 @@ import java.io.File; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.annotation.DirtiesContext; import org.springframework.test.annotation.DirtiesContext.ClassMode; import org.springframework.test.context.ContextConfiguration; @@ -75,6 +76,8 @@ public class AbstractReportIntegrationTest extends AbstractModelIntegrationTest protected final static File USER_ADMINISTRATOR_FILE = new File(TEST_DIR_COMMON, "user-administrator.xml"); @Autowired protected ReportManager reportManager; + + @Qualifier("reportJasperCreateTaskHandler") @Autowired protected ReportJasperCreateTaskHandler reportTaskHandler; protected PrismObject userAdministrator; From 8c073e68aba3f2a6bf48931cb35d62a8d8aeaadd Mon Sep 17 00:00:00 2001 From: Radovan Semancik Date: Wed, 17 Apr 2019 11:58:32 +0200 Subject: [PATCH 2/3] Fixes for XXE vulnerabilities (MID-5285) --- .../common/validator/LegacyValidator.java | 5 ++ .../impl/lex/dom/DomLexicalProcessor.java | 10 +++- .../impl/schema/SchemaToDomProcessor.java | 15 ++++-- .../midpoint/prism/TestPrismParsingXml.java | 50 ++++++++++++++++++- .../resources/common/xml/user-jack-xxe.xml | 35 +++++++++++++ .../com/evolveum/midpoint/util/DOMUtil.java | 9 +++- .../model/client/ModelClientUtil.java | 14 ++++-- 7 files changed, 128 insertions(+), 10 deletions(-) create mode 100644 infra/prism-impl/src/test/resources/common/xml/user-jack-xxe.xml diff --git a/infra/common/src/main/java/com/evolveum/midpoint/common/validator/LegacyValidator.java b/infra/common/src/main/java/com/evolveum/midpoint/common/validator/LegacyValidator.java index adc907a085a..26e3c3eca52 100644 --- a/infra/common/src/main/java/com/evolveum/midpoint/common/validator/LegacyValidator.java +++ b/infra/common/src/main/java/com/evolveum/midpoint/common/validator/LegacyValidator.java @@ -190,6 +190,11 @@ public void validate(InputStream inputStream, OperationResult validatorResult, S stream = xmlInputFactory.createXMLStreamReader(inputStream); int eventType = stream.nextTag(); + if (eventType == XMLStreamConstants.DTD || eventType == XMLStreamConstants.ENTITY_DECLARATION + || eventType == XMLStreamConstants.ENTITY_REFERENCE || eventType == XMLStreamConstants.NOTATION_DECLARATION) { + // We do not want those, e.g. we want to void XXE vulnerabilities. Make this check explicit. + throw new SystemException("Use of "+eventType+" in XML is prohibited"); + } if (eventType == XMLStreamConstants.START_ELEMENT) { if (!QNameUtil.match(stream.getName(), SchemaConstants.C_OBJECTS)) { // This has to be an import file with a single objects. Try diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java index 26196cda825..02c63eef955 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/lex/dom/DomLexicalProcessor.java @@ -105,6 +105,14 @@ public List readObjects(@NotNull ParserSource source, @NotNull Pa } } } + + private XMLInputFactory getXMLInputFactory() { + XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance(); + xmlInputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, false); + xmlInputFactory.setProperty("javax.xml.stream.isSupportingExternalEntities", false); + // TODO: cache? static? prism context? + return xmlInputFactory; + } // code taken from Validator class @Override @@ -113,7 +121,7 @@ public void readObjectsIteratively(@NotNull ParserSource source, @NotNull Parsin InputStream is = source.getInputStream(); XMLStreamReader stream = null; try { - stream = XMLInputFactory.newInstance().createXMLStreamReader(is); + stream = getXMLInputFactory().createXMLStreamReader(is); int eventType = stream.nextTag(); if (eventType != XMLStreamConstants.START_ELEMENT) { diff --git a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/schema/SchemaToDomProcessor.java b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/schema/SchemaToDomProcessor.java index 5dce7f7ecb9..a2d45507bb0 100644 --- a/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/schema/SchemaToDomProcessor.java +++ b/infra/prism-impl/src/main/java/com/evolveum/midpoint/prism/impl/schema/SchemaToDomProcessor.java @@ -597,10 +597,17 @@ private void init() throws ParserConfigurationException { LOGGER.trace("Using namespace prefix mapper to serialize schema:\n{}",DebugUtil.dump(namespacePrefixMapper)); } - DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); - dbf.setNamespaceAware(true); - dbf.setValidating(false); - DocumentBuilder db = dbf.newDocumentBuilder(); + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + documentBuilderFactory.setValidating(false); + // XXE + documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false); + documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + documentBuilderFactory.setXIncludeAware(false); + documentBuilderFactory.setExpandEntityReferences(false); + DocumentBuilder db = documentBuilderFactory.newDocumentBuilder(); document = db.newDocument(); Element root = createElement(new QName(W3C_XML_SCHEMA_NS_URI, "schema")); diff --git a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/TestPrismParsingXml.java b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/TestPrismParsingXml.java index 070cd2ff947..e03a06db695 100644 --- a/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/TestPrismParsingXml.java +++ b/infra/prism-impl/src/test/java/com/evolveum/midpoint/prism/TestPrismParsingXml.java @@ -15,11 +15,19 @@ */ package com.evolveum.midpoint.prism; -import static com.evolveum.midpoint.prism.PrismInternalTestUtil.*; +import static org.testng.AssertJUnit.assertTrue; +import static com.evolveum.midpoint.prism.PrismInternalTestUtil.USER_JACK_ADHOC_BASENAME; +import static com.evolveum.midpoint.prism.PrismInternalTestUtil.USER_JACK_FILE_BASENAME; +import static com.evolveum.midpoint.prism.PrismInternalTestUtil.USER_JACK_OID; +import static com.evolveum.midpoint.prism.PrismInternalTestUtil.assertPropertyValue; +import static com.evolveum.midpoint.prism.PrismInternalTestUtil.assertUserJack; +import static com.evolveum.midpoint.prism.PrismInternalTestUtil.constructInitializedPrismContext; +import static org.testng.AssertJUnit.assertEquals; import static org.testng.AssertJUnit.assertNotNull; import java.io.IOException; +import org.testng.AssertJUnit; import org.testng.annotations.Test; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -30,6 +38,8 @@ public class TestPrismParsingXml extends TestPrismParsing { + public static final String USER_JACK_XXE_BASENAME = "user-jack-xxe"; + @Override protected String getSubdirName() { return "xml"; @@ -88,6 +98,44 @@ public void testPrismParseDomAdhoc() throws Exception { assertUserAdhoc(user, true); } + + @Test + public void testPrismParseXxe() throws Exception { + final String TEST_NAME = "testPrismParseXxe"; + PrismInternalTestUtil.displayTestTitle(TEST_NAME); + + PrismContext prismContext = constructInitializedPrismContext(); + + try { + // WHEN + prismContext.parseObject(getFile(USER_JACK_XXE_BASENAME)); + + AssertJUnit.fail("Unexpected success"); + } catch (IllegalStateException e) { + // THEN + System.out.println("Expected exception: "+e); + assertTrue("Unexpected exception message: "+e.getMessage(), e.getMessage().contains("DOCTYPE")); + } + + } + + @Test + public void testPrismParseDomXxe() throws Exception { + final String TEST_NAME = "testPrismParseDomXxe"; + PrismInternalTestUtil.displayTestTitle(TEST_NAME); + + try { + // WHEN + DOMUtil.parseFile(getFile(USER_JACK_XXE_BASENAME)); + + AssertJUnit.fail("Unexpected success"); + } catch (IllegalStateException e) { + // THEN + System.out.println("Expected exception: "+e); + assertTrue("Unexpected exception message: "+e.getMessage(), e.getMessage().contains("DOCTYPE")); + } + + } @Override protected void validateXml(String xmlString, PrismContext prismContext) throws SAXException, IOException { diff --git a/infra/prism-impl/src/test/resources/common/xml/user-jack-xxe.xml b/infra/prism-impl/src/test/resources/common/xml/user-jack-xxe.xml new file mode 100644 index 00000000000..dcb2991f2ab --- /dev/null +++ b/infra/prism-impl/src/test/resources/common/xml/user-jack-xxe.xml @@ -0,0 +1,35 @@ + + ]> + + + jack + &file; + cpt. Jack Sparrow + Jack + Sparrow + + diff --git a/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java b/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java index 0a912e1e99a..5a3a0c507db 100644 --- a/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java +++ b/infra/util/src/main/java/com/evolveum/midpoint/util/DOMUtil.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2010-2013 Evolveum + * Copyright (c) 2010-2019 Evolveum * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -171,6 +171,13 @@ public class DOMUtil { documentBuilderFactory.setFeature("http://xml.org/sax/features/validation", false); documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false); documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + // XXE + documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false); + documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + documentBuilderFactory.setXIncludeAware(false); + documentBuilderFactory.setExpandEntityReferences(false); return documentBuilderFactory.newDocumentBuilder(); } catch (ParserConfigurationException e) { throw new RuntimeException(e); diff --git a/model/model-client/src/main/java/com/evolveum/midpoint/model/client/ModelClientUtil.java b/model/model-client/src/main/java/com/evolveum/midpoint/model/client/ModelClientUtil.java index 02d10ece86a..437bbe292c9 100644 --- a/model/model-client/src/main/java/com/evolveum/midpoint/model/client/ModelClientUtil.java +++ b/model/model-client/src/main/java/com/evolveum/midpoint/model/client/ModelClientUtil.java @@ -510,9 +510,17 @@ public static String toString(PolyStringType poly) { static { try { - DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); - factory.setNamespaceAware(true); - domDocumentBuilder = factory.newDocumentBuilder(); + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + documentBuilderFactory.setNamespaceAware(true); + // XXE + documentBuilderFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + documentBuilderFactory.setFeature("http://xml.org/sax/features/external-general-entities", false); + documentBuilderFactory.setFeature("http://xml.org/sax/features/external-parameter-entities", false); + documentBuilderFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); + documentBuilderFactory.setXIncludeAware(false); + documentBuilderFactory.setExpandEntityReferences(false); + domDocumentBuilder = documentBuilderFactory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { throw new IllegalStateException("Error creating XML document " + e.getMessage()); } From c7aa9f72999d2dce1d00e98adf73d6df77aa9424 Mon Sep 17 00:00:00 2001 From: Pavol Mederly Date: Wed, 17 Apr 2019 13:06:26 +0200 Subject: [PATCH 3/3] Support use of repo DB pool for Quartz (MID-5213) An experimental useRepositoryConnectionProvider parameter is available. It tells Quartz to reuse repository connection provider (either HikariCP or an externally configured one). --- .../midpoint/repo/sql/DataSourceFactory.java | 22 +++--- .../quartzimpl/TaskManagerConfiguration.java | 79 +++++++++++-------- .../execution/LocalNodeManager.java | 14 +++- .../RepositoryConnectionProvider.java | 67 ++++++++++++++++ 4 files changed, 139 insertions(+), 43 deletions(-) create mode 100644 repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/execution/RepositoryConnectionProvider.java diff --git a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/DataSourceFactory.java b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/DataSourceFactory.java index e46f62927c5..4963e4a1f0d 100644 --- a/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/DataSourceFactory.java +++ b/repo/repo-sql-impl/src/main/java/com/evolveum/midpoint/repo/sql/DataSourceFactory.java @@ -39,6 +39,7 @@ public class DataSourceFactory { private SqlRepositoryConfiguration configuration; + private DataSource internalDataSource; private DataSource dataSource; public SqlRepositoryConfiguration getConfiguration() { @@ -57,24 +58,27 @@ public DataSource createDataSource() throws RepositoryServiceFactoryException { try { if (StringUtils.isNotEmpty(configuration.getDataSource())) { - LOGGER.info("JDNI datasource present in configuration, looking for '{}'.", - new Object[]{configuration.getDataSource()}); - return createJNDIDataSource(); + LOGGER.info("JNDI datasource present in configuration, looking for '{}'.", configuration.getDataSource()); + dataSource = createJNDIDataSource(); + } else { + LOGGER.info("Constructing default datasource with connection pooling; JDBC URL: {}", configuration.getJdbcUrl()); + internalDataSource = createDataSourceInternal(); + dataSource = internalDataSource; } - - LOGGER.info("Constructing default datasource with connection pooling; JDBC URL: {}", configuration.getJdbcUrl()); - dataSource = createDataSourceInternal(); return dataSource; } catch (Exception ex) { throw new RepositoryServiceFactoryException("Couldn't initialize datasource, reason: " + ex.getMessage(), ex); } } + public DataSource getDataSource() { + return dataSource; + } + private DataSource createJNDIDataSource() throws IllegalArgumentException, NamingException { JndiObjectFactoryBean factory = new JndiObjectFactoryBean(); factory.setJndiName(configuration.getDataSource()); factory.afterPropertiesSet(); - return (DataSource) factory.getObject(); } @@ -135,8 +139,8 @@ private DataSource createDataSourceInternal() { @PreDestroy public void destroy() throws IOException { - if (dataSource instanceof Closeable) { - ((Closeable) dataSource).close(); + if (internalDataSource instanceof Closeable) { + ((Closeable) internalDataSource).close(); } } } diff --git a/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/TaskManagerConfiguration.java b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/TaskManagerConfiguration.java index c8c4eff4cce..03ea67513b8 100644 --- a/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/TaskManagerConfiguration.java +++ b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/TaskManagerConfiguration.java @@ -66,6 +66,8 @@ public class TaskManagerConfiguration { private static final String JDBC_USER_CONFIG_ENTRY = "jdbcUser"; private static final String JDBC_PASSWORD_CONFIG_ENTRY = "jdbcPassword"; private static final String DATA_SOURCE_CONFIG_ENTRY = "dataSource"; + private static final String USE_REPOSITORY_CONNECTION_PROVIDER_CONFIG_ENTRY = "useRepositoryConnectionProvider"; // experimental + private static final String SQL_SCHEMA_FILE_CONFIG_ENTRY = "sqlSchemaFile"; private static final String CREATE_QUARTZ_TABLES_CONFIG_ENTRY = "createQuartzTables"; private static final String JDBC_DRIVER_DELEGATE_CLASS_CONFIG_ENTRY = "jdbcDriverDelegateClass"; @@ -169,6 +171,7 @@ public class TaskManagerConfiguration { private String jdbcUser; private String jdbcPassword; private String dataSource; + private boolean useRepositoryConnectionProvider; private boolean createQuartzTables; private Database database; @@ -202,6 +205,7 @@ public class TaskManagerConfiguration { JDBC_USER_CONFIG_ENTRY, JDBC_PASSWORD_CONFIG_ENTRY, DATA_SOURCE_CONFIG_ENTRY, + USE_REPOSITORY_CONNECTION_PROVIDER_CONFIG_ENTRY, SQL_SCHEMA_FILE_CONFIG_ENTRY, CREATE_QUARTZ_TABLES_CONFIG_ENTRY, JDBC_DRIVER_DELEGATE_CLASS_CONFIG_ENTRY, @@ -383,36 +387,6 @@ void setJdbcJobStoreInformation(MidpointConfiguration masterConfig, SqlRepositor Configuration c = masterConfig.getConfiguration(MidpointConfiguration.TASK_MANAGER_CONFIGURATION); - jdbcDriver = c.getString(JDBC_DRIVER_CONFIG_ENTRY, sqlConfig != null ? sqlConfig.getDriverClassName() : null); - - String explicitJdbcUrl = c.getString(JDBC_URL_CONFIG_ENTRY, null); - if (explicitJdbcUrl == null) { - if (sqlConfig != null) { - if (sqlConfig.isEmbedded()) { - jdbcUrl = defaultJdbcUrlPrefix + "-quartz;MVCC=TRUE;DB_CLOSE_ON_EXIT=FALSE"; - } else { - jdbcUrl = sqlConfig.getJdbcUrl(); - } - } else { - jdbcUrl = null; - } - } else { - jdbcUrl = explicitJdbcUrl; - } - dataSource = c.getString(DATA_SOURCE_CONFIG_ENTRY, null); - if (dataSource == null && explicitJdbcUrl == null && sqlConfig != null) { - dataSource = sqlConfig.getDataSource(); // we want to use quartz-specific JDBC if there is one (i.e. we do not want to inherit data source from repo in such a case) - } - - if (dataSource != null) { - LOGGER.info("Quartz database is at {} (a data source)", dataSource); - } else { - LOGGER.info("Quartz database is at {} (a JDBC URL)", jdbcUrl); - } - - jdbcUser = c.getString(JDBC_USER_CONFIG_ENTRY, sqlConfig != null ? sqlConfig.getJdbcUsername() : null); - jdbcPassword = c.getString(JDBC_PASSWORD_CONFIG_ENTRY, sqlConfig != null ? sqlConfig.getJdbcPassword() : null); - database = sqlConfig != null ? sqlConfig.getDatabase() : null; String defaultSqlSchemaFile = schemas.get(database); @@ -423,6 +397,44 @@ void setJdbcJobStoreInformation(MidpointConfiguration masterConfig, SqlRepositor createQuartzTables = c.getBoolean(CREATE_QUARTZ_TABLES_CONFIG_ENTRY, CREATE_QUARTZ_TABLES_DEFAULT); databaseIsEmbedded = sqlConfig != null && sqlConfig.isEmbedded(); + + useRepositoryConnectionProvider = c.getBoolean(USE_REPOSITORY_CONNECTION_PROVIDER_CONFIG_ENTRY, false); + if (useRepositoryConnectionProvider) { + LOGGER.info("Using connection provider from repository (ignoring all the other database-related configuration)"); + if (sqlConfig != null && sqlConfig.isUsingH2()) { + LOGGER.warn("This option is not supported for H2! Please change the task manager configuration."); + } + } else { + jdbcDriver = c.getString(JDBC_DRIVER_CONFIG_ENTRY, sqlConfig != null ? sqlConfig.getDriverClassName() : null); + + String explicitJdbcUrl = c.getString(JDBC_URL_CONFIG_ENTRY, null); + if (explicitJdbcUrl == null) { + if (sqlConfig != null) { + if (sqlConfig.isEmbedded()) { + jdbcUrl = defaultJdbcUrlPrefix + "-quartz;MVCC=TRUE;DB_CLOSE_ON_EXIT=FALSE"; + } else { + jdbcUrl = sqlConfig.getJdbcUrl(); + } + } else { + jdbcUrl = null; + } + } else { + jdbcUrl = explicitJdbcUrl; + } + dataSource = c.getString(DATA_SOURCE_CONFIG_ENTRY, null); + if (dataSource == null && explicitJdbcUrl == null && sqlConfig != null) { + dataSource = sqlConfig.getDataSource(); // we want to use quartz-specific JDBC if there is one (i.e. we do not want to inherit data source from repo in such a case) + } + + if (dataSource != null) { + LOGGER.info("Quartz database is at {} (a data source)", dataSource); + } else { + LOGGER.info("Quartz database is at {} (a JDBC URL)", jdbcUrl); + } + + jdbcUser = c.getString(JDBC_USER_CONFIG_ENTRY, sqlConfig != null ? sqlConfig.getJdbcUsername() : null); + jdbcPassword = c.getString(JDBC_PASSWORD_CONFIG_ENTRY, sqlConfig != null ? sqlConfig.getJdbcPassword() : null); + } } /** @@ -448,8 +460,7 @@ void validateBasicInformation() throws TaskManagerConfigurationException { } void validateJdbcJobStoreInformation() throws TaskManagerConfigurationException { - - if (StringUtils.isEmpty(dataSource)) { + if (!useRepositoryConnectionProvider && StringUtils.isEmpty(dataSource)) { notEmpty(jdbcDriver, "JDBC driver must be specified (either explicitly or via data source; in task manager or in SQL repository configuration)"); notEmpty(jdbcUrl, "JDBC URL must be specified (either explicitly or via data source; in task manager or in SQL repository configuration)"); notNull(jdbcUser, "JDBC user name must be specified (either explicitly or via data source; in task manager or in SQL repository configuration)"); @@ -625,6 +636,10 @@ public void setCreateQuartzTables(boolean createQuartzTables) { this.createQuartzTables = createQuartzTables; } + public boolean isUseRepositoryConnectionProvider() { + return useRepositoryConnectionProvider; + } + public String getDataSource() { return dataSource; } diff --git a/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/execution/LocalNodeManager.java b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/execution/LocalNodeManager.java index c9e4eade38c..2e93988af06 100644 --- a/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/execution/LocalNodeManager.java +++ b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/execution/LocalNodeManager.java @@ -16,6 +16,7 @@ package com.evolveum.midpoint.task.quartzimpl.execution; +import com.evolveum.midpoint.repo.sql.DataSourceFactory; import com.evolveum.midpoint.schema.result.OperationResult; import com.evolveum.midpoint.task.api.*; import com.evolveum.midpoint.task.quartzimpl.TaskManagerConfiguration; @@ -87,7 +88,13 @@ void initializeScheduler() throws TaskManagerInitializationException { String MY_DS = "myDS"; quartzProperties.put("org.quartz.jobStore.dataSource", MY_DS); - if (configuration.getDataSource() != null) { + if (configuration.isUseRepositoryConnectionProvider()) { + DataSourceFactory dataSourceFactory = (DataSourceFactory) taskManager.getBeanFactory().getBean("dataSourceFactory"); + int index = (int) (Math.random() * Integer.MAX_VALUE); + RepositoryConnectionProvider.dataSources.put(index, dataSourceFactory.getDataSource()); + quartzProperties.put("org.quartz.dataSource."+MY_DS+".connectionProvider.class", RepositoryConnectionProvider.class.getName()); + quartzProperties.put("org.quartz.dataSource."+MY_DS+".dataSourceIndex", String.valueOf(index)); + } else if (configuration.getDataSource() != null) { quartzProperties.put("org.quartz.dataSource."+MY_DS+".jndiURL", configuration.getDataSource()); } else { quartzProperties.put("org.quartz.dataSource."+MY_DS+".provider", "hikaricp"); @@ -216,7 +223,10 @@ private void createQuartzDbSchema(TaskManagerConfiguration configuration) throws private Connection getConnection(TaskManagerConfiguration configuration) throws TaskManagerInitializationException { Connection connection; try { - if (configuration.getDataSource() != null) { + if (configuration.isUseRepositoryConnectionProvider()) { + DataSourceFactory dataSourceFactory = (DataSourceFactory) taskManager.getBeanFactory().getBean("dataSourceFactory"); + connection = dataSourceFactory.getDataSource().getConnection(); + } else if (configuration.getDataSource() != null) { DataSource dataSource; try { InitialContext context = new InitialContext(); diff --git a/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/execution/RepositoryConnectionProvider.java b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/execution/RepositoryConnectionProvider.java new file mode 100644 index 00000000000..19e7008f08e --- /dev/null +++ b/repo/task-quartz-impl/src/main/java/com/evolveum/midpoint/task/quartzimpl/execution/RepositoryConnectionProvider.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2010-2019 Evolveum + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.evolveum.midpoint.task.quartzimpl.execution; + +import org.quartz.utils.ConnectionProvider; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Quartz connection provider that uses built-in midPoint data source factory. + */ +public class RepositoryConnectionProvider implements ConnectionProvider { + + /** + * Maybe too cautious; we could probably go forward with a single-valued static dataSource property here. + */ + static final Map dataSources = new ConcurrentHashMap<>(); + + private int dataSourceIndex; + + @SuppressWarnings("unused") // probably called by Quartz + public int getDataSourceIndex() { + return dataSourceIndex; + } + + @SuppressWarnings("unused") // called by Quartz + public void setDataSourceIndex(int dataSourceIndex) { + this.dataSourceIndex = dataSourceIndex; + } + + @Override + public Connection getConnection() throws SQLException { + DataSource dataSource = dataSources.get(dataSourceIndex); + if (dataSource == null) { + throw new IllegalStateException("no data source with index " + dataSourceIndex); + } + return dataSource.getConnection(); + } + + @Override + public void shutdown() { + dataSources.remove(dataSourceIndex); + // connection pool will be closed on repository shutdown + } + + @Override + public void initialize() { + } +}