/* * Copyright (c) 2010-2013 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.wf.impl; import com.evolveum.midpoint.common.configuration.api.MidpointConfiguration; import com.evolveum.midpoint.init.RepositoryFactory; import com.evolveum.midpoint.repo.api.RepositoryServiceFactoryException; import com.evolveum.midpoint.repo.sql.SqlRepositoryConfiguration; import com.evolveum.midpoint.repo.sql.SqlRepositoryFactory; import com.evolveum.midpoint.util.exception.SystemException; import com.evolveum.midpoint.util.logging.LoggingUtils; import com.evolveum.midpoint.util.logging.Trace; import com.evolveum.midpoint.util.logging.TraceManager; import com.evolveum.midpoint.wf.impl.processors.ChangeProcessor; import org.apache.commons.configuration.Configuration; import org.apache.commons.lang.StringUtils; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.DependsOn; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import java.util.*; /** * * @author Pavol Mederly */ @Component @DependsOn({ "midpointConfiguration" }) public class WfConfiguration implements BeanFactoryAware { private static final transient Trace LOGGER = TraceManager.getTrace(WfConfiguration.class); private static final String WF_CONFIG_SECTION = "midpoint.workflow"; private static final String CHANGE_PROCESSORS_SECTION = "changeProcessors"; // deprecated public static final String KEY_ENABLED = "enabled"; public static final String KEY_JDBC_DRIVER = "jdbcDriver"; public static final String KEY_JDBC_URL = "jdbcUrl"; public static final String KEY_JDBC_USERNAME = "jdbcUsername"; public static final String KEY_JDBC_PASSWORD = "jdbcPassword"; public static final String KEY_DATA_SOURCE = "dataSource"; public static final String KEY_ACTIVITI_SCHEMA_UPDATE = "activitiSchemaUpdate"; public static final String KEY_PROCESS_CHECK_INTERVAL = "processCheckInterval"; public static final String KEY_AUTO_DEPLOYMENT_FROM = "autoDeploymentFrom"; public static final String KEY_ALLOW_APPROVE_OTHERS_ITEMS = "allowApproveOthersItems"; public static final List<String> KNOWN_KEYS = Arrays.asList("midpoint.home", KEY_ENABLED, KEY_JDBC_DRIVER, KEY_JDBC_URL, KEY_JDBC_USERNAME, KEY_JDBC_PASSWORD, KEY_DATA_SOURCE, KEY_ACTIVITI_SCHEMA_UPDATE, KEY_AUTO_DEPLOYMENT_FROM); public static final List<String> DEPRECATED_KEYS = Arrays.asList(CHANGE_PROCESSORS_SECTION, KEY_PROCESS_CHECK_INTERVAL, KEY_ALLOW_APPROVE_OTHERS_ITEMS); @Autowired private MidpointConfiguration midpointConfiguration; private BeanFactory beanFactory; @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } private static final String AUTO_DEPLOYMENT_FROM_DEFAULT = "classpath*:processes/*.bpmn20.xml"; private boolean enabled; private boolean activitiSchemaUpdate; private String jdbcDriver; private String jdbcUrl; private String jdbcUser; private String jdbcPassword; private String dataSource; private List<ChangeProcessor> changeProcessors = new ArrayList<>(); private String[] autoDeploymentFrom; boolean dropDatabase; @PostConstruct void initialize() { Configuration c = midpointConfiguration.getConfiguration(WF_CONFIG_SECTION); checkAllowedKeys(c, KNOWN_KEYS, DEPRECATED_KEYS); enabled = c.getBoolean(KEY_ENABLED, true); if (!enabled) { LOGGER.info("Workflows are disabled."); return; } // activiti properties related to database connection will be taken from SQL repository SqlRepositoryConfiguration sqlConfig = null; String defaultJdbcUrlPrefix = null; dropDatabase = false; try { RepositoryFactory repositoryFactory = (RepositoryFactory) beanFactory.getBean("repositoryFactory"); if (!(repositoryFactory.getFactory() instanceof SqlRepositoryFactory)) { // it may be null as well LOGGER.debug("SQL configuration cannot be found; Activiti database configuration (if any) will be taken from 'workflow' configuration section only"); if (LOGGER.isTraceEnabled()) { LOGGER.trace("repositoryFactory.getFactory() = " + repositoryFactory); } } else { SqlRepositoryFactory sqlRepositoryFactory = (SqlRepositoryFactory) repositoryFactory.getFactory(); sqlConfig = sqlRepositoryFactory.getSqlConfiguration(); if (sqlConfig.isEmbedded()) { defaultJdbcUrlPrefix = sqlRepositoryFactory.prepareJdbcUrlPrefix(sqlConfig); dropDatabase = sqlConfig.isDropIfExists(); } } } catch(NoSuchBeanDefinitionException e) { LOGGER.debug("SqlRepositoryFactory is not available, Activiti database configuration (if any) will be taken from 'workflow' configuration section only."); LOGGER.trace("Reason is", e); } catch (RepositoryServiceFactoryException e) { LoggingUtils.logUnexpectedException(LOGGER, "Cannot determine default JDBC URL for embedded database", e); } String explicitJdbcUrl = c.getString(KEY_JDBC_URL, null); if (explicitJdbcUrl == null) { if (sqlConfig == null || sqlConfig.isEmbedded()) { jdbcUrl = defaultJdbcUrlPrefix + "-activiti;DB_CLOSE_ON_EXIT=FALSE;MVCC=FALSE"; } else { jdbcUrl = sqlConfig.getJdbcUrl(); } } else { jdbcUrl = explicitJdbcUrl; } dataSource = c.getString(KEY_DATA_SOURCE, null); if (dataSource == null && explicitJdbcUrl == null && sqlConfig != null) { dataSource = sqlConfig.getDataSource(); // we want to use wf-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("Activiti database is at " + dataSource + " (a data source)"); } else { LOGGER.info("Activiti database is at " + jdbcUrl + " (a JDBC URL)"); } boolean defaultSchemaUpdate = sqlConfig == null || "update".equals(sqlConfig.getHibernateHbm2ddl()); activitiSchemaUpdate = c.getBoolean(KEY_ACTIVITI_SCHEMA_UPDATE, defaultSchemaUpdate); LOGGER.info("Activiti automatic schema update: {}", activitiSchemaUpdate); jdbcDriver = c.getString(KEY_JDBC_DRIVER, sqlConfig != null ? sqlConfig.getDriverClassName() : null); jdbcUser = c.getString(KEY_JDBC_USERNAME, sqlConfig != null ? sqlConfig.getJdbcUsername() : null); jdbcPassword = c.getString(KEY_JDBC_PASSWORD, sqlConfig != null ? sqlConfig.getJdbcPassword() : null); autoDeploymentFrom = c.getStringArray(KEY_AUTO_DEPLOYMENT_FROM); if (autoDeploymentFrom.length == 0) { autoDeploymentFrom = new String[] { AUTO_DEPLOYMENT_FROM_DEFAULT }; } // hibernateDialect = sqlConfig != null ? sqlConfig.getHibernateDialect() : ""; validate(); } public void checkAllowedKeys(Configuration c, List<String> knownKeys, List<String> deprecatedKeys) { Set<String> knownKeysSet = new HashSet<>(knownKeys); Set<String> deprecatedKeysSet = new HashSet<>(deprecatedKeys); Iterator<String> keyIterator = c.getKeys(); while (keyIterator.hasNext()) { String keyName = keyIterator.next(); String normalizedKeyName = StringUtils.substringBefore(keyName, "."); // because of subkeys normalizedKeyName = StringUtils.substringBefore(normalizedKeyName, "["); // because of [@xmlns:c] int colon = normalizedKeyName.indexOf(':'); // because of c:generalChangeProcessorConfiguration if (colon != -1) { normalizedKeyName = normalizedKeyName.substring(colon + 1); } if (deprecatedKeysSet.contains(keyName) || deprecatedKeysSet.contains(normalizedKeyName)) { throw new SystemException("Deprecated key " + keyName + " in workflow configuration. Please see https://wiki.evolveum.com/display/midPoint/Workflow+configuration."); } if (!knownKeysSet.contains(keyName) && !knownKeysSet.contains(normalizedKeyName)) { // ...we need to test both because of keys like 'midpoint.home' throw new SystemException("Unknown key " + keyName + " in workflow configuration"); } } } // public Configuration getChangeProcessorsConfig() { // Validate.notNull(midpointConfiguration, "midpointConfiguration was not initialized correctly (check spring beans initialization order)"); // return midpointConfiguration.getConfiguration(WF_CONFIG_SECTION).subset(CHANGE_PROCESSORS_SECTION); // via subset, because getConfiguration puts 'midpoint.home' property to the result // } void validate() { if (dataSource != null) { notEmpty(dataSource, "Data source (if specified) must not be an empty string"); } else { notEmpty(jdbcDriver, "JDBC driver or a data source must be specified (either explicitly or in SQL repository configuration)"); notEmpty(jdbcUrl, "JDBC URL or a data source must be specified (either explicitly or in SQL repository configuration)."); notNull(jdbcUser, "JDBC user name or a data source must be specified (either explicitly or in SQL repository configuration)."); notNull(jdbcPassword, "JDBC password or a data source must be specified (either explicitly or in SQL repository configuration)."); } } private void notEmpty(String value, String message) { if (StringUtils.isEmpty(value)) { throw new SystemException(message); } } private void notNull(String value, String message) { if (value == null) { throw new SystemException(message); } } public boolean isActivitiSchemaUpdate() { return activitiSchemaUpdate; } public boolean isEnabled() { return enabled; } public String getJdbcDriver() { return jdbcDriver; } public String getJdbcPassword() { return jdbcPassword; } public String getJdbcUrl() { return jdbcUrl; } public String getJdbcUser() { return jdbcUser; } public String getDataSource() { return dataSource; } public String[] getAutoDeploymentFrom() { return autoDeploymentFrom; } public boolean isDropDatabase() { return dropDatabase; } public ChangeProcessor findChangeProcessor(String processorClassName) { for (ChangeProcessor cp : changeProcessors) { if (cp.getClass().getName().equals(processorClassName)) { return cp; } } throw new IllegalStateException("Change processor " + processorClassName + " is not registered."); } public void registerProcessor(ChangeProcessor changeProcessor) { changeProcessors.add(changeProcessor); } public List<ChangeProcessor> getChangeProcessors() { return changeProcessors; } }