From: Tang Cheng Date: Mon, 23 Dec 2019 07:49:12 +0000 (+0800) Subject: 重构 multi-tenant library X-Git-Url: https://source.supwisdom.com/gerrit/gitweb?a=commitdiff_plain;h=6c67f5484ee0a9e25c6f6dac573f30e4d7273ebd;p=epayment%2Ffood_payapi.git 重构 multi-tenant library --- diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/HttpHeaderAutoRegistrar.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/HttpHeaderAutoRegistrar.java new file mode 100644 index 00000000..c18c0099 --- /dev/null +++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/HttpHeaderAutoRegistrar.java @@ -0,0 +1,33 @@ +package com.supwisdom.multitenant; + +import com.supwisdom.multitenant.config.HttpHeaderWebMvcConfig; +import com.supwisdom.multitenant.impl.HttpHeaderTenantInterceptor; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.GenericBeanDefinition; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +public class HttpHeaderAutoRegistrar implements ImportBeanDefinitionRegistrar { + public static final String BEAN_NAME = "httpHeaderTenantInterceptor"; + + private void registerInterceptor(BeanDefinitionRegistry registry) { + GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); + beanDefinition.setBeanClass(HttpHeaderTenantInterceptor.class); + beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + registry.registerBeanDefinition(BEAN_NAME, beanDefinition); + } + + private void registerMvcConfigure(BeanDefinitionRegistry registry) { + GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); + beanDefinition.setBeanClass(HttpHeaderWebMvcConfig.class); + beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + registry.registerBeanDefinition(BEAN_NAME + "MvcConfig", beanDefinition); + } + + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { + registerInterceptor(registry); + registerMvcConfigure(registry); + } +} diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/HttpSessionAutoRegistrar.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/HttpSessionAutoRegistrar.java new file mode 100644 index 00000000..4064aeaa --- /dev/null +++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/HttpSessionAutoRegistrar.java @@ -0,0 +1,33 @@ +package com.supwisdom.multitenant; + +import com.supwisdom.multitenant.config.HttpSessionWebMvcConfig; +import com.supwisdom.multitenant.impl.HttpSessionTenantInterceptor; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.GenericBeanDefinition; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +public class HttpSessionAutoRegistrar implements ImportBeanDefinitionRegistrar { + public static final String BEAN_NAME = "httpSessionTenantInterceptor"; + + private void registerInterceptor(BeanDefinitionRegistry registry) { + GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); + beanDefinition.setBeanClass(HttpSessionTenantInterceptor.class); + beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + registry.registerBeanDefinition(BEAN_NAME, beanDefinition); +} + + private void registerMvcConfigurater(BeanDefinitionRegistry registry) { + GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); + beanDefinition.setBeanClass(HttpSessionWebMvcConfig.class); + beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); + registry.registerBeanDefinition(BEAN_NAME + "MvcConfig", beanDefinition); + } + + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { + registerInterceptor(registry); + registerMvcConfigurater(registry); + } +} diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/MultiTenantAutoConfiguration.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/MultiTenantAutoConfiguration.java index 3ea1eb15..6a13357c 100644 --- a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/MultiTenantAutoConfiguration.java +++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/MultiTenantAutoConfiguration.java @@ -1,31 +1,38 @@ package com.supwisdom.multitenant; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Role; +@Configuration +@ComponentScan(basePackages = {"com.supwisdom.multitenant"}) +@Role(BeanDefinition.ROLE_INFRASTRUCTURE) public class MultiTenantAutoConfiguration { - @Configuration - @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - @ComponentScan(basePackages = {"com.supwisdom.multitenant"}) + @Bean + @ConditionalOnMissingBean(TenantDetailsProvider.class) + public TenantDetailsProvider createDetailsProvider() { + return new TenantDetailsProvider() { + }; + } + public static class SessionTenantInterceptorAutoConfig { } - // @ConditionalOnProperty( +// @ConditionalOnProperty( // name = "multi-tenant.interceptor.httpsession.enabled", // havingValue = "true") // @Bean // @Role(BeanDefinition.ROLE_INFRASTRUCTURE) -// @ConditionalOnBean({SessionTenantInterceptorAutoConfig.class}) -// public SessionTenantInterceptorAutoConfig createSessionInterceptor() { +// public SessionTenantInterceptorAutoConfig sessionTenantInterceptorAutoConfig() { // return new SessionTenantInterceptorAutoConfig(); // } - @Configuration - @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - @ComponentScan(basePackages = {"com.supwisdom.multitenant"}) + public static class HttpHeaderTenantInterceptorAutoConfig { } @@ -34,7 +41,7 @@ public class MultiTenantAutoConfiguration { // name = "multi-tenant.interceptor.httpheader.enabled", // havingValue = "true") // @Role(BeanDefinition.ROLE_INFRASTRUCTURE) -// public HttpHeaderTenantInterceptorAutoConfig createHttpHeaderInterceptor() { +// public HttpHeaderTenantInterceptorAutoConfig httpHeaderTenantInterceptorAutoConfig() { // return new HttpHeaderTenantInterceptorAutoConfig(); // } } diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/TenantDetailsProvider.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/TenantDetailsProvider.java new file mode 100644 index 00000000..d8d9990b --- /dev/null +++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/TenantDetailsProvider.java @@ -0,0 +1,9 @@ +package com.supwisdom.multitenant; + +public interface TenantDetailsProvider { + default TenantDetails createDetailsById(String id) { + TenantDetails details = new TenantDetails(); + details.setId(id); + return details; + } +} diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/annotations/EnableHttpHeaderTenantInterceptor.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/annotations/EnableHttpHeaderTenantInterceptor.java index 97f8625a..daf9d4c3 100644 --- a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/annotations/EnableHttpHeaderTenantInterceptor.java +++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/annotations/EnableHttpHeaderTenantInterceptor.java @@ -1,5 +1,6 @@ package com.supwisdom.multitenant.annotations; +import com.supwisdom.multitenant.HttpHeaderAutoRegistrar; import com.supwisdom.multitenant.MultiTenantAutoConfiguration; import org.springframework.context.annotation.Import; @@ -7,7 +8,7 @@ import java.lang.annotation.*; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) -@Import(MultiTenantAutoConfiguration.HttpHeaderTenantInterceptorAutoConfig.class) +@Import({MultiTenantAutoConfiguration.class, HttpHeaderAutoRegistrar.class}) @Documented public @interface EnableHttpHeaderTenantInterceptor { } diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/annotations/EnableSessionTenantInterceptor.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/annotations/EnableSessionTenantInterceptor.java index 885140be..347b58da 100644 --- a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/annotations/EnableSessionTenantInterceptor.java +++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/annotations/EnableSessionTenantInterceptor.java @@ -1,5 +1,6 @@ package com.supwisdom.multitenant.annotations; +import com.supwisdom.multitenant.HttpSessionAutoRegistrar; import com.supwisdom.multitenant.MultiTenantAutoConfiguration; import org.springframework.context.annotation.Import; @@ -7,7 +8,7 @@ import java.lang.annotation.*; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) -@Import(MultiTenantAutoConfiguration.SessionTenantInterceptorAutoConfig.class) +@Import({MultiTenantAutoConfiguration.class, HttpSessionAutoRegistrar.class}) @Documented public @interface EnableSessionTenantInterceptor { } diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/condition/OnPgDatabase.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/condition/OnPgDatabase.java new file mode 100644 index 00000000..c09439dd --- /dev/null +++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/condition/OnPgDatabase.java @@ -0,0 +1,15 @@ +package com.supwisdom.multitenant.condition; + +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +public class OnPgDatabase implements Condition { + private static final String DATABASE_PLATFORM = "spring.jpa.database-platform"; + + @Override + public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { + String platform = context.getEnvironment().getProperty(DATABASE_PLATFORM, String.class); + return "postgresql".equals(platform); + } +} diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/HttpHeaderWebMvcConfig.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/HttpHeaderWebMvcConfig.java index b2d95853..0227a23d 100644 --- a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/HttpHeaderWebMvcConfig.java +++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/HttpHeaderWebMvcConfig.java @@ -2,13 +2,11 @@ package com.supwisdom.multitenant.config; import com.supwisdom.multitenant.impl.HttpHeaderTenantInterceptor; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.context.annotation.Configuration; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -@Configuration -@ConditionalOnBean(value = {HttpHeaderTenantInterceptor.class}) +@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) public class HttpHeaderWebMvcConfig implements WebMvcConfigurer { private final HttpHeaderTenantInterceptor interceptor; diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/HttpSessionWebMvcConfig.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/HttpSessionWebMvcConfig.java new file mode 100644 index 00000000..c4d80304 --- /dev/null +++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/config/HttpSessionWebMvcConfig.java @@ -0,0 +1,20 @@ +package com.supwisdom.multitenant.config; + +import com.supwisdom.multitenant.impl.HttpSessionTenantInterceptor; +import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) +public class HttpSessionWebMvcConfig implements WebMvcConfigurer { + private final HttpSessionTenantInterceptor interceptor; + + public HttpSessionWebMvcConfig(HttpSessionTenantInterceptor interceptor) { + this.interceptor = interceptor; + } + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(interceptor); + } +} diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/HttpHeaderTenantInterceptor.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/HttpHeaderTenantInterceptor.java index b8ae3426..375eda04 100644 --- a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/HttpHeaderTenantInterceptor.java +++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/HttpHeaderTenantInterceptor.java @@ -1,18 +1,16 @@ package com.supwisdom.multitenant.impl; -import com.supwisdom.multitenant.*; +import com.supwisdom.multitenant.TenantContextHolder; +import com.supwisdom.multitenant.TenantDetails; +import com.supwisdom.multitenant.TenantInterceptor; import com.supwisdom.multitenant.config.TenantHeaderProperties; import com.supwisdom.multitenant.config.TenantJwtProperties; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.stereotype.Component; import org.springframework.util.StringUtils; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -@Component -@ConditionalOnBean(MultiTenantAutoConfiguration.HttpHeaderTenantInterceptorAutoConfig.class) public class HttpHeaderTenantInterceptor extends TenantInterceptor { private final TenantJwtProperties properties; diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/HttpSessionTenantInterceptor.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/HttpSessionTenantInterceptor.java new file mode 100644 index 00000000..cb711e00 --- /dev/null +++ b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/HttpSessionTenantInterceptor.java @@ -0,0 +1,45 @@ +package com.supwisdom.multitenant.impl; + +import com.supwisdom.multitenant.TenantContextHolder; +import com.supwisdom.multitenant.TenantDetails; +import com.supwisdom.multitenant.TenantDetailsProvider; +import com.supwisdom.multitenant.TenantInterceptor; +import com.supwisdom.multitenant.config.TenantSessionProperties; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +public class HttpSessionTenantInterceptor extends TenantInterceptor { + private final TenantSessionProperties properties; + + private final TenantDetailsProvider provider; + + public HttpSessionTenantInterceptor(TenantSessionProperties properties, + TenantDetailsProvider provider) { + this.properties = properties; + this.provider = provider; + } + + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { + Object details = request.getSession().getAttribute(properties.getName()); + if (details != null) { + if (details instanceof TenantDetails) { + TenantContextHolder.getContext().setTenant((TenantDetails) details); + } else if (details instanceof String) { + TenantContextHolder.getContext().setTenant(provider.createDetailsById((String) details)); + } else { + return false; + } + } + return super.preHandle(request, response, handler); + } + + @Override + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, + ModelAndView modelAndView) throws Exception { + TenantContextHolder.clearContext(); + super.postHandle(request, response, handler, modelAndView); + } +} diff --git a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/SessionTenantInterceptor.java b/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/SessionTenantInterceptor.java deleted file mode 100644 index 3f9ae2c0..00000000 --- a/multi-tenant-core/src/main/java/com/supwisdom/multitenant/impl/SessionTenantInterceptor.java +++ /dev/null @@ -1,39 +0,0 @@ -package com.supwisdom.multitenant.impl; - -import com.supwisdom.multitenant.*; -import com.supwisdom.multitenant.config.TenantSessionProperties; -import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; -import org.springframework.stereotype.Component; -import org.springframework.util.StringUtils; -import org.springframework.web.servlet.ModelAndView; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -@Component -@ConditionalOnBean(MultiTenantAutoConfiguration.SessionTenantInterceptorAutoConfig.class) -public class SessionTenantInterceptor extends TenantInterceptor { - private final TenantSessionProperties properties; - - public SessionTenantInterceptor(TenantSessionProperties properties) { - this.properties = properties; - } - - @Override - public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { - String tenant = (String) request.getSession().getAttribute(properties.getName()); - if (tenant != null && !StringUtils.isEmpty(tenant)) { - TenantDetails details = new TenantDetails(); - details.setId(tenant); - TenantContextHolder.getContext().setTenant(details); - } - return super.preHandle(request, response, handler); - } - - @Override - public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, - ModelAndView modelAndView) throws Exception { - TenantContextHolder.clearContext(); - super.postHandle(request, response, handler, modelAndView); - } -} diff --git a/multi-tenant-core/src/test/java/com/supwisdom/multitenant/HttpHeaderTest.java b/multi-tenant-core/src/test/java/com/supwisdom/multitenant/HttpHeaderTest.java new file mode 100644 index 00000000..ce1ce580 --- /dev/null +++ b/multi-tenant-core/src/test/java/com/supwisdom/multitenant/HttpHeaderTest.java @@ -0,0 +1,59 @@ +package com.supwisdom.multitenant; + +import com.supwisdom.multitenant.annotations.EnableHttpHeaderTenantInterceptor; +import com.supwisdom.multitenant.condition.OnPgDatabase; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.test.context.TestPropertySource; + +import java.util.Arrays; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; + +@SpringBootTest(classes = {HttpHeaderTest.class}) +@TestPropertySource("/test.properties") +@EnableHttpHeaderTenantInterceptor +public class HttpHeaderTest { + + @Autowired + private ApplicationContext appContext; + + @Test + public void testSessionOk() { + assertThat(TenantContextHolder.getContext(), notNullValue()); + assertThat(findBeanByName(HttpHeaderAutoRegistrar.BEAN_NAME), is(true)); + } + + @Test + public void testError() { + assertThat(findBeanByName(HttpSessionAutoRegistrar.BEAN_NAME), is(false)); + } + + @Configuration + static class PostgresTest { + @Bean + @Conditional(OnPgDatabase.class) + public PostgresTest testPg() { + return new PostgresTest(); + } + } + + + private boolean findBeanByName(String name) { + String[] beans = appContext.getBeanDefinitionNames(); + Arrays.sort(beans); + for (String bean : beans) { + if (name.equals(bean)) { + return true; + } + } + return false; + } +} diff --git a/multi-tenant-core/src/test/java/com/supwisdom/multitenant/AutoConfigurationTest.java b/multi-tenant-core/src/test/java/com/supwisdom/multitenant/HttpSessionTest.java similarity index 66% rename from multi-tenant-core/src/test/java/com/supwisdom/multitenant/AutoConfigurationTest.java rename to multi-tenant-core/src/test/java/com/supwisdom/multitenant/HttpSessionTest.java index 4286f3ca..ea612e1c 100644 --- a/multi-tenant-core/src/test/java/com/supwisdom/multitenant/AutoConfigurationTest.java +++ b/multi-tenant-core/src/test/java/com/supwisdom/multitenant/HttpSessionTest.java @@ -10,24 +10,36 @@ import org.springframework.test.context.TestPropertySource; import java.util.Arrays; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.notNullValue; -@SpringBootTest(classes = {AutoConfigurationTest.class}) -@EnableSessionTenantInterceptor +@SpringBootTest(classes = {HttpSessionTest.class}) @TestPropertySource("/test.properties") -public class AutoConfigurationTest { - +@EnableSessionTenantInterceptor +public class HttpSessionTest { @Autowired private ApplicationContext appContext; @Test public void testSessionOk() { + assertThat(TenantContextHolder.getContext(), notNullValue()); + assertThat(findBeanByName(HttpSessionAutoRegistrar.BEAN_NAME), is(true)); + } + + @Test + public void testError() { + assertThat(findBeanByName(HttpHeaderAutoRegistrar.BEAN_NAME), is(false)); + } + + + private boolean findBeanByName(String name) { String[] beans = appContext.getBeanDefinitionNames(); Arrays.sort(beans); for (String bean : beans) { - System.out.println(bean); + if (name.equals(bean)) { + return true; + } } - assertThat(TenantContextHolder.getContext(), notNullValue()); + return false; } - } diff --git a/multi-tenant-core/src/test/resources/test.properties b/multi-tenant-core/src/test/resources/test.properties index 80c6377d..d6cc728b 100644 --- a/multi-tenant-core/src/test/resources/test.properties +++ b/multi-tenant-core/src/test/resources/test.properties @@ -1 +1,2 @@ -debug=true \ No newline at end of file +debug=true +spring.jpa.database-platform=postgresql diff --git a/multi-tenant-datasource/build.gradle b/multi-tenant-datasource/build.gradle index 09ed0146..bc2a8127 100644 --- a/multi-tenant-datasource/build.gradle +++ b/multi-tenant-datasource/build.gradle @@ -17,7 +17,7 @@ bootJar { } dependencies { - implementation(":multi-tenant-core") + implementation project(":multi-tenant-core") implementation "org.springframework.boot:spring-boot-autoconfigure" implementation "commons-beanutils:commons-beanutils:${beanutilsVersion}" implementation "commons-codec:commons-codec:${codecVersion}" diff --git a/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/MultiTenantConnectionProviderImpl.java b/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/MultiTenantConnectionProviderImpl.java new file mode 100644 index 00000000..737cba0d --- /dev/null +++ b/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/MultiTenantConnectionProviderImpl.java @@ -0,0 +1,79 @@ +package com.supwisdom.multitenant.datasource; + +import com.supwisdom.multitenant.TenantContextHolder; +import com.supwisdom.multitenant.TenantDetails; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.HibernateException; +import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +/** + * Created by shuwei on 2018/12/4. + */ +@Slf4j +@Component +public class MultiTenantConnectionProviderImpl implements MultiTenantConnectionProvider { + private final DataSource dataSource; + + private final MultiTenantDataSourceProvider dsProvider; + + public MultiTenantConnectionProviderImpl(DataSource dataSource, + MultiTenantDataSourceProvider provider) { + this.dataSource = dataSource; + this.dsProvider = provider; + } + + @Override + public Connection getAnyConnection() throws SQLException { + return dataSource.getConnection(); + } + + @Override + public void releaseAnyConnection(Connection connection) throws SQLException { + connection.close(); + } + + @Override + public Connection getConnection(String ti) throws SQLException { + TenantDetails tenant = TenantContextHolder.getContext().getTenant(); + final Connection connection = getAnyConnection(); + try { + if (tenant != null) { + log.debug("postgresql set search path to <" + tenant.getDbSchema() + ">"); + connection.createStatement().execute(dsProvider.getTenantSQL(tenant)); + } else { + log.debug("postgresql set search path to public"); + connection.createStatement().execute(dsProvider.getDefaultSQL()); + } + } catch (SQLException e) { + assert tenant != null; + throw new HibernateException("Problem setting schema to " + tenant.toString(), e); + } + return connection; + } + + @Override + public void releaseConnection(String tenantIdentifier, Connection connection) throws SQLException { + connection.close(); + } + + @Override + public boolean supportsAggressiveRelease() { + return false; + } + + @SuppressWarnings("rawtypes") + @Override + public boolean isUnwrappableAs(Class unwrapType) { + return false; + } + + @Override + public T unwrap(Class unwrapType) { + return null; + } +} diff --git a/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/MultiTenantDataSourceProvider.java b/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/MultiTenantDataSourceProvider.java new file mode 100644 index 00000000..394b743f --- /dev/null +++ b/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/MultiTenantDataSourceProvider.java @@ -0,0 +1,9 @@ +package com.supwisdom.multitenant.datasource; + +import com.supwisdom.multitenant.TenantDetails; + +public interface MultiTenantDataSourceProvider { + String getDefaultSQL(); + + String getTenantSQL(TenantDetails details); +} diff --git a/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/MultiTenantFactory.java b/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/MultiTenantFactory.java new file mode 100644 index 00000000..6706bd18 --- /dev/null +++ b/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/MultiTenantFactory.java @@ -0,0 +1,7 @@ +package com.supwisdom.multitenant.datasource; + +import com.supwisdom.multitenant.datasource.beans.TenantProps; + +public interface MultiTenantFactory { + TenantProps createMultiTenant(); +} diff --git a/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/beans/TenantProps.java b/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/beans/TenantProps.java new file mode 100644 index 00000000..5e0b6daa --- /dev/null +++ b/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/beans/TenantProps.java @@ -0,0 +1,5 @@ +package com.supwisdom.multitenant.datasource.beans; + +public class TenantProps { + private String schema; +} diff --git a/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/config/MultiTenantDataSourceAutoConfiguration.java b/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/config/MultiTenantDataSourceAutoConfiguration.java new file mode 100644 index 00000000..db0c5f75 --- /dev/null +++ b/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/config/MultiTenantDataSourceAutoConfiguration.java @@ -0,0 +1,22 @@ +package com.supwisdom.multitenant.datasource.config; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; +import org.springframework.core.type.AnnotationMetadata; + +@Configuration +public class MultiTenantDataSourceAutoConfiguration implements ImportBeanDefinitionRegistrar { + + @Value("${spring.jpa.database-platform:all}") + private String datasourcePlatform; + + @Override + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { +// GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); +// beanDefinition.setBeanClass(); +// registry.registerBeanDefinition(); + } + +} diff --git a/multi-tenant-datasource/src/main/java/com/supwisdom/payapi/multitenant/config/HibernateConfig.java b/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/config/MutliTenantHibernateConfig.java similarity index 85% rename from multi-tenant-datasource/src/main/java/com/supwisdom/payapi/multitenant/config/HibernateConfig.java rename to multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/config/MutliTenantHibernateConfig.java index 3365265e..87be5f6e 100644 --- a/multi-tenant-datasource/src/main/java/com/supwisdom/payapi/multitenant/config/HibernateConfig.java +++ b/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/config/MutliTenantHibernateConfig.java @@ -1,12 +1,14 @@ -package com.supwisdom.payapi.multitenant.config; +package com.supwisdom.multitenant.datasource.config; +import com.supwisdom.multitenant.MultiTenantAutoConfiguration; import lombok.extern.slf4j.Slf4j; import org.hibernate.MultiTenancyStrategy; import org.hibernate.cfg.Environment; import org.hibernate.context.spi.CurrentTenantIdentifierResolver; import org.hibernate.engine.jdbc.connections.spi.MultiTenantConnectionProvider; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.autoconfigure.orm.jpa.HibernateProperties; import org.springframework.boot.autoconfigure.orm.jpa.HibernateSettings; import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties; @@ -21,15 +23,16 @@ import java.util.HashMap; import java.util.Map; @Configuration +@ConditionalOnBean({MultiTenantAutoConfiguration.class}) @Slf4j -public class HibernateConfig { +public class MutliTenantHibernateConfig { private final JpaProperties jpaProperties; private final HibernateProperties hibernateProperties; - public HibernateConfig(@Autowired JpaProperties jpaProperties, - HibernateProperties hibernateProperties) { + public MutliTenantHibernateConfig(@Autowired JpaProperties jpaProperties, + HibernateProperties hibernateProperties) { this.jpaProperties = jpaProperties; this.hibernateProperties = hibernateProperties; } diff --git a/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/impl/PostgresDataSourceProvider.java b/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/impl/PostgresDataSourceProvider.java new file mode 100644 index 00000000..19305ef3 --- /dev/null +++ b/multi-tenant-datasource/src/main/java/com/supwisdom/multitenant/datasource/impl/PostgresDataSourceProvider.java @@ -0,0 +1,25 @@ +package com.supwisdom.multitenant.datasource.impl; + +import com.supwisdom.multitenant.TenantDetails; +import com.supwisdom.multitenant.datasource.MultiTenantDataSourceProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.context.annotation.Configuration; +import org.springframework.util.StringUtils; + +@Configuration +@ConditionalOnExpression("${spring.jpa.database-platform:postgresql}") +public class PostgresDataSourceProvider implements MultiTenantDataSourceProvider { + @Override + public String getTenantSQL(TenantDetails details) { + if (details == null || details.getDbSchema() == null + || StringUtils.isEmpty(details.getDbSchema())) { + return getDefaultSQL(); + } + return String.format("set search_path \"%s\", public; ", details.getDbSchema()); + } + + @Override + public String getDefaultSQL() { + return "set search_path public;"; + } +} diff --git a/multi-tenant-datasource/src/main/java/com/supwisdom/payapi/multitenant/DefaultMultiTenantFactory.java b/multi-tenant-datasource/src/main/java/com/supwisdom/payapi/multitenant/DefaultMultiTenantFactory.java deleted file mode 100644 index 898e6474..00000000 --- a/multi-tenant-datasource/src/main/java/com/supwisdom/payapi/multitenant/DefaultMultiTenantFactory.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.supwisdom.payapi.multitenant; - -public class DefaultMultiTenantFactory implements MultiTenantFactory { -} diff --git a/multi-tenant-datasource/src/main/java/com/supwisdom/payapi/multitenant/MultiTenantFactory.java b/multi-tenant-datasource/src/main/java/com/supwisdom/payapi/multitenant/MultiTenantFactory.java deleted file mode 100644 index 1c829f82..00000000 --- a/multi-tenant-datasource/src/main/java/com/supwisdom/payapi/multitenant/MultiTenantFactory.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.supwisdom.payapi.multitenant; - -public interface MultiTenantFactory { - -} diff --git a/multi-tenant-datasource/src/main/java/com/supwisdom/payapi/multitenant/TenantContext.java b/multi-tenant-datasource/src/main/java/com/supwisdom/payapi/multitenant/TenantContext.java deleted file mode 100644 index 4236e297..00000000 --- a/multi-tenant-datasource/src/main/java/com/supwisdom/payapi/multitenant/TenantContext.java +++ /dev/null @@ -1,15 +0,0 @@ -package com.supwisdom.payapi.multitenant; - -import com.supwisdom.payapi.multitenant.beans.TenantProps; - -public class TenantContext { - private static final ThreadLocal tenantHolder = new ThreadLocal<>(); - - public static TenantProps getTenant() { - return tenantHolder.get(); - } - - public static void setTenant(String tenant) { - - } -} diff --git a/multi-tenant-datasource/src/main/java/com/supwisdom/payapi/multitenant/beans/TenantProps.java b/multi-tenant-datasource/src/main/java/com/supwisdom/payapi/multitenant/beans/TenantProps.java deleted file mode 100644 index 6e0abf88..00000000 --- a/multi-tenant-datasource/src/main/java/com/supwisdom/payapi/multitenant/beans/TenantProps.java +++ /dev/null @@ -1,4 +0,0 @@ -package com.supwisdom.payapi.multitenant.beans; - -public class TenantProps { -} diff --git a/multi-tenant-datasource/src/main/java/com/supwisdom/payapi/multitenant/config/MultiTenantAutoConfiguration.java b/multi-tenant-datasource/src/main/java/com/supwisdom/payapi/multitenant/config/MultiTenantAutoConfiguration.java deleted file mode 100644 index 718d869d..00000000 --- a/multi-tenant-datasource/src/main/java/com/supwisdom/payapi/multitenant/config/MultiTenantAutoConfiguration.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.supwisdom.payapi.multitenant.config; - -import com.supwisdom.payapi.multitenant.DefaultMultiTenantFactory; -import com.supwisdom.payapi.multitenant.MultiTenantFactory; -import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class MultiTenantAutoConfiguration { - @Bean - @ConditionalOnMissingBean(MultiTenantFactory.class) - public MultiTenantFactory createFactory() { - return new DefaultMultiTenantFactory(); - } -} diff --git a/multi-tenant-datasource/src/test/java/com/supwisdom/multitenant/datasource/PgDatasourceTest.java b/multi-tenant-datasource/src/test/java/com/supwisdom/multitenant/datasource/PgDatasourceTest.java new file mode 100644 index 00000000..7aae07af --- /dev/null +++ b/multi-tenant-datasource/src/test/java/com/supwisdom/multitenant/datasource/PgDatasourceTest.java @@ -0,0 +1,17 @@ +package com.supwisdom.multitenant.datasource; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.TestPropertySource; + +@SpringBootTest(classes = {PgDatasourceTest.class}, + properties = {"multi-tenant.interceptor.httpsession.enabled=true", + "multi-tenant.interceptor.httpheader.enabled=true"}) +@TestPropertySource("/test-pg.properties") +public class PgDatasourceTest { + + @Test + public void connectDatasource() { + + } +} diff --git a/multi-tenant-datasource/src/test/resources/test-pg.properties b/multi-tenant-datasource/src/test/resources/test-pg.properties new file mode 100644 index 00000000..3613c3ba --- /dev/null +++ b/multi-tenant-datasource/src/test/resources/test-pg.properties @@ -0,0 +1,13 @@ +spring.main.banner-mode=off +# create and drop tables and sequences, loads import.sql +spring.jpa.hibernate.ddl-auto=update +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect +spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults=false +# Postgresql settings +spring.datasource.platform=postgresql +#spring.datasource.url=jdbc:postgresql://ykt.supwisdom.com:15432/payapidev +spring.datasource.url=jdbc:postgresql://172.28.201.70:15432/payapidev +spring.datasource.username=payapi +spring.datasource.password=123456 +spring.datasource.continue-on-error=true +spring.datasource.initialization-mode=always \ No newline at end of file