View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.syncope.core.persistence.jpa;
20  
21  import com.zaxxer.hikari.HikariConfig;
22  import com.zaxxer.hikari.HikariDataSource;
23  import java.io.ByteArrayInputStream;
24  import java.nio.charset.StandardCharsets;
25  import java.util.Map;
26  import java.util.Objects;
27  import javax.persistence.EntityManagerFactory;
28  import javax.sql.DataSource;
29  import org.apache.syncope.common.keymaster.client.api.model.Domain;
30  import org.apache.syncope.core.persistence.api.DomainRegistry;
31  import org.apache.syncope.core.persistence.jpa.openjpa.ConnectorManagerRemoteCommitListener;
32  import org.apache.syncope.core.persistence.jpa.spring.DomainEntityManagerFactoryBean;
33  import org.slf4j.Logger;
34  import org.slf4j.LoggerFactory;
35  import org.springframework.beans.factory.annotation.Qualifier;
36  import org.springframework.beans.factory.config.BeanDefinition;
37  import org.springframework.beans.factory.support.AbstractBeanDefinition;
38  import org.springframework.beans.factory.support.AutowireCandidateQualifier;
39  import org.springframework.beans.factory.support.BeanDefinitionBuilder;
40  import org.springframework.beans.factory.support.DefaultListableBeanFactory;
41  import org.springframework.context.ConfigurableApplicationContext;
42  import org.springframework.core.io.ClassPathResource;
43  import org.springframework.jdbc.datasource.init.DataSourceInitializer;
44  import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
45  import org.springframework.jndi.JndiObjectFactoryBean;
46  import org.springframework.orm.jpa.JpaTransactionManager;
47  import org.springframework.orm.jpa.vendor.OpenJpaVendorAdapter;
48  import org.springframework.transaction.support.TransactionTemplate;
49  
50  public class DomainConfFactory implements DomainRegistry {
51  
52      protected static final Logger LOG = LoggerFactory.getLogger(DomainConfFactory.class);
53  
54      protected final ConfigurableApplicationContext ctx;
55  
56      public DomainConfFactory(final ConfigurableApplicationContext ctx) {
57          this.ctx = ctx;
58      }
59  
60      protected DefaultListableBeanFactory beanFactory() {
61          return (DefaultListableBeanFactory) ctx.getBeanFactory();
62      }
63  
64      protected void unregisterSingleton(final String name) {
65          if (beanFactory().containsSingleton(name)) {
66              beanFactory().destroySingleton(name);
67          }
68      }
69  
70      protected void registerSingleton(final String name, final Object bean) {
71          unregisterSingleton(name);
72          beanFactory().registerSingleton(name, bean);
73      }
74  
75      protected void unregisterBeanDefinition(final String name) {
76          if (beanFactory().containsBeanDefinition(name)) {
77              beanFactory().removeBeanDefinition(name);
78          }
79      }
80  
81      protected void registerBeanDefinition(final String name, final BeanDefinition beanDefinition) {
82          unregisterBeanDefinition(name);
83          beanFactory().registerBeanDefinition(name, beanDefinition);
84      }
85  
86      @Override
87      public void register(final Domain domain) {
88          HikariConfig hikariConfig = new HikariConfig();
89          hikariConfig.setDriverClassName(domain.getJdbcDriver());
90          hikariConfig.setJdbcUrl(domain.getJdbcURL());
91          hikariConfig.setUsername(domain.getDbUsername());
92          hikariConfig.setPassword(domain.getDbPassword());
93          hikariConfig.setSchema(domain.getDbSchema());
94          hikariConfig.setTransactionIsolation(domain.getTransactionIsolation().name());
95          hikariConfig.setMaximumPoolSize(domain.getPoolMaxActive());
96          hikariConfig.setMinimumIdle(domain.getPoolMinIdle());
97  
98          // domainDataSource
99          registerBeanDefinition(
100                 domain.getKey() + "DataSource",
101                 BeanDefinitionBuilder.rootBeanDefinition(JndiObjectFactoryBean.class).
102                         addPropertyValue("jndiName", "java:comp/env/jdbc/syncope" + domain.getKey() + "DataSource").
103                         addPropertyValue("defaultObject", new HikariDataSource(hikariConfig)).
104                         getBeanDefinition());
105         DataSource initedDataSource = beanFactory().getBean(domain.getKey() + "DataSource", DataSource.class);
106 
107         // domainResourceDatabasePopulator
108         ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
109         databasePopulator.setContinueOnError(true);
110         databasePopulator.setIgnoreFailedDrops(true);
111         databasePopulator.setSqlScriptEncoding(StandardCharsets.UTF_8.name());
112         databasePopulator.addScript(new ClassPathResource("/audit/" + domain.getAuditSql()));
113         registerSingleton(domain.getKey().toLowerCase() + "ResourceDatabasePopulator", databasePopulator);
114 
115         // domainDataSourceInitializer
116         DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
117         dataSourceInitializer.setDataSource(initedDataSource);
118         dataSourceInitializer.setEnabled(true);
119         dataSourceInitializer.setDatabasePopulator(databasePopulator);
120         registerSingleton(domain.getKey().toLowerCase() + "DataSourceInitializer", dataSourceInitializer);
121 
122         // domainEntityManagerFactory
123         OpenJpaVendorAdapter vendorAdapter = new OpenJpaVendorAdapter();
124         vendorAdapter.setShowSql(false);
125         vendorAdapter.setGenerateDdl(true);
126         vendorAdapter.setDatabasePlatform(domain.getDatabasePlatform());
127 
128         ConnectorManagerRemoteCommitListener connectorManagerRemoteCommitListener =
129                 new ConnectorManagerRemoteCommitListener(domain.getKey());
130 
131         BeanDefinitionBuilder emf = BeanDefinitionBuilder.rootBeanDefinition(DomainEntityManagerFactoryBean.class).
132                 addPropertyValue("mappingResources", domain.getOrm()).
133                 addPropertyValue("persistenceUnitName", domain.getKey()).
134                 addPropertyReference("dataSource", domain.getKey() + "DataSource").
135                 addPropertyValue("jpaVendorAdapter", vendorAdapter).
136                 addPropertyReference("commonEntityManagerFactoryConf", "commonEMFConf").
137                 addPropertyValue("connectorManagerRemoteCommitListener", connectorManagerRemoteCommitListener);
138         if (ctx.getEnvironment().containsProperty("openjpaMetaDataFactory")) {
139             emf.addPropertyValue("jpaPropertyMap", Map.of(
140                     "openjpa.MetaDataFactory",
141                     Objects.requireNonNull(ctx.getEnvironment().getProperty("openjpaMetaDataFactory")).
142                             replace("##orm##", domain.getOrm())));
143         }
144         registerBeanDefinition(domain.getKey() + "EntityManagerFactory", emf.getBeanDefinition());
145         beanFactory().getBean(domain.getKey() + "EntityManagerFactory");
146 
147         // domainTransactionManager
148         AbstractBeanDefinition domainTransactionManager =
149                 BeanDefinitionBuilder.rootBeanDefinition(JpaTransactionManager.class).
150                         addPropertyReference("entityManagerFactory", domain.getKey() + "EntityManagerFactory").
151                         getBeanDefinition();
152         domainTransactionManager.addQualifier(new AutowireCandidateQualifier(Qualifier.class, domain.getKey()));
153         registerBeanDefinition(domain.getKey() + "TransactionManager", domainTransactionManager);
154 
155         // domainTransactionTemplate
156         AbstractBeanDefinition domainTransactionTemplate =
157                 BeanDefinitionBuilder.rootBeanDefinition(TransactionTemplate.class).
158                         addPropertyReference("transactionManager", domain.getKey() + "TransactionManager").
159                         getBeanDefinition();
160         registerBeanDefinition(domain.getKey() + "TransactionTemplate", domainTransactionTemplate);
161 
162         // domainContentXML
163         registerBeanDefinition(domain.getKey() + "ContentXML",
164                 BeanDefinitionBuilder.rootBeanDefinition(ByteArrayInputStream.class).
165                         addConstructorArgValue(domain.getContent().getBytes()).
166                         getBeanDefinition());
167 
168         // domainKeymasterConfParamsJSON
169         registerBeanDefinition(domain.getKey() + "KeymasterConfParamsJSON",
170                 BeanDefinitionBuilder.rootBeanDefinition(ByteArrayInputStream.class).
171                         addConstructorArgValue(domain.getKeymasterConfParams().getBytes()).
172                         getBeanDefinition());
173     }
174 
175     @Override
176     public void unregister(final String domain) {
177         // domainKeymasterConfParamsJSON
178         unregisterSingleton(domain + "KeymasterConfParamsJSON");
179         unregisterBeanDefinition(domain + "KeymasterConfParamsJSON");
180 
181         // domainContentXML
182         unregisterSingleton(domain + "ContentXML");
183         unregisterBeanDefinition(domain + "ContentXML");
184 
185         // domainEntityManagerFactory
186         try {
187             EntityManagerFactory emf = beanFactory().
188                     getBean(domain + "EntityManagerFactory", EntityManagerFactory.class);
189             emf.close();
190         } catch (Exception e) {
191             LOG.error("Could not close EntityManagerFactory for Domain {}", domain, e);
192         }
193         unregisterSingleton(domain + "EntityManagerFactory");
194         unregisterBeanDefinition(domain + "EntityManagerFactory");
195 
196         // domainTransactionManager
197         unregisterSingleton(domain + "TransactionManager");
198         unregisterBeanDefinition(domain + "TransactionManager");
199 
200         // domainDataSourceInitializer
201         unregisterSingleton(domain.toLowerCase() + "DataSourceInitializer");
202 
203         // domainResourceDatabasePopulator
204         unregisterSingleton(domain.toLowerCase() + "ResourceDatabasePopulator");
205 
206         // domainDataSource
207         unregisterSingleton(domain + "DataSource");
208         unregisterBeanDefinition(domain + "DataSource");
209     }
210 }