From 34c195cb0b83b10fb78e878650fde37106041598 Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Thu, 21 Dec 2023 17:31:56 +0100 Subject: [PATCH 01/14] feat: Cleanup Dependencies to rely on Spring Dependencies Tree - Meeds-io/MIPs#57 --- component/api/pom.xml | 77 +- .../kernel/PortalApplicationContext.java | 249 +++ .../GrantedAuthorityDefaults.java | 43 + .../web/configuration/WebConfiguration.java | 46 + .../WebSecurityConfiguration.java | 70 + .../web/filter/PortalIdentityFilter.java | 30 + .../web/filter/PortalTransactionFilter.java | 95 + .../security/PortalAuthenticationManager.java | 172 ++ .../portal/mop/navigation/NavigationData.java | 2 +- .../portal/mop/navigation/NodeData.java | 2 +- .../src/main/resources/application.properties | 25 + component/application-registry/pom.xml | 2 +- .../registry/dao/ApplicationDAOImpl.java | 4 +- .../registry/dao/CategoryDAOImpl.java | 4 +- .../registry/entity/ApplicationEntity.java | 2 +- .../registry/entity/CategoryEntity.java | 2 +- component/common/pom.xml | 2 +- ...omIdentifierGeneratorStrategyProvider.java | 60 + .../persistence/impl/EntityManagerHolder.java | 2 +- .../impl/EntityManagerService.java | 14 +- .../impl/ExoTransactionalAspect.java | 6 +- .../persistence/impl/GenericDAOJPAImpl.java | 10 +- .../impl/LiquibaseDataInitializer.java | 44 +- .../settings/jpa/dao/SettingContextDAO.java | 6 +- .../settings/jpa/dao/SettingScopeDAO.java | 4 +- .../settings/jpa/dao/SettingsDAO.java | 6 +- .../settings/jpa/entity/ContextEntity.java | 2 +- .../settings/jpa/entity/ScopeEntity.java | 2 +- .../settings/jpa/entity/SettingsEntity.java | 2 +- .../main/resources/META-INF/persistence.xml | 6 +- .../jpa/mock/MockEntityManagerService.java | 4 +- .../resources/conf/configuration.properties | 1 - component/file-storage/pom.xml | 2 +- .../resource/FileSystemResourceProvider.java | 2 +- .../file/services/impl/FileServiceImpl.java | 2 +- .../services/impl/NameSpaceServiceImpl.java | 2 +- .../storage/dao/impl/FileBinaryDAOImpl.java | 4 +- .../storage/dao/impl/FileInfoDAOImpl.java | 2 +- .../storage/dao/impl/NameSpaceDAOImpl.java | 4 +- .../storage/dao/impl/OrphanFileDAOImpl.java | 4 +- .../file/storage/entity/FileBinaryEntity.java | 2 +- .../file/storage/entity/FileInfoEntity.java | 2 +- .../file/storage/entity/NameSpaceEntity.java | 2 +- .../file/storage/entity/OrphanFileEntity.java | 2 +- .../file/services/FileServiceImplTest.java | 4 +- .../conf/standalone/test-configuration.xml | 43 - component/identity/pom.xml | 79 +- .../auth/OrganizationAuthenticatorImpl.java | 2 +- .../organization/idm/AbstractDAOImpl.java | 16 +- .../idm/CustomHibernateServiceImpl.java | 5 +- .../idm/DisabledUserMigrationScript.java | 1 - .../idm/IDMMembershipListAccess.java | 2 +- .../organization/idm/IDMUserListAccess.java | 2 +- .../PicketLinkIDMOrganizationServiceImpl.java | 46 +- .../idm/cache/CacheableGroupHandlerImpl.java | 2 +- .../CacheableUserProfileHandlerImpl.java | 2 +- .../idm/externalstore/jpa/IDMQueueEntity.java | 2 +- .../search/GroupSearchServiceImpl.java | 2 +- .../search/UserSearchServiceImpl.java | 2 +- .../ExoFallbackIdentityStoreRepository.java | 2 +- .../ExoHibernateIdentityStoreImpl.java | 2 +- .../hibernate/HibernateIdentityObject.java | 246 +-- .../HibernateIdentityObjectAttribute.java | 39 +- ...ateIdentityObjectAttributeBinaryValue.java | 19 +- .../HibernateIdentityObjectCredential.java | 19 +- ...teIdentityObjectCredentialBinaryValue.java | 19 +- ...HibernateIdentityObjectCredentialType.java | 17 +- .../HibernateIdentityObjectRelationship.java | 132 +- ...bernateIdentityObjectRelationshipName.java | 56 +- ...bernateIdentityObjectRelationshipType.java | 17 +- .../HibernateIdentityObjectType.java | 17 +- .../impl/model/hibernate/HibernateRealm.java | 69 +- .../hibernate/HibernateIdentityStoreImpl.java | 1905 +++++++---------- .../PatchedHibernateIdentityStoreImpl.java | 50 +- .../db/changelog/idm.db.changelog-1.0.0.xml | 36 + .../AbstractTestOrganizationService.java | 40 +- .../organization/TestLDAPOrganization.java | 2 +- .../organization/TestOrganization.java | 56 +- .../organization/TestUserSearchService.java | 4 +- .../auth/TestOrganizationAuthenticator.java | 2 +- .../AbstractOrganizationServiceTest.java | 327 +++ .../tck/organization/TestGroupHandler.java | 480 +++++ .../organization/TestMembershipHandler.java | 1102 ++++++++++ .../TestMembershipTypeHandler.java | 417 ++++ .../tck/organization/TestUserHandler.java | 1208 +++++++++++ .../organization/TestUserProfileHandler.java | 280 +++ component/portal/pom.xml | 6 +- .../DefaultUserMembershipListener.java | 2 +- .../service/SecuritySettingService.java | 2 +- .../DynamicPortalLayoutMatcherPlugin.java | 2 +- .../MetaPortalConfigUpdateListener.java | 2 +- .../config/NavigationCategoryService.java | 2 +- .../config/NewPortalConfigListener.java | 6 +- .../config/UserPortalConfigService.java | 5 +- .../portal/jdbc/entity/ContainerEntity.java | 2 +- .../portal/jdbc/entity/DescriptionEntity.java | 2 +- .../portal/jdbc/entity/DescriptionState.java | 4 +- .../portal/jdbc/entity/NavigationEntity.java | 2 +- .../portal/jdbc/entity/NodeEntity.java | 38 +- .../portal/jdbc/entity/PageEntity.java | 28 +- .../portal/jdbc/entity/PermissionEntity.java | 2 +- .../portal/jdbc/entity/SiteEntity.java | 2 +- .../portal/jdbc/entity/WindowEntity.java | 26 +- .../portal/mop/dao/ContainerDAOImpl.java | 2 +- .../portal/mop/dao/DescriptionDAOImpl.java | 4 +- .../portal/mop/dao/NavigationDAOImpl.java | 4 +- .../portal/mop/dao/NodeDAOImpl.java | 2 +- .../portal/mop/dao/PageDAOImpl.java | 18 +- .../portal/mop/dao/PermissionDAOImpl.java | 4 +- .../portal/mop/dao/SiteDAOImpl.java | 6 +- .../portal/mop/dao/WindowDAOImpl.java | 4 +- .../portal/mop/rest/EntityBuilder.java | 2 +- .../portal/mop/rest/NavigationRest.java | 2 +- .../mop/service/NavigationServiceImpl.java | 2 +- .../portal/mop/storage/LayoutStorage.java | 2 +- .../portal/mop/storage/SiteStorageImpl.java | 2 +- .../rest/MembershipTypeRestResourcesV1.java | 2 +- .../settings/rest/SettingResource.java | 2 +- .../portal/mop/dao/ContainerDAOTest.java | 2 +- .../portal/mop/dao/NavigationDAOTest.java | 2 +- .../portal/mop/dao/WindowDAOTest.java | 2 +- .../portal/mop/storage/TestModelStorage.java | 2 +- .../groovyscript/GroovyTemplate.java | 2 +- .../groovyscript/text/TemplateService.java | 2 +- component/test/core/pom.xml | 24 + .../main}/resources/META-INF/persistence.xml | 8 +- .../conf/base-root-configuration.xml | 4 +- .../core/src/main/resources/logback-test.xml | 3 +- component/web/api/pom.xml | 2 +- .../portal/resource/SkinConfigPlugin.java | 2 +- .../portal/resource/SkinService.java | 2 +- .../web/application/RequireJS.java | 2 +- .../javascript/JavascriptConfigService.java | 2 +- .../resource/TestJavascriptConfigService.java | 2 +- .../web/security/codec/CodecInitializer.java | 2 +- .../web/security/jpa/TokenDAOImpl.java | 6 +- .../web/security/jpa/TokenEntity.java | 2 +- .../web/handler/UploadHandler.java | 2 +- .../conf/organization/idm-configuration.xml | 2 +- .../portal/webui/application/UIPortlet.gtmpl | 2 +- .../webui/workspace/UIPortalApplication.gtmpl | 2 +- .../account/UIUserGroupSelector.gtmpl | 6 +- .../organization/account/UIUserSelector.gtmpl | 6 +- webui/core/pom.xml | 2 +- .../webui/form/UIFormStringInput.java | 2 +- .../webui/utils/TimeConvertUtils.java | 2 +- .../DefaultLocalePolicyService.java | 2 +- .../portal/page/PageTemplateService.java | 2 +- .../portal/url/PortalURLContext.java | 2 +- .../webui/workspace/UIPortalApplication.java | 2 +- .../workspace/UIStandaloneApplication.java | 2 +- 151 files changed, 6094 insertions(+), 2005 deletions(-) create mode 100644 component/api/src/main/java/io/meeds/spring/integration/kernel/PortalApplicationContext.java create mode 100644 component/api/src/main/java/io/meeds/spring/integration/web/configuration/GrantedAuthorityDefaults.java create mode 100644 component/api/src/main/java/io/meeds/spring/integration/web/configuration/WebConfiguration.java create mode 100644 component/api/src/main/java/io/meeds/spring/integration/web/configuration/WebSecurityConfiguration.java create mode 100644 component/api/src/main/java/io/meeds/spring/integration/web/filter/PortalIdentityFilter.java create mode 100644 component/api/src/main/java/io/meeds/spring/integration/web/filter/PortalTransactionFilter.java create mode 100644 component/api/src/main/java/io/meeds/spring/integration/web/security/PortalAuthenticationManager.java create mode 100644 component/api/src/main/resources/application.properties create mode 100644 component/common/src/main/java/io/meeds/common/persistence/HibernateCustomIdentifierGeneratorStrategyProvider.java delete mode 100644 component/file-storage/src/test/resources/conf/standalone/test-configuration.xml create mode 100644 component/identity/src/test/java/org/exoplatform/services/tck/organization/AbstractOrganizationServiceTest.java create mode 100644 component/identity/src/test/java/org/exoplatform/services/tck/organization/TestGroupHandler.java create mode 100644 component/identity/src/test/java/org/exoplatform/services/tck/organization/TestMembershipHandler.java create mode 100644 component/identity/src/test/java/org/exoplatform/services/tck/organization/TestMembershipTypeHandler.java create mode 100644 component/identity/src/test/java/org/exoplatform/services/tck/organization/TestUserHandler.java create mode 100644 component/identity/src/test/java/org/exoplatform/services/tck/organization/TestUserProfileHandler.java rename component/{common/src/test => test/core/src/main}/resources/META-INF/persistence.xml (71%) diff --git a/component/api/pom.xml b/component/api/pom.xml index 96d326142c..2c44e89c73 100644 --- a/component/api/pom.xml +++ b/component/api/pom.xml @@ -52,12 +52,6 @@ org.exoplatform.kernel exo.kernel.container - - - org.ogce - xpp3 - - org.exoplatform.kernel @@ -108,15 +102,15 @@ org.exoplatform.gatein.pc - pc-portlet + pc-api org.exoplatform.gatein.pc - pc-federation + pc-portlet org.exoplatform.gatein.pc - pc-api + pc-federation @@ -159,7 +153,7 @@ io.swagger.core.v3 - swagger-annotations + swagger-annotations-jakarta compile commons-codec @@ -209,7 +200,7 @@ aspectjrt - org.hibernate + org.hibernate.orm hibernate-core @@ -230,14 +221,15 @@ - - org.hibernate.common - hibernate-commons-annotations - org.liquibase liquibase-core + + + com.mattbertolini + liquibase-slf4j + org.jboss.logging jboss-logging @@ -255,8 +247,55 @@ org.bouncycastle bcprov-jdk15on + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-data-jpa + + + org.springframework.data + spring-data-elasticsearch + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + + org.apache.logging.log4j + log4j-to-slf4j + + + org.slf4j + slf4j-api + + + + + + + src/main/resources/ + true + + org.jibx diff --git a/component/api/src/main/java/io/meeds/spring/integration/kernel/PortalApplicationContext.java b/component/api/src/main/java/io/meeds/spring/integration/kernel/PortalApplicationContext.java new file mode 100644 index 0000000000..b155d2327a --- /dev/null +++ b/component/api/src/main/java/io/meeds/spring/integration/kernel/PortalApplicationContext.java @@ -0,0 +1,249 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.integration.kernel; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Stream; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Component; +import org.springframework.stereotype.Service; + +import org.exoplatform.container.PortalContainer; +import org.exoplatform.container.RootContainer.PortalContainerPostInitTask; +import org.exoplatform.container.spi.ComponentAdapter; + +import jakarta.servlet.ServletContext; + +/** + * A class to let Spring context initialized once the + * {@link PortalContainer}finishes startup to bring all defined Kernel Services + * into Spring Context in order to be able to use annotations to inject Kernel + * services + */ +public class PortalApplicationContext extends AnnotationConfigServletWebServerApplicationContext { + + private static final Logger LOG = LoggerFactory.getLogger(PortalApplicationContext.class); + + private static final Set KERNEL_BEAN_NAMES = new HashSet<>(); + + private static final Map BEAN_NAMES = new HashMap<>(); + + private static final Map BEAN_DEFINITIONS = new HashMap<>(); + + private ServletContext servletContext; + + public PortalApplicationContext(ServletContext servletContext, DefaultListableBeanFactory beanFactory) { + super(beanFactory); + this.servletContext = servletContext; + } + + @Override + protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { + PortalContainer.addInitTask(servletContext, new PortalContainerPostInitTask() { + @Override + public void execute(ServletContext context, PortalContainer portalContainer) { + // Register beans in Spring + BeanDefinitionRegistry beanRegistry = (BeanDefinitionRegistry) beanFactory; + registerKernelComponentsAsBeans(beanRegistry, portalContainer); + registerSharedBeans(beanRegistry); + // Continue startup of Spring + initSpringContext(beanFactory); + // Register Spring Beans in Kernel Container + registerBeansInKernelContainer(portalContainer); + } + + }, "portal"); + } + + @Override + protected void finishRefresh() { + // Override to not be invoked in Context startup time + } + + private void initSpringContext(ConfigurableListableBeanFactory beanFactory) { + long start = System.currentTimeMillis(); + LOG.info("Start Spring context initialization of webapp '{}'", servletContext.getServletContextName()); + PortalApplicationContext.super.finishBeanFactoryInitialization(beanFactory); + PortalApplicationContext.super.finishRefresh(); + LOG.info("Spring context '{}' initialized in {}ms", + servletContext.getServletContextName(), + System.currentTimeMillis() - start); + } + + private void registerBeansInKernelContainer(PortalContainer portalContainer) { + Stream.of(getBeanDefinitionNames()) + .filter(beanName -> portalContainer.getComponentAdapter(beanName) == null) + .filter(beanName -> !KERNEL_BEAN_NAMES.contains(beanName)) + .forEach(beanName -> { + Object bean = getBean(beanName); + BeanDefinition beanDefinition = getBeanDefinition(beanName); + String beanClassName = beanDefinition.getBeanClassName(); + Class beanClass = getBeanClass(portalContainer, beanClassName, bean); + if (beanClass != null) { + LOG.debug("Register Spring Bean '{}' as Kernel service", beanClass.getName()); + portalContainer.registerComponentInstance(beanClass, bean); + BEAN_NAMES.put(beanName, beanClass.getName()); + BEAN_DEFINITIONS.put(beanClass.getName(), beanDefinition); + } + }); + } + + private void registerSharedBeans(BeanDefinitionRegistry beanRegistry) { + BEAN_NAMES.forEach((beanName, beanClassName) -> beanRegistry.registerBeanDefinition(beanName, + BEAN_DEFINITIONS.get(beanClassName))); + } + + @SuppressWarnings({ + "unchecked", "rawtypes" + }) + private List registerKernelComponentsAsBeans(BeanDefinitionRegistry beanRegistry, PortalContainer portalContainer) { + List beanClasses = portalContainer.getComponentAdapters() + .stream() + .filter(adapter -> !BEAN_DEFINITIONS.containsKey(adapter.getComponentKey())) + .map(adapter -> this.beanNameToClass(adapter, portalContainer)) + .filter(Objects::nonNull) + .distinct() + .toList(); + + return beanClasses.stream() + // Avoid having two instances inheriting from the same API + // interface to not get NoUniqueBeanDefinitionException + .filter(c -> { + Class ec = beanClasses.stream() + .filter(oc -> !oc.equals(c) && oc.isAssignableFrom(c)) + .findFirst() + .orElse(null); + if (ec != null || KERNEL_BEAN_NAMES.contains(c.getName())) { + LOG.debug("Kernel Service '{}' is already defined by other Service with Key {}. Registration will be ignored.", + c.getName(), + ec.getName()); + return false; + } else { + KERNEL_BEAN_NAMES.add(c.getName()); + return true; + } + }) + .map(beanClassName -> registerBean(portalContainer, beanRegistry, beanClassName)) + .filter(Objects::nonNull) + .toList(); + } + + private Class registerBean(PortalContainer portalContainer, BeanDefinitionRegistry beanRegistry, Class beanClass) { + RootBeanDefinition beanDefinition = createBeanDefinition(beanClass, portalContainer); + beanRegistry.registerBeanDefinition(beanClass.getName(), beanDefinition); + LOG.debug("Kernel service '{}' injected in Spring context", beanClass.getName()); + return beanClass; + } + + private RootBeanDefinition createBeanDefinition(Class keyClass, PortalContainer portalContainer) { + RootBeanDefinition beanDefinition = new RootBeanDefinition(keyClass, + () -> { + T instance = + portalContainer.getComponentInstanceOfType(keyClass); + LOG.debug("Getting Kernel Service '{}' to inject in Spring context. Found = {}", + keyClass, + instance != null); + return instance; + }); + beanDefinition.setLazyInit(true); + beanDefinition.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); + return beanDefinition; + } + + private Class getBeanClass(PortalContainer portalContainer, + String beanClassName, + Object bean) { + try { + if (StringUtils.isBlank(beanClassName) || bean == null) { + return null; + } + Class beanClass = portalContainer.getPortalClassLoader().loadClass(beanClassName); + if (bean.getClass().isAssignableFrom(beanClass) + && (isBeanClassValid(beanClass) || isBeanClassValid(bean.getClass())) + && !isConfigurationBean(beanClass) + && !isConfigurationBean(bean.getClass()) + && (isServiceBean(beanClass) || isServiceBean(bean.getClass()))) { + if (isServiceBean(bean.getClass())) { + return bean.getClass(); + } else { + return beanClass; + } + } else { + LOG.debug("Ignore registering Spring Bean '{}/{}' as Kernel service", + beanClassName, + bean.getClass().getName()); + return null; + } + } catch (ClassNotFoundException e) { + LOG.debug("Ignore registering Spring Bean '{}' as Kernel service since it's class is not found in shared ClassLoader", + beanClassName); + return null; + } + } + + private boolean isConfigurationBean(Class beanClass) { + return beanClass.isAnnotationPresent(Configuration.class) || beanClass.isAnnotationPresent(SpringBootApplication.class); + } + + private boolean isBeanClassValid(Class beanClass) { + return !StringUtils.contains(beanClass.getName(), "org.springframework") + && !StringUtils.startsWith(beanClass.getName(), "java") + && !StringUtils.startsWith(beanClass.getName(), "jdk") + && !StringUtils.contains(beanClass.getName(), "$"); + } + + private boolean isServiceBean(Class beanClass) { + return beanClass.isAnnotationPresent(Component.class) || beanClass.isAnnotationPresent(Service.class); + } + + @SuppressWarnings("rawtypes") + private Class beanNameToClass(ComponentAdapter adapter, PortalContainer portalContainer) { + Object key = adapter.getComponentKey(); + if (key instanceof Class keyClass) { + return keyClass; + } else { + if (key instanceof String keyString) { + try { + return portalContainer.getPortalClassLoader().loadClass(keyString); + } catch (ClassNotFoundException e) { + return adapter.getComponentImplementation(); + } + } else { + return key.getClass(); + } + } + } + +} \ No newline at end of file diff --git a/component/api/src/main/java/io/meeds/spring/integration/web/configuration/GrantedAuthorityDefaults.java b/component/api/src/main/java/io/meeds/spring/integration/web/configuration/GrantedAuthorityDefaults.java new file mode 100644 index 0000000000..6eb05de606 --- /dev/null +++ b/component/api/src/main/java/io/meeds/spring/integration/web/configuration/GrantedAuthorityDefaults.java @@ -0,0 +1,43 @@ +package io.meeds.spring.integration.web.configuration; + +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.core.Ordered; +import org.springframework.core.PriorityOrdered; +import org.springframework.security.access.annotation.Jsr250MethodSecurityMetadataSource; +import org.springframework.security.access.expression.method.DefaultMethodSecurityExpressionHandler; +import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler; +import org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter; + +@SuppressWarnings("deprecation") +public class GrantedAuthorityDefaults implements BeanPostProcessor, PriorityOrdered { + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + // remove this if you are not using JSR-250 + if (bean instanceof Jsr250MethodSecurityMetadataSource securityBean) { + securityBean.setDefaultRolePrefix(null); + } + if (bean instanceof DefaultMethodSecurityExpressionHandler securityBean) { + securityBean.setDefaultRolePrefix(null); + } + if (bean instanceof DefaultWebSecurityExpressionHandler securityBean) { + securityBean.setDefaultRolePrefix(null); + } + if (bean instanceof SecurityContextHolderAwareRequestFilter securityBean) { + securityBean.setRolePrefix(""); + } + return bean; + } + + @Override + public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { + return bean; + } + + @Override + public int getOrder() { + return Ordered.HIGHEST_PRECEDENCE; + } + +} diff --git a/component/api/src/main/java/io/meeds/spring/integration/web/configuration/WebConfiguration.java b/component/api/src/main/java/io/meeds/spring/integration/web/configuration/WebConfiguration.java new file mode 100644 index 0000000000..10ba2ce1c2 --- /dev/null +++ b/component/api/src/main/java/io/meeds/spring/integration/web/configuration/WebConfiguration.java @@ -0,0 +1,46 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * Copyright (C) 2020 - 2022 Meeds Association contact@meeds.io + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.integration.web.configuration; + +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import io.meeds.spring.integration.web.filter.PortalIdentityFilter; +import io.meeds.spring.integration.web.filter.PortalTransactionFilter; + +@Configuration +public class WebConfiguration { + + @Bean + public FilterRegistrationBean identityFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new PortalIdentityFilter()); + registrationBean.addUrlPatterns("/rest/*"); + registrationBean.setOrder(1); + return registrationBean; + } + + @Bean + public FilterRegistrationBean transactionFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new PortalTransactionFilter()); + registrationBean.addUrlPatterns("/rest/*"); + registrationBean.setOrder(2); + return registrationBean; + } + +} diff --git a/component/api/src/main/java/io/meeds/spring/integration/web/configuration/WebSecurityConfiguration.java b/component/api/src/main/java/io/meeds/spring/integration/web/configuration/WebSecurityConfiguration.java new file mode 100644 index 0000000000..f8dc994b81 --- /dev/null +++ b/component/api/src/main/java/io/meeds/spring/integration/web/configuration/WebSecurityConfiguration.java @@ -0,0 +1,70 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * Copyright (C) 2020 - 2022 Meeds Association contact@meeds.io + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.integration.web.configuration; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; +import org.springframework.security.config.annotation.web.configurers.CsrfConfigurer; +import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.CacheControlConfig; +import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.ContentTypeOptionsConfig; +import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.FrameOptionsConfig; +import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.XXssConfig; +import org.springframework.security.config.annotation.web.configurers.JeeConfigurer; +import org.springframework.security.web.SecurityFilterChain; + +import io.meeds.spring.integration.web.security.PortalAuthenticationManager; + +@Configuration +@EnableWebSecurity +@EnableMethodSecurity(prePostEnabled = true, + securedEnabled = true, + jsr250Enabled = true) +public class WebSecurityConfiguration { + + @Bean + public static GrantedAuthorityDefaults grantedAuthorityDefaults() { + // Reset prefix to be empty. By default it adds "ROLE_" prefix + return new GrantedAuthorityDefaults(); + } + + @SuppressWarnings("removal") + @Bean + public SecurityFilterChain filterChain(HttpSecurity http, PortalAuthenticationManager authenticationProvider) throws Exception { + return http.authenticationProvider(authenticationProvider) + .authorizeHttpRequests(customizer -> { + try { + customizer.anyRequest() + .authenticated() + .and() + .jee(JeeConfigurer::and) + .csrf(CsrfConfigurer::disable) + .headers(headers -> { + headers.cacheControl(CacheControlConfig::disable); + headers.frameOptions(FrameOptionsConfig::disable); + headers.xssProtection(XXssConfig::disable); + headers.contentTypeOptions(ContentTypeOptionsConfig::disable); + }); + } catch (Exception e) { + throw new IllegalStateException("Unable to configure Security Filter Chain", e); + } + }) + .build(); + } + +} diff --git a/component/api/src/main/java/io/meeds/spring/integration/web/filter/PortalIdentityFilter.java b/component/api/src/main/java/io/meeds/spring/integration/web/filter/PortalIdentityFilter.java new file mode 100644 index 0000000000..0db66d9e87 --- /dev/null +++ b/component/api/src/main/java/io/meeds/spring/integration/web/filter/PortalIdentityFilter.java @@ -0,0 +1,30 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package io.meeds.spring.integration.web.filter; + +import org.exoplatform.services.security.web.SetCurrentIdentityFilter; + +/** + * A Web filter to retrieve currently authenticated user Identity to current Web + * context session + */ +public class PortalIdentityFilter extends SetCurrentIdentityFilter { + +} \ No newline at end of file diff --git a/component/api/src/main/java/io/meeds/spring/integration/web/filter/PortalTransactionFilter.java b/component/api/src/main/java/io/meeds/spring/integration/web/filter/PortalTransactionFilter.java new file mode 100644 index 0000000000..5e00e19906 --- /dev/null +++ b/component/api/src/main/java/io/meeds/spring/integration/web/filter/PortalTransactionFilter.java @@ -0,0 +1,95 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +package io.meeds.spring.integration.web.filter; + +import java.io.IOException; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.exoplatform.container.ExoContainerContext; +import org.exoplatform.container.PortalContainer; +import org.exoplatform.container.component.ComponentRequestLifecycle; +import org.exoplatform.container.component.RequestLifeCycle; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; +import org.exoplatform.services.rest.impl.EnvironmentContext; + +import jakarta.servlet.Filter; +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.ServletRequest; +import jakarta.servlet.ServletResponse; +import jakarta.servlet.http.HttpServletRequest; + +/** + * A class to start a Kernel transaction before any call to Spring REST endpoint + */ +public class PortalTransactionFilter implements Filter { + + private static final Log LOG = ExoLogger.getLogger(PortalTransactionFilter.class); + + private PortalContainer container; + + private List transactionalServices; + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + if (container == null) { + initFilter(); + } + ExoContainerContext.setCurrentContainer(container); + RequestLifeCycle.begin(container); + try { + chain.doFilter(request, response); + } finally { + Map results = RequestLifeCycle.end(); + for (Entry entry : results.entrySet()) { + if (entry.getValue() != null) { + LOG.error("An error occurred while calling the method endRequest on " + entry.getKey(), entry.getValue()); + } + } + EnvironmentContext.setCurrent(null); + for (ComponentRequestLifecycle service : transactionalServices) { + if (service.isStarted(container)) { + if (LOG.isDebugEnabled()) { + LOG.debug("The service {} didn't called endRequest, uri = {}, http method = {}. Commit transaction anyway.", + service.getClass().getName(), + ((HttpServletRequest) request).getRequestURI(), + ((HttpServletRequest) request).getMethod()); + } + service.endRequest(container); + if (service.isStarted(container)) { + LOG.error("The service {} didn't ended properly even after calling endRequest", + service.getClass().getName(), + ((HttpServletRequest) request).getRequestURI(), + ((HttpServletRequest) request).getMethod()); + } + } + } + } + } + + private void initFilter() { + container = PortalContainer.getInstance(); + transactionalServices = container.getComponentInstancesOfType(ComponentRequestLifecycle.class); + } + +} \ No newline at end of file diff --git a/component/api/src/main/java/io/meeds/spring/integration/web/security/PortalAuthenticationManager.java b/component/api/src/main/java/io/meeds/spring/integration/web/security/PortalAuthenticationManager.java new file mode 100644 index 0000000000..31569d82aa --- /dev/null +++ b/component/api/src/main/java/io/meeds/spring/integration/web/security/PortalAuthenticationManager.java @@ -0,0 +1,172 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2022 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.integration.web.security; + +import java.security.Principal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.collections4.CollectionUtils; +import org.springframework.security.authentication.AnonymousAuthenticationToken; +import org.springframework.security.authentication.AuthenticationProvider; +import org.springframework.security.authentication.AuthenticationServiceException; +import org.springframework.security.authentication.jaas.JaasGrantedAuthority; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; +import org.springframework.stereotype.Component; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import org.exoplatform.container.ExoContainerContext; +import org.exoplatform.services.security.Authenticator; +import org.exoplatform.services.security.ConversationRegistry; +import org.exoplatform.services.security.ConversationState; +import org.exoplatform.services.security.Identity; +import org.exoplatform.services.security.IdentityConstants; +import org.exoplatform.services.security.IdentityRegistry; +import org.exoplatform.services.security.StateKey; +import org.exoplatform.services.security.web.HttpSessionStateKey; + +import jakarta.servlet.http.HttpServletRequest; + +@Component +public class PortalAuthenticationManager implements AuthenticationProvider { + + @Override + public Authentication authenticate(Authentication authentication) throws AuthenticationException { + ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); + HttpServletRequest request = requestAttributes.getRequest(); + + try { + ConversationState conversationState = getCurrentState(request); + Principal userPrincipal = request.getUserPrincipal(); + if (conversationState == null + || (!conversationState.getIdentity().isMemberOf("/platform/users") + && !conversationState.getIdentity().isMemberOf("/platform/externals"))) { + return new AnonymousAuthenticationToken(IdentityConstants.ANONIM, + authentication.getPrincipal(), + Collections.singletonList(new JaasGrantedAuthority("guests", + userPrincipal))); + } else { + List authorities = getAuthorities(conversationState.getIdentity(), + userPrincipal); + List extendedAuthorities = getExtendedAuthorities(userPrincipal); + if (CollectionUtils.isNotEmpty(extendedAuthorities)) { + authorities = new ArrayList<>(authorities); + authorities.addAll(extendedAuthorities); + } + authentication.setAuthenticated(true); + return new PreAuthenticatedAuthenticationToken(authentication.getPrincipal(), + null, + authorities); + } + } catch (Exception e) { + throw new AuthenticationServiceException("An unknown error is encountered while authenticating user", e); + } + } + + @Override + public boolean supports(Class authentication) { + return true; + } + + public ConversationState getCurrentState(HttpServletRequest httpRequest) throws Exception { + ConversationState state = ConversationState.getCurrent(); + if (state != null) { + return state; + } + + String userId = httpRequest.getRemoteUser(); + // only if user authenticated, otherwise there is no reason to do anythings + if (userId != null) { + StateKey stateKey = new HttpSessionStateKey(httpRequest.getSession()); + + state = getStateBySessionId(userId, stateKey); + if (state == null) { + return buildState(userId, stateKey); + } else { + return state; + } + } else { + return new ConversationState(new Identity(IdentityConstants.ANONIM)); + } + } + + /** + * A method to allow extending attributed roles to user in current portal + * context + * + * @param userPrincipal current user {@link Principal} + * @return {@link List} of {@link GrantedAuthority} to add to current user + */ + protected List getExtendedAuthorities(Principal userPrincipal) { + return Collections.emptyList(); + } + + private List getAuthorities(Identity identity, Principal principal) { + return identity.getRoles() + .stream() + .map(role -> (GrantedAuthority) new JaasGrantedAuthority(role, principal)) + .toList(); + } + + private ConversationState buildState(String userId, StateKey stateKey) throws Exception { + Identity identity = buildIdentity(userId); + if (identity == null) { + return null; + } else { + return buildState(identity, stateKey); + } + } + + private ConversationState buildState(Identity identity, StateKey stateKey) { + ConversationRegistry conversationRegistry = ExoContainerContext.getService(ConversationRegistry.class); + + ConversationState state = new ConversationState(identity); + conversationRegistry.register(stateKey, state); + return state; + } + + private Identity buildIdentity(String userId) throws Exception { + IdentityRegistry identityRegistry = ExoContainerContext.getService(IdentityRegistry.class); + Authenticator authenticator = ExoContainerContext.getService(Authenticator.class); + + Identity identity = identityRegistry.getIdentity(userId); + if (identity == null) { + identity = authenticator.createIdentity(userId); + identityRegistry.register(identity); + } + return identity; + } + + private ConversationState getStateBySessionId(String userId, StateKey stateKey) { + ConversationRegistry conversationRegistry = ExoContainerContext.getService(ConversationRegistry.class); + + ConversationState state = conversationRegistry.getState(stateKey); + if (state != null && !userId.equals(state.getIdentity().getUserId())) { + state = null; + conversationRegistry.unregister(stateKey, false); + } + return state; + } + +} diff --git a/component/api/src/main/java/org/exoplatform/portal/mop/navigation/NavigationData.java b/component/api/src/main/java/org/exoplatform/portal/mop/navigation/NavigationData.java index 13600fc130..fd2f25fd6e 100644 --- a/component/api/src/main/java/org/exoplatform/portal/mop/navigation/NavigationData.java +++ b/component/api/src/main/java/org/exoplatform/portal/mop/navigation/NavigationData.java @@ -21,7 +21,7 @@ import java.io.Serializable; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.gatein.mop.api.workspace.Navigation; import org.exoplatform.portal.mop.SiteKey; diff --git a/component/api/src/main/java/org/exoplatform/portal/mop/navigation/NodeData.java b/component/api/src/main/java/org/exoplatform/portal/mop/navigation/NodeData.java index 84e36244e7..6eb5f8c296 100644 --- a/component/api/src/main/java/org/exoplatform/portal/mop/navigation/NodeData.java +++ b/component/api/src/main/java/org/exoplatform/portal/mop/navigation/NodeData.java @@ -22,7 +22,7 @@ import java.io.Serializable; import java.util.*; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.portal.mop.NodeTarget; import org.exoplatform.portal.mop.SiteKey; diff --git a/component/api/src/main/resources/application.properties b/component/api/src/main/resources/application.properties new file mode 100644 index 0000000000..89cad93a91 --- /dev/null +++ b/component/api/src/main/resources/application.properties @@ -0,0 +1,25 @@ +# +# This file is part of the Meeds project (https://meeds.io/). +# +# Copyright (C) 2023 Meeds Association contact@meeds.io +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 3 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +spring.main.banner-mode=off +logging.level.org.springframework=info +application.buildNumber=${buildNumber} +# Essential to not have collision between Spring Controller DispatcherServlet and Portlet Container Dispatcher +spring.mvc.servlet.path=/rest/ +spring.jpa.open-in-view=false diff --git a/component/application-registry/pom.xml b/component/application-registry/pom.xml index 4ea1b012db..9308d6970e 100644 --- a/component/application-registry/pom.xml +++ b/component/application-registry/pom.xml @@ -29,7 +29,7 @@ jar - 0.52 + 0.49 diff --git a/component/application-registry/src/main/java/org/exoplatform/application/registry/dao/ApplicationDAOImpl.java b/component/application-registry/src/main/java/org/exoplatform/application/registry/dao/ApplicationDAOImpl.java index 71d1038019..c8973c3c62 100644 --- a/component/application-registry/src/main/java/org/exoplatform/application/registry/dao/ApplicationDAOImpl.java +++ b/component/application-registry/src/main/java/org/exoplatform/application/registry/dao/ApplicationDAOImpl.java @@ -1,7 +1,7 @@ package org.exoplatform.application.registry.dao; -import javax.persistence.NoResultException; -import javax.persistence.TypedQuery; +import jakarta.persistence.NoResultException; +import jakarta.persistence.TypedQuery; import org.exoplatform.application.registry.entity.ApplicationEntity; import org.exoplatform.commons.persistence.impl.GenericDAOJPAImpl; diff --git a/component/application-registry/src/main/java/org/exoplatform/application/registry/dao/CategoryDAOImpl.java b/component/application-registry/src/main/java/org/exoplatform/application/registry/dao/CategoryDAOImpl.java index ab5816d1ca..3e3b3f1563 100644 --- a/component/application-registry/src/main/java/org/exoplatform/application/registry/dao/CategoryDAOImpl.java +++ b/component/application-registry/src/main/java/org/exoplatform/application/registry/dao/CategoryDAOImpl.java @@ -1,7 +1,7 @@ package org.exoplatform.application.registry.dao; -import javax.persistence.NoResultException; -import javax.persistence.TypedQuery; +import jakarta.persistence.NoResultException; +import jakarta.persistence.TypedQuery; import org.exoplatform.application.registry.entity.CategoryEntity; import org.exoplatform.commons.persistence.impl.GenericDAOJPAImpl; diff --git a/component/application-registry/src/main/java/org/exoplatform/application/registry/entity/ApplicationEntity.java b/component/application-registry/src/main/java/org/exoplatform/application/registry/entity/ApplicationEntity.java index cc02330cf3..8fda845dc8 100644 --- a/component/application-registry/src/main/java/org/exoplatform/application/registry/entity/ApplicationEntity.java +++ b/component/application-registry/src/main/java/org/exoplatform/application/registry/entity/ApplicationEntity.java @@ -20,7 +20,7 @@ import java.io.Serializable; -import javax.persistence.*; +import jakarta.persistence.*; import org.exoplatform.commons.api.persistence.ExoEntity; diff --git a/component/application-registry/src/main/java/org/exoplatform/application/registry/entity/CategoryEntity.java b/component/application-registry/src/main/java/org/exoplatform/application/registry/entity/CategoryEntity.java index 9a52bc9ee4..b90c0dce29 100644 --- a/component/application-registry/src/main/java/org/exoplatform/application/registry/entity/CategoryEntity.java +++ b/component/application-registry/src/main/java/org/exoplatform/application/registry/entity/CategoryEntity.java @@ -4,7 +4,7 @@ import java.util.HashSet; import java.util.Set; -import javax.persistence.*; +import jakarta.persistence.*; import org.exoplatform.commons.api.persistence.ExoEntity; diff --git a/component/common/pom.xml b/component/common/pom.xml index ace0453376..25e3229f3f 100644 --- a/component/common/pom.xml +++ b/component/common/pom.xml @@ -28,7 +28,7 @@ GateIn Portal Component Common - 0.41 + 0.40 diff --git a/component/common/src/main/java/io/meeds/common/persistence/HibernateCustomIdentifierGeneratorStrategyProvider.java b/component/common/src/main/java/io/meeds/common/persistence/HibernateCustomIdentifierGeneratorStrategyProvider.java new file mode 100644 index 0000000000..593db30031 --- /dev/null +++ b/component/common/src/main/java/io/meeds/common/persistence/HibernateCustomIdentifierGeneratorStrategyProvider.java @@ -0,0 +1,60 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.common.persistence; + +import java.util.Collections; +import java.util.Map; + +import org.hibernate.id.IdentityGenerator; +import org.hibernate.id.enhanced.SequenceStyleGenerator; +import org.hibernate.jpa.spi.IdentifierGeneratorStrategyProvider; + +import org.exoplatform.commons.utils.PropertyManager; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; + +public class HibernateCustomIdentifierGeneratorStrategyProvider implements IdentifierGeneratorStrategyProvider { + + private static final Log LOG = ExoLogger.getLogger(HibernateCustomIdentifierGeneratorStrategyProvider.class); + + private static final boolean IS_MYSQL = + Boolean.parseBoolean(System.getProperty("io.meeds.hibernate.legacy_mysql_sequence_generator", + "true")); + + @Override + public Map> getStrategies() { + if (isMySQLDriver()) { + if (PropertyManager.isDevelopping()) { + LOG.info("Using Old Strategy generator. This might be deleted by hibernate new versions."); + } + return Collections.singletonMap(SequenceStyleGenerator.class.getName(), IdentityGenerator.class); + } else { + return Collections.singletonMap(SequenceStyleGenerator.class.getName(), SequenceStyleGenerator.class); + } + } + + private boolean isMySQLDriver() { + try { + return IS_MYSQL && getClass().getClassLoader().loadClass("com.mysql.cj.jdbc.Driver") != null; + } catch (ClassNotFoundException e) { + return false; + } + } + +} diff --git a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/EntityManagerHolder.java b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/EntityManagerHolder.java index 1ad7129d98..bbffe99868 100644 --- a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/EntityManagerHolder.java +++ b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/EntityManagerHolder.java @@ -23,7 +23,7 @@ import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; -import javax.persistence.EntityManager; +import jakarta.persistence.EntityManager; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; diff --git a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/EntityManagerService.java b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/EntityManagerService.java index d42e0ea257..57ae3d26c2 100644 --- a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/EntityManagerService.java +++ b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/EntityManagerService.java @@ -18,7 +18,7 @@ */ package org.exoplatform.commons.persistence.impl; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.commons.utils.PropertyManager; import org.exoplatform.container.ExoContainer; @@ -28,10 +28,10 @@ import org.hibernate.cfg.AvailableSettings; -import javax.persistence.EntityManager; -import javax.persistence.EntityManagerFactory; -import javax.persistence.EntityTransaction; -import javax.persistence.Persistence; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityManagerFactory; +import jakarta.persistence.EntityTransaction; +import jakarta.persistence.Persistence; import java.lang.reflect.Field; import java.util.ArrayList; @@ -40,7 +40,7 @@ /** * This service is responsible to create a single EntityManagerFactory, with the - * persistence unit name exo-pu. + * persistence unit name meeds-jpa. *

* The service is also bound to use of the RequestLifecycle that there is only * one EntityManager will be created at beginning of the request lifecycle. The @@ -51,7 +51,7 @@ * @version $Revision$ */ public class EntityManagerService implements ComponentRequestLifecycle { - public static final String PERSISTENCE_UNIT_NAME = "exo-pu"; + public static final String PERSISTENCE_UNIT_NAME = "meeds-jpa"; private static final Log LOGGER = ExoLogger.getLogger(EntityManagerService.class); private static final String EXO_JPA_DATASOURCE_NAME = "exo.jpa.datasource.name"; private static final String EXO_PREFIX_FOR_HIB_SETTINGS = "exo.jpa."; diff --git a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/ExoTransactionalAspect.java b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/ExoTransactionalAspect.java index a346743f66..0143d598f9 100644 --- a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/ExoTransactionalAspect.java +++ b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/ExoTransactionalAspect.java @@ -18,9 +18,9 @@ */ package org.exoplatform.commons.persistence.impl; -import javax.persistence.EntityManager; -import javax.persistence.EntityTransaction; -import javax.persistence.PersistenceException; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityTransaction; +import jakarta.persistence.PersistenceException; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; diff --git a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/GenericDAOJPAImpl.java b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/GenericDAOJPAImpl.java index 806ef6872c..abffcc7780 100644 --- a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/GenericDAOJPAImpl.java +++ b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/GenericDAOJPAImpl.java @@ -20,12 +20,12 @@ import java.lang.reflect.ParameterizedType; import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Root; +import jakarta.persistence.EntityManager; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Root; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.hibernate.Session; import org.hibernate.dialect.Dialect; import org.hibernate.internal.SessionFactoryImpl; diff --git a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/LiquibaseDataInitializer.java b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/LiquibaseDataInitializer.java index cdcd2bda73..1e913a2646 100644 --- a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/LiquibaseDataInitializer.java +++ b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/LiquibaseDataInitializer.java @@ -1,12 +1,19 @@ package org.exoplatform.commons.persistence.impl; -import liquibase.Liquibase; -import liquibase.database.Database; -import liquibase.database.DatabaseFactory; -import liquibase.database.jvm.JdbcConnection; -import liquibase.exception.DatabaseException; -import liquibase.exception.LiquibaseException; -import liquibase.resource.ClassLoaderResourceAccessor; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.sql.DataSource; + +import org.apache.commons.lang3.StringUtils; +import org.picocontainer.Startable; + import org.exoplatform.commons.api.persistence.DataInitializer; import org.exoplatform.container.xml.InitParams; import org.exoplatform.container.xml.ValueParam; @@ -14,15 +21,15 @@ import org.exoplatform.services.log.Log; import org.exoplatform.services.naming.InitialContextInitializer; -import org.apache.commons.lang3.StringUtils; -import org.picocontainer.Startable; - -import javax.naming.Context; -import javax.naming.InitialContext; -import javax.naming.NamingException; -import javax.sql.DataSource; -import java.sql.SQLException; -import java.util.*; +import liquibase.Liquibase; +import liquibase.Scope; +import liquibase.database.Database; +import liquibase.database.DatabaseFactory; +import liquibase.database.jvm.JdbcConnection; +import liquibase.exception.DatabaseException; +import liquibase.exception.LiquibaseException; +import liquibase.resource.ClassLoaderResourceAccessor; +import liquibase.ui.LoggerUIService; /** * Startable service to initialize all the data with Liquibase. @@ -67,6 +74,11 @@ public LiquibaseDataInitializer(InitParams initParams) { } LOG.info("LiquibaseDataInitializer created with : datasourceName={}, contexts={}", datasourceName, liquibaseContexts); + try { + Scope.enter(Map.of(Scope.Attr.ui.name(), new LoggerUIService())); + } catch (Exception e) { + LOG.debug("Error Switching Liquibase logging service"); + } } public String getDatasourceName() { diff --git a/component/common/src/main/java/org/exoplatform/settings/jpa/dao/SettingContextDAO.java b/component/common/src/main/java/org/exoplatform/settings/jpa/dao/SettingContextDAO.java index 29219d8bed..37909a5bfd 100644 --- a/component/common/src/main/java/org/exoplatform/settings/jpa/dao/SettingContextDAO.java +++ b/component/common/src/main/java/org/exoplatform/settings/jpa/dao/SettingContextDAO.java @@ -7,9 +7,9 @@ import org.exoplatform.services.log.Log; import org.exoplatform.settings.jpa.entity.ContextEntity; -import javax.persistence.NoResultException; -import javax.persistence.NonUniqueResultException; -import javax.persistence.TypedQuery; +import jakarta.persistence.NoResultException; +import jakarta.persistence.NonUniqueResultException; +import jakarta.persistence.TypedQuery; import java.util.List; public class SettingContextDAO extends GenericDAOJPAImpl implements org.exoplatform.settings.jpa.SettingContextDAO { diff --git a/component/common/src/main/java/org/exoplatform/settings/jpa/dao/SettingScopeDAO.java b/component/common/src/main/java/org/exoplatform/settings/jpa/dao/SettingScopeDAO.java index 90a457865f..c7d5bb34a3 100644 --- a/component/common/src/main/java/org/exoplatform/settings/jpa/dao/SettingScopeDAO.java +++ b/component/common/src/main/java/org/exoplatform/settings/jpa/dao/SettingScopeDAO.java @@ -2,8 +2,8 @@ import java.util.List; -import javax.persistence.NoResultException; -import javax.persistence.TypedQuery; +import jakarta.persistence.NoResultException; +import jakarta.persistence.TypedQuery; import org.apache.commons.lang3.StringUtils; diff --git a/component/common/src/main/java/org/exoplatform/settings/jpa/dao/SettingsDAO.java b/component/common/src/main/java/org/exoplatform/settings/jpa/dao/SettingsDAO.java index e28922d63b..885c67c60f 100644 --- a/component/common/src/main/java/org/exoplatform/settings/jpa/dao/SettingsDAO.java +++ b/component/common/src/main/java/org/exoplatform/settings/jpa/dao/SettingsDAO.java @@ -7,9 +7,9 @@ import org.exoplatform.services.log.Log; import org.exoplatform.settings.jpa.entity.SettingsEntity; -import javax.persistence.NoResultException; -import javax.persistence.NonUniqueResultException; -import javax.persistence.TypedQuery; +import jakarta.persistence.NoResultException; +import jakarta.persistence.NonUniqueResultException; +import jakarta.persistence.TypedQuery; import java.util.List; /** diff --git a/component/common/src/main/java/org/exoplatform/settings/jpa/entity/ContextEntity.java b/component/common/src/main/java/org/exoplatform/settings/jpa/entity/ContextEntity.java index ee50d13480..bc66cf5b69 100644 --- a/component/common/src/main/java/org/exoplatform/settings/jpa/entity/ContextEntity.java +++ b/component/common/src/main/java/org/exoplatform/settings/jpa/entity/ContextEntity.java @@ -2,7 +2,7 @@ import org.exoplatform.commons.api.persistence.ExoEntity; -import javax.persistence.*; +import jakarta.persistence.*; import java.util.Set; /** diff --git a/component/common/src/main/java/org/exoplatform/settings/jpa/entity/ScopeEntity.java b/component/common/src/main/java/org/exoplatform/settings/jpa/entity/ScopeEntity.java index 450192d007..6065a2cd33 100644 --- a/component/common/src/main/java/org/exoplatform/settings/jpa/entity/ScopeEntity.java +++ b/component/common/src/main/java/org/exoplatform/settings/jpa/entity/ScopeEntity.java @@ -2,7 +2,7 @@ import org.exoplatform.commons.api.persistence.ExoEntity; -import javax.persistence.*; +import jakarta.persistence.*; import java.util.Set; /** diff --git a/component/common/src/main/java/org/exoplatform/settings/jpa/entity/SettingsEntity.java b/component/common/src/main/java/org/exoplatform/settings/jpa/entity/SettingsEntity.java index fa8df8851e..c4fef126cf 100644 --- a/component/common/src/main/java/org/exoplatform/settings/jpa/entity/SettingsEntity.java +++ b/component/common/src/main/java/org/exoplatform/settings/jpa/entity/SettingsEntity.java @@ -18,7 +18,7 @@ import org.exoplatform.commons.api.persistence.ExoEntity; -import javax.persistence.*; +import jakarta.persistence.*; /** * Created by The eXo Platform SAS diff --git a/component/common/src/main/resources/META-INF/persistence.xml b/component/common/src/main/resources/META-INF/persistence.xml index 757a43578d..326587d14d 100644 --- a/component/common/src/main/resources/META-INF/persistence.xml +++ b/component/common/src/main/resources/META-INF/persistence.xml @@ -2,12 +2,13 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd" version="2.2"> - + org.hibernate.jpa.HibernatePersistenceProvider java:/comp/env/exo-jpa_portal false + NONE - + @@ -15,6 +16,7 @@ + \ No newline at end of file diff --git a/component/common/src/test/java/org/exoplatform/jpa/mock/MockEntityManagerService.java b/component/common/src/test/java/org/exoplatform/jpa/mock/MockEntityManagerService.java index ecd3fd6878..cb3c809b80 100644 --- a/component/common/src/test/java/org/exoplatform/jpa/mock/MockEntityManagerService.java +++ b/component/common/src/test/java/org/exoplatform/jpa/mock/MockEntityManagerService.java @@ -17,7 +17,7 @@ import java.util.Properties; -import javax.persistence.EntityManagerFactory; +import jakarta.persistence.EntityManagerFactory; import org.exoplatform.commons.persistence.impl.EntityManagerService; @@ -41,6 +41,6 @@ public MockEntityManagerService() { @Override public String getPersistenceUnitName() { - return "test"; + return "meeds-jpa-test"; } } diff --git a/component/common/src/test/resources/conf/configuration.properties b/component/common/src/test/resources/conf/configuration.properties index 1724795c61..e69de29bb2 100644 --- a/component/common/src/test/resources/conf/configuration.properties +++ b/component/common/src/test/resources/conf/configuration.properties @@ -1 +0,0 @@ - exo.jpa.hibernate.dialect=org.hibernate.dialect.HSQLDialect \ No newline at end of file diff --git a/component/file-storage/pom.xml b/component/file-storage/pom.xml index 97ca02f833..46ea367806 100644 --- a/component/file-storage/pom.xml +++ b/component/file-storage/pom.xml @@ -27,7 +27,7 @@ jar GateIn Portal Component File Storage - 0.53 + 0.51 diff --git a/component/file-storage/src/main/java/org/exoplatform/commons/file/resource/FileSystemResourceProvider.java b/component/file-storage/src/main/java/org/exoplatform/commons/file/resource/FileSystemResourceProvider.java index 3b1d25e46e..79e91e88e8 100644 --- a/component/file-storage/src/main/java/org/exoplatform/commons/file/resource/FileSystemResourceProvider.java +++ b/component/file-storage/src/main/java/org/exoplatform/commons/file/resource/FileSystemResourceProvider.java @@ -18,7 +18,7 @@ */ package org.exoplatform.commons.file.resource; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.exoplatform.commons.file.model.FileInfo; diff --git a/component/file-storage/src/main/java/org/exoplatform/commons/file/services/impl/FileServiceImpl.java b/component/file-storage/src/main/java/org/exoplatform/commons/file/services/impl/FileServiceImpl.java index 053ff234bf..b1a41efd23 100644 --- a/component/file-storage/src/main/java/org/exoplatform/commons/file/services/impl/FileServiceImpl.java +++ b/component/file-storage/src/main/java/org/exoplatform/commons/file/services/impl/FileServiceImpl.java @@ -1,6 +1,6 @@ package org.exoplatform.commons.file.services.impl; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.commons.api.persistence.ExoTransactional; import org.exoplatform.commons.file.model.NameSpace; diff --git a/component/file-storage/src/main/java/org/exoplatform/commons/file/services/impl/NameSpaceServiceImpl.java b/component/file-storage/src/main/java/org/exoplatform/commons/file/services/impl/NameSpaceServiceImpl.java index 2296357332..dfa7448539 100644 --- a/component/file-storage/src/main/java/org/exoplatform/commons/file/services/impl/NameSpaceServiceImpl.java +++ b/component/file-storage/src/main/java/org/exoplatform/commons/file/services/impl/NameSpaceServiceImpl.java @@ -18,7 +18,7 @@ */ package org.exoplatform.commons.file.services.impl; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.commons.api.persistence.DataInitializer; import org.exoplatform.commons.api.persistence.ExoTransactional; import org.exoplatform.commons.file.model.NameSpace; diff --git a/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/dao/impl/FileBinaryDAOImpl.java b/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/dao/impl/FileBinaryDAOImpl.java index 7d718b1083..f3f7fb2520 100644 --- a/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/dao/impl/FileBinaryDAOImpl.java +++ b/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/dao/impl/FileBinaryDAOImpl.java @@ -4,8 +4,8 @@ import org.exoplatform.commons.file.storage.entity.FileBinaryEntity; import org.exoplatform.commons.persistence.impl.GenericDAOJPAImpl; -import javax.persistence.NoResultException; -import javax.persistence.TypedQuery; +import jakarta.persistence.NoResultException; +import jakarta.persistence.TypedQuery; /** * Data Access Object layer for binary data files. diff --git a/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/dao/impl/FileInfoDAOImpl.java b/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/dao/impl/FileInfoDAOImpl.java index c22a14f61a..fbb1d5d9cd 100644 --- a/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/dao/impl/FileInfoDAOImpl.java +++ b/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/dao/impl/FileInfoDAOImpl.java @@ -23,7 +23,7 @@ import org.exoplatform.commons.persistence.impl.GenericDAOJPAImpl; import org.exoplatform.services.log.ExoLogger; -import javax.persistence.TypedQuery; +import jakarta.persistence.TypedQuery; import java.util.Date; import java.util.List; diff --git a/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/dao/impl/NameSpaceDAOImpl.java b/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/dao/impl/NameSpaceDAOImpl.java index 3f5211a3f2..ccf4a63213 100644 --- a/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/dao/impl/NameSpaceDAOImpl.java +++ b/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/dao/impl/NameSpaceDAOImpl.java @@ -26,8 +26,8 @@ import java.util.List; -import javax.persistence.NoResultException; -import javax.persistence.TypedQuery; +import jakarta.persistence.NoResultException; +import jakarta.persistence.TypedQuery; /** * Created by The eXo Platform SAS Author : eXoPlatform exo@exoplatform.com diff --git a/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/dao/impl/OrphanFileDAOImpl.java b/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/dao/impl/OrphanFileDAOImpl.java index 364701625a..4bbe6fcac5 100644 --- a/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/dao/impl/OrphanFileDAOImpl.java +++ b/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/dao/impl/OrphanFileDAOImpl.java @@ -23,8 +23,8 @@ import org.exoplatform.commons.persistence.impl.GenericDAOJPAImpl; import org.exoplatform.services.log.ExoLogger; -import javax.persistence.NoResultException; -import javax.persistence.TypedQuery; +import jakarta.persistence.NoResultException; +import jakarta.persistence.TypedQuery; import java.util.Date; import java.util.List; diff --git a/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/entity/FileBinaryEntity.java b/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/entity/FileBinaryEntity.java index aba7ef77db..5a1b2c1573 100644 --- a/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/entity/FileBinaryEntity.java +++ b/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/entity/FileBinaryEntity.java @@ -2,7 +2,7 @@ import org.exoplatform.commons.api.persistence.ExoEntity; -import javax.persistence.*; +import jakarta.persistence.*; import java.util.Date; /** diff --git a/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/entity/FileInfoEntity.java b/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/entity/FileInfoEntity.java index f55946f316..8443036ff3 100644 --- a/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/entity/FileInfoEntity.java +++ b/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/entity/FileInfoEntity.java @@ -2,7 +2,7 @@ import org.exoplatform.commons.api.persistence.ExoEntity; -import javax.persistence.*; +import jakarta.persistence.*; import java.util.Date; /** diff --git a/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/entity/NameSpaceEntity.java b/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/entity/NameSpaceEntity.java index 4502098255..92ccdeb255 100644 --- a/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/entity/NameSpaceEntity.java +++ b/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/entity/NameSpaceEntity.java @@ -20,7 +20,7 @@ import org.exoplatform.commons.api.persistence.ExoEntity; -import javax.persistence.*; +import jakarta.persistence.*; import java.util.List; /** diff --git a/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/entity/OrphanFileEntity.java b/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/entity/OrphanFileEntity.java index a50892a6bd..3ea94ca672 100644 --- a/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/entity/OrphanFileEntity.java +++ b/component/file-storage/src/main/java/org/exoplatform/commons/file/storage/entity/OrphanFileEntity.java @@ -20,7 +20,7 @@ import org.exoplatform.commons.api.persistence.ExoEntity; -import javax.persistence.*; +import jakarta.persistence.*; import java.util.Date; /** diff --git a/component/file-storage/src/test/java/org/exoplatform/commons/file/services/FileServiceImplTest.java b/component/file-storage/src/test/java/org/exoplatform/commons/file/services/FileServiceImplTest.java index fbfc2ba10f..e4dd1d00eb 100644 --- a/component/file-storage/src/test/java/org/exoplatform/commons/file/services/FileServiceImplTest.java +++ b/component/file-storage/src/test/java/org/exoplatform/commons/file/services/FileServiceImplTest.java @@ -25,8 +25,8 @@ import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; -import javax.persistence.EntityManager; -import javax.persistence.EntityTransaction; +import jakarta.persistence.EntityManager; +import jakarta.persistence.EntityTransaction; import java.io.ByteArrayInputStream; import java.util.Date; diff --git a/component/file-storage/src/test/resources/conf/standalone/test-configuration.xml b/component/file-storage/src/test/resources/conf/standalone/test-configuration.xml deleted file mode 100644 index 341ed62792..0000000000 --- a/component/file-storage/src/test/resources/conf/standalone/test-configuration.xml +++ /dev/null @@ -1,43 +0,0 @@ - - - - - - org.exoplatform.commons.file.services.NameSpaceService - - WikiNameSpacePlugin - addNameSpacePlugin - org.exoplatform.commons.file.services.NameSpacePlugin - - - fileNameSpace.params - - - - - - - - \ No newline at end of file diff --git a/component/identity/pom.xml b/component/identity/pom.xml index 046f9e42e7..3e053d2686 100644 --- a/component/identity/pom.xml +++ b/component/identity/pom.xml @@ -26,12 +26,12 @@ 4.0.0 exo.portal.component.identity jar - GateIn Portal Component JBoss IDM integration + GateIn Portal Component IDM 1.0.0 3.2.76 - 0.39 + 0.40 @@ -70,17 +70,6 @@ test-jar - - org.exoplatform.core - exo.core.component.organization.tests - test - - - org.exoplatform.core - exo.core.component.organization.tests - test-sources - test - sun-opends OpenDS @@ -149,69 +138,5 @@ - - - - com.jcabi - jcabi-maven-plugin - - - org.apache.maven.plugins - maven-dependency-plugin - - - unpack - generate-test-sources - - unpack - - - - - org.exoplatform.core - exo.core.component.organization.tests - test-sources - jar - false - - - ${project.build.directory}/org-service-tck-tests - - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - add-test-resource - generate-test-sources - - add-test-resource - - - - - ${project.build.directory}/org-service-tck-tests - - - - - - add-test-source - generate-test-sources - - add-test-source - - - - ${project.build.directory}/org-service-tck-tests - - - - - - diff --git a/component/identity/src/main/java/org/exoplatform/services/organization/auth/OrganizationAuthenticatorImpl.java b/component/identity/src/main/java/org/exoplatform/services/organization/auth/OrganizationAuthenticatorImpl.java index 849702870e..590041349b 100644 --- a/component/identity/src/main/java/org/exoplatform/services/organization/auth/OrganizationAuthenticatorImpl.java +++ b/component/identity/src/main/java/org/exoplatform/services/organization/auth/OrganizationAuthenticatorImpl.java @@ -18,7 +18,7 @@ */ package org.exoplatform.services.organization.auth; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.container.component.ComponentRequestLifecycle; import org.exoplatform.container.component.RequestLifeCycle; diff --git a/component/identity/src/main/java/org/exoplatform/services/organization/idm/AbstractDAOImpl.java b/component/identity/src/main/java/org/exoplatform/services/organization/idm/AbstractDAOImpl.java index d84cfa4cf9..c21ef091fc 100644 --- a/component/identity/src/main/java/org/exoplatform/services/organization/idm/AbstractDAOImpl.java +++ b/component/identity/src/main/java/org/exoplatform/services/organization/idm/AbstractDAOImpl.java @@ -56,17 +56,17 @@ public void handleException(String messageToLog, Exception e) { log.warn("Unable to set Transaction status to be rollback only", tre); } } else { - orgService.recoverFromIDMError(e); + orgService.recoverFromIDMError(); } + // Always throw the original exception to make sure that top layer services + // are triggered about the error + throw new IllegalStateException(messageToLog, e); + } catch (IllegalStateException e1) { + throw e1; } catch (Exception e1) { - // If an error happens when rollbacking the transaction, display the - // original error and after that the erro about rollbacking - log.warn(messageToLog, e); - log.warn("Error while rollbacking IDM Transaction due to previous error", e1); + log.warn("Second Exception when rolling back original error {}/{}", messageToLog, e.getMessage(), e1); + throw new IllegalStateException(messageToLog, e); } - // Always throw the original exception to make sure that top layer services - // are triggered about the error - throw new RuntimeException(messageToLog, e); } protected IdentitySession getIdentitySession() throws Exception { diff --git a/component/identity/src/main/java/org/exoplatform/services/organization/idm/CustomHibernateServiceImpl.java b/component/identity/src/main/java/org/exoplatform/services/organization/idm/CustomHibernateServiceImpl.java index a4e7e4cef1..7223444c4a 100644 --- a/component/identity/src/main/java/org/exoplatform/services/organization/idm/CustomHibernateServiceImpl.java +++ b/component/identity/src/main/java/org/exoplatform/services/organization/idm/CustomHibernateServiceImpl.java @@ -141,7 +141,7 @@ final public void closeSession() { } public Object findExactOne(Session session, String query, String id) throws Exception { - Object res = session.createQuery(query).setString("id", id).uniqueResult(); + Object res = session.createQuery(query).setParameter("id", id).uniqueResult(); if (res == null) { throw new ObjectNotFoundException("Cannot find the object with id: " + id); } @@ -149,7 +149,7 @@ public Object findExactOne(Session session, String query, String id) throws Exce } public Object findOne(Session session, String query, String id) throws Exception { - List l = session.createQuery(query).setString("id", id).list(); + List l = session.createQuery(query).setParameter("id", id).list(); if (l.size() == 0) { return null; } else if (l.size() > 1) { @@ -244,4 +244,5 @@ public boolean isStarted(ExoContainer container) { Session s = threadLocal_.get(); return s != null && s.isOpen(); } + } diff --git a/component/identity/src/main/java/org/exoplatform/services/organization/idm/DisabledUserMigrationScript.java b/component/identity/src/main/java/org/exoplatform/services/organization/idm/DisabledUserMigrationScript.java index 6e9b8b974f..0033a55400 100644 --- a/component/identity/src/main/java/org/exoplatform/services/organization/idm/DisabledUserMigrationScript.java +++ b/component/identity/src/main/java/org/exoplatform/services/organization/idm/DisabledUserMigrationScript.java @@ -150,7 +150,6 @@ private void setupHibernate(Properties config) throws Exception { conf_.setProperty("hibernate.connection.url", config.getProperty("hibernate.connection.url")); conf_.setProperty("hibernate.connection.username", config.getProperty("hibernate.connection.username")); conf_.setProperty("hibernate.connection.password", config.getProperty("hibernate.connection.password")); - conf_.setProperty("hibernate.dialect", config.getProperty("hibernate.dialect")); String config_path = config.getProperty("hibernate.config_path"); URL url = Thread.currentThread().getContextClassLoader().getResource(config_path); diff --git a/component/identity/src/main/java/org/exoplatform/services/organization/idm/IDMMembershipListAccess.java b/component/identity/src/main/java/org/exoplatform/services/organization/idm/IDMMembershipListAccess.java index 0a360e11ed..83904d36e0 100644 --- a/component/identity/src/main/java/org/exoplatform/services/organization/idm/IDMMembershipListAccess.java +++ b/component/identity/src/main/java/org/exoplatform/services/organization/idm/IDMMembershipListAccess.java @@ -22,7 +22,7 @@ import java.io.Serializable; import java.util.*; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import org.exoplatform.services.log.LogLevel; diff --git a/component/identity/src/main/java/org/exoplatform/services/organization/idm/IDMUserListAccess.java b/component/identity/src/main/java/org/exoplatform/services/organization/idm/IDMUserListAccess.java index 5d18b9cf46..67a3d93ecb 100644 --- a/component/identity/src/main/java/org/exoplatform/services/organization/idm/IDMUserListAccess.java +++ b/component/identity/src/main/java/org/exoplatform/services/organization/idm/IDMUserListAccess.java @@ -26,7 +26,7 @@ import java.util.List; import java.util.Set; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import org.exoplatform.services.log.LogLevel; diff --git a/component/identity/src/main/java/org/exoplatform/services/organization/idm/PicketLinkIDMOrganizationServiceImpl.java b/component/identity/src/main/java/org/exoplatform/services/organization/idm/PicketLinkIDMOrganizationServiceImpl.java index de904b32c0..3431f815c6 100644 --- a/component/identity/src/main/java/org/exoplatform/services/organization/idm/PicketLinkIDMOrganizationServiceImpl.java +++ b/component/identity/src/main/java/org/exoplatform/services/organization/idm/PicketLinkIDMOrganizationServiceImpl.java @@ -125,22 +125,18 @@ public final org.picketlink.idm.api.Group getJBIDMGroup(String groupId) throws E @Override public void start() { - try { - if (configuration.isUseJTA()) { - jtaTransactionLifecycleService.registerListener(new IDMTransactionSyncListener(idmService_)); - } - acceptComponentRequestCall = true; - - RequestLifeCycle.begin(this); - - super.start(); - - } catch (Exception e) { - log.error(e.getMessage(), e); - } finally { - RequestLifeCycle.end(); - } - + if (configuration.isUseJTA()) { + jtaTransactionLifecycleService.registerListener(new IDMTransactionSyncListener(idmService_)); + } + acceptComponentRequestCall = true; + RequestLifeCycle.begin(this); + try { + super.start(); + } catch (Exception e) { + log.error("Error Starting IDM Service", e); + } finally { + RequestLifeCycle.end(); + } } @Override @@ -201,8 +197,8 @@ public void flush() { idmService_.getIdentitySession().save(); } } catch (Exception e) { - log.error(e.getMessage(), e); - recoverFromIDMError(e); + log.error("Error while saving transaction", e); + recoverFromIDMError(); } } } @@ -228,7 +224,7 @@ public void endRequest(ExoContainer container) { } } catch (Exception e) { log.error(e.getMessage(), e); - recoverFromIDMError(e); + recoverFromIDMError(); } } } @@ -256,31 +252,27 @@ public boolean isStarted(ExoContainer container) { * Recover from an IDM error * Should be used only for non-JTA environment. */ - public void recoverFromIDMError(Exception e) { + public void recoverFromIDMError() { try { // We need to restart Hibernate transaction if it's available. First rollback old one and then start new one Transaction idmTransaction = idmService_.getIdentitySession().getTransaction(); if (idmTransaction != null && idmTransaction.isActive()) { try { - log.warn("IDM Transaction rollback", e); idmTransaction.rollback(); - log.info("IDM Transaction has been rolled-backed"); + log.warn("IDM Transaction has been rolled-backed"); } catch (Exception e1) { log.warn("Error during IDM Transaction rollback.", e1); } try { - log.info("IDM Transaction restart"); idmTransaction.start(); - log.info("IDM Transaction restarted"); + log.warn("IDM Transaction restarted"); } catch (Exception e1) { log.warn("Error during IDM Transaction restart, a new transaction will be started", e1); idmService_.getIdentitySession().beginTransaction(); - log.info("New IDM Transaction started"); } } else { - log.warn("IDM Transaction rollbacked. New IDM Transaction start", e); idmService_.getIdentitySession().beginTransaction(); - log.info("New IDM Transaction started"); + log.warn("New IDM Transaction started"); } } catch (Exception e1) { log.warn("Error during recovery of old error", e1); diff --git a/component/identity/src/main/java/org/exoplatform/services/organization/idm/cache/CacheableGroupHandlerImpl.java b/component/identity/src/main/java/org/exoplatform/services/organization/idm/cache/CacheableGroupHandlerImpl.java index dd113a83ee..5befc4b21c 100644 --- a/component/identity/src/main/java/org/exoplatform/services/organization/idm/cache/CacheableGroupHandlerImpl.java +++ b/component/identity/src/main/java/org/exoplatform/services/organization/idm/cache/CacheableGroupHandlerImpl.java @@ -20,7 +20,7 @@ import java.util.Collection; import java.util.List; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.commons.cache.future.FutureExoCache; import org.exoplatform.commons.cache.future.Loader; diff --git a/component/identity/src/main/java/org/exoplatform/services/organization/idm/cache/CacheableUserProfileHandlerImpl.java b/component/identity/src/main/java/org/exoplatform/services/organization/idm/cache/CacheableUserProfileHandlerImpl.java index 4018193577..39b6823fd4 100644 --- a/component/identity/src/main/java/org/exoplatform/services/organization/idm/cache/CacheableUserProfileHandlerImpl.java +++ b/component/identity/src/main/java/org/exoplatform/services/organization/idm/cache/CacheableUserProfileHandlerImpl.java @@ -18,7 +18,7 @@ import java.util.Collection; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.commons.cache.future.FutureExoCache; import org.exoplatform.commons.cache.future.Loader; diff --git a/component/identity/src/main/java/org/exoplatform/services/organization/idm/externalstore/jpa/IDMQueueEntity.java b/component/identity/src/main/java/org/exoplatform/services/organization/idm/externalstore/jpa/IDMQueueEntity.java index 221e7b547d..1fb5c527da 100644 --- a/component/identity/src/main/java/org/exoplatform/services/organization/idm/externalstore/jpa/IDMQueueEntity.java +++ b/component/identity/src/main/java/org/exoplatform/services/organization/idm/externalstore/jpa/IDMQueueEntity.java @@ -3,7 +3,7 @@ import java.io.Serializable; import java.util.Calendar; -import javax.persistence.*; +import jakarta.persistence.*; import org.exoplatform.commons.api.persistence.ExoEntity; import org.exoplatform.services.organization.externalstore.model.IDMOperationType; diff --git a/component/identity/src/main/java/org/exoplatform/services/organization/search/GroupSearchServiceImpl.java b/component/identity/src/main/java/org/exoplatform/services/organization/search/GroupSearchServiceImpl.java index 5264e3fb1b..3485452b25 100644 --- a/component/identity/src/main/java/org/exoplatform/services/organization/search/GroupSearchServiceImpl.java +++ b/component/identity/src/main/java/org/exoplatform/services/organization/search/GroupSearchServiceImpl.java @@ -1,6 +1,6 @@ package org.exoplatform.services.organization.search; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.commons.utils.ListAccess; import org.exoplatform.services.organization.Group; diff --git a/component/identity/src/main/java/org/exoplatform/services/organization/search/UserSearchServiceImpl.java b/component/identity/src/main/java/org/exoplatform/services/organization/search/UserSearchServiceImpl.java index 32fe54eae5..8261d2fa3c 100644 --- a/component/identity/src/main/java/org/exoplatform/services/organization/search/UserSearchServiceImpl.java +++ b/component/identity/src/main/java/org/exoplatform/services/organization/search/UserSearchServiceImpl.java @@ -1,6 +1,6 @@ package org.exoplatform.services.organization.search; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.commons.utils.ListAccess; import org.exoplatform.services.organization.*; diff --git a/component/identity/src/main/java/org/gatein/portal/idm/impl/repository/ExoFallbackIdentityStoreRepository.java b/component/identity/src/main/java/org/gatein/portal/idm/impl/repository/ExoFallbackIdentityStoreRepository.java index e882f3b8e7..e91a853fe0 100644 --- a/component/identity/src/main/java/org/gatein/portal/idm/impl/repository/ExoFallbackIdentityStoreRepository.java +++ b/component/identity/src/main/java/org/gatein/portal/idm/impl/repository/ExoFallbackIdentityStoreRepository.java @@ -2,7 +2,7 @@ import java.util.*; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.picketlink.idm.common.exception.IdentityException; import org.picketlink.idm.impl.repository.AbstractIdentityStoreRepository; import org.picketlink.idm.impl.store.SimpleIdentityStoreInvocationContext; diff --git a/component/identity/src/main/java/org/gatein/portal/idm/impl/store/hibernate/ExoHibernateIdentityStoreImpl.java b/component/identity/src/main/java/org/gatein/portal/idm/impl/store/hibernate/ExoHibernateIdentityStoreImpl.java index 2737d9a8ef..38dc9ced28 100644 --- a/component/identity/src/main/java/org/gatein/portal/idm/impl/store/hibernate/ExoHibernateIdentityStoreImpl.java +++ b/component/identity/src/main/java/org/gatein/portal/idm/impl/store/hibernate/ExoHibernateIdentityStoreImpl.java @@ -3,10 +3,10 @@ import java.io.Serializable; import java.util.logging.Level; import java.util.logging.Logger; + import org.gatein.portal.idm.IdentityStoreSource; import org.hibernate.HibernateException; import org.hibernate.Session; -import org.hibernate.criterion.Restrictions; import org.picketlink.idm.common.exception.IdentityException; import org.picketlink.idm.impl.api.PasswordCredential; import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObject; diff --git a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObject.java b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObject.java index 624570bb45..c8ffd0f6b8 100644 --- a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObject.java +++ b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObject.java @@ -22,105 +22,120 @@ package org.picketlink.idm.impl.model.hibernate; -import java.util.*; - -import javax.persistence.*; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; -import org.hibernate.annotations.LazyCollection; -import org.hibernate.annotations.LazyCollectionOption; import org.picketlink.idm.common.exception.PolicyValidationException; import org.picketlink.idm.spi.model.IdentityObject; import org.picketlink.idm.spi.model.IdentityObjectCredentialType; +import jakarta.persistence.CascadeType; +import jakarta.persistence.CollectionTable; +import jakarta.persistence.Column; +import jakarta.persistence.ElementCollection; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapKeyColumn; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.OneToMany; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import lombok.Data; + @Entity(name = "HibernateIdentityObject") @Table(name = "jbid_io") -@NamedQueries( - { - @NamedQuery( - name = "HibernateIdentityObject.findIdentityObjectByNameAndTypeIgnoreCase", - query = "SELECT o FROM HibernateIdentityObject o WHERE o.realm.name = :realmName AND lower(o.name) = :name AND o.identityType.name = :typeName" - ), - @NamedQuery( - name = "HibernateIdentityObject.findIdentityObjectByNameAndType", - query = "SELECT o FROM HibernateIdentityObject o WHERE o.realm.name = :realmName AND o.name = :name AND o.identityType.name = :typeName" - ), - @NamedQuery( - name = "HibernateIdentityObject.countIdentityObjectByNameAndTypeIgnoreCase", - query = "SELECT count(o) FROM HibernateIdentityObject o WHERE o.realm.name = :realmName AND lower(o.name) = :name AND o.identityType.name = :typeName" - ), - @NamedQuery( - name = "HibernateIdentityObject.countIdentityObjectByNameAndType", - query = "SELECT count(o) FROM HibernateIdentityObject o" - + " WHERE o.realm.name = :realmName" - + " AND o.name = :name" - + " AND o.identityType.name = :typeName" - ), - @NamedQuery( - name = "HibernateIdentityObject.countIdentityObjectsByType", - query = "SELECT count(o.id) FROM HibernateIdentityObject o" - + " WHERE o.realm.name = :realmName" - + " AND o.identityType.name = :typeName" - ), - } -) +@NamedQuery( + name = "HibernateIdentityObject.findIdentityObjectByNameAndTypeIgnoreCase", + query = "SELECT o FROM HibernateIdentityObject o WHERE o.realm.name = :realmName AND lower(o.name) = :name AND o.identityType.name = :typeName") +@NamedQuery( + name = "HibernateIdentityObject.findIdentityObjectByNameAndType", + query = "SELECT o FROM HibernateIdentityObject o WHERE o.realm.name = :realmName AND o.name = :name AND o.identityType.name = :typeName") +@NamedQuery( + name = "HibernateIdentityObject.countIdentityObjectByNameAndTypeIgnoreCase", + query = "SELECT count(o) FROM HibernateIdentityObject o WHERE o.realm.name = :realmName AND lower(o.name) = :name AND o.identityType.name = :typeName") +@NamedQuery( + name = "HibernateIdentityObject.countIdentityObjectByNameAndType", + query = "SELECT count(o) FROM HibernateIdentityObject o" + " WHERE o.realm.name = :realmName" + + " AND o.name = :name" + " AND o.identityType.name = :typeName") +@NamedQuery( + name = "HibernateIdentityObject.countIdentityObjectsByType", + query = "SELECT count(o.id) FROM HibernateIdentityObject o" + " WHERE o.realm.name = :realmName" + + " AND o.identityType.name = :typeName") +@Data public class HibernateIdentityObject implements IdentityObject { @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.AUTO, generator="JBID_IO_ID_SEQ") + @SequenceGenerator(name = "JBID_IO_ID_SEQ", sequenceName = "JBID_IO_ID_SEQ", allocationSize = 1) @Column(name = "ID") private Long id; - @Column(name = "NAME", unique = true) + @Column(name = "NAME", + unique = true) private String name; @ManyToOne(fetch = FetchType.EAGER) @Fetch(FetchMode.JOIN) - @JoinColumn(name = "IDENTITY_TYPE", nullable = false) + @JoinColumn(name = "IDENTITY_TYPE", + nullable = false) private HibernateIdentityObjectType identityType; - @OneToMany(orphanRemoval = true, cascade = { CascadeType.ALL }, mappedBy = "fromIdentityObject") + @OneToMany(orphanRemoval = true, + cascade = { CascadeType.ALL }, + mappedBy = "fromIdentityObject", + fetch = FetchType.LAZY) @Fetch(FetchMode.SUBSELECT) - @LazyCollection(LazyCollectionOption.EXTRA) - private Set fromRelationships = - new HashSet(); + private Set fromRelationships = new HashSet<>(); - @OneToMany(orphanRemoval = true, cascade = { CascadeType.ALL }, mappedBy = "toIdentityObject") + @OneToMany(orphanRemoval = true, + cascade = { CascadeType.ALL }, + mappedBy = "toIdentityObject", + fetch = FetchType.LAZY) @Fetch(FetchMode.SUBSELECT) - @LazyCollection(LazyCollectionOption.EXTRA) - private Set toRelationships = - new HashSet(); + private Set toRelationships = new HashSet<>(); - @OneToMany(orphanRemoval = true, cascade = { CascadeType.ALL }, mappedBy = "identityObject") + @OneToMany(orphanRemoval = true, + cascade = { CascadeType.ALL }, + mappedBy = "identityObject", + fetch = FetchType.LAZY) @Fetch(FetchMode.SUBSELECT) - @LazyCollection(LazyCollectionOption.EXTRA) - private Set attributes = - new HashSet(); + private Set attributes = new HashSet<>(); - @ElementCollection + @ElementCollection(fetch = FetchType.LAZY) @MapKeyColumn(name = "PROP_NAME") @Column(name = "PROP_VALUE") - @CollectionTable(name = "jbid_io_props", joinColumns = { @JoinColumn(name = "PROP_ID", referencedColumnName = "ID") }) + @CollectionTable(name = "jbid_io_props", + joinColumns = { @JoinColumn(name = "PROP_ID", + referencedColumnName = "ID") }) @Fetch(FetchMode.SUBSELECT) - @LazyCollection(LazyCollectionOption.EXTRA) - private Map properties = new HashMap(); + private Map properties = new HashMap<>(); @OneToMany( orphanRemoval = true, cascade = CascadeType.REMOVE, fetch = FetchType.LAZY, - targetEntity = HibernateIdentityObjectCredential.class - ) + targetEntity = HibernateIdentityObjectCredential.class) @JoinColumn(name = "IDENTITY_OBJECT_ID") @Fetch(FetchMode.SUBSELECT) - @LazyCollection(LazyCollectionOption.EXTRA) - private Set credentials = - new HashSet(); + private Set credentials = new HashSet<>(); @ManyToOne(fetch = FetchType.EAGER) @Fetch(FetchMode.SELECT) - @JoinColumn(name = "REALM", nullable = false) + @JoinColumn(name = "REALM", + nullable = false) private HibernateRealm realm; public HibernateIdentityObject() { @@ -144,47 +159,17 @@ public void setId(String id) { this.id = Long.parseLong(id); } - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public HibernateIdentityObjectType getIdentityType() { - return identityType; - } - - public void setIdentityType(HibernateIdentityObjectType identityType) { - this.identityType = identityType; - } - - public String getFQDN() { - return null; - } - - public Set getAttributes() { - return attributes; - } - - public void setAttributes(Set attributes) { - this.attributes = attributes; - } - public void addAttribute(HibernateIdentityObjectAttribute attribute) { attribute.setIdentityObject(this); this.attributes.add(attribute); } public Map getAttributesAsMap() { - Map map = new HashMap(); - + Map map = new HashMap<>(); for (HibernateIdentityObjectAttribute attribute : attributes) { Collection values = attribute.getValues(); map.put(attribute.getName(), values); } - return Collections.unmodifiableMap(map); } @@ -193,7 +178,7 @@ public void addTextAttribute(String name, String[] values) { name, HibernateIdentityObjectAttribute.TYPE_TEXT); List list = Arrays.asList(values); - Set vals = new HashSet(list); + Set vals = new HashSet<>(list); attr.setTextValues(vals); attributes.add(attr); } @@ -213,58 +198,23 @@ public void removeAttribute(String name) { } } - public Set getFromRelationships() { - return fromRelationships; - } - - public void setFromRelationships(Set fromRelationships) { - this.fromRelationships = fromRelationships; - } - public void addFromRelationship(HibernateIdentityObjectRelationship fromRelationship) { fromRelationship.setFromIdentityObject(this); fromRelationships.add(fromRelationship); } - public Set getToRelationships() { - return toRelationships; - } - - public void setToRelationships(Set toRelationships) { - this.toRelationships = toRelationships; - } - public void addToRelationship(HibernateIdentityObjectRelationship toRelationship) { toRelationship.setToIdentityObject(this); fromRelationships.add(toRelationship); } - public HibernateRealm getRealm() { - return realm; - } - - public void setRealm(HibernateRealm realm) { - this.realm = realm; - } - - public Set getCredentials() { - return credentials; - } - - public void setCredentials(Set credentials) { - this.credentials = credentials; - } - public void addCredential(HibernateIdentityObjectCredential credential) { credential.setIdentityObject(this); credentials.add(credential); } public boolean hasCredentials() { - if (credentials != null && credentials.size() > 0) { - return true; - } - return false; + return credentials != null && !credentials.isEmpty(); } public boolean hasCredential(IdentityObjectCredentialType type) { @@ -289,48 +239,8 @@ public HibernateIdentityObjectCredential getCredential(IdentityObjectCredentialT return null; } - public Map getProperties() { - return properties; - } - - public void setProperties(Map properties) { - this.properties = properties; - } - public void validatePolicy() throws PolicyValidationException { - - } - - @Override - public String toString() { - return "IdentityObject[id=" + getId() + "; name=" + getName() + "; type=" + getIdentityType().getName() + "]"; - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (!(o instanceof IdentityObject)) { - return false; - } - - IdentityObject that = (IdentityObject) o; - - if (name != null ? !name.equals(that.getName()) : that.getName() != null) { - return false; - } - if (identityType != null ? !identityType.equals(that.getIdentityType()) : that.getIdentityType() != null) { - return false; - } - - return true; + // No validation } - @Override - public int hashCode() { - int result = name != null ? name.hashCode() : 0; - result = 31 * result + (identityType != null ? identityType.hashCode() : 0); - return result; - } } diff --git a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectAttribute.java b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectAttribute.java index 84b2f6be2d..639367def4 100644 --- a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectAttribute.java +++ b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectAttribute.java @@ -27,16 +27,30 @@ import java.util.HashSet; import java.util.Set; -import javax.persistence.*; - import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; -import org.hibernate.annotations.LazyToOne; -import org.hibernate.annotations.LazyToOneOption; import org.picketlink.idm.spi.model.IdentityObjectAttribute; +import jakarta.persistence.CascadeType; +import jakarta.persistence.CollectionTable; +import jakarta.persistence.Column; +import jakarta.persistence.ElementCollection; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; + @Entity(name = "HibernateIdentityObjectAttribute") @Table(name = "jbid_io_attr") +@NamedQuery( + name = "HibernateIdentityObjectAttribute.findIdentityAttributes", + query = "SELECT a FROM HibernateIdentityObjectAttribute a INNER JOIN a.identityObject io ON io =:identity") public class HibernateIdentityObjectAttribute implements IdentityObjectAttribute { public static final String TYPE_TEXT = "text"; @@ -44,33 +58,36 @@ public class HibernateIdentityObjectAttribute implements IdentityObjectAttribute public static final String TYPE_BINARY = "binary"; @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.AUTO, generator="JBID_IO_ATTR_ID_SEQ") + @SequenceGenerator(name = "JBID_IO_ATTR_ID_SEQ", sequenceName = "JBID_IO_ATTR_ID_SEQ", allocationSize = 1) @Column(name = "ATTRIBUTE_ID") private Long id; @ManyToOne @Fetch(FetchMode.JOIN) - @JoinColumn(name = "IDENTITY_OBJECT_ID", nullable = false) + @JoinColumn(name = "IDENTITY_OBJECT_ID", + nullable = false) private HibernateIdentityObject identityObject; @Column(name = "NAME") private String name; - @Column(name = "ATTRIBUTE_TYPE", nullable = false) + @Column(name = "ATTRIBUTE_TYPE", + nullable = false) private String type; - @ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL) + @ManyToOne(fetch = FetchType.LAZY, + cascade = CascadeType.ALL) @Fetch(FetchMode.SELECT) @JoinColumn(name = "BIN_VALUE_ID") - @LazyToOne(LazyToOneOption.PROXY) private HibernateIdentityObjectAttributeBinaryValue binaryValue = null; @ElementCollection(fetch = FetchType.EAGER) @Column(name = "ATTR_VALUE") @CollectionTable( name = "jbid_io_attr_text_values", - joinColumns = { @JoinColumn(name = "TEXT_ATTR_VALUE_ID", referencedColumnName = "ATTRIBUTE_ID") } - ) + joinColumns = { @JoinColumn(name = "TEXT_ATTR_VALUE_ID", + referencedColumnName = "ATTRIBUTE_ID") }) @Fetch(FetchMode.JOIN) private Set textValues = new HashSet(); diff --git a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectAttributeBinaryValue.java b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectAttributeBinaryValue.java index 7ac338f417..8920b215a2 100644 --- a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectAttributeBinaryValue.java +++ b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectAttributeBinaryValue.java @@ -22,25 +22,24 @@ package org.picketlink.idm.impl.model.hibernate; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Table; - -import org.hibernate.annotations.Type; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; @Entity(name = "HibernateIdentityObjectAttributeBinaryValue") @Table(name = "jbid_attr_bin_value") public class HibernateIdentityObjectAttributeBinaryValue { @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.AUTO, generator="JBID_ATTR_BIN_VALUE_ID_SEQ") + @SequenceGenerator(name = "JBID_ATTR_BIN_VALUE_ID_SEQ", sequenceName = "JBID_ATTR_BIN_VALUE_ID_SEQ", allocationSize = 1) @Column(name = "BIN_VALUE_ID") private Long id; @Column(name = "VALUE", length = 10240000) - @Type(type = "org.hibernate.type.BinaryType") private byte[] value = null; public HibernateIdentityObjectAttributeBinaryValue() { diff --git a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectCredential.java b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectCredential.java index cfd6d210b9..741968cbde 100644 --- a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectCredential.java +++ b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectCredential.java @@ -25,7 +25,7 @@ import java.util.HashMap; import java.util.Map; -import javax.persistence.*; +import jakarta.persistence.*; import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; @@ -37,21 +37,18 @@ @Entity(name = "HibernateIdentityObjectCredential") @Table(name = "jbid_io_creden") -@NamedQueries( - { - @NamedQuery( - name = "HibernateIdentityObjectCredential.findCredentialByTypeAndIdentity", - query = "SELECT c FROM HibernateIdentityObjectCredential c" - + " INNER JOIN c.type type ON type.name = :cTypeName" - + " WHERE c.identityObject.id = :ioId" - ), - } +@NamedQuery( + name = "HibernateIdentityObjectCredential.findCredentialByTypeAndIdentity", + query = "SELECT c FROM HibernateIdentityObjectCredential c" + + " INNER JOIN c.type type ON type.name = :cTypeName" + + " WHERE c.identityObject.id = :ioId" ) public class HibernateIdentityObjectCredential implements IdentityObjectCredential { @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.AUTO, generator="JBID_IO_CREDEN_ID_SEQ") @Column(name = "ID") + @SequenceGenerator(name = "JBID_IO_CREDEN_ID_SEQ", sequenceName = "JBID_IO_CREDEN_ID_SEQ", allocationSize = 1) private Long id; @ManyToOne(fetch = FetchType.EAGER) diff --git a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectCredentialBinaryValue.java b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectCredentialBinaryValue.java index d3467f7365..f8c560e50c 100644 --- a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectCredentialBinaryValue.java +++ b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectCredentialBinaryValue.java @@ -22,25 +22,24 @@ package org.picketlink.idm.impl.model.hibernate; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.Table; - -import org.hibernate.annotations.Type; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; @Entity(name = "HibernateIdentityObjectCredentialBinaryValue") @Table(name = "jbid_creden_bin_value") public class HibernateIdentityObjectCredentialBinaryValue { @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.AUTO, generator="JBID_CREDEN_BIN_VALUE_ID_SEQ") + @SequenceGenerator(name = "JBID_CREDEN_BIN_VALUE_ID_SEQ", sequenceName = "JBID_CREDEN_BIN_VALUE_ID_SEQ", allocationSize = 1) @Column(name = "BIN_VALUE_ID") private Long id; @Column(name = "VALUE", length = 10240000) - @Type(type = "org.hibernate.type.BinaryType") private byte[] value = null; public HibernateIdentityObjectCredentialBinaryValue() { diff --git a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectCredentialType.java b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectCredentialType.java index efad62adae..a8bb5e5db6 100644 --- a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectCredentialType.java +++ b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectCredentialType.java @@ -22,25 +22,22 @@ package org.picketlink.idm.impl.model.hibernate; -import javax.persistence.*; +import jakarta.persistence.*; import org.picketlink.idm.spi.model.IdentityObjectCredentialType; @Entity(name = "HibernateIdentityObjectCredentialType") @Table(name = "jbid_io_creden_type") -@NamedQueries( - { - @NamedQuery( - name = "HibernateIdentityObjectCredentialType.findIdentityCredentialTypeByName", - query = "SELECT ct FROM HibernateIdentityObjectCredentialType ct" - + " WHERE ct.name = :name" - ), - } +@NamedQuery( + name = "HibernateIdentityObjectCredentialType.findIdentityCredentialTypeByName", + query = "SELECT ct FROM HibernateIdentityObjectCredentialType ct" + + " WHERE ct.name = :name" ) public class HibernateIdentityObjectCredentialType implements IdentityObjectCredentialType { @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.AUTO, generator="JBID_IO_CREDEN_TYPE_ID_SEQ") + @SequenceGenerator(name = "JBID_IO_CREDEN_TYPE_ID_SEQ", sequenceName = "JBID_IO_CREDEN_TYPE_ID_SEQ", allocationSize = 1) @Column(name = "ID") private Long id; diff --git a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectRelationship.java b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectRelationship.java index d519f1ebdc..468044aac8 100644 --- a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectRelationship.java +++ b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectRelationship.java @@ -25,108 +25,104 @@ import java.util.HashMap; import java.util.Map; -import javax.persistence.*; - import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; -import org.hibernate.annotations.LazyCollection; -import org.hibernate.annotations.LazyCollectionOption; -import org.hibernate.annotations.LazyToOne; -import org.hibernate.annotations.LazyToOneOption; import org.picketlink.idm.spi.model.IdentityObjectRelationship; import org.picketlink.idm.spi.model.IdentityObjectRelationshipType; +import jakarta.persistence.CollectionTable; +import jakarta.persistence.Column; +import jakarta.persistence.ElementCollection; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapKeyColumn; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; + @Entity(name = "HibernateIdentityObjectRelationship") @Table(name = "jbid_io_rel") -@NamedQueries( - { - @NamedQuery( - name = "HibernateIdentityObjectRelationship.findIdentityObjectRelationshipWithoutName", - query = "SELECT r FROM HibernateIdentityObjectRelationship r" - + " WHERE r.type.id = :typeId" - + " AND r.fromIdentityObject = :fromIdentityObject" - + " AND r.toIdentityObject = :toIdentityObject" - ), - @NamedQuery( - name = "HibernateIdentityObjectRelationship.findIdentityObjectRelationshipByAttributes", - query = "SELECT r FROM HibernateIdentityObjectRelationship r" - + " WHERE r.type.id = :typeId" - + " AND r.name.name = :name" - + " AND r.fromIdentityObject = :fromIdentityObject" - + " AND r.toIdentityObject = :toIdentityObject" - ), - @NamedQuery( - name = "HibernateIdentityObjectRelationship.findIdentityObjectRelationshipsByIdentities", - query = "SELECT r FROM HibernateIdentityObjectRelationship r" - + " WHERE" - + " (" - + " r.fromIdentityObject = :hio1" - + " AND" - + " r.toIdentityObject = :hio2" - + " ) OR (" - + " r.fromIdentityObject = :hio2" - + " AND" - + " r.toIdentityObject = :hio1" - + " ) " - ), - @NamedQuery( - name = "HibernateIdentityObjectRelationship.findIdentityObjectRelationshipByIdentityByType", - query = "SELECT r FROM HibernateIdentityObjectRelationship r" - + " WHERE r.type.name = :typeName" - + " AND r.fromIdentityObject = :fromIdentityObject" - + " AND r.toIdentityObject = :toIdentityObject" - ), - @NamedQuery( - name = "HibernateIdentityObjectRelationship.findIdentityObjectRelationshipByIdentity", - query = "SELECT r FROM HibernateIdentityObjectRelationship r" - + " WHERE r.fromIdentityObject = :fromIdentityObject" - + " AND r.toIdentityObject = :toIdentityObject" - ), - @NamedQuery( - name = "HibernateIdentityObjectRelationship.removeRelationshipsByName", - query = "DELETE FROM HibernateIdentityObjectRelationship " - + " WHERE name.id = :nameId" - ), - } +@NamedQuery( + name = "HibernateIdentityObjectRelationship.findIdentityObjectRelationshipWithoutName", + query = "SELECT r FROM HibernateIdentityObjectRelationship r" + " WHERE r.type.id = :typeId" + + " AND r.fromIdentityObject = :fromIdentityObject" + " AND r.toIdentityObject = :toIdentityObject") +@NamedQuery( + name = "HibernateIdentityObjectRelationship.findIdentityObjectRelationshipByIdentitiesAndTypeAndName", + query = "SELECT r FROM HibernateIdentityObjectRelationship r" + + " WHERE r.type.name = :typeName" + + " AND r.name.name = :name" + + " AND r.fromIdentityObject = :fromIdentityObject" + + " AND r.toIdentityObject = :toIdentityObject") +@NamedQuery( + name = "HibernateIdentityObjectRelationship.findIdentityObjectRelationshipsByIdentities", + query = "SELECT r FROM HibernateIdentityObjectRelationship r" + " WHERE" + " (" + " r.fromIdentityObject = :hio1" + + " AND" + " r.toIdentityObject = :hio2" + " ) OR (" + " r.fromIdentityObject = :hio2" + " AND" + + " r.toIdentityObject = :hio1" + " ) ") +@NamedQuery( + name = "HibernateIdentityObjectRelationship.findIdentityObjectRelationshipByIdentityByType", + query = "SELECT r FROM HibernateIdentityObjectRelationship r" + " WHERE r.type.name = :typeName" + + " AND r.fromIdentityObject = :fromIdentityObject" + " AND r.toIdentityObject = :toIdentityObject") +@NamedQuery( + name = "HibernateIdentityObjectRelationship.findIdentityObjectRelationshipByAttributes", + query = "SELECT r FROM HibernateIdentityObjectRelationship r" + " WHERE r.type.id = :typeId" + " AND r.name.name = :name" + + " AND r.fromIdentityObject = :fromIdentityObject" + " AND r.toIdentityObject = :toIdentityObject") +@NamedQuery( + name = "HibernateIdentityObjectRelationship.findIdentityObjectRelationshipByIdentity", + query = "SELECT r FROM HibernateIdentityObjectRelationship r" + " WHERE r.fromIdentityObject = :fromIdentityObject" + + " AND r.toIdentityObject = :toIdentityObject") +@NamedQuery( + name = "HibernateIdentityObjectRelationship.getRelationshipsByName", + query = "SELECT r FROM HibernateIdentityObjectRelationship r " + + " WHERE r.name.id = :nameId") +@NamedQuery( + name = "HibernateIdentityObjectRelationship.removeRelationshipsByName", + query = "DELETE FROM HibernateIdentityObjectRelationship " + + " WHERE name.id = :nameId" ) public class HibernateIdentityObjectRelationship implements IdentityObjectRelationship { @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.AUTO, generator="JBID_IO_REL_ID_SEQ") + @SequenceGenerator(name = "JBID_IO_REL_ID_SEQ", sequenceName = "JBID_IO_REL_ID_SEQ", allocationSize = 1) @Column(name = "ID") private Long id; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "NAME") @Fetch(FetchMode.JOIN) - @LazyToOne(LazyToOneOption.PROXY) private HibernateIdentityObjectRelationshipName name; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "REL_TYPE", nullable = false) + @JoinColumn(name = "REL_TYPE", + nullable = false) @Fetch(FetchMode.SELECT) - @LazyToOne(LazyToOneOption.PROXY) private HibernateIdentityObjectRelationshipType type; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "FROM_IDENTITY", nullable = false) + @JoinColumn(name = "FROM_IDENTITY", + nullable = false) @Fetch(FetchMode.SELECT) - @LazyToOne(LazyToOneOption.PROXY) private HibernateIdentityObject fromIdentityObject; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "TO_IDENTITY", nullable = false) + @JoinColumn(name = "TO_IDENTITY", + nullable = false) @Fetch(FetchMode.SELECT) - @LazyToOne(LazyToOneOption.PROXY) private HibernateIdentityObject toIdentityObject; - @ElementCollection + @ElementCollection(fetch = FetchType.LAZY) @MapKeyColumn(name = "PROP_NAME") @Column(name = "PROP_VALUE") - @CollectionTable(name = "jbid_io_rel_props", joinColumns = { @JoinColumn(name = "PROP_ID", referencedColumnName = "ID") }) + @CollectionTable(name = "jbid_io_rel_props", + joinColumns = { @JoinColumn(name = "PROP_ID", + referencedColumnName = "ID") }) @Fetch(FetchMode.SUBSELECT) - @LazyCollection(LazyCollectionOption.EXTRA) - private Map properties = new HashMap(); + private Map properties = new HashMap<>(); public HibernateIdentityObjectRelationship() { } diff --git a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectRelationshipName.java b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectRelationshipName.java index dabd5a5037..283ecd9649 100644 --- a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectRelationshipName.java +++ b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectRelationshipName.java @@ -19,58 +19,60 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ - package org.picketlink.idm.impl.model.hibernate; import java.util.HashMap; import java.util.Map; -import javax.persistence.*; - import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; -import org.hibernate.annotations.LazyCollection; -import org.hibernate.annotations.LazyCollectionOption; -import org.hibernate.annotations.LazyToOne; -import org.hibernate.annotations.LazyToOneOption; + +import jakarta.persistence.CollectionTable; +import jakarta.persistence.Column; +import jakarta.persistence.ElementCollection; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.MapKeyColumn; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; @Entity(name = "HibernateIdentityObjectRelationshipName") @Table(name = "jbid_io_rel_name") -@NamedQueries( - { - @NamedQuery( - name = "HibernateIdentityObjectRelationshipName.findIdentityObjectRelationshipNameByName", - query = "select rn from HibernateIdentityObjectRelationshipName rn where rn.name like :name and rn.realm.name = :realmName" - ), - } -) +@NamedQuery( + name = "HibernateIdentityObjectRelationshipName.findIdentityObjectRelationshipNameByName", + query = "select rn from HibernateIdentityObjectRelationshipName rn where rn.name like :name and rn.realm.name = :realmName") public class HibernateIdentityObjectRelationshipName { - public static final String findIdentityObjectRelationshipNameByName = - "select rn from HibernateIdentityObjectRelationshipName rn where rn.name like :name and rn.realm.name = :realmName"; - @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.AUTO, generator="JBID_IO_REL_NAME_ID_SEQ") + @SequenceGenerator(name = "JBID_IO_REL_NAME_ID_SEQ", sequenceName = "JBID_IO_REL_NAME_ID_SEQ", allocationSize = 1) @Column(name = "ID") private Long id; - @Column(name = "NAME", nullable = false) + @Column(name = "NAME", + nullable = false) private String name; @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "REALM", nullable = false) + @JoinColumn(name = "REALM", + nullable = false) @Fetch(FetchMode.SELECT) - @LazyToOne(LazyToOneOption.PROXY) private HibernateRealm realm; - @ElementCollection + @ElementCollection(fetch = FetchType.LAZY) @MapKeyColumn(name = "PROP_NAME") @Column(name = "PROP_VALUE") - @CollectionTable(name = "jbid_io_rel_name_props", joinColumns = { @JoinColumn(name = "PROP_ID", referencedColumnName = "ID") }) + @CollectionTable(name = "jbid_io_rel_name_props", + joinColumns = { @JoinColumn(name = "PROP_ID", + referencedColumnName = "ID") }) @Fetch(FetchMode.SUBSELECT) - @LazyCollection(LazyCollectionOption.EXTRA) - private Map properties = - new HashMap(); + private Map properties = new HashMap<>(); public HibernateIdentityObjectRelationshipName() { } diff --git a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectRelationshipType.java b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectRelationshipType.java index e8bccef2ec..cb9ca88253 100644 --- a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectRelationshipType.java +++ b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectRelationshipType.java @@ -22,25 +22,22 @@ package org.picketlink.idm.impl.model.hibernate; -import javax.persistence.*; +import jakarta.persistence.*; import org.picketlink.idm.spi.model.IdentityObjectRelationshipType; @Entity(name = "HibernateIdentityObjectRelationshipType") @Table(name = "jbid_io_rel_type") -@NamedQueries( - { - @NamedQuery( - name = "HibernateIdentityObjectRelationshipType.findIdentityRelationshipTypeByName", - query = "SELECT t FROM HibernateIdentityObjectRelationshipType t" - + " WHERE t.name = :name" - ), - } +@NamedQuery( + name = "HibernateIdentityObjectRelationshipType.findIdentityRelationshipTypeByName", + query = "SELECT t FROM HibernateIdentityObjectRelationshipType t" + + " WHERE t.name = :name" ) public class HibernateIdentityObjectRelationshipType implements IdentityObjectRelationshipType { @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.AUTO, generator="JBID_IO_REL_TYPE_ID_SEQ") + @SequenceGenerator(name = "JBID_IO_REL_TYPE_ID_SEQ", sequenceName = "JBID_IO_REL_TYPE_ID_SEQ", allocationSize = 1) @Column(name = "ID") private Long id; diff --git a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectType.java b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectType.java index e11c0edbe3..dfe6049003 100644 --- a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectType.java +++ b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateIdentityObjectType.java @@ -22,25 +22,22 @@ package org.picketlink.idm.impl.model.hibernate; -import javax.persistence.*; +import jakarta.persistence.*; import org.picketlink.idm.spi.model.IdentityObjectType; @Entity(name = "HibernateIdentityObjectType") @Table(name = "jbid_io_type") -@NamedQueries( - { - @NamedQuery( - name = "HibernateIdentityObjectType.findIdentityObjectTypeByName", - query = "SELECT t FROM HibernateIdentityObjectType t" - + " WHERE t.name = :name" - ), - } +@NamedQuery( + name = "HibernateIdentityObjectType.findIdentityObjectTypeByName", + query = "SELECT t FROM HibernateIdentityObjectType t" + + " WHERE t.name = :name" ) public class HibernateIdentityObjectType implements IdentityObjectType { @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.AUTO, generator="JBID_IO_TYPE_ID_SEQ") + @SequenceGenerator(name = "JBID_IO_TYPE_ID_SEQ", sequenceName = "JBID_IO_TYPE_ID_SEQ", allocationSize = 1) @Column(name = "ID") private Long id; diff --git a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateRealm.java b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateRealm.java index efb2956520..2a5f0c8da7 100644 --- a/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateRealm.java +++ b/component/identity/src/main/java/org/picketlink/idm/impl/model/hibernate/HibernateRealm.java @@ -25,40 +25,48 @@ import java.util.HashMap; import java.util.Map; -import javax.persistence.*; - import org.hibernate.annotations.Fetch; import org.hibernate.annotations.FetchMode; -import org.hibernate.annotations.LazyCollection; -import org.hibernate.annotations.LazyCollectionOption; + +import jakarta.persistence.CollectionTable; +import jakarta.persistence.Column; +import jakarta.persistence.ElementCollection; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.MapKeyColumn; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import lombok.Data; @Entity(name = "HibernateRealm") @Table(name = "jbid_realm") -@NamedQueries( - { - @NamedQuery( - name = "HibernateRealm.findIRealmByName", - query = "SELECT o FROM HibernateRealm o WHERE o.name = :name" - ) - } +@NamedQuery( + name = "HibernateRealm.findRealmByName", + query = "SELECT o FROM HibernateRealm o WHERE o.name = :name" ) +@Data public class HibernateRealm { @Id - @GeneratedValue(strategy = GenerationType.AUTO) + @GeneratedValue(strategy = GenerationType.AUTO, generator="JBID_REALM_ID_SEQ") + @SequenceGenerator(name = "JBID_REALM_ID_SEQ", sequenceName = "JBID_REALM_ID_SEQ", allocationSize = 1) @Column(name = "ID") private Long id; @Column(name = "NAME", nullable = false) private String name; - @ElementCollection + @ElementCollection(fetch = FetchType.LAZY) @MapKeyColumn(name = "PROP_NAME") @Column(name = "PROP_VALUE") @CollectionTable(name = "jbid_real_props", joinColumns = { @JoinColumn(name = "PROP_ID", referencedColumnName = "ID") }) @Fetch(FetchMode.SUBSELECT) - @LazyCollection(LazyCollectionOption.EXTRA) - private Map properties = new HashMap(); + private Map properties = new HashMap<>(); public HibernateRealm() { } @@ -67,35 +75,4 @@ public HibernateRealm(String name) { this.name = name; } - public Long getId() { - return id; - } - - public void setId(Long id) { - this.id = id; - } - - public String getName() { - return name; - } - - @Override - public String toString() { - return "HibernateRealm{" + - "name='" + name + '\'' + - '}'; - } - - public void setName(String name) { - this.name = name; - } - - public Map getProperties() { - return properties; - } - - public void setProperties(Map properties) { - this.properties = properties; - } - } diff --git a/component/identity/src/main/java/org/picketlink/idm/impl/store/hibernate/HibernateIdentityStoreImpl.java b/component/identity/src/main/java/org/picketlink/idm/impl/store/hibernate/HibernateIdentityStoreImpl.java index 5b1061c2a4..6a3227b39b 100644 --- a/component/identity/src/main/java/org/picketlink/idm/impl/store/hibernate/HibernateIdentityStoreImpl.java +++ b/component/identity/src/main/java/org/picketlink/idm/impl/store/hibernate/HibernateIdentityStoreImpl.java @@ -19,25 +19,41 @@ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ - package org.picketlink.idm.impl.store.hibernate; -import java.io.Serializable; -import java.util.*; -import java.util.logging.Level; -import java.util.logging.Logger; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; import javax.naming.InitialContext; import javax.naming.NamingException; -import org.hibernate.*; +import org.hibernate.Hibernate; +import org.hibernate.HibernateException; +import org.hibernate.Session; +import org.hibernate.SessionFactory; import org.hibernate.cfg.Configuration; -import org.hibernate.criterion.Order; -import org.hibernate.criterion.Projections; -import org.hibernate.criterion.Restrictions; +import org.hibernate.query.Query; import org.picketlink.idm.common.exception.IdentityException; import org.picketlink.idm.impl.helper.Tools; -import org.picketlink.idm.impl.model.hibernate.*; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObject; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObjectAttribute; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObjectAttributeBinaryValue; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObjectCredential; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObjectCredentialBinaryValue; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObjectCredentialType; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObjectRelationship; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObjectRelationshipName; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObjectRelationshipType; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObjectType; +import org.picketlink.idm.impl.model.hibernate.HibernateRealm; import org.picketlink.idm.impl.store.FeaturesMetaDataImpl; import org.picketlink.idm.impl.types.SimpleIdentityObject; import org.picketlink.idm.spi.configuration.IdentityStoreConfigurationContext; @@ -45,8 +61,13 @@ import org.picketlink.idm.spi.configuration.metadata.IdentityObjectTypeMetaData; import org.picketlink.idm.spi.configuration.metadata.IdentityStoreConfigurationMetaData; import org.picketlink.idm.spi.configuration.metadata.RealmConfigurationMetaData; -import org.picketlink.idm.spi.exception.OperationNotSupportedException; -import org.picketlink.idm.spi.model.*; +import org.picketlink.idm.spi.model.IdentityObject; +import org.picketlink.idm.spi.model.IdentityObjectAttribute; +import org.picketlink.idm.spi.model.IdentityObjectCredential; +import org.picketlink.idm.spi.model.IdentityObjectCredentialType; +import org.picketlink.idm.spi.model.IdentityObjectRelationship; +import org.picketlink.idm.spi.model.IdentityObjectRelationshipType; +import org.picketlink.idm.spi.model.IdentityObjectType; import org.picketlink.idm.spi.search.IdentityObjectSearchCriteria; import org.picketlink.idm.spi.store.FeaturesMetaData; import org.picketlink.idm.spi.store.IdentityObjectSearchCriteriaType; @@ -59,10 +80,20 @@ * Dawidowicz * @version : 0.1 $ */ -public class HibernateIdentityStoreImpl implements IdentityStore, Serializable { +public class HibernateIdentityStoreImpl implements IdentityStore { + + private static final String NOT_PRESENT_IN_THE_STORE = "] not present in the store."; + + private static final String ATTRIBUTE_NAME = + ". Attribute name: "; + + private static final String OPTION_IF_NEEDED_ATTRIBUTE_NAME = + "' option if needed. Attribute name: "; - private static Logger log = - Logger.getLogger(HibernateIdentityStoreImpl.class.getName()); + private static final String CANNOT_OBTAIN_RELATIONSHIP_PROPERTIES = + "Cannot obtain relationship properties: "; + + private static final String IDENTITY_PARAM = "identity"; public static final String HIBERNATE_SESSION_FACTORY_REGISTRY_NAME = "hibernateSessionFactoryRegistryName"; @@ -70,9 +101,6 @@ public class HibernateIdentityStoreImpl implements IdentityStore, Serializable { public static final String HIBERNATE_CONFIGURATION = "hibernateConfiguration"; - public static final String ADD_HIBERNATE_MAPPINGS = - "addHibernateMappings"; - public static final String HIBERNATE_SESSION_FACTORY_JNDI_NAME = "hibernateSessionFactoryJNDIName"; @@ -101,16 +129,27 @@ public class HibernateIdentityStoreImpl implements IdentityStore, Serializable { "lazyStartOfHibernateTransaction"; public static final String DEFAULT_REALM_NAME = - HibernateIdentityStoreImpl.class.getName() - + ".DEFAULT_REALM"; + HibernateIdentityStoreImpl.class.getName() + + ".DEFAULT_REALM"; public static final String CREDENTIAL_TYPE_PASSWORD = "PASSWORD"; public static final String CREDENTIAL_TYPE_BINARY = "BINARY"; + public static final String NAME_PARAM = "name"; + + public static final String REALM_PARAM = "realm"; + + private static final String TYPE_NAME_PARAM = "typeName"; + + public static final String IDENTITY_TYPE_NAME = + TYPE_NAME_PARAM; + + public static final String REALM_NAME_PARAM = "realmName"; + private String id; - private FeaturesMetaData supportedFeatures; + private FeaturesMetaData supportedFeatures; // NOSONAR private SessionFactory sessionFactory; @@ -126,26 +165,25 @@ public class HibernateIdentityStoreImpl implements IdentityStore, Serializable { private boolean isManageTransactionDuringBootstrap = true; - // TODO: rewrite this into some more handy object - private IdentityStoreConfigurationMetaData configurationMD; + private IdentityStoreConfigurationMetaData configurationMD; // NOSONAR private static Set supportedIdentityObjectSearchCriteria = - new HashSet(); + new HashSet<>(); private static Set supportedCredentialTypes = - new HashSet(); + new HashSet<>(); // > private Map> attributeMappings = - new HashMap>(); + new HashMap<>(); // - private Map> attributesMetaData = - new HashMap>(); + private Map> attributesMetaData = // NOSONAR + new HashMap<>(); // private Map> reverseAttributeMappings = - new HashMap>(); + new HashMap<>(); private static final long serialVersionUID = -130355852189832805L; @@ -168,7 +206,7 @@ public HibernateIdentityStoreImpl(String id) { this.id = id; } - public void bootstrap(IdentityStoreConfigurationContext configurationContext) throws IdentityException { + public void bootstrap(IdentityStoreConfigurationContext configurationContext) throws IdentityException { // NOSONAR this.configurationMD = configurationContext.getStoreConfigurationMetaData(); id = configurationMD.getId(); @@ -177,7 +215,7 @@ public void bootstrap(IdentityStoreConfigurationContext configurationContext) th supportedIdentityObjectSearchCriteria, true, true, - new HashSet()); + new HashSet<>()); String populateMembershipTypes = configurationMD.getOptionSingleValue(POPULATE_MEMBERSHIP_TYPES); String populateIdentityObjectTypes = configurationMD.getOptionSingleValue(POPULATE_IDENTITY_OBJECT_TYPES); @@ -195,9 +233,9 @@ public void bootstrap(IdentityStoreConfigurationContext configurationContext) th // Attribute mappings - helper structures for (IdentityObjectTypeMetaData identityObjectTypeMetaData : configurationMD.getSupportedIdentityTypes()) { - Set names = new HashSet(); - Map metadataMap = new HashMap(); - Map reverseMap = new HashMap(); + Set names = new HashSet<>(); + Map metadataMap = new HashMap<>(); + Map reverseMap = new HashMap<>(); for (IdentityObjectAttributeMetaData attributeMetaData : identityObjectTypeMetaData.getAttributes()) { names.add(attributeMetaData.getName()); metadataMap.put(attributeMetaData.getName(), attributeMetaData); @@ -221,63 +259,43 @@ public void bootstrap(IdentityStoreConfigurationContext configurationContext) th } if (populateMembershipTypes != null && populateMembershipTypes.equalsIgnoreCase("true")) { - List memberships = new LinkedList(); - + List memberships = new LinkedList<>(); for (String membership : configurationMD.getSupportedRelationshipTypes()) { memberships.add(membership); } - try { populateRelationshipTypes(hibernateSession, memberships.toArray(new String[memberships.size()])); } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Failed to populate relationship types", e); } - } if (populateIdentityObjectTypes != null && populateIdentityObjectTypes.equalsIgnoreCase("true")) { - List types = new LinkedList(); - + List types = new LinkedList<>(); for (IdentityObjectTypeMetaData metaData : configurationMD.getSupportedIdentityTypes()) { types.add(metaData.getName()); } - try { populateObjectTypes(hibernateSession, types.toArray(new String[types.size()])); } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Failed to populate identity object types", e); } - } - if (supportedCredentialTypes != null && supportedCredentialTypes.size() > 0) { + if (supportedCredentialTypes != null && !supportedCredentialTypes.isEmpty()) { try { populateCredentialTypes(hibernateSession, supportedCredentialTypes.toArray(new String[supportedCredentialTypes.size()])); } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Failed to populated credential types"); } } String realmAware = configurationMD.getOptionSingleValue(IS_REALM_AWARE); - if (realmAware != null && realmAware.equalsIgnoreCase("true")) { this.isRealmAware = true; } String allowNotDefineAttributes = configurationMD.getOptionSingleValue(ALLOW_NOT_DEFINED_ATTRIBUTES); - if (allowNotDefineAttributes != null && allowNotDefineAttributes.equalsIgnoreCase("true")) { this.isAllowNotDefinedAttributes = true; } @@ -294,26 +312,14 @@ public void bootstrap(IdentityStoreConfigurationContext configurationContext) th this.isAllowNotCaseSensitiveSearch = true; } - String lazyStartOfHibernateTransaction = configurationMD.getOptionSingleValue(LAZY_START_OF_HIBERNATE_TRANSACTION); - - if (lazyStartOfHibernateTransaction != null && lazyStartOfHibernateTransaction.equalsIgnoreCase("true")) { + String lazyStartOfHibernateTransactionValue = configurationMD.getOptionSingleValue(LAZY_START_OF_HIBERNATE_TRANSACTION); + if (lazyStartOfHibernateTransactionValue != null && lazyStartOfHibernateTransactionValue.equalsIgnoreCase("true")) { this.lazyStartOfHibernateTransaction = true; } // Default realm - HibernateRealm realm = null; - - try { - - realm = (HibernateRealm) hibernateSession.createCriteria(HibernateRealm.class) - .add(Restrictions.eq("name", DEFAULT_REALM_NAME)) - .uniqueResult(); - - } catch (HibernateException e) { - // Realm does not exist - } - + HibernateRealm realm = getRealmByName(hibernateSession, DEFAULT_REALM_NAME); if (realm == null) { addRealm(hibernateSession, DEFAULT_REALM_NAME); } @@ -321,34 +327,24 @@ public void bootstrap(IdentityStoreConfigurationContext configurationContext) th // If store is realm aware than creat all configured realms if (isRealmAware()) { - Set realmNames = new HashSet(); - + Set realmNames = new HashSet<>(); for (RealmConfigurationMetaData realmMD : configurationContext.getConfigurationMetaData().getRealms()) { realmNames.add(realmMD.getId()); } - for (String rid : realmNames) { - realm = (HibernateRealm) hibernateSession.createCriteria(HibernateRealm.class) - .add(Restrictions.eq("name", rid)) - .setCacheable(true) - .uniqueResult(); - + realm = getRealmByName(hibernateSession, rid); if (realm == null) { addRealm(hibernateSession, rid); } } } - if (isManageTransactionDuringBootstrap()) { hibernateSession.getTransaction().commit(); } - if (hibernateSession.getTransaction().isActive()) { hibernateSession.flush(); } - hibernateSession.close(); - } protected SessionFactory bootstrapHibernateSessionFactory(IdentityStoreConfigurationContext configurationContext) throws IdentityException { @@ -356,18 +352,12 @@ protected SessionFactory bootstrapHibernateSessionFactory(IdentityStoreConfigura .getOptionSingleValue(HIBERNATE_SESSION_FACTORY_JNDI_NAME); String sfRegistryName = configurationContext.getStoreConfigurationMetaData() .getOptionSingleValue(HIBERNATE_SESSION_FACTORY_REGISTRY_NAME); - String addMappedClasses = configurationContext.getStoreConfigurationMetaData().getOptionSingleValue(ADD_HIBERNATE_MAPPINGS); String hibernateConfiguration = configurationContext.getStoreConfigurationMetaData() .getOptionSingleValue(HIBERNATE_CONFIGURATION); - if (sfJNDIName != null) { try { return (SessionFactory) new InitialContext().lookup(sfJNDIName); } catch (NamingException e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Cannot obtain hibernate SessionFactory from provided JNDI name: " + sfJNDIName, e); } } else if (sfRegistryName != null) { @@ -378,59 +368,46 @@ protected SessionFactory bootstrapHibernateSessionFactory(IdentityStoreConfigura } if (!(registryObject instanceof SessionFactory)) { - throw new IdentityException("Cannot obtain hibernate SessionFactory from provided registry name: " + sfRegistryName - + "; Registered object is not an instance of SessionFactory: " + registryObject.getClass().getName()); + throw new IdentityException("Cannot obtain hibernate SessionFactory from provided registry name: " + sfRegistryName + + "; Registered object is not an instance of SessionFactory: " + registryObject.getClass().getName()); } return (SessionFactory) registryObject; } else if (hibernateConfiguration != null) { - try { Configuration config = new Configuration().configure(hibernateConfiguration); - SessionFactory sessionFactory = config - .addAnnotatedClass(HibernateIdentityObject.class) - .addAnnotatedClass(HibernateIdentityObjectCredentialBinaryValue.class) - .addAnnotatedClass(HibernateIdentityObjectAttributeBinaryValue.class) - .addAnnotatedClass(HibernateIdentityObjectAttribute.class) - .addAnnotatedClass(HibernateIdentityObjectCredential.class) - .addAnnotatedClass(HibernateIdentityObjectCredentialType.class) - .addAnnotatedClass(HibernateIdentityObjectRelationship.class) - .addAnnotatedClass(HibernateIdentityObjectRelationshipName.class) - .addAnnotatedClass(HibernateIdentityObjectRelationshipType.class) - .addAnnotatedClass(HibernateIdentityObjectType.class) - .addAnnotatedClass(HibernateRealm.class) - .buildSessionFactory(); - return sessionFactory; + return config.addAnnotatedClass(HibernateIdentityObject.class) + .addAnnotatedClass(HibernateIdentityObjectCredentialBinaryValue.class) + .addAnnotatedClass(HibernateIdentityObjectAttributeBinaryValue.class) + .addAnnotatedClass(HibernateIdentityObjectAttribute.class) + .addAnnotatedClass(HibernateIdentityObjectCredential.class) + .addAnnotatedClass(HibernateIdentityObjectCredentialType.class) + .addAnnotatedClass(HibernateIdentityObjectRelationship.class) + .addAnnotatedClass(HibernateIdentityObjectRelationshipName.class) + .addAnnotatedClass(HibernateIdentityObjectRelationshipType.class) + .addAnnotatedClass(HibernateIdentityObjectType.class) + .addAnnotatedClass(HibernateRealm.class) + .buildSessionFactory(); } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - - throw new IdentityException("Cannot obtain hibernate SessionFactory using provided hibernate configuration: " - + hibernateConfiguration, e); + throw new IdentityException("Cannot obtain hibernate SessionFactory using provided hibernate configuration: " + + hibernateConfiguration, e); } - + } else { + throw new IdentityException("Cannot obtain hibernate SessionFactory. None of supported options specified: " + + HIBERNATE_SESSION_FACTORY_JNDI_NAME + ", " + HIBERNATE_SESSION_FACTORY_REGISTRY_NAME + ", " + HIBERNATE_CONFIGURATION); } - throw new IdentityException("Cannot obtain hibernate SessionFactory. None of supported options specified: " - + HIBERNATE_SESSION_FACTORY_JNDI_NAME + ", " + HIBERNATE_SESSION_FACTORY_REGISTRY_NAME + ", " + HIBERNATE_CONFIGURATION); - } public IdentityStoreSession createIdentityStoreSession() throws IdentityException { try { return new HibernateIdentityStoreSessionImpl(sessionFactory, lazyStartOfHibernateTransaction); } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Failed to obtain Hibernate SessionFactory", e); } } - public IdentityStoreSession createIdentityStoreSession( - Map sessionOptions) throws IdentityException { + public IdentityStoreSession createIdentityStoreSession(Map sessionOptions) throws IdentityException { return createIdentityStoreSession(); } @@ -462,48 +439,25 @@ public IdentityObject createIdentityObject(IdentityStoreInvocationContext ctx, } checkIOType(identityObjectType); - Session session = getHibernateSession(ctx); - HibernateRealm realm = getRealm(session, ctx); - - Number boxedSize = (Number) session.createCriteria(HibernateIdentityObject.class) - .createAlias("identityType", "type") - .createAlias("realm", "rm") - .add(Restrictions.eq("name", name)) - .add(Restrictions.eq("rm.name", realm.getName())) - .add(Restrictions.eq("type.name", identityObjectType.getName())) - .setProjection(Projections.rowCount()) - .setCacheable(true) - .list() - .get(0); - - int size = boxedSize.intValue(); + int size = countIdentityObjectByNameAndType(session, name, identityObjectType.getName(), realm.getName()); if (size != 0) { throw new IdentityException("IdentityObject already present in this IdentityStore:" + "name=" + name + "; type=" + identityObjectType.getName() + "; realm=" + realm); } - HibernateIdentityObjectType hibernateType = getHibernateIdentityObjectType(ctx, identityObjectType); - HibernateIdentityObject io = new HibernateIdentityObject(name, hibernateType, realm); - if (attributes != null) { for (Map.Entry entry : attributes.entrySet()) { io.addTextAttribute(entry.getKey(), entry.getValue()); } } - try { getHibernateSession(ctx).persist(io); } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Cannot persist new IdentityObject" + io, e); } - return io; } @@ -513,14 +467,13 @@ public void removeIdentityObject(IdentityStoreInvocationContext ctx, IdentityObj Session hibernateSession = getHibernateSession(ctx); try { - // Remove all related relationships HibernateIdentityObjectRelationship[] from = new HibernateIdentityObjectRelationship[hibernateObject.getFromRelationships() .size()]; for (HibernateIdentityObjectRelationship relationship : hibernateObject.getFromRelationships().toArray(from)) { relationship.getFromIdentityObject().getFromRelationships().remove(relationship); relationship.getToIdentityObject().getToRelationships().remove(relationship); - hibernateSession.delete(relationship); + hibernateSession.remove(relationship); hibernateSession.flush(); } @@ -530,19 +483,12 @@ public void removeIdentityObject(IdentityStoreInvocationContext ctx, IdentityObj relationship.getFromIdentityObject().getFromRelationships().remove(relationship); relationship.getToIdentityObject().getToRelationships().remove(relationship); - hibernateSession.delete(relationship); + hibernateSession.remove(relationship); hibernateSession.flush(); - } - - hibernateSession.delete(hibernateObject); + hibernateSession.remove(hibernateObject); hibernateSession.flush(); - } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Cannot remove IdentityObject" + identity, e); } } @@ -550,21 +496,16 @@ public void removeIdentityObject(IdentityStoreInvocationContext ctx, IdentityObj public int getIdentityObjectsCount(IdentityStoreInvocationContext ctx, IdentityObjectType identityType) throws IdentityException { checkIOType(identityType); - HibernateIdentityObjectType jpaType = getHibernateIdentityObjectType(ctx, identityType); Session hibernateSession = getHibernateSession(ctx); try { return hibernateSession.createNamedQuery("HibernateIdentityObject.countIdentityObjectsByType", Long.class) - .setParameter("typeName", jpaType.getName()) - .setParameter("realmName", getRealmName(ctx)) + .setParameter(IDENTITY_TYPE_NAME, jpaType.getName()) + .setParameter(REALM_NAME_PARAM, getRealmName(ctx)) .setCacheable(true) .uniqueResult() .intValue(); } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Cannot count stored IdentityObjects with type: " + identityType.getName(), e); } } @@ -580,40 +521,29 @@ public IdentityObject findIdentityObject(IdentityStoreInvocationContext ctx, checkIOType(type); HibernateIdentityObject hibernateObject = safeGet(ctx, new SimpleIdentityObject(name, type)); - // Check result with case sensitive compare: if (isAllowNotCaseSensitiveSearch()) { return hibernateObject; - } else if (hibernateObject != null && hibernateObject.getName().equals(name)) { - + } else if (hibernateObject != null // NOSONAR + && hibernateObject.getName().equals(name)) { return hibernateObject; - + } else { + return null; } - return null; } public IdentityObject findIdentityObject(IdentityStoreInvocationContext ctx, String id) throws IdentityException { if (id == null) { throw new IllegalArgumentException("id is null"); } - - HibernateIdentityObject hibernateObject; - try { - hibernateObject = (HibernateIdentityObject) getHibernateSession(ctx).get(HibernateIdentityObject.class, new Long(id)); + return getHibernateSession(ctx).get(HibernateIdentityObject.class, id); } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Cannot find IdentityObject with id: " + id, e); } - - return hibernateObject; } - @SuppressWarnings("unchecked") - public Collection findIdentityObject(IdentityStoreInvocationContext ctx, + public Collection findIdentityObject(IdentityStoreInvocationContext ctx, // NOSONAR IdentityObjectType identityType, IdentityObjectSearchCriteria criteria) throws IdentityException { checkIOType(identityType); @@ -621,21 +551,18 @@ public Collection findIdentityObject(IdentityStoreInvocationCont HibernateIdentityObjectType hibernateType = getHibernateIdentityObjectType(ctx, identityType); HibernateRealm realm = getRealm(getHibernateSession(ctx), ctx); - List results; - Session hibernateSession = getHibernateSession(ctx); - try { StringBuilder hqlBuilderSelect = new StringBuilder("select distinct io from HibernateIdentityObject io"); - Map queryParams = new HashMap(); + Map queryParams = new HashMap<>(); StringBuilder hqlBuilderConditions = new StringBuilder(" where io.realm=:realm and io.identityType=:identityType"); - queryParams.put("realm", realm); + queryParams.put(REALM_PARAM, realm); queryParams.put("identityType", hibernateType); hqlBuilderConditions.append(" and io.name like :ioName"); if (criteria != null && criteria.getFilter() != null) { - queryParams.put("ioName", criteria.getFilter().replaceAll("\\*", "%")); + queryParams.put("ioName", criteria.getFilter().replace("\\*", "%").replace("*", "%")); } else { queryParams.put("ioName", "%"); } @@ -644,17 +571,10 @@ public Collection findIdentityObject(IdentityStoreInvocationCont int i = 0; for (Map.Entry entry : criteria.getValues().entrySet()) { // Resolve attribute name from the store attribute mapping - String mappedAttributeName = null; - try { - mappedAttributeName = resolveAttributeStoreMapping(hibernateType, entry.getKey()); - } catch (IdentityException e) { - // Nothing - } - - Set given = new HashSet(Arrays.asList(entry.getValue())); - + String mappedAttributeName = resolveAttributeStoreMapping(hibernateType, entry.getKey()); + List given = Arrays.stream(entry.getValue()).distinct().toList(); for (String attrValue : given) { - attrValue = attrValue.replaceAll("\\*", "%"); + attrValue = attrValue.replace("\\*", "%").replace("*", "%"); i++; String attrTableJoinName = "attrs" + i; @@ -681,31 +601,25 @@ public Collection findIdentityObject(IdentityStoreInvocationCont } } - Query hibernateQuery = hibernateSession.createQuery(hqlBuilderSelect.toString() + hqlBuilderConditions.toString()); - + Query hibernateQuery = hibernateSession.createQuery( + hqlBuilderSelect.toString() + + hqlBuilderConditions.toString(), + IdentityObject.class); if (criteria != null && criteria.isPaged()) { if (criteria.getMaxResults() > 0) { hibernateQuery.setMaxResults(criteria.getMaxResults()); } hibernateQuery.setFirstResult(criteria.getFirstResult()); } - // Apply parameters to Hibernate query applyQueryParameters(hibernateQuery, queryParams); - hibernateQuery.setCacheable(true); - - results = (List) hibernateQuery.list(); + List results = hibernateQuery.list(); Hibernate.initialize(results); + return results; } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Cannot find IdentityObjects with type '" + identityType.getName() + "'", e); } - - return results; } public Collection findIdentityObject(IdentityStoreInvocationContext ctx, @@ -721,7 +635,6 @@ public int getIdentityObjectCount(IdentityStoreInvocationContext invocationCxt, return getIdentityObjectCount(invocationCxt, identity, relationshipType, null, parent, criteria); } - @SuppressWarnings("unchecked") public Collection findIdentityObject(IdentityStoreInvocationContext invocationCxt, IdentityObject identity, IdentityObjectRelationshipType relationshipType, @@ -736,82 +649,58 @@ public int getIdentityObjectCount(IdentityStoreInvocationContext ctx, Collection excludes, boolean parent, IdentityObjectSearchCriteria criteria) throws IdentityException { - int result; - try { - org.hibernate.Query q = prepareIdentityObjectQuery( - ctx, - identity, - relationshipType, - excludes, - parent, - criteria, - false); - - result = ((Integer) q.iterate().next()).intValue(); + Query q = prepareIdentityObjectQuery(ctx, + identity, + relationshipType, + excludes, + parent, + criteria, + false); + + return q.uniqueResult().intValue(); } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Cannot get IdentityObject count", e); } - return result; - } - @SuppressWarnings("unchecked") public Collection findIdentityObject(IdentityStoreInvocationContext ctx, IdentityObject identity, IdentityObjectRelationshipType relationshipType, Collection excludes, boolean parent, IdentityObjectSearchCriteria criteria) throws IdentityException { - - List results; - try { - org.hibernate.Query q = prepareIdentityObjectQuery( - ctx, - identity, - relationshipType, - excludes, - parent, - criteria, - false); - - results = q.list(); + Query q = prepareIdentityObjectQuery( + ctx, + identity, + relationshipType, + excludes, + parent, + criteria, + false); + List results = q.list(); Hibernate.initialize(results); - - } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); + if (criteria != null && criteria.isFiltered()) { + filterByAttributesValues(results, criteria.getValues()); + if (criteria.isPaged()) { + return cutPageFromResults(results, criteria); + } } - + return results; + } catch (Exception e) { throw new IdentityException("Cannot find IdentityObjects", e); } - - if (criteria != null && criteria.isFiltered()) { - filterByAttributesValues(results, criteria.getValues()); - if (criteria.isPaged()) { - results = (LinkedList) cutPageFromResults(results, criteria); - } - } - - return results; } - @SuppressWarnings("unchecked") - public org.hibernate.Query prepareIdentityObjectQuery(IdentityStoreInvocationContext ctx, - IdentityObject identity, - IdentityObjectRelationshipType relationshipType, - Collection excludes, - boolean parent, - IdentityObjectSearchCriteria criteria, - boolean count) throws IdentityException { - // TODO:test - + public Query prepareIdentityObjectQuery(IdentityStoreInvocationContext ctx, // NOSONAR + IdentityObject identity, + IdentityObjectRelationshipType relationshipType, + Collection excludes, + boolean parent, + IdentityObjectSearchCriteria criteria, + boolean count) throws IdentityException { HibernateIdentityObject hibernateObject = safeGet(ctx, identity); HibernateRealm realm = getRealm(getHibernateSession(ctx), ctx); @@ -823,39 +712,27 @@ public org.hibernate.Query prepareIdentityObjectQuery(IdentityStoreInvocationCon ascending = criteria.isAscending(); } - org.hibernate.Query q = null; - try { - StringBuilder hqlString = new StringBuilder(""); - if (parent) { - if (count) { hqlString.append("select count(distinct toio) from HibernateIdentityObjectRelationship ior join ior.toIdentityObject toio where "); } else { hqlString.append("select distinct toio from HibernateIdentityObjectRelationship ior join ior.toIdentityObject toio where "); } - hqlString.append("toio.realm = :realm and ior.fromIdentityObject.realm = :realm and "); - if (relationshipType != null) { - hqlString.append("toio.name like :nameFilter and ior.type.name = :relType and ior.fromIdentityObject = :identity"); } else { hqlString.append("toio.name like :nameFilter and ior.fromIdentityObject = :identity"); } - - if (excludes != null && excludes.size() > 0) { - - int i = 0; - for (IdentityObjectType exclude : excludes) { + if (excludes != null && !excludes.isEmpty()) { + for (int i = 0; i < excludes.size(); i++) { hqlString.append(" and toio.identityType.id <> ") - .append(":exclude" + i++); + .append(":exclude" + i); } } - if (orderByName) { hqlString.append(" order by toio.name"); if (ascending) { @@ -878,15 +755,12 @@ public org.hibernate.Query prepareIdentityObjectQuery(IdentityStoreInvocationCon } else { hqlString.append("fromio.name like :nameFilter and ior.toIdentityObject = :identity"); } - - if (excludes != null && excludes.size() > 0) { - int i = 0; - for (IdentityObjectType exclude : excludes) { + if (excludes != null && !excludes.isEmpty()) { + for (int i = 0; i < excludes.size(); i++) { hqlString.append(" and fromio.identityType.id <> ") - .append(":exclude" + i++); + .append(":exclude" + i); } } - if (orderByName) { hqlString.append(" order by fromio.name"); if (ascending) { @@ -895,22 +769,20 @@ public org.hibernate.Query prepareIdentityObjectQuery(IdentityStoreInvocationCon } } - q = getHibernateSession(ctx).createQuery(hqlString.toString()) - .setParameter("identity", hibernateObject) - .setParameter("realm", realm) - .setCacheable(true); - + @SuppressWarnings({ "unchecked", "deprecation" }) + Query q = getHibernateSession(ctx).createQuery(hqlString.toString()) // NOSONAR + .setParameter(IDENTITY_PARAM, hibernateObject) + .setParameter(REALM_PARAM, realm) + .setCacheable(true); if (relationshipType != null) { q.setParameter("relType", relationshipType.getName()); } - if (criteria != null && criteria.getFilter() != null) { - q.setParameter("nameFilter", criteria.getFilter().replaceAll("\\*", "%")); + q.setParameter("nameFilter", criteria.getFilter().replace("\\*", "%").replace("*", "%")); } else { q.setParameter("nameFilter", "%"); } - - if (excludes != null && excludes.size() > 0) { + if (excludes != null && !excludes.isEmpty()) { int i = 0; for (IdentityObjectType exclude : excludes) { HibernateIdentityObjectType exType = getHibernateIdentityObjectType(ctx, exclude); @@ -923,17 +795,10 @@ public org.hibernate.Query prepareIdentityObjectQuery(IdentityStoreInvocationCon q.setMaxResults(criteria.getMaxResults()); } } - - q.setCacheable(true); + return q; } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Cannot prepare hibernate query", e); } - - return q; } public Collection findIdentityObject(IdentityStoreInvocationContext ctx, @@ -958,33 +823,22 @@ public IdentityObjectRelationship createRelationship(IdentityStoreInvocationCont HibernateIdentityObject toIO = safeGet(ctx, toIdentity); HibernateIdentityObjectRelationshipType type = getHibernateIdentityObjectRelationshipType(ctx, relationshipType); - HibernateRealm realm = getRealm(getHibernateSession(ctx), ctx); - - if (!getSupportedFeatures().isRelationshipTypeSupported(fromIO.getIdentityType(), toIO.getIdentityType(), relationshipType)) { - if (!isAllowNotDefinedIdentityObjectTypes()) { - throw new IdentityException("Relationship not supported. RelationshipType[ " + relationshipType.getName() + " ] " + - "beetween: [ " + fromIO.getIdentityType().getName() + " ] and [ " + toIO.getIdentityType().getName() + " ]"); - } + if (!getSupportedFeatures().isRelationshipTypeSupported(fromIO.getIdentityType(), toIO.getIdentityType(), relationshipType) + && !isAllowNotDefinedIdentityObjectTypes()) { + throw new IdentityException("Relationship not supported. RelationshipType[ " + relationshipType.getName() + " ] " + + "beetween: [ " + fromIO.getIdentityType().getName() + " ] and [ " + toIO.getIdentityType().getName() + " ]"); } HibernateIdentityObjectRelationship relationship = null; - HibernateRealm hibernateRealm = getRealm(getHibernateSession(ctx), ctx); - + HibernateRealm realm = getRealm(getHibernateSession(ctx), ctx); if (name != null) { - HibernateIdentityObjectRelationshipName relationshipName = - (HibernateIdentityObjectRelationshipName) getHibernateSession(ctx).createCriteria(HibernateIdentityObjectRelationshipName.class) - .setCacheable(true) - .add(Restrictions.eq("name", - name)) - .add(Restrictions.eq("realm", - hibernateRealm)) - .uniqueResult(); - + findIdentityObjectRelationshipNameByName(getHibernateSession(ctx), + name, + realm.getName()); if (relationshipName == null) { - throw new IdentityException("Relationship name not present in the store"); + throw new IdentityException("Relationship name " + name + " not present in the store"); } - relationship = new HibernateIdentityObjectRelationship(type, fromIO, toIO, relationshipName); } else { relationship = new HibernateIdentityObjectRelationship(type, fromIO, toIO); @@ -994,17 +848,10 @@ public IdentityObjectRelationship createRelationship(IdentityStoreInvocationCont Session session = getHibernateSession(ctx); session.persist(relationship); session.flush(); - + return relationship; } catch (HibernateException e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Cannot create relationship: ", e); } - - return relationship; - } public void removeRelationship(IdentityStoreInvocationContext ctx, @@ -1021,65 +868,24 @@ public void removeRelationship(IdentityStoreInvocationContext ctx, HibernateIdentityObject toIO = safeGet(ctx, toIdentity); HibernateIdentityObjectRelationshipType type = getHibernateIdentityObjectRelationshipType(ctx, relationshipType); - HibernateRealm realm = getRealm(getHibernateSession(ctx), ctx); - - org.hibernate.Query query = null; - Criteria crit = null; - + HibernateIdentityObjectRelationship relationship; if (name == null) { - - crit = getHibernateSession(ctx).createCriteria(HibernateIdentityObjectRelationship.class) - .createAlias("type", "t") - .add(Restrictions.eq("fromIdentityObject", fromIO)) - .add(Restrictions.eq("toIdentityObject", toIO)) - .add(Restrictions.eq("t.name", type.getName())) - .setCacheable(true); - + relationship = findIdentityObjectRelationshipByIdentityByType(getHibernateSession(ctx), fromIO, toIO, type.getName()); } else { - HibernateIdentityObjectRelationshipName relationshipName = - (HibernateIdentityObjectRelationshipName) getHibernateSession(ctx) - .createCriteria(HibernateIdentityObjectRelationshipName.class) - .add(Restrictions.eq("name", - name)) - .createAlias("realm", - "rm") - .add(Restrictions.eq("rm.name", - getRealmName(ctx))) - .uniqueResult(); - - if (relationshipName == null) { - throw new IdentityException("Relationship name not present in the store"); - } - - crit = getHibernateSession(ctx).createCriteria(HibernateIdentityObjectRelationship.class) - .createAlias("type", "t") - .createAlias("name", "n") - .add(Restrictions.eq("fromIdentityObject", fromIO)) - .add(Restrictions.eq("toIdentityObject", toIO)) - .add(Restrictions.eq("t.name", type.getName())) - .add(Restrictions.eq("n.name", name)) - .setCacheable(true); + relationship = findIdentityObjectRelationshipByAttributes(getHibernateSession(ctx), fromIO, toIO, type.getId(), name); } - - HibernateIdentityObjectRelationship relationship = (HibernateIdentityObjectRelationship) crit.uniqueResult(); - if (relationship == null) { throw new IdentityException("Relationship not present in the store"); - } - - try { - fromIO.getFromRelationships().remove(relationship); - toIO.getToRelationships().remove(relationship); - getHibernateSession(ctx).delete(relationship); - getHibernateSession(ctx).flush(); - } catch (HibernateException e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); + } else { + try { + fromIO.getFromRelationships().remove(relationship); + toIO.getToRelationships().remove(relationship); + getHibernateSession(ctx).remove(relationship); + getHibernateSession(ctx).flush(); + } catch (HibernateException e) { + throw new IdentityException("Cannot remove relationship"); } - - throw new IdentityException("Cannot remove relationship"); } - } public void removeRelationships(IdentityStoreInvocationContext ctx, @@ -1088,36 +894,19 @@ public void removeRelationships(IdentityStoreInvocationContext ctx, boolean named) throws IdentityException { HibernateIdentityObject hio1 = safeGet(ctx, identity1); HibernateIdentityObject hio2 = safeGet(ctx, identity2); - - Criteria criteria = getHibernateSession(ctx).createCriteria(HibernateIdentityObjectRelationship.class) - .setCacheable(true) - .add(Restrictions.or( - Restrictions.and( - Restrictions.eq("fromIdentityObject", hio1), - Restrictions.eq("toIdentityObject", hio2)), - Restrictions.and( - Restrictions.eq("fromIdentityObject", hio2), - Restrictions.eq("toIdentityObject", - hio1)))); - - List results = criteria.list(); + List results = findIdentityObjectRelationshipsByIdentities(getHibernateSession(ctx), + hio1, + hio2); Hibernate.initialize(results); - - for (Iterator iterator = results.iterator(); iterator.hasNext();) { - HibernateIdentityObjectRelationship relationship = (HibernateIdentityObjectRelationship) iterator.next(); - - if ((named && relationship.getName() != null) || - (!named && relationship.getName() == null)) { + for (HibernateIdentityObjectRelationship relationship : results) { + if ((named && relationship.getName() != null) + || (!named && relationship.getName() == null)) { try { relationship.getFromIdentityObject().getFromRelationships().remove(relationship); relationship.getToIdentityObject().getToRelationships().remove(relationship); - getHibernateSession(ctx).delete(relationship); + getHibernateSession(ctx).remove(relationship); getHibernateSession(ctx).flush(); } catch (HibernateException e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Cannot remove relationship"); } } @@ -1131,21 +920,19 @@ public Set resolveRelationships(IdentityStoreInvocat HibernateIdentityObject hio1 = safeGet(ctx, fromIdentity); HibernateIdentityObject hio2 = safeGet(ctx, toIdentity); - - Criteria criteria = getHibernateSession(ctx).createCriteria(HibernateIdentityObjectRelationship.class) - .setCacheable(true); - + List results; if (relationshipType != null) { - criteria.createAlias("type", "t").add(Restrictions.eq("t.name", relationshipType.getName())); + HibernateIdentityObjectRelationship relationship = + findIdentityObjectRelationshipByIdentityByType(getHibernateSession(ctx), + hio1, + hio2, + relationshipType.getName()); + results = relationship == null ? Collections.emptyList() : Collections.singletonList(relationship); + } else { + results = findIdentityObjectRelationshipsByIdentities(getHibernateSession(ctx), hio1, hio2); } - - criteria.add(Restrictions.eq("fromIdentityObject", hio1)) - .add(Restrictions.eq("toIdentityObject", hio2)); - - List results = criteria.list(); Hibernate.initialize(results); - - return new HashSet(results); + return new HashSet<>(results); } public int getRelationshipsCount(IdentityStoreInvocationContext ctx, @@ -1156,24 +943,17 @@ public int getRelationshipsCount(IdentityStoreInvocationContext ctx, String name, IdentityObjectSearchCriteria searchCriteria) throws IdentityException { - Criteria criteria = prepareResolveRelationshipsCriteria( - ctx, - identity, - type, - parent, - named, - name, - searchCriteria); - - criteria.setProjection(Projections.rowCount()); - - Iterator result = criteria.list().iterator(); - - if (!result.hasNext()) { - return 0; - } - - return Tools.convertToInt((Number) result.next()); + Query query = prepareResolveRelationshipsCriteria( + ctx, + identity, + type, + parent, + named, + name, + searchCriteria, + true); + Number count = query.uniqueResult(); + return count == null ? 0 : count.intValue(); } @@ -1184,548 +964,298 @@ public Set resolveRelationships(IdentityStoreInvocat boolean named, String name, IdentityObjectSearchCriteria searchCriteria) throws IdentityException { - HibernateIdentityObject hio = safeGet(ctx, identity); - - Criteria criteria = prepareResolveRelationshipsCriteria( - ctx, - identity, - type, - parent, - named, - name, - searchCriteria); - - List results = criteria.list(); - + Query query = prepareResolveRelationshipsCriteria(ctx, + identity, + type, + parent, + named, + name, + searchCriteria, + false); + List results = query.list(); Hibernate.initialize(results); - - return new HashSet(results); + return new HashSet<>(results); } - public Criteria prepareResolveRelationshipsCriteria(IdentityStoreInvocationContext ctx, - IdentityObject identity, - IdentityObjectRelationshipType type, - boolean parent, - boolean named, - String name, - IdentityObjectSearchCriteria searchCriteria) throws IdentityException { + @SuppressWarnings("unchecked") + public Query prepareResolveRelationshipsCriteria(IdentityStoreInvocationContext ctx, // NOSONAR + IdentityObject identity, + IdentityObjectRelationshipType type, + boolean parent, + boolean named, + String name, + IdentityObjectSearchCriteria searchCriteria, + boolean count) throws IdentityException { HibernateIdentityObject hio = safeGet(ctx, identity); - Criteria criteria = getHibernateSession(ctx).createCriteria(HibernateIdentityObjectRelationship.class); - criteria.setCacheable(true); + StringBuilder queryString = new StringBuilder(count ? "SELECT COUNT(r) FROM HibernateIdentityObjectRelationship r" : + "SELECT r FROM HibernateIdentityObjectRelationship r"); + queryString.append(" FETCH JOIN r.fromIdentityObject fromIo"); + queryString.append(" FETCH JOIN r.toIdentityObject toIo"); + queryString.append(" WHERE"); + List paramNames = new ArrayList<>(); + List paramValues = new ArrayList<>(); if (type != null) { HibernateIdentityObjectRelationshipType hibernateType = getHibernateIdentityObjectRelationshipType(ctx, type); + paramNames.add("type"); + paramValues.add(hibernateType); - criteria.add(Restrictions.eq("type", hibernateType)); - } - - if (name != null) { - criteria.add(Restrictions.eq("name.name", name)); - } else if (named) { - criteria.add(Restrictions.isNotNull("name")); - } else { - criteria.add(Restrictions.isNull("name")); + queryString.append(" r.type = :type"); + queryString.append(" AND"); } + paramNames.add(IDENTITY_PARAM); + paramValues.add(hio); if (parent) { - criteria.add(Restrictions.eq("fromIdentityObject", hio)); + queryString.append(" r.fromIdentityObject = :identity"); + queryString.append(" AND"); } else { - criteria.add(Restrictions.eq("toIdentityObject", hio)); + queryString.append(" r.toIdentityObject = :identity"); + queryString.append(" AND"); } - criteria.setFetchMode("fromIdentityObject", FetchMode.JOIN); - criteria.setFetchMode("toIdentityObject", FetchMode.JOIN); + if (name != null) { + paramNames.add("name"); + paramValues.add(name); - if (searchCriteria != null && searchCriteria.isPaged() && !searchCriteria.isFiltered()) { - if (searchCriteria.getMaxResults() > 0) { - criteria.setMaxResults(searchCriteria.getMaxResults()); - } - criteria.setFirstResult(searchCriteria.getFirstResult()); + queryString.append(" r.name.name = :name"); + } else if (named) { + queryString.append(" r.name IS NOT NULL"); + } else { + queryString.append(" r.name.name IS NULL"); } - if (searchCriteria != null && searchCriteria.isSorted()) { if (parent) { - criteria.createAlias("toIdentityObject", "io"); if (searchCriteria.isAscending()) { - criteria.addOrder(Order.asc("io.name")); + queryString.append(" ORDER BY toIo.name ASC"); } else { - criteria.addOrder(Order.desc("io.name")); + queryString.append(" ORDER BY toIo.name DESC"); } } else { - criteria.createAlias("fromIdentityObject", "io"); if (searchCriteria.isAscending()) { - criteria.addOrder(Order.asc("io.name")); + queryString.append(" ORDER BY fromIo.name ASC"); } else { - criteria.addOrder(Order.desc("io.name")); + queryString.append(" ORDER BY fromIo.name DESC"); } } } - - return criteria; - } - - public String createRelationshipName(IdentityStoreInvocationContext ctx, String name) throws IdentityException, - OperationNotSupportedException { - if (name == null) { - throw new IllegalArgumentException("name is null"); + @SuppressWarnings({ "rawtypes", "deprecation" }) + Query query = getHibernateSession(ctx).createQuery(queryString.toString()); // NOSONAR + if (searchCriteria != null && searchCriteria.isPaged() && !searchCriteria.isFiltered()) { + if (searchCriteria.getMaxResults() > 0) { + query.setMaxResults(searchCriteria.getMaxResults()); + } + query.setFirstResult(searchCriteria.getFirstResult()); } + for (int i = 0; i < paramNames.size(); i++) { + query.setParameter(paramNames.get(i), paramValues.get(i)); + } + return query; + } + public String createRelationshipName(IdentityStoreInvocationContext ctx, String name) throws IdentityException { + checkName(name); Session hibernateSession = getHibernateSession(ctx); - HibernateRealm realm = getRealm(hibernateSession, ctx); - try { - HibernateIdentityObjectRelationshipName hiorn = - (HibernateIdentityObjectRelationshipName) hibernateSession.createQuery(HibernateIdentityObjectRelationshipName.findIdentityObjectRelationshipNameByName) - .setParameter("name", - name) - .setParameter("realmName", - realm.getName()) - .uniqueResult(); - + HibernateIdentityObjectRelationshipName hiorn = findIdentityObjectRelationshipNameByName(hibernateSession, + name, + realm.getName()); if (hiorn != null) { throw new IdentityException("Relationship name already exists"); } - hiorn = new HibernateIdentityObjectRelationshipName(name, realm); getHibernateSession(ctx).persist(hiorn); getHibernateSession(ctx).flush(); - + return name; } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Cannot create new relationship name: " + name, e); } - - return name; } - public String removeRelationshipName(IdentityStoreInvocationContext ctx, String name) throws IdentityException, - OperationNotSupportedException { - if (name == null) { - throw new IllegalArgumentException("name is null"); - } + public String removeRelationshipName(IdentityStoreInvocationContext ctx, String name) throws IdentityException { + checkName(name); Session hibernateSession = getHibernateSession(ctx); - try { - HibernateIdentityObjectRelationshipName hiorn = (HibernateIdentityObjectRelationshipName) hibernateSession - .createCriteria(HibernateIdentityObjectRelationshipName.class) - .createAlias("realm", - "rm") - .add(Restrictions.eq("name", - name)) - .add(Restrictions.eq("rm.name", - getRealmName(ctx))) - .setCacheable(true) - .uniqueResult(); - - if (hiorn == null) { - throw new IdentityException("Relationship name doesn't exist"); - } - - removeRelationshipsByName(ctx, hiorn); - - hibernateSession.delete(hiorn); + HibernateIdentityObjectRelationshipName relationshipName = getRelationshipName(ctx, name, hibernateSession); + removeRelationshipsByName(ctx, relationshipName); + hibernateSession.remove(relationshipName); hibernateSession.flush(); - + return name; } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Cannot remove new relationship name: " + name, e); } + } + + public Set getRelationshipNames(IdentityStoreInvocationContext ctx, + IdentityObjectSearchCriteria criteria) throws IdentityException { + return getRelationshipNames(ctx, criteria, null); + } - return name; + public Set getRelationshipNames(IdentityStoreInvocationContext ctx) throws IdentityException { + return getRelationshipNames(ctx, (IdentityObjectSearchCriteria) null); } public Set getRelationshipNames(IdentityStoreInvocationContext ctx, - IdentityObjectSearchCriteria criteria) throws IdentityException, - OperationNotSupportedException { + IdentityObject identity, + IdentityObjectSearchCriteria criteria) throws IdentityException { + if (identity == null) { + throw new IllegalArgumentException("identity is mandatory"); + } + HibernateIdentityObject hibernateObject = safeGet(ctx, identity); + return getRelationshipNames(ctx, hibernateObject, criteria); + } - Set names = null; + public Set getRelationshipNames(IdentityStoreInvocationContext ctx, + IdentityObject identity) throws IdentityException { + return getRelationshipNames(ctx, identity, null); + } + public Map getRelationshipNameProperties(IdentityStoreInvocationContext ctx, + String name) throws IdentityException { + checkName(name); Session hibernateSession = getHibernateSession(ctx); - try { - // Query q = null; - - Criteria c = hibernateSession.createCriteria(HibernateIdentityObjectRelationshipName.class) - .setCacheable(true) - .createAlias("realm", "r") - .setProjection(Projections.property("name")); - - if (criteria != null && criteria.isSorted()) { - if (criteria.isAscending()) { - c.addOrder(Order.asc("name")); - } else { - c.addOrder(Order.desc("name")); - } - } - - c.add(Restrictions.eq("r.name", getRealmName(ctx))); - - if (criteria != null && criteria.getFilter() != null) { - c.add(Restrictions.like("name", criteria.getFilter().replaceAll("\\*", "%"))); - } else { - c.add(Restrictions.like("name", "%")); - } - - if (criteria != null && criteria.isPaged()) { - c.setFirstResult(criteria.getFirstResult()); - if (criteria.getMaxResults() > 0) { - c.setMaxResults(criteria.getMaxResults()); - } - } - - List results = (List) c.list(); - - Hibernate.initialize(results); - - names = new HashSet(results); - - } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - - throw new IdentityException("Cannot get relationship names. ", e); - } - - return names; - } - - public Set getRelationshipNames(IdentityStoreInvocationContext ctx) throws IdentityException, - OperationNotSupportedException { - return getRelationshipNames(ctx, (IdentityObjectSearchCriteria) null); - } - - public Set getRelationshipNames(IdentityStoreInvocationContext ctx, - IdentityObject identity, - IdentityObjectSearchCriteria criteria) throws IdentityException, - OperationNotSupportedException { - - Set names; - - HibernateIdentityObject hibernateObject = safeGet(ctx, identity); - - Session hibernateSession = getHibernateSession(ctx); - - try { - Criteria c = hibernateSession.createCriteria(HibernateIdentityObjectRelationship.class) - .setCacheable(true) - .createAlias("name", "n") - .setProjection(Projections.property("n.name")); - - if (criteria != null) { - if (criteria.isAscending()) { - c.addOrder(Order.asc("n.name")); - } else { - c.addOrder(Order.desc("n.name")); - } - } - - c.add(Restrictions.or( - Restrictions.eq("fromIdentityObject", hibernateObject), - Restrictions.eq("toIdentityObject", hibernateObject))); - - if (criteria != null && criteria.isPaged()) { - c.setFirstResult(criteria.getFirstResult()); - if (criteria.getMaxResults() > 0) { - c.setMaxResults(criteria.getMaxResults()); - } - } - - List results = (List) c.list(); - - Hibernate.initialize(results); - - names = new HashSet(results); - - } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - - throw new IdentityException("Cannot get relationship names. ", e); - } - - return names; - } - - public Set getRelationshipNames(IdentityStoreInvocationContext ctx, - IdentityObject identity) throws IdentityException, OperationNotSupportedException { - return getRelationshipNames(ctx, identity, null); - } - - public Map getRelationshipNameProperties(IdentityStoreInvocationContext ctx, - String name) throws IdentityException, OperationNotSupportedException { - if (name == null) { - throw new IllegalArgumentException("name is null"); - } - - Session hibernateSession = getHibernateSession(ctx); - - try { - - HibernateIdentityObjectRelationshipName hiorn = - (HibernateIdentityObjectRelationshipName) hibernateSession.createCriteria(HibernateIdentityObjectRelationshipName.class) - .setCacheable(true) - .createAlias("realm", - "r") - .add(Restrictions.eq("name", - name)) - .add(Restrictions.eq("r.name", - getRealmName(ctx))) - .uniqueResult(); - - if (hiorn == null) { - throw new IdentityException("Relationship name doesn't exist"); - } - - Hibernate.initialize(hiorn.getProperties()); - - return new HashMap(hiorn.getProperties()); - - } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - - throw new IdentityException("Cannot get relationship name properties: " + name, e); - } - } + HibernateIdentityObjectRelationshipName relationshipName = getRelationshipName(ctx, name, hibernateSession); + Hibernate.initialize(relationshipName.getProperties()); + return new HashMap<>(relationshipName.getProperties()); + } catch (Exception e) { + throw new IdentityException("Cannot get relationship name properties: " + name, e); + } + } public void setRelationshipNameProperties(IdentityStoreInvocationContext ctx, String name, - Map properties) throws IdentityException, - OperationNotSupportedException { - if (name == null) { - throw new IllegalArgumentException("name is null"); - } - + Map properties) throws IdentityException { + checkName(name); Session hibernateSession = getHibernateSession(ctx); - try { - HibernateIdentityObjectRelationshipName hiorn = - (HibernateIdentityObjectRelationshipName) hibernateSession.createCriteria(HibernateIdentityObjectRelationshipName.class) - .setCacheable(true) - .createAlias("realm", - "r") - .add(Restrictions.eq("name", - name)) - .add(Restrictions.eq("r.name", - getRealmName(ctx))) - .uniqueResult(); - - if (hiorn == null) { - throw new IdentityException("Relationship name doesn't exist"); - } - - hiorn.getProperties().putAll(properties); - + HibernateIdentityObjectRelationshipName relationshipName = getRelationshipName(ctx, name, hibernateSession); + relationshipName.getProperties().putAll(properties); + hibernateSession.persist(relationshipName); + hibernateSession.flush(); } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Cannot set relationship name properties: " + name, e); } } public void removeRelationshipNameProperties(IdentityStoreInvocationContext ctx, String name, - Set properties) throws IdentityException, OperationNotSupportedException { - if (name == null) { - throw new IllegalArgumentException("name is null"); - } - + Set properties) throws IdentityException { + checkName(name); Session hibernateSession = getHibernateSession(ctx); - try { - HibernateIdentityObjectRelationshipName hiorn = - (HibernateIdentityObjectRelationshipName) hibernateSession.createCriteria(HibernateIdentityObjectRelationshipName.class) - .setCacheable(true) - .createAlias("realm", - "r") - .add(Restrictions.eq("name", - name)) - .add(Restrictions.eq("r.name", - getRealmName(ctx))) - .uniqueResult(); - - if (hiorn == null) { - throw new IdentityException("Relationship name doesn't exist"); - } - - Hibernate.initialize(hiorn.getProperties()); + HibernateIdentityObjectRelationshipName relationshipName = getRelationshipName(ctx, name, hibernateSession); + Hibernate.initialize(relationshipName.getProperties()); for (String property : properties) { - hiorn.getProperties().remove(property); + relationshipName.getProperties().remove(property); } - + hibernateSession.persist(relationshipName); + hibernateSession.flush(); } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Cannot remove relationship name properties: " + name, e); } } public Map getRelationshipProperties(IdentityStoreInvocationContext ctx, - IdentityObjectRelationship relationship) throws IdentityException, - OperationNotSupportedException { + IdentityObjectRelationship relationship) throws IdentityException { HibernateIdentityObject fromIO = safeGet(ctx, relationship.getFromIdentityObject()); HibernateIdentityObject toIO = safeGet(ctx, relationship.getToIdentityObject()); HibernateIdentityObjectRelationshipType type = getHibernateIdentityObjectRelationshipType(ctx, relationship.getType()); - - Criteria criteria = getHibernateSession(ctx).createCriteria(HibernateIdentityObjectRelationship.class) - .setCacheable(true) - .createAlias("type", "t") - .add(Restrictions.eq("t.name", type.getName())) - .add(Restrictions.eq("fromIdentityObject", fromIO)) - .add(Restrictions.eq("toIdentityObject", toIO)); - - if (relationship.getName() != null) { - HibernateIdentityObjectRelationshipName relationshipName = - (HibernateIdentityObjectRelationshipName) getHibernateSession(ctx) - .createCriteria(HibernateIdentityObjectRelationshipName.class) - .add(Restrictions.eq("name", - relationship.getName())) - .setCacheable(true) - .uniqueResult(); - - if (relationshipName == null) { - throw new IdentityException("Relationship name not present in the store"); - } - - criteria.createAlias("name", "n").add(Restrictions.eq("n.name", relationship.getName())); - } - try { - HibernateIdentityObjectRelationship hibernateRelationship = (HibernateIdentityObjectRelationship) criteria.uniqueResult(); - + HibernateIdentityObjectRelationship hibernateRelationship; + if (relationship.getName() != null) { + hibernateRelationship = findIdentityObjectRelationshipByAttributes(getHibernateSession(ctx), + fromIO, + toIO, + type.getId(), + relationship.getName()); + } else { + hibernateRelationship = findIdentityObjectRelationshipByIdentityByType(getHibernateSession(ctx), + fromIO, + toIO, + type.getName()); + } Hibernate.initialize(hibernateRelationship.getProperties()); - - return new HashMap(hibernateRelationship.getProperties()); + return new HashMap<>(hibernateRelationship.getProperties()); } catch (HibernateException e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - - throw new IdentityException("Cannot obtain relationship properties: ", e); + throw new IdentityException(CANNOT_OBTAIN_RELATIONSHIP_PROPERTIES + relationship, e); } } public void setRelationshipProperties(IdentityStoreInvocationContext ctx, IdentityObjectRelationship relationship, - Map properties) throws IdentityException, OperationNotSupportedException { + Map properties) throws IdentityException { HibernateIdentityObject fromIO = safeGet(ctx, relationship.getFromIdentityObject()); HibernateIdentityObject toIO = safeGet(ctx, relationship.getToIdentityObject()); HibernateIdentityObjectRelationshipType type = getHibernateIdentityObjectRelationshipType(ctx, relationship.getType()); - - Criteria criteria = getHibernateSession(ctx).createCriteria(HibernateIdentityObjectRelationship.class) - .setCacheable(true) - .createAlias("type", "t") - .add(Restrictions.eq("t.name", type.getName())) - .add(Restrictions.eq("fromIdentityObject", fromIO)) - .add(Restrictions.eq("toIdentityObject", toIO)); - - if (relationship.getName() != null) { - - HibernateIdentityObjectRelationshipName relationshipName = - (HibernateIdentityObjectRelationshipName) getHibernateSession(ctx) - .createCriteria(HibernateIdentityObjectRelationshipName.class) - .add(Restrictions.eq("name", - relationship.getName())) - .setCacheable(true) - .uniqueResult(); - - if (relationshipName == null) { - throw new IdentityException("Relationship name not present in the store"); - } - - criteria.createAlias("name", "n").add(Restrictions.eq("n.name", relationship.getName())); - - } - try { - HibernateIdentityObjectRelationship hibernateRelationship = (HibernateIdentityObjectRelationship) criteria.uniqueResult(); - + HibernateIdentityObjectRelationship hibernateRelationship; + Session hibernateSession = getHibernateSession(ctx); + if (relationship.getName() != null) { + hibernateRelationship = findIdentityObjectRelationshipByAttributes(hibernateSession, + fromIO, + toIO, + type.getId(), + relationship.getName()); + } else { + hibernateRelationship = findIdentityObjectRelationshipByIdentityByType(hibernateSession, + fromIO, + toIO, + type.getName()); + } + Hibernate.initialize(hibernateRelationship.getProperties()); hibernateRelationship.getProperties().putAll(properties); + hibernateSession.persist(hibernateRelationship); + hibernateSession.flush(); } catch (HibernateException e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - - throw new IdentityException("Cannot update relationship properties: ", e); + throw new IdentityException(CANNOT_OBTAIN_RELATIONSHIP_PROPERTIES + relationship, e); } } public void removeRelationshipProperties(IdentityStoreInvocationContext ctx, IdentityObjectRelationship relationship, - Set properties) throws IdentityException, OperationNotSupportedException { + Set properties) throws IdentityException { HibernateIdentityObject fromIO = safeGet(ctx, relationship.getFromIdentityObject()); HibernateIdentityObject toIO = safeGet(ctx, relationship.getToIdentityObject()); HibernateIdentityObjectRelationshipType type = getHibernateIdentityObjectRelationshipType(ctx, relationship.getType()); - - Criteria criteria = getHibernateSession(ctx).createCriteria(HibernateIdentityObjectRelationship.class) - .setCacheable(true) - .createAlias("type", "t") - .add(Restrictions.eq("t.name", type.getName())) - .add(Restrictions.eq("fromIdentityObject", fromIO)) - .add(Restrictions.eq("toIdentityObject", toIO)); - - if (relationship.getName() == null) { - HibernateIdentityObjectRelationshipName relationshipName = - (HibernateIdentityObjectRelationshipName) getHibernateSession(ctx) - .createCriteria(HibernateIdentityObjectRelationshipName.class) - .add(Restrictions.eq("name", - relationship.getName())) - .setCacheable(true) - .uniqueResult(); - - if (relationshipName == null) { - throw new IdentityException("Relationship name not present in the store"); - } - - criteria.createAlias("name", "n").add(Restrictions.eq("n.name", relationship.getName())); - } - try { - HibernateIdentityObjectRelationship hibernateRelationship = (HibernateIdentityObjectRelationship) criteria.uniqueResult(); - + HibernateIdentityObjectRelationship hibernateRelationship; + Session hibernateSession = getHibernateSession(ctx); + if (relationship.getName() != null) { + hibernateRelationship = findIdentityObjectRelationshipByAttributes(hibernateSession, + fromIO, + toIO, + type.getId(), + relationship.getName()); + } else { + hibernateRelationship = findIdentityObjectRelationshipByIdentityByType(hibernateSession, + fromIO, + toIO, + type.getName()); + } Hibernate.initialize(hibernateRelationship.getProperties()); - for (String property : properties) { hibernateRelationship.getProperties().remove(property); } + hibernateSession.persist(hibernateRelationship); + hibernateSession.flush(); } catch (HibernateException e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - - throw new IdentityException("Cannot update relationship properties: ", e); + throw new IdentityException(CANNOT_OBTAIN_RELATIONSHIP_PROPERTIES + relationship, e); } } // Attribute store - public Set getSupportedAttributeNames(IdentityStoreInvocationContext ctx, IdentityObjectType identityType) throws IdentityException { checkIOType(identityType); - if (attributeMappings.containsKey(identityType.getName())) { return attributeMappings.get(identityType.getName()); } - - return new HashSet(); - + return new HashSet<>(); } public IdentityObjectAttribute getAttribute(IdentityStoreInvocationContext ctx, @@ -1751,32 +1281,18 @@ public IdentityObjectAttribute getAttribute(IdentityStoreInvocationContext ctx, public Map getAttributes(IdentityStoreInvocationContext ctx, IdentityObject identity) throws IdentityException { - HibernateRealm realm = getRealm(getHibernateSession(ctx), ctx); - HibernateIdentityObjectType hibernateType = getHibernateIdentityObjectType(ctx, identity.getIdentityType()); - - Map result = new HashMap(); - - Criteria criteria = getHibernateSession(ctx).createCriteria(HibernateIdentityObjectAttribute.class) - .setCacheable(true) - .setFetchMode("textValues", FetchMode.JOIN) - .setFetchSize(20) - .createAlias("identityObject", "io") - .add(Restrictions.eq("io.name", identity.getName())) - .add(Restrictions.eq("io.realm", realm)) - .add(Restrictions.eq("io.identityType", hibernateType)); - - Collection storeAttributes = (Collection) criteria.list(); - + HibernateIdentityObject hibernateIdentityObject = getHibernateIdentityObject(ctx, identity); + List storeAttributes = findIdentityAttributes(getHibernateSession(ctx), + hibernateIdentityObject); // Remap the names + Map result = new HashMap<>(); for (HibernateIdentityObjectAttribute attribute : storeAttributes) { String name = resolveAttributeNameFromStoreMapping(identity.getIdentityType(), attribute.getName()); if (name != null) { result.put(name, attribute); } } - return result; - } public Map getAttributesMetaData(IdentityStoreInvocationContext invocationContext, @@ -1784,33 +1300,24 @@ public Map getAttributesMetaData(Identi return attributesMetaData.get(identityType.getName()); } - public void updateAttributes(IdentityStoreInvocationContext ctx, + @SuppressWarnings("unchecked") + public void updateAttributes(IdentityStoreInvocationContext ctx, // NOSONAR IdentityObject identity, IdentityObjectAttribute[] attributes) throws IdentityException { + checkAttributes(attributes); - if (attributes == null) { - throw new IllegalArgumentException("attributes are null"); - } - - Map mappedAttributes = new HashMap(); - + Map mappedAttributes = new HashMap<>(); Map mdMap = attributesMetaData.get(identity.getIdentityType().getName()); - for (IdentityObjectAttribute attribute : attributes) { String name = resolveAttributeStoreMapping(identity.getIdentityType(), attribute.getName()); mappedAttributes.put(name, attribute); - if (mdMap == null || !mdMap.containsKey(attribute.getName())) { - if (!isAllowNotDefinedAttributes) { - throw new IdentityException("Cannot add not defined attribute. Use '" + ALLOW_NOT_DEFINED_ATTRIBUTES + - "' option if needed. Attribute name: " + attribute.getName()); - } + if ((mdMap == null || !mdMap.containsKey(attribute.getName())) && !isAllowNotDefinedAttributes) { + throw new IdentityException("Cannot add not defined attribute. Use '" + ALLOW_NOT_DEFINED_ATTRIBUTES + + OPTION_IF_NEEDED_ATTRIBUTE_NAME + attribute.getName()); } - if (mdMap != null && mdMap.containsKey(attribute.getName())) { - IdentityObjectAttributeMetaData amd = mdMap.get(attribute.getName()); - if (!amd.isMultivalued() && attribute.getSize() > 1) { throw new IdentityException("Cannot assigned multiply values to single valued attribute: " + attribute.getName()); } @@ -1818,15 +1325,12 @@ public void updateAttributes(IdentityStoreInvocationContext ctx, // Just silently fail and go on mappedAttributes.remove(name); continue; - // throw new IdentityException("Cannot update readonly attribute: " + - // attribute.getName()); } if (amd.isUnique()) { IdentityObject checkIdentity = findIdentityObjectByUniqueAttribute(ctx, identity.getIdentityType(), attribute); - if (checkIdentity != null && !checkIdentity.getName().equals(identity.getName())) { throw new IdentityException("Unique attribute '" + attribute.getName() + " value already set for identityObject: " + checkIdentity); @@ -1836,15 +1340,14 @@ public void updateAttributes(IdentityStoreInvocationContext ctx, String type = amd.getType(); // check if all values have proper type - for (Object value : attribute.getValues()) { if (type.equals(IdentityObjectAttributeMetaData.TEXT_TYPE) && !(value instanceof String)) { - throw new IdentityException("Cannot update text type attribute with not String type value: " - + attribute.getName() + " / " + value); + throw new IdentityException("Cannot update text type attribute with not String type value: " + attribute.getName() + + " / " + value); } if (type.equals(IdentityObjectAttributeMetaData.BINARY_TYPE) && !(value instanceof byte[])) { - throw new IdentityException("Cannot update binary type attribute with not byte[] type value: " - + attribute.getName() + " / " + value); + throw new IdentityException("Cannot update binary type attribute with not byte[] type value: " + attribute.getName() + + " / " + value); } } if (type.equals(IdentityObjectAttributeMetaData.BINARY_TYPE) && attribute.getValues().size() > 1) { @@ -1858,7 +1361,7 @@ public void updateAttributes(IdentityStoreInvocationContext ctx, Hibernate.initialize(hibernateObject.getAttributes()); - for (String name : mappedAttributes.keySet()) { + for (String name : mappedAttributes.keySet()) { // NOSONAR IdentityObjectAttribute attribute = mappedAttributes.get(name); IdentityObjectAttributeMetaData amd = null; @@ -1877,11 +1380,11 @@ public void updateAttributes(IdentityStoreInvocationContext ctx, present = true; if (storeAttribute.getType().equals(HibernateIdentityObjectAttribute.TYPE_TEXT)) { if (!type.equals(IdentityObjectAttributeMetaData.TEXT_TYPE)) { - throw new IdentityException("Wrong attribute mapping. Attribute persisted as text is mapped with: " - + type + ". Attribute name: " + name); + throw new IdentityException("Wrong attribute mapping. Attribute persisted as text is mapped with: " + type + + ATTRIBUTE_NAME + name); } - Set v = new HashSet(); + Set v = new HashSet<>(); for (Object value : attribute.getValues()) { v.add(value.toString()); } @@ -1890,8 +1393,8 @@ public void updateAttributes(IdentityStoreInvocationContext ctx, } else if (storeAttribute.getType().equals(HibernateIdentityObjectAttribute.TYPE_BINARY)) { if (!type.equals(IdentityObjectAttributeMetaData.BINARY_TYPE)) { - throw new IdentityException("Wrong attribute mapping. Attribute persisted as binary is mapped with: " - + type + ". Attribute name: " + name); + throw new IdentityException("Wrong attribute mapping. Attribute persisted as binary is mapped with: " + type + + ATTRIBUTE_NAME + name); } HibernateIdentityObjectAttributeBinaryValue bv = new HibernateIdentityObjectAttributeBinaryValue((byte[]) attribute.getValue()); @@ -1904,7 +1407,7 @@ public void updateAttributes(IdentityStoreInvocationContext ctx, } } - if (!present && attribute.getValues() != null && attribute.getValues().size() > 0) { + if (!present && attribute.getValues() != null && !attribute.getValues().isEmpty()) { HibernateIdentityObjectAttribute newAttribute = new HibernateIdentityObjectAttribute(hibernateObject, name, type); if (type.equals(HibernateIdentityObjectAttribute.TYPE_TEXT)) { newAttribute.setTextValues(attribute.getValues()); @@ -1921,20 +1424,15 @@ public void updateAttributes(IdentityStoreInvocationContext ctx, } - public void addAttributes(IdentityStoreInvocationContext ctx, + @SuppressWarnings("unchecked") + public void addAttributes(IdentityStoreInvocationContext ctx, // NOSONAR IdentityObject identity, IdentityObjectAttribute[] attributes) throws IdentityException { - if (attributes == null) { - throw new IllegalArgumentException("attributes are null"); - } - - Map mappedAttributes = new HashMap(); + checkAttributes(attributes); + Map mappedAttributes = new HashMap<>(); Map mdMap = attributesMetaData.get(identity.getIdentityType().getName()); - - Session hibernateSession = getHibernateSession(ctx); - for (IdentityObjectAttribute attribute : attributes) { String name = resolveAttributeStoreMapping(identity.getIdentityType(), attribute.getName()); mappedAttributes.put(name, attribute); @@ -1942,7 +1440,7 @@ public void addAttributes(IdentityStoreInvocationContext ctx, if ((mdMap == null || !mdMap.containsKey(attribute.getName())) && !isAllowNotDefinedAttributes) { throw new IdentityException("Cannot add not defined attribute. Use '" + ALLOW_NOT_DEFINED_ATTRIBUTES + - "' option if needed. Attribute name: " + attribute.getName()); + OPTION_IF_NEEDED_ATTRIBUTE_NAME + attribute.getName()); } @@ -1961,8 +1459,6 @@ public void addAttributes(IdentityStoreInvocationContext ctx, // Just silently fail and go on mappedAttributes.remove(name); continue; - // throw new IdentityException("Cannot add readonly attribute: " + - // attribute.getName()); } if (amd.isUnique()) { @@ -1982,12 +1478,12 @@ public void addAttributes(IdentityStoreInvocationContext ctx, for (Object value : attribute.getValues()) { if (type.equals(IdentityObjectAttributeMetaData.TEXT_TYPE) && !(value instanceof String)) { - throw new IdentityException("Cannot add text type attribute with not String type value: " - + attribute.getName() + " / " + value); + throw new IdentityException("Cannot add text type attribute with not String type value: " + attribute.getName() + + " / " + value); } if (type.equals(IdentityObjectAttributeMetaData.BINARY_TYPE) && !(value instanceof byte[])) { - throw new IdentityException("Cannot add binary type attribute with not byte[] type value: " - + attribute.getName() + " / " + value); + throw new IdentityException("Cannot add binary type attribute with not byte[] type value: " + attribute.getName() + + " / " + value); } } @@ -2002,7 +1498,7 @@ public void addAttributes(IdentityStoreInvocationContext ctx, Hibernate.initialize(hibernateObject.getAttributes()); - for (String name : mappedAttributes.keySet()) { + for (String name : mappedAttributes.keySet()) { // NOSONAR IdentityObjectAttribute attribute = mappedAttributes.get(name); IdentityObjectAttributeMetaData amd = mdMap != null ? mdMap.get(attribute.getName()) : null; @@ -2022,11 +1518,11 @@ public void addAttributes(IdentityStoreInvocationContext ctx, if (hibernateAttribute != null) { if (hibernateAttribute.getType().equals(HibernateIdentityObjectAttribute.TYPE_TEXT)) { if (!type.equals(IdentityObjectAttributeMetaData.TEXT_TYPE)) { - throw new IdentityException("Wrong attribute mapping. Attribute persisted as text is mapped with: " - + type + ". Attribute name: " + name); + throw new IdentityException("Wrong attribute mapping. Attribute persisted as text is mapped with: " + type + + ATTRIBUTE_NAME + name); } - Set mergedValues = new HashSet(hibernateAttribute.getValues()); + Set mergedValues = new HashSet<>(hibernateAttribute.getValues()); for (Object value : attribute.getValues()) { mergedValues.add(value.toString()); } @@ -2035,8 +1531,8 @@ public void addAttributes(IdentityStoreInvocationContext ctx, } else if (hibernateAttribute.getType().equals(HibernateIdentityObjectAttribute.TYPE_BINARY)) { if (!type.equals(IdentityObjectAttributeMetaData.BINARY_TYPE)) { - throw new IdentityException("Wrong attribute mapping. Attribute persisted as binary is mapped with: " - + type + ". Attribute name: " + name); + throw new IdentityException("Wrong attribute mapping. Attribute persisted as binary is mapped with: " + type + + ATTRIBUTE_NAME + name); } HibernateIdentityObjectAttributeBinaryValue bv = @@ -2050,8 +1546,7 @@ public void addAttributes(IdentityStoreInvocationContext ctx, } else { if (type.equals(IdentityObjectAttributeMetaData.TEXT_TYPE)) { - Set values = new HashSet(); - + Set values = new HashSet<>(); for (Object value : attribute.getValues()) { values.add(value.toString()); } @@ -2060,8 +1555,7 @@ public void addAttributes(IdentityStoreInvocationContext ctx, HibernateIdentityObjectAttribute.TYPE_TEXT); hibernateAttribute.setTextValues(values); } else if (type.equals(IdentityObjectAttributeMetaData.BINARY_TYPE)) { - Set values = new HashSet(); - + Set values = new HashSet<>(); for (Object value : attribute.getValues()) { values.add((byte[]) value); } @@ -2104,7 +1598,7 @@ public void removeAttributes(IdentityStoreInvocationContext ctx, } else { if (!isAllowNotDefinedAttributes) { throw new IdentityException("Cannot remove not defined attribute. Use '" + ALLOW_NOT_DEFINED_ATTRIBUTES + - "' option if needed. Attribute name: " + attributes[i]); + OPTION_IF_NEEDED_ATTRIBUTE_NAME + attributes[i]); } } @@ -2127,18 +1621,11 @@ public IdentityObject findIdentityObjectByUniqueAttribute(IdentityStoreInvocatio } checkIOType(identityObjectType); - - // TODO: check both binary and text with multivalue - String attrMappedName = resolveAttributeStoreMapping(identityObjectType, attribute.getName()); - HibernateIdentityObjectType hiot = getHibernateIdentityObjectType(invocationCtx, identityObjectType); - Session session = getHibernateSession(invocationCtx); - HibernateRealm realm = getRealm(session, invocationCtx); - - if (attribute.getValues() == null || attribute.getValues().size() == 0) { + if (attribute.getValues() == null || attribute.getValues().isEmpty()) { return null; } @@ -2148,10 +1635,9 @@ public IdentityObject findIdentityObjectByUniqueAttribute(IdentityStoreInvocatio attrDuctTypeText = false; } - StringBuffer queryString = - new StringBuffer("select a from HibernateIdentityObjectAttribute a where a.identityObject.identityType = :identityType " - + - "and a.name = :attributeName and a.identityObject.realm = :realm"); + StringBuilder queryString = + new StringBuilder("select a from HibernateIdentityObjectAttribute a where a.identityObject.identityType = :identityType " + + "and a.name = :attributeName and a.identityObject.realm = :realm"); if (attrDuctTypeText) { for (int i = 0; i < attribute.getValues().size(); i++) { @@ -2163,10 +1649,11 @@ public IdentityObject findIdentityObjectByUniqueAttribute(IdentityStoreInvocatio queryString.append(" and :value = a.binaryValue"); } - Query q = session.createQuery(queryString.toString()); + Query q = + session.createQuery(queryString.toString(), HibernateIdentityObjectAttribute.class); q.setParameter("identityType", hiot) .setParameter("attributeName", attrMappedName) - .setParameter("realm", realm); + .setParameter(REALM_PARAM, realm); if (attrDuctTypeText) { int i = 0; @@ -2180,18 +1667,15 @@ public IdentityObject findIdentityObjectByUniqueAttribute(IdentityStoreInvocatio q.setParameter("value", attribute.getValue()); } - List attrs = (List) q.list(); - - if (attrs.size() == 0) { + List attrs = q.list(); + if (attrs.isEmpty()) { return null; + } else if (attrs.size() > 1) { + throw new IdentityException("Illegal state - more than one IdentityObject with the same unique attribute value: " + + attribute); + } else { + return attrs.get(0).getIdentityObject(); } - if (attrs.size() > 1) { - throw new IdentityException("Illegal state - more than one IdentityObject with the same unique attribute value: " - + attribute); - } - - return attrs.get(0).getIdentityObject(); - } public boolean validateCredential(IdentityStoreInvocationContext ctx, @@ -2205,39 +1689,23 @@ public boolean validateCredential(IdentityStoreInvocationContext ctx, if (supportedFeatures.isCredentialSupported(hibernateObject.getIdentityType(), credential.getType())) { - HibernateIdentityObjectCredential hibernateCredential = null; - - hibernateCredential = (HibernateIdentityObjectCredential) getHibernateSession(ctx) - .createCriteria(HibernateIdentityObjectCredential.class) - .createAlias("type", "t") - .add(Restrictions.eq("t.name", - credential.getType() - .getName())) - .add(Restrictions.eq("identityObject", - hibernateObject)) - .setCacheable(true) - .uniqueResult(); - + HibernateIdentityObjectCredential hibernateCredential = + getHibernateSession(ctx).createNamedQuery("HibernateIdentityObjectCredential.findCredentialByTypeAndIdentity", + HibernateIdentityObjectCredential.class) + .setParameter("cTypeName", + credential.getType().getName()) + .setParameter("ioId", hibernateObject.getId()) + .uniqueResult(); if (hibernateCredential == null) { return false; } - // Handle generic impl - - Object value = null; - Object tmpEncodedValue = credential.getEncodedValue(); - if (tmpEncodedValue != null) { - value = tmpEncodedValue; - } else { - // TODO: support for empty password should be configurable - value = credential.getValue(); - } - - if (value instanceof String && hibernateCredential.getTextValue() != null) { - return value.toString().equals(hibernateCredential.getTextValue()); - } else if (value instanceof byte[] && hibernateCredential.getBinaryValue() != null) { - return Arrays.equals((byte[]) value, hibernateCredential.getBinaryValue().getValue()); + Object value = tmpEncodedValue == null ? credential.getValue() : tmpEncodedValue; + if (value instanceof String valueString && hibernateCredential.getTextValue() != null) { + return valueString.equals(hibernateCredential.getTextValue()); + } else if (value instanceof byte[] valueBytes && hibernateCredential.getBinaryValue() != null) { + return Arrays.equals(valueBytes, hibernateCredential.getBinaryValue().getValue()); } else { throw new IdentityException("Not supported credential value: " + value.getClass()); } @@ -2276,22 +1744,12 @@ public void updateCredential(IdentityStoreInvocationContext ctx, hibernateObject.addCredential(hibernateCredential); } - Object value = null; - - // Handle generic impl - Object tmpEncodedValue = credential.getEncodedValue(); - if (tmpEncodedValue != null) { - value = tmpEncodedValue; - } else { - // TODO: support for empty password should be configurable - value = credential.getValue(); - } - - if (value instanceof String) { - hibernateCredential.setTextValue(value.toString()); - } else if (value instanceof byte[]) { - HibernateIdentityObjectCredentialBinaryValue bv = new HibernateIdentityObjectCredentialBinaryValue((byte[]) value); + Object value = tmpEncodedValue == null ? credential.getValue() : tmpEncodedValue; + if (value instanceof String valueString) { + hibernateCredential.setTextValue(valueString); + } else if (value instanceof byte[] valueBytes) { + HibernateIdentityObjectCredentialBinaryValue bv = new HibernateIdentityObjectCredentialBinaryValue(valueBytes); getHibernateSession(ctx).persist(bv); hibernateCredential.setBinaryValue(bv); } else { @@ -2299,11 +1757,8 @@ public void updateCredential(IdentityStoreInvocationContext ctx, } hibernateSession.persist(hibernateCredential); - hibernateObject.addCredential(hibernateCredential); - hibernateSession.flush(); - } else { throw new IdentityException("CredentialType not supported for a given IdentityObjectType"); } @@ -2336,10 +1791,6 @@ protected Session getHibernateSession(IdentityStoreInvocationContext ctx) throws return ((Session) hbIdentityStoreSession.getSessionContext()); } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Cannot obtain Hibernate Session", e); } } @@ -2353,13 +1804,11 @@ private void checkIOInstance(IdentityObject io) { private HibernateIdentityObject safeGet(IdentityStoreInvocationContext ctx, IdentityObject io) throws IdentityException { checkIOInstance(io); - - if (io instanceof HibernateIdentityObject) { - return (HibernateIdentityObject) io; + if (io instanceof HibernateIdentityObject identityObject) { + return identityObject; + } else { + return getHibernateIdentityObject(ctx, io); } - - return getHibernateIdentityObject(ctx, io); - } private void checkIOType(IdentityObjectType iot) throws IdentityException { @@ -2367,201 +1816,113 @@ private void checkIOType(IdentityObjectType iot) throws IdentityException { throw new IllegalArgumentException("IdentityObjectType is null"); } - if (!getSupportedFeatures().isIdentityObjectTypeSupported(iot)) { - if (!isAllowNotDefinedIdentityObjectTypes()) { - throw new IdentityException("IdentityType not supported by this IdentityStore implementation: " + iot); - } + if (!getSupportedFeatures().isIdentityObjectTypeSupported(iot) && !isAllowNotDefinedIdentityObjectTypes()) { + throw new IdentityException("IdentityType not supported by this IdentityStore implementation: " + iot); } } private HibernateIdentityObjectType getHibernateIdentityObjectType(IdentityStoreInvocationContext ctx, IdentityObjectType type) throws IdentityException { - checkIOType(type); - - HibernateIdentityObjectType hibernateType = null; - - Session hibernateSession = getHibernateSession(ctx); - + HibernateIdentityObjectType hibernateType; + String typeName = type.getName(); try { - - Criteria crit = hibernateSession.createCriteria(HibernateIdentityObjectType.class) - .add(Restrictions.eq("name", type.getName())) - .setCacheable(true); - - hibernateType = (HibernateIdentityObjectType) crit.uniqueResult(); - + Session hibernateSession = getHibernateSession(ctx); + hibernateType = findIdentityObjectTypeByName(hibernateSession, typeName); if (hibernateType == null) { if (isAllowNotDefinedIdentityObjectTypes()) { - populateObjectTypes(hibernateSession, new String[] { type.getName() }); + populateObjectTypes(hibernateSession, new String[] { typeName }); } - - hibernateType = (HibernateIdentityObjectType) crit.uniqueResult(); + hibernateType = findIdentityObjectTypeByName(hibernateSession, typeName); } - } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - - throw new IdentityException("IdentityObjectType[" + type.getName() + "] not present in the store.", e); + throw new IdentityException("IdentityObjectType[" + typeName + NOT_PRESENT_IN_THE_STORE, e); } - if (hibernateType == null) { - throw new IdentityException("IdentityObjectType[" + type.getName() + "] not present in the store."); + throw new IdentityException("IdentityObjectType[" + typeName + NOT_PRESENT_IN_THE_STORE); } - return hibernateType; } private HibernateIdentityObject getHibernateIdentityObject(IdentityStoreInvocationContext ctx, IdentityObject io) throws IdentityException { - HibernateIdentityObject hibernateObject = null; - HibernateIdentityObjectType hibernateType = getHibernateIdentityObjectType(ctx, io.getIdentityType()); - HibernateRealm realm = getRealm(getHibernateSession(ctx), ctx); - Session hibernateSession = getHibernateSession(ctx); - + HibernateIdentityObjectType hibernateType = getHibernateIdentityObjectType(ctx, io.getIdentityType()); + HibernateRealm realm = getRealm(hibernateSession, ctx); + HibernateIdentityObject identity; try { - - hibernateObject = (HibernateIdentityObject) hibernateSession.createCriteria(HibernateIdentityObject.class) - .add(Restrictions.eq("name", io.getName())) - .add(Restrictions.eq("identityType", hibernateType)) - .add(Restrictions.eq("realm", realm)) - .setCacheable(true) - .uniqueResult(); - + identity = findIdentityObjectByNameAndType(hibernateSession, io.getName(), hibernateType.getName(), realm.getName()); } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - - throw new IdentityException("IdentityObject[ " + io.getName() + " | " + io.getIdentityType().getName() - + "] not present in the store.", e); + throw new IdentityException("IdentityObject[ " + io.getName() + " | " + io.getIdentityType().getName() + + NOT_PRESENT_IN_THE_STORE, e); } - - return hibernateObject; + if (identity == null) { + throw new IdentityException("IdentityObject[ " + io.getName() + " | " + io.getIdentityType().getName() + + NOT_PRESENT_IN_THE_STORE); + } + return identity; } private HibernateIdentityObjectRelationshipType getHibernateIdentityObjectRelationshipType(IdentityStoreInvocationContext ctx, IdentityObjectRelationshipType iot) throws IdentityException { - HibernateIdentityObjectRelationshipType relationshipType = null; - - Session hibernateSession = getHibernateSession(ctx); - try { - - relationshipType = (HibernateIdentityObjectRelationshipType) hibernateSession - .createCriteria(HibernateIdentityObjectRelationshipType.class) - .add(Restrictions.eq("name", iot.getName())) - .setCacheable(true) - .uniqueResult(); + return findIdentityRelationshipTypeByName(getHibernateSession(ctx), iot.getName()); } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - - throw new IdentityException("IdentityObjectRelationshipType[ " + iot.getName() + "] not present in the store."); + throw new IdentityException("IdentityObjectRelationshipType[ " + iot.getName() + NOT_PRESENT_IN_THE_STORE); } - - return relationshipType; } private HibernateIdentityObjectCredentialType getHibernateIdentityObjectCredentialType(IdentityStoreInvocationContext ctx, IdentityObjectCredentialType credentialType) throws IdentityException { - Session session = getHibernateSession(ctx); - - HibernateIdentityObjectCredentialType hibernateType = null; - try { - hibernateType = (HibernateIdentityObjectCredentialType) session - .createCriteria(HibernateIdentityObjectCredentialType.class) - .add(Restrictions.eq("name", credentialType.getName())) - .setCacheable(true) - .uniqueResult(); + return findIdentityCredentialTypeByName(getHibernateSession(ctx), credentialType.getName()); } catch (HibernateException e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - - throw new IdentityException("IdentityObjectCredentialType[ " + credentialType.getName() + "] not present in the store."); + throw new IdentityException("IdentityObjectCredentialType[ " + credentialType.getName() + NOT_PRESENT_IN_THE_STORE); } - - return hibernateType; - } - public void populateObjectTypes(Session hibernateSession, String[] typeNames) throws Exception { - + public void populateObjectTypes(Session hibernateSession, String[] typeNames) { for (String typeName : typeNames) { - // Check if present - - HibernateIdentityObjectType hibernateType = - (HibernateIdentityObjectType) hibernateSession.createCriteria(HibernateIdentityObjectType.class) - .add(Restrictions.eq("name", - typeName)) - .uniqueResult(); - + HibernateIdentityObjectType hibernateType = findIdentityObjectTypeByName(hibernateSession, typeName); if (hibernateType == null) { hibernateType = new HibernateIdentityObjectType(typeName); hibernateSession.persist(hibernateType); + hibernateSession.flush(); } - } - } - public void populateRelationshipTypes(Session hibernateSession, String[] typeNames) throws Exception { - + public void populateRelationshipTypes(Session hibernateSession, String[] typeNames) { for (String typeName : typeNames) { - HibernateIdentityObjectRelationshipType hibernateType = - (HibernateIdentityObjectRelationshipType) hibernateSession.createCriteria(HibernateIdentityObjectRelationshipType.class) - .add(Restrictions.eq("name", - typeName)) - .uniqueResult(); - - if (hibernateType == null) { - hibernateType = new HibernateIdentityObjectRelationshipType(typeName); - hibernateSession.persist(hibernateType); + HibernateIdentityObjectRelationshipType relationshipType = findIdentityRelationshipTypeByName(hibernateSession, typeName); + if (relationshipType == null) { + relationshipType = new HibernateIdentityObjectRelationshipType(typeName); + hibernateSession.persist(relationshipType); + hibernateSession.flush(); } - } - } - public void populateCredentialTypes(Session hibernateSession, String[] typeNames) throws Exception { - + public void populateCredentialTypes(Session hibernateSession, String[] typeNames) { for (String typeName : typeNames) { - HibernateIdentityObjectCredentialType hibernateType = - (HibernateIdentityObjectCredentialType) hibernateSession.createCriteria(HibernateIdentityObjectCredentialType.class) - .add(Restrictions.eq("name", - typeName)) - .uniqueResult(); - + HibernateIdentityObjectCredentialType hibernateType = findIdentityCredentialTypeByName(hibernateSession, typeName); if (hibernateType == null) { hibernateType = new HibernateIdentityObjectCredentialType(typeName); hibernateSession.persist(hibernateType); + hibernateSession.flush(); } - } - } public void addRealm(Session hibernateSession, String realmName) throws IdentityException { - try { - HibernateRealm realm = new HibernateRealm(realmName); hibernateSession.persist(realm); - + hibernateSession.flush(); } catch (Exception e) { - if (log.isLoggable(Level.FINER)) { - log.log(Level.FINER, "Exception occurred: ", e); - } - throw new IdentityException("Failed to create store realm", e); } } @@ -2571,36 +1932,26 @@ private HibernateRealm getRealm(Session hibernateSession, IdentityStoreInvocatio throw new IllegalStateException("Realm Id not present"); } - HibernateRealm realm = null; - // If store is not realm aware return null to create/get objects accessible // from other realms if (!isRealmAware()) { - realm = (HibernateRealm) hibernateSession.createCriteria(HibernateRealm.class) - .add(Restrictions.eq("name", DEFAULT_REALM_NAME)) - .setCacheable(true) - .uniqueResult(); - + HibernateRealm realm = getRealmByName(hibernateSession, DEFAULT_REALM_NAME); if (realm == null) { - throw new IllegalStateException("Default store realm is not present: " + DEFAULT_REALM_NAME); + throw new IdentityException("Default store realm is not present: " + DEFAULT_REALM_NAME); + } else { + return realm; } - } else { - realm = (HibernateRealm) hibernateSession.createCriteria(HibernateRealm.class) - .add(Restrictions.eq("name", getRealmName(ctx))) - .setCacheable(true) - .uniqueResult(); - - // TODO: other way to not lazy initialize realm? special method called on - // every new session creation + HibernateRealm realm = getRealmByName(hibernateSession, getRealmName(ctx)); if (realm == null) { HibernateRealm newRealm = new HibernateRealm(getRealmName(ctx)); hibernateSession.persist(newRealm); + hibernateSession.flush(); return newRealm; + } else { + return realm; } } - - return realm; } private String getRealmName(IdentityStoreInvocationContext ctx) { @@ -2652,8 +2003,7 @@ private String resolveAttributeNameFromStoreMapping(IdentityObjectType type, Str Map map = reverseAttributeMappings.get(type.getName()); if (map != null) { - String name = map.containsKey(mapping) ? map.get(mapping) : mapping; - return name; + return map.containsKey(mapping) ? map.get(mapping) : mapping; } } @@ -2663,15 +2013,15 @@ private String resolveAttributeNameFromStoreMapping(IdentityObjectType type, Str return null; } - // TODO: this kills performance and is present here only as "quick" hack to - // have the feature present and let to add test cases - // TODO: needs to be redone at the hibernate query level - private void filterByAttributesValues(Collection objects, Map attrs) { - Set toRemove = new HashSet(); + // TODO: this kills performance and is present here only as "quick" hack to // NOSONAR + // have the feature present and let to add test cases. Needs to be redone at the hibernate query level + @SuppressWarnings("rawtypes") + private void filterByAttributesValues(Collection objects, Map attrs) { // NOSONAR + Set toRemove = new HashSet<>(); for (IdentityObject object : objects) { Map presentAttrs = ((HibernateIdentityObject) object).getAttributesAsMap(); - for (Map.Entry entry : attrs.entrySet()) { + for (Map.Entry entry : attrs.entrySet()) { // NOSONAR // Resolve attribute name from the store attribute mapping String mappedAttributeName = null; try { @@ -2686,7 +2036,7 @@ private void filterByAttributesValues(Collection objects, Map given = new HashSet(Arrays.asList(entry.getValue())); + Set given = new HashSet<>(Arrays.asList(entry.getValue())); Collection present = presentAttrs.get(mappedAttributeName); @@ -2720,11 +2070,8 @@ private void filterByAttributesValues(Collection objects, Map List cutPageFromResults(List objects, IdentityObjectSearchCriteria criteria) { - - List results = new LinkedList(); - + List results = new LinkedList<>(); if (criteria.getMaxResults() == 0) { for (int i = criteria.getFirstResult(); i < objects.size(); i++) { if (i < objects.size()) { @@ -2741,7 +2088,7 @@ private List cutPageFromResults(List objects, IdentityObjectSearchCrit return results; } - private void applyQueryParameters(Query hibernateQuery, Map queryParams) { + private void applyQueryParameters(Query hibernateQuery, Map queryParams) { for (Map.Entry entry : queryParams.entrySet()) { hibernateQuery.setParameter(entry.getKey(), entry.getValue()); } @@ -2761,19 +2108,217 @@ public boolean isAllowNotCaseSensitiveSearch() { private void removeRelationshipsByName(IdentityStoreInvocationContext ctx, HibernateIdentityObjectRelationshipName hiorn) throws IdentityException { - List rels = - (List) getHibernateSession(ctx).createCriteria(HibernateIdentityObjectRelationship.class) - .add(Restrictions.eq("name", - hiorn)) - .setCacheable(true) - .list(); + List relationships = + getHibernateSession(ctx).createNamedQuery("HibernateIdentityObjectRelationship.getRelationshipsByName", + HibernateIdentityObjectRelationship.class) + .setParameter("nameId", hiorn.getId()) + .list(); - Hibernate.initialize(rels); + Hibernate.initialize(relationships); // Remove all present usages - for (HibernateIdentityObjectRelationship rel : rels) { + for (HibernateIdentityObjectRelationship rel : relationships) { removeRelationship(ctx, rel.getFromIdentityObject(), rel.getToIdentityObject(), rel.getType(), rel.getName()); } } + private void checkName(String name) { + if (name == null) { + throw new IllegalArgumentException("name is null"); + } + } + + private void checkAttributes(IdentityObjectAttribute[] attributes) { + if (attributes == null) { + throw new IllegalArgumentException("attributes are null"); + } + } + + private HibernateIdentityObjectRelationshipName getRelationshipName(IdentityStoreInvocationContext ctx, + String name, + Session hibernateSession) throws IdentityException { + HibernateIdentityObjectRelationshipName relationshipName = findIdentityObjectRelationshipNameByName(hibernateSession, + name, + getRealmName(ctx)); + checkRelationshipName(name, relationshipName); + return relationshipName; + } + + private void checkRelationshipName(String name, + HibernateIdentityObjectRelationshipName relationshipName) throws IdentityException { + if (relationshipName == null) { + throw new IdentityException("Relationship name " + name + " doesn't exist"); + } + } + + private Set getRelationshipNames(IdentityStoreInvocationContext ctx, // NOSONAR + IdentityObjectSearchCriteria criteria, + HibernateIdentityObject hibernateObject) throws IdentityException { + List paramNames = new ArrayList<>(); + List paramValues = new ArrayList<>(); + StringBuilder queryString = new StringBuilder(); + if (hibernateObject != null) { + queryString.append("SELECT rn.name FROM HibernateIdentityObjectRelationshipName rn"); + queryString.append("INNER JOIN r.name rn"); + queryString.append(" WHERE"); + } else { + queryString.append(" SELECT rn.name FROM HibernateIdentityObjectRelationship r"); + queryString.append(" INNER JOIN r.name rn"); + queryString.append(" WHERE (r.fromIdentityObject = :identity OR r.toIdentityObject = :identity)"); + queryString.append(" AND"); + paramNames.add(IDENTITY_PARAM); + paramValues.add(hibernateObject); + } + + try { + paramNames.add(REALM_NAME_PARAM); + paramValues.add(getRealmName(ctx)); + queryString.append(" rn.realm.name = :").append(REALM_NAME_PARAM); + + if (criteria != null && criteria.getFilter() != null) { + paramNames.add(NAME_PARAM); + paramValues.add(criteria.getFilter().replace("\\*", "%").replace("*", "%")); + queryString.append(" AND rn.name LIKE :").append(NAME_PARAM); + } else { + queryString.append(" AND rn.name IS NOT NULL"); + } + + if (hibernateObject != null) { + queryString.append(" AND (rn.name IS NOT NULL)"); + } + + if (criteria != null && criteria.isSorted()) { + if (criteria.isAscending()) { + queryString.append(" ORDER BY rn.name ASC"); + } else { + queryString.append(" ORDER BY rn.name DESC"); + } + } + + Query query = getHibernateSession(ctx).createQuery(queryString.toString(), String.class); // NOSONAR + if (criteria != null && criteria.isPaged() && !criteria.isFiltered()) { + if (criteria.getMaxResults() > 0) { + query.setMaxResults(criteria.getMaxResults()); + } + query.setFirstResult(criteria.getFirstResult()); + } + for (int i = 0; i < paramNames.size(); i++) { + query.setParameter(paramNames.get(i), paramValues.get(i)); + } + + List results = query.list(); + Hibernate.initialize(results); + return new HashSet<>(results); + } catch (Exception e) { + throw new IdentityException("Cannot get relationship names. ", e); + } + } + + private HibernateRealm getRealmByName(Session hibernateSession, String name) { + return hibernateSession.createNamedQuery("HibernateRealm.findRealmByName", HibernateRealm.class) + .setParameter(NAME_PARAM, name) + .uniqueResult(); + } + + private int countIdentityObjectByNameAndType(Session hibernateSession, + String identityName, + String identityType, + String realmName) { + Number result = hibernateSession.createNamedQuery("HibernateIdentityObject.countIdentityObjectByNameAndType", Number.class) + .setParameter(REALM_NAME_PARAM, realmName) + .setParameter(NAME_PARAM, identityName) + .setParameter(IDENTITY_TYPE_NAME, identityType) + .uniqueResult(); + return result == null ? 0 : result.intValue(); + } + + private HibernateIdentityObject findIdentityObjectByNameAndType(Session hibernateSession, + String identityName, + String identityType, + String realmName) { + return hibernateSession.createNamedQuery("HibernateIdentityObject.findIdentityObjectByNameAndType", + HibernateIdentityObject.class) + .setParameter(REALM_NAME_PARAM, realmName) + .setParameter(NAME_PARAM, identityName) + .setParameter(IDENTITY_TYPE_NAME, identityType) + .uniqueResult(); + } + + private HibernateIdentityObjectRelationshipName findIdentityObjectRelationshipNameByName(Session hibernateSession, + String relationshipName, + String realmName) { + return hibernateSession.createNamedQuery("HibernateIdentityObjectRelationshipName.findIdentityObjectRelationshipNameByName", + HibernateIdentityObjectRelationshipName.class) + .setParameter(REALM_NAME_PARAM, realmName) + .setParameter(NAME_PARAM, relationshipName) + .uniqueResult(); + } + + private HibernateIdentityObjectRelationship findIdentityObjectRelationshipByIdentityByType(Session hibernateSession, + HibernateIdentityObject fromIdentityObject, + HibernateIdentityObject toIdentityObject, + String typeName) { + return hibernateSession.createNamedQuery("HibernateIdentityObjectRelationship.findIdentityObjectRelationshipByIdentityByType", + HibernateIdentityObjectRelationship.class) + .setParameter("fromIdentityObject", fromIdentityObject) + .setParameter("toIdentityObject", toIdentityObject) + .setParameter(TYPE_NAME_PARAM, typeName) + .uniqueResult(); + } + + private HibernateIdentityObjectRelationship findIdentityObjectRelationshipByAttributes(Session hibernateSession, + HibernateIdentityObject fromIdentityObject, + HibernateIdentityObject toIdentityObject, + Long typeId, + String relationName) { + return hibernateSession.createNamedQuery("HibernateIdentityObjectRelationship.findIdentityObjectRelationshipByAttributes", + HibernateIdentityObjectRelationship.class) + .setParameter("fromIdentityObject", fromIdentityObject) + .setParameter("toIdentityObject", toIdentityObject) + .setParameter("typeId", typeId) + .setParameter("name", relationName) + .uniqueResult(); + } + + private List findIdentityObjectRelationshipsByIdentities(Session hibernateSession, + HibernateIdentityObject hio1, + HibernateIdentityObject hio2) { + return hibernateSession.createNamedQuery("HibernateIdentityObjectRelationship.findIdentityObjectRelationshipsByIdentities", + HibernateIdentityObjectRelationship.class) + .setParameter("hio1", hio1) + .setParameter("hio2", hio2) + .list(); + } + + private List findIdentityAttributes(Session hibernateSession, + HibernateIdentityObject identityObject) { + return hibernateSession.createNamedQuery("HibernateIdentityObjectAttribute.findIdentityAttributes", + HibernateIdentityObjectAttribute.class) + .setParameter("identityObject", identityObject) + .list(); + } + + private HibernateIdentityObjectRelationshipType findIdentityRelationshipTypeByName(Session hibernateSession, + String name) { + return hibernateSession.createNamedQuery("HibernateIdentityObjectRelationshipType.findIdentityRelationshipTypeByName", + HibernateIdentityObjectRelationshipType.class) + .setParameter(NAME_PARAM, name) + .uniqueResult(); + } + + private HibernateIdentityObjectCredentialType findIdentityCredentialTypeByName(Session hibernateSession, + String name) { + return hibernateSession.createNamedQuery("HibernateIdentityObjectCredentialType.findIdentityCredentialTypeByName", + HibernateIdentityObjectCredentialType.class) + .setParameter(NAME_PARAM, name) + .uniqueResult(); + } + + private HibernateIdentityObjectType findIdentityObjectTypeByName(Session hibernateSession, String typeName) { + return hibernateSession.createNamedQuery("HibernateIdentityObjectType.findIdentityObjectTypeByName", + HibernateIdentityObjectType.class) + .setParameter(NAME_PARAM, typeName) + .uniqueResult(); + } + } diff --git a/component/identity/src/main/java/org/picketlink/idm/impl/store/hibernate/PatchedHibernateIdentityStoreImpl.java b/component/identity/src/main/java/org/picketlink/idm/impl/store/hibernate/PatchedHibernateIdentityStoreImpl.java index 10720cfa7f..986124008c 100644 --- a/component/identity/src/main/java/org/picketlink/idm/impl/store/hibernate/PatchedHibernateIdentityStoreImpl.java +++ b/component/identity/src/main/java/org/picketlink/idm/impl/store/hibernate/PatchedHibernateIdentityStoreImpl.java @@ -23,14 +23,24 @@ package org.picketlink.idm.impl.store.hibernate; import java.io.Serializable; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; import javax.naming.InitialContext; import javax.naming.NamingException; -import javax.persistence.Tuple; import org.hibernate.Hibernate; import org.hibernate.HibernateException; @@ -41,7 +51,17 @@ import org.hibernate.query.Query; import org.picketlink.idm.common.exception.IdentityException; import org.picketlink.idm.impl.helper.Tools; -import org.picketlink.idm.impl.model.hibernate.*; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObject; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObjectAttribute; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObjectAttributeBinaryValue; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObjectCredential; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObjectCredentialBinaryValue; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObjectCredentialType; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObjectRelationship; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObjectRelationshipName; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObjectRelationshipType; +import org.picketlink.idm.impl.model.hibernate.HibernateIdentityObjectType; +import org.picketlink.idm.impl.model.hibernate.HibernateRealm; import org.picketlink.idm.impl.store.FeaturesMetaDataImpl; import org.picketlink.idm.impl.types.SimpleIdentityObject; import org.picketlink.idm.spi.configuration.IdentityStoreConfigurationContext; @@ -49,7 +69,13 @@ import org.picketlink.idm.spi.configuration.metadata.IdentityObjectTypeMetaData; import org.picketlink.idm.spi.configuration.metadata.IdentityStoreConfigurationMetaData; import org.picketlink.idm.spi.configuration.metadata.RealmConfigurationMetaData; -import org.picketlink.idm.spi.model.*; +import org.picketlink.idm.spi.model.IdentityObject; +import org.picketlink.idm.spi.model.IdentityObjectAttribute; +import org.picketlink.idm.spi.model.IdentityObjectCredential; +import org.picketlink.idm.spi.model.IdentityObjectCredentialType; +import org.picketlink.idm.spi.model.IdentityObjectRelationship; +import org.picketlink.idm.spi.model.IdentityObjectRelationshipType; +import org.picketlink.idm.spi.model.IdentityObjectType; import org.picketlink.idm.spi.search.IdentityObjectSearchCriteria; import org.picketlink.idm.spi.store.FeaturesMetaData; import org.picketlink.idm.spi.store.IdentityObjectSearchCriteriaType; @@ -59,6 +85,8 @@ import org.exoplatform.services.organization.idm.EntityMapperUtils; +import jakarta.persistence.Tuple; + /** * @author Boleslaw Dawidowicz * @version : 0.1 $ @@ -634,7 +662,7 @@ public Collection findIdentityObject(IdentityStoreInvocationCont /* END SOC-6210 */ /* BEGIN CAL-1225: User picker in Participants tab is case sensitive */ if (criteria != null && criteria.getFilter() != null) { - String attrValue = criteria.getFilter().replaceAll("\\*", "%"); + String attrValue = criteria.getFilter().replace("\\*", "%").replace("*", "%"); String operator = "="; if (attrValue.contains("%")) { operator = "like"; @@ -669,10 +697,10 @@ public Collection findIdentityObject(IdentityStoreInvocationCont queryParams.put(attrParamName, mappedAttributeName); /** End eXo customization **/ } else { - Set given = new HashSet(Arrays.asList(entry.getValue())); + Set given = new HashSet<>(Arrays.asList(entry.getValue())); for (String attrValue : given) { - attrValue = attrValue.replaceAll("\\*", "%"); + attrValue = attrValue.replace("\\*", "%").replace("*", "%"); /* * BEGIN CAL-1225: User picker in Participants tab is case @@ -929,7 +957,7 @@ public Query prepareIdentityObjectQuery(IdentityStoreInvocationContext ctx, } if (criteria != null && criteria.getFilter() != null) { - q.setParameter("nameFilter", criteria.getFilter().replaceAll("\\*", "%")); + q.setParameter("nameFilter", criteria.getFilter().replace("\\*", "%").replace("*", "%")); } else { q.setParameter("nameFilter", "%"); } @@ -1346,7 +1374,7 @@ public Set getRelationshipNames(IdentityStoreInvocationContext ctx, org.hibernate.query.Query query = getHibernateSession(ctx).createQuery(queryString.toString(), String.class); query.setParameter("realmName", getRealmName(ctx)); if (hasFilter) { - query.setParameter("name", criteria.getFilter().replaceAll("\\*", "%")); + query.setParameter("name", criteria.getFilter().replace("\\*", "%").replace("*", "%")); } if (criteria != null && criteria.isPaged()) { @@ -1400,7 +1428,7 @@ public Set getRelationshipNames(IdentityStoreInvocationContext ctx, org.hibernate.query.Query query = getHibernateSession(ctx).createQuery(queryString.toString(), String.class); if (hasFilter) { - query.setParameter("name", criteria.getFilter().replaceAll("\\*", "%")); + query.setParameter("name", criteria.getFilter().replace("\\*", "%").replace("*", "%")); } if (identity != null) { HibernateIdentityObject hibernateObject = safeGet(ctx, identity); @@ -2558,7 +2586,7 @@ private void removeRelationshipsByName(IdentityStoreInvocationContext ctx, } private HibernateRealm getHibernateRealmByName(Session hibernateSession, String realmName) { - return hibernateSession.createNamedQuery("HibernateRealm.findIRealmByName", HibernateRealm.class) + return hibernateSession.createNamedQuery("HibernateRealm.findRealmByName", HibernateRealm.class) .setParameter("name", realmName) .uniqueResult(); } diff --git a/component/identity/src/main/resources/db/changelog/idm.db.changelog-1.0.0.xml b/component/identity/src/main/resources/db/changelog/idm.db.changelog-1.0.0.xml index a4a53be9f4..c3263be506 100644 --- a/component/identity/src/main/resources/db/changelog/idm.db.changelog-1.0.0.xml +++ b/component/identity/src/main/resources/db/changelog/idm.db.changelog-1.0.0.xml @@ -377,6 +377,10 @@ + + + + + + + + + + + + + + + + + + + + + SELECT setval(‘JBID_IO_ID_SEQ’, (SELECT MAX(ID) FROM JBID_IO)) + SELECT setval(‘JBID_IO_ATTR_ID_SEQ’, (SELECT MAX(ID) FROM JBID_IO_ATTR)) + SELECT setval(‘JBID_ATTR_BIN_VALUE_ID_SEQ’, (SELECT MAX(ID) FROM JBID_ATTR_BIN_VALUE)) + SELECT setval(‘JBID_IO_CREDEN_ID_SEQ’, (SELECT MAX(ID) FROM JBID_IO_CREDEN)) + SELECT setval(‘JBID_CREDEN_BIN_VALUE_ID_SEQ’, (SELECT MAX(ID) FROM JBID_CREDEN_BIN_VALUE)) + SELECT setval(‘JBID_IO_CREDEN_TYPE_ID_SEQ’, (SELECT MAX(ID) FROM JBID_IO_CREDEN_TYPE)) + SELECT setval(‘JBID_IO_REL_ID_SEQ’, (SELECT MAX(ID) FROM JBID_IO_REL)) + SELECT setval(‘JBID_IO_REL_NAME_ID_SEQ’, (SELECT MAX(ID) FROM JBID_IO_REL_NAME)) + SELECT setval(‘JBID_IO_REL_TYPE_ID_SEQ’, (SELECT MAX(ID) FROM JBID_IO_REL_TYPE)) + SELECT setval(‘JBID_IO_TYPE_ID_SEQ’, (SELECT MAX(ID) FROM JBID_IO_TYPE)) + SELECT setval(‘JBID_REALM_ID_SEQ’, (SELECT MAX(ID) FROM JBID_REALM)) + diff --git a/component/identity/src/test/java/org/exoplatform/services/organization/AbstractTestOrganizationService.java b/component/identity/src/test/java/org/exoplatform/services/organization/AbstractTestOrganizationService.java index 46cce37e02..5abfcc3c6a 100644 --- a/component/identity/src/test/java/org/exoplatform/services/organization/AbstractTestOrganizationService.java +++ b/component/identity/src/test/java/org/exoplatform/services/organization/AbstractTestOrganizationService.java @@ -23,14 +23,25 @@ package org.exoplatform.services.organization; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import org.apache.commons.lang3.StringUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + import org.exoplatform.commons.utils.ListAccess; import org.exoplatform.commons.utils.PageList; +import org.exoplatform.component.test.AbstractKernelTest; import org.exoplatform.container.PortalContainer; import org.exoplatform.container.component.ComponentRequestLifecycle; import org.exoplatform.container.component.RequestLifeCycle; @@ -38,15 +49,10 @@ import org.exoplatform.services.organization.idm.PicketLinkIDMOrganizationServiceImpl; import org.exoplatform.services.organization.idm.UserDAOImpl; -import org.apache.commons.lang3.StringUtils; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - /** * Created by The eXo Platform SAS Author : Hoa Pham hoapham@exoplatform.com,phamvuxuanhoa@yahoo.com Oct 27, 2005 */ -public class AbstractTestOrganizationService { +public abstract class AbstractTestOrganizationService { private static String Group1 = "Group1"; private static String Group2 = "Group2"; @@ -149,15 +155,15 @@ public void testSimple() throws Exception { @Test public void testUserPageSize() throws Exception { - int initialSize = userHandler_.findAllUsers().getSize(); - - for (String name : USERS) - createUser(name); + for (String name : USERS) { + createUser(name); + } Query query = new Query(); + query.setUserName(USER); PageList users = userHandler_.findUsers(query); // newly created plus one 'demo' from configuration - assertEquals(USERS_LIST_SIZE + initialSize, users.getAll().size()); + assertEquals(USERS_LIST_SIZE, users.getAll().size()); assertEquals(1, users.getAvailablePage()); List usersList = users.getPage(1); for (String username : USERS) { @@ -310,7 +316,11 @@ public void testFindUsers() throws Exception { public void testGroup() throws Exception { /* Create a parent group with name is: GroupParent */ String parentName = "GroupParent"; - Group groupParent = groupHandler_.createGroupInstance(); + Group groupParent = groupHandler_.findGroupById("/" + parentName); + if (groupParent != null) { + groupHandler_.removeGroup(groupParent, true); + } + groupParent = groupHandler_.createGroupInstance(); groupParent.setGroupName(parentName); groupParent.setDescription("This is description"); groupHandler_.addChild(null, groupParent, true); @@ -377,6 +387,10 @@ public void testGroup() throws Exception { groups = groupHandler_.resolveGroupByMembership("demo", "member"); assertNotNull(groups); assertEquals(1, groups.size()); + + groupHandler_.removeGroup(groupChild2, true); + groupHandler_.removeGroup(groupParent, true); + assertNull("Expect ParentGroup is removed", groupHandler_.findGroupById(groupParent.getId())); } @Test diff --git a/component/identity/src/test/java/org/exoplatform/services/organization/TestLDAPOrganization.java b/component/identity/src/test/java/org/exoplatform/services/organization/TestLDAPOrganization.java index 8684b77822..ee543fa6ef 100644 --- a/component/identity/src/test/java/org/exoplatform/services/organization/TestLDAPOrganization.java +++ b/component/identity/src/test/java/org/exoplatform/services/organization/TestLDAPOrganization.java @@ -2,7 +2,7 @@ import exo.portal.component.identiy.opendsconfig.opends.OpenDSService; import junit.framework.Assert; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.commons.utils.ListAccess; import org.exoplatform.component.test.ConfigurationUnit; import org.exoplatform.component.test.ConfiguredBy; diff --git a/component/identity/src/test/java/org/exoplatform/services/organization/TestOrganization.java b/component/identity/src/test/java/org/exoplatform/services/organization/TestOrganization.java index 464968466e..7e7a34befb 100644 --- a/component/identity/src/test/java/org/exoplatform/services/organization/TestOrganization.java +++ b/component/identity/src/test/java/org/exoplatform/services/organization/TestOrganization.java @@ -92,6 +92,8 @@ protected void setUp() throws Exception { mtHandler_ = organizationService.getMembershipTypeHandler(); membershipHandler_ = organizationService.getMembershipHandler(); + resetData(); + createGroup(null, GROUP_1); createGroup(GROUP_1, GROUP_2); createGroup(GROUP_1, GROUP_3); @@ -103,15 +105,12 @@ protected void setUp() throws Exception { @Override protected void tearDown() throws Exception { - deleteGroup("/" + GROUP_1); - - deleteUser(USER_1); - deleteUser(USER_2); - deleteUser(USER_3); - - end(); - - super.tearDown(); + try { + resetData(); + } finally { + end(); + super.tearDown(); + } } public void testIDMConfiguration(){ @@ -278,27 +277,27 @@ public void testFindUserByGroup() throws Exception { public void testLastLoginTime() throws Exception { UserHandler uHandler = organizationService.getUserHandler(); - User user = uHandler.findUserByName("root"); + User user = uHandler.findUserByName(USER_1); Assert.assertNotNull(user); // Assert that last login time is updated by default Thread.sleep(1); Date current = new Date(); Thread.sleep(1); - user = uHandler.findUserByName("root"); + user = uHandler.findUserByName(USER_1); Assert.assertNotNull(user); Date oldLastLoginTime = user.getLastLoginTime(); Assert.assertNotNull(oldLastLoginTime); - Assert.assertTrue(uHandler.authenticate("root", "gtn")); - user = uHandler.findUserByName("root"); + Assert.assertTrue(uHandler.authenticate(USER_1, DEFAULT_PASSWORD)); + user = uHandler.findUserByName(USER_1); Assert.assertTrue(user.getLastLoginTime().equals(oldLastLoginTime)); - Assert.assertTrue(uHandler.authenticate("root", "gtn")); + Assert.assertTrue(uHandler.authenticate(USER_1, DEFAULT_PASSWORD)); updateLoginTimeListener.onEvent(new Event("nothing", null, - new ConversationState(new Identity("root")))); - user = uHandler.findUserByName("root"); + new ConversationState(new Identity(USER_1)))); + user = uHandler.findUserByName(USER_1); Assert.assertTrue(user.getLastLoginTime().after(oldLastLoginTime)); Assert.assertTrue(user.getLastLoginTime().after(current)); } @@ -420,6 +419,7 @@ public void testCreateValidMembershiptype() throws Exception { } protected void createGroup(String parent, String name) { + restartTransaction(); GroupHandler groupHandler = organizationService.getGroupHandler(); try { Group parentGroup = null; @@ -437,6 +437,7 @@ protected void createGroup(String parent, String name) { } private void deleteGroup(String name) { + restartTransaction(); GroupHandler groupHandler = organizationService.getGroupHandler(); try { Group group = groupHandler.findGroupById(name); @@ -455,6 +456,7 @@ private void deleteGroup(String name) { } protected void createUser(String username, String... groups) throws Exception { + restartTransaction(); UserHandler userHandler = organizationService.getUserHandler(); User user = userHandler.createUserInstance(username); user.setPassword(DEFAULT_PASSWORD); @@ -468,12 +470,20 @@ protected void createUser(String username, String... groups) throws Exception { userHandler.createUser(user, true); } - protected void deleteUser(String username) { - UserHandler userHandler = organizationService.getUserHandler(); - try { - userHandler.removeUser(username, true); - } catch (Exception e) { + protected void deleteUser(String username) throws Exception { + restartTransaction(); + UserHandler userHandler = organizationService.getUserHandler(); + if (userHandler.findUserByName(username) != null) { + userHandler.removeUser(username, true); + } + } + + private void resetData() throws Exception { + deleteGroup("/" + GROUP_1); + + deleteUser(USER_1); + deleteUser(USER_2); + deleteUser(USER_3); + } - } - } } diff --git a/component/identity/src/test/java/org/exoplatform/services/organization/TestUserSearchService.java b/component/identity/src/test/java/org/exoplatform/services/organization/TestUserSearchService.java index 9809874b98..cb8df69d4b 100644 --- a/component/identity/src/test/java/org/exoplatform/services/organization/TestUserSearchService.java +++ b/component/identity/src/test/java/org/exoplatform/services/organization/TestUserSearchService.java @@ -38,12 +38,12 @@ public void testUserSearch() throws Exception { assertTrue(users.getSize() > 1); Query query = new Query(); - query.setUserName("*ro*"); + query.setUserName("*roo*"); users = userHandler.findUsersByQuery(query); assertNotNull(users); assertEquals(1, users.getSize()); - searchedUsers = userSearchService.searchUsers("ro"); + searchedUsers = userSearchService.searchUsers("roo"); assertNotNull(searchedUsers); assertEquals(users.getSize(), searchedUsers.getSize()); } diff --git a/component/identity/src/test/java/org/exoplatform/services/organization/auth/TestOrganizationAuthenticator.java b/component/identity/src/test/java/org/exoplatform/services/organization/auth/TestOrganizationAuthenticator.java index c5855b8747..20a3ea3914 100644 --- a/component/identity/src/test/java/org/exoplatform/services/organization/auth/TestOrganizationAuthenticator.java +++ b/component/identity/src/test/java/org/exoplatform/services/organization/auth/TestOrganizationAuthenticator.java @@ -20,7 +20,7 @@ import junit.framework.TestCase; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.container.StandaloneContainer; import org.exoplatform.services.organization.DisabledUserException; diff --git a/component/identity/src/test/java/org/exoplatform/services/tck/organization/AbstractOrganizationServiceTest.java b/component/identity/src/test/java/org/exoplatform/services/tck/organization/AbstractOrganizationServiceTest.java new file mode 100644 index 0000000000..2c3ced1bdd --- /dev/null +++ b/component/identity/src/test/java/org/exoplatform/services/tck/organization/AbstractOrganizationServiceTest.java @@ -0,0 +1,327 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.exoplatform.services.tck.organization; + +import junit.framework.TestCase; + +import org.exoplatform.commons.utils.ListAccess; +import org.exoplatform.container.StandaloneContainer; +import org.exoplatform.container.component.RequestLifeCycle; +import org.exoplatform.services.organization.Group; +import org.exoplatform.services.organization.GroupHandler; +import org.exoplatform.services.organization.MembershipHandler; +import org.exoplatform.services.organization.MembershipType; +import org.exoplatform.services.organization.MembershipTypeHandler; +import org.exoplatform.services.organization.OrganizationService; +import org.exoplatform.services.organization.User; +import org.exoplatform.services.organization.UserHandler; +import org.exoplatform.services.organization.UserProfile; +import org.exoplatform.services.organization.UserProfileHandler; +import org.exoplatform.services.organization.UserStatus; + +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +/** + * Created by The eXo Platform SAS. + * + *
Date: 2011 + * + * @author Alex Reshetnyak + * @version $Id: AbstractOrganizationServiceTest.java 111 2011-11-11 11:11:11Z rainf0x $ + */ +public abstract class AbstractOrganizationServiceTest extends TestCase { + + protected GroupHandler gHandler; + + protected MembershipHandler mHandler; + + protected UserHandler uHandler; + + protected MembershipTypeHandler mtHandler; + + protected UserProfileHandler upHandler; + + protected String membershipType = "type"; + + protected String userName = "user"; + + protected String newUserName = "newUser"; + + protected String groupName1 = "group1"; + + protected String groupName2 = "group2"; + + protected StandaloneContainer container; + + /** + * The list of users which have been created during test. + * Will be removed in tearDown() method. + */ + private List users = new ArrayList(); + + /** + * The list of membership types which have been created during test. + * Will be removed in tearDown() method. + */ + private List types = new ArrayList(); + + /** + * The list of group which have been created during test. + * Will be removed in tearDown() method. + */ + private List groups = new ArrayList(); + + /** + * {@inheritDoc} + */ + public void setUp() throws Exception + { + super.setUp(); + + + String configPath = System.getProperty("orgservice.test.configuration.file"); + if (configPath == null) + { + configPath = "/conf/standalone/test-configuration.xml"; + } + + String containerConf = getClass().getResource(configPath).toString(); + + StandaloneContainer.addConfigurationURL(containerConf); + container = StandaloneContainer.getInstance(); + + OrganizationService organizationService = + container.getComponentInstanceOfType(OrganizationService.class); + + gHandler = organizationService.getGroupHandler(); + uHandler = organizationService.getUserHandler(); + mHandler = organizationService.getMembershipHandler(); + mtHandler = organizationService.getMembershipTypeHandler(); + upHandler = organizationService.getUserProfileHandler(); + + users.add(userName); + users.add(newUserName); + + groups.add("/" + groupName1); + groups.add("/" + groupName1 + "/" + groupName2); + + types.add(membershipType); + + RequestLifeCycle.begin(container); + } + + /** + * Create new user for test purpose only. + */ + protected void createUser(String userName) throws Exception + { + User u = uHandler.createUserInstance(userName); + u.setEmail("email@test"); + u.setFirstName("first"); + u.setLastLoginTime(Calendar.getInstance().getTime()); + u.setCreatedDate(Calendar.getInstance().getTime()); + u.setLastName("last"); + u.setPassword("pwdADDSomeSaltToBeCompliantWithSomeIS00"); + + uHandler.createUser(u, true); + + users.add(userName); + } + + /** + * Create user with profile. + */ + protected void createUserProfile(String userName) throws Exception + { + UserProfile up = upHandler.createUserProfileInstance(userName); + Map attributes = up.getUserInfoMap(); + attributes.put("key1", "value1"); + attributes.put("key2", "value2"); + upHandler.saveUserProfile(up, true); + } + + /** + * Create membership type. + */ + protected void createMembershipType(String type, String desc) throws Exception + { + MembershipType mt = mtHandler.createMembershipTypeInstance(); + mt.setName(type); + mt.setDescription(desc); + mtHandler.createMembershipType(mt, true); + + types.add(type); + } + + /** + * Create new group. + */ + protected void createGroup(String parentId, String name, String label, String desc) throws Exception + { + Group parent = parentId == null ? null : gHandler.findGroupById(parentId); + + Group child = gHandler.createGroupInstance(); + child.setGroupName(name); + child.setLabel(label); + child.setDescription(desc); + gHandler.addChild(parent, child, true); + + groups.add((parent == null ? "" : parentId) + "/" + name); + } + + /** + * Create membership. + */ + protected void createMembership(String userName, String groupName, String type) throws Exception + { + createUser(userName); + createGroup(null, groupName, "label", "desc"); + createMembershipType(type, "desc"); + + // link membership + mHandler.linkMembership(uHandler.findUserByName(userName), gHandler.findGroupById("/" + groupName), mtHandler + .findMembershipType(type), true); + } + + /** + * Create new group instance. + */ + protected Group createGroupInstance(String name) throws Exception + { + createGroup(null, name, "label", "desc"); + return gHandler.removeGroup(gHandler.findGroupById("/" + name), true); + } + + /** + * {@inheritDoc} + */ + public void tearDown() throws Exception + { + + RequestLifeCycle.end(); + RequestLifeCycle.begin(container); + + try { + + // remove all users + Iterator iter = users.iterator(); + while (iter.hasNext()) + { + String userName = iter.next(); + + if (uHandler.findUserByName(userName) != null) + { + uHandler.removeUser(userName, true); + } + iter.remove(); + } + + // remove all membership types + iter = types.iterator(); + while (iter.hasNext()) + { + String type = iter.next(); + + if (mtHandler.findMembershipType(type) != null) + { + mtHandler.removeMembershipType(type, true); + } + iter.remove(); + } + + // remove all groups + iter = groups.iterator(); + while (iter.hasNext()) + { + String groupId = iter.next(); + + removeGroups(groupId); + iter.remove(); + } + } finally { + RequestLifeCycle.end(); + } + } + + private void removeGroups(String parentId) throws Exception + { + Group group = gHandler.findGroupById(parentId); + if (group != null) + { + + Collection childs = gHandler.findGroups(group); + for (Group child : childs) + { + removeGroups(child.getId()); + } + + gHandler.removeGroup(group, true); + } + } + + protected void assertSizeEquals(int expectedSize, ListAccess list) throws Exception + { + int size; + assertEquals(expectedSize, size = list.getSize()); + Object[] values = list.load(0, size); + size = 0; + for (int i = 0; i < values.length; i++) + { + if (values[i] != null) + { + size++; + } + } + assertEquals(expectedSize, size); + } + + protected void assertSizeEquals(int expectedSize, Collection list) throws Exception + { + int size; + assertEquals(expectedSize, list.size()); + size = 0; + for (Object value : list) + { + if (value != null) + { + size++; + } + } + assertEquals(expectedSize, size); + } + + protected void assertSizeEquals(int expectedSize, ListAccess list, UserStatus status) throws Exception + { + int size; + assertEquals(expectedSize, size = list.getSize()); + User[] values = list.load(0, size); + size = 0; + for (int i = 0; i < values.length; i++) + { + User usr = values[i]; + if (usr != null && status.matches(usr.isEnabled())) + { + size++; + } + } + assertEquals(expectedSize, size); + } +} diff --git a/component/identity/src/test/java/org/exoplatform/services/tck/organization/TestGroupHandler.java b/component/identity/src/test/java/org/exoplatform/services/tck/organization/TestGroupHandler.java new file mode 100644 index 0000000000..4bfd4739c6 --- /dev/null +++ b/component/identity/src/test/java/org/exoplatform/services/tck/organization/TestGroupHandler.java @@ -0,0 +1,480 @@ +/* + * Copyright (C) 2003-2007 eXo Platform SAS. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see. + */ +package org.exoplatform.services.tck.organization; + +import org.exoplatform.services.organization.Group; +import org.exoplatform.services.organization.GroupEventListener; +import org.exoplatform.services.organization.GroupEventListenerHandler; +import org.exoplatform.services.organization.MembershipTypeHandler; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * Created by The eXo Platform SAS. + * + * @author Anatoliy Bazko + * @version $Id: TestOrganizationService.java 111 2008-11-11 11:11:11Z $ + */ +public class TestGroupHandler extends AbstractOrganizationServiceTest +{ + + private MyGroupEventListener listener; + + @Override + public void setUp() throws Exception + { + super.setUp(); + listener = new MyGroupEventListener(); + gHandler.addGroupEventListener(listener); + } + + @Override + public void tearDown() throws Exception + { + gHandler.removeGroupEventListener(listener); + super.tearDown(); + } + + /** + * Find group by id. + */ + public void testFindGroupById() throws Exception + { + Group g = gHandler.findGroupById("/platform/administrators"); + assertNotNull(g); + assertEquals(g.getDescription(), "the /platform/administrators group"); + assertEquals(g.getGroupName(), "administrators"); + assertEquals(g.getId(), "/platform/administrators"); + assertEquals(g.getLabel(), "Administrators"); + assertEquals(g.getParentId(), "/platform"); + + // try to find not existed group. We are supposed to get "null" instead of Exception + try + { + assertNull(gHandler.findGroupById("/not-existed-group")); + } + catch (Exception e) + { + fail("Exception should be thrown"); + } + + // Check the listener's counters + assertEquals(0, listener.preSaveNew); + assertEquals(0, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(0, listener.preDelete); + assertEquals(0, listener.postDelete); + } + + /** + * Find groups by user. + */ + public void testFindGroupsByUser() throws Exception + { + assertSizeEquals(3, gHandler.findGroupsOfUser("john")); + assertSizeEquals(0, gHandler.findGroupsOfUser("fake-user")); + + createMembership(userName, groupName1, membershipType); + createGroup(null, groupName2, "label", "desc"); + mHandler.linkMembership(uHandler.findUserByName(userName), gHandler.findGroupById("/" + groupName2), + mtHandler.findMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE), true); + + Collection groups; + assertSizeEquals(2, groups = gHandler.findGroupsOfUser(userName)); + Set groupNames = new HashSet(); + for (Group g : groups) + { + groupNames.add(g.getGroupName()); + } + assertTrue(groupNames.contains(groupName1)); + assertTrue(groupNames.contains(groupName2)); + + Iterator it = groups.iterator(); + gHandler.removeGroup(it.next(), true); + + assertSizeEquals(1, gHandler.findGroupsOfUser(userName)); + + gHandler.removeGroup(it.next(), true); + + assertSizeEquals(0, gHandler.findGroupsOfUser(userName)); + + createMembership(userName + "2", groupName2, membershipType + "2"); + + assertSizeEquals(1, gHandler.findGroupsOfUser(userName + "2")); + + mHandler.removeMembershipByUser(userName + "2", false); + + assertSizeEquals(0, gHandler.findGroupsOfUser(userName + "2")); + + createMembership(userName + "3", groupName2 + "3", membershipType + "3"); + + assertSizeEquals(1, gHandler.findGroupsOfUser(userName + "3")); + + uHandler.removeUser(userName + "3", false); + + assertSizeEquals(0, gHandler.findGroupsOfUser(userName + "3")); + + // Check the listener's counters + assertEquals(4, listener.preSaveNew); + assertEquals(4, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(2, listener.preDelete); + assertEquals(2, listener.postDelete); + } + + /** + * Find groups. + */ + public void testFindGroups() throws Exception + { + assertTrue(gHandler.findGroups(null).size() >= 4); + assertSizeEquals(2, gHandler.findGroups(gHandler.findGroupById("/organization/operations"))); + assertSizeEquals(0, gHandler.findGroups(gHandler.findGroupById("/organization/management/executive-board"))); + + // Check the listener's counters + assertEquals(0, listener.preSaveNew); + assertEquals(0, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(0, listener.preDelete); + assertEquals(0, listener.postDelete); + } + + /** + * Get all groups. + */ + public void testGetAllGroups() throws Exception + { + assertTrue(gHandler.getAllGroups().size() >= 16); + + // Check the listener's counters + assertEquals(0, listener.preSaveNew); + assertEquals(0, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(0, listener.preDelete); + assertEquals(0, listener.postDelete); + } + + /** + * Get groups by keyword. + */ + public void testGetGroupsByKeyword() throws Exception + { + try { + assertSizeEquals(2, gHandler.findGroupsByKeyword("us")); // customers and users + assertSizeEquals(1, gHandler.findGroupsByKeyword("ad")); // administrators + + // Check the listener's counters + assertEquals(0, listener.preSaveNew); + assertEquals(0, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(0, listener.preDelete); + assertEquals(0, listener.postDelete); + } catch (UnsupportedOperationException e) { + // Catch unsupported implementations for ldap and hibernate + } + } + + /** + * Remove group. + */ + public void testRemoveGroup() throws Exception + { + createUser(userName); + createGroup("/organization", groupName1, "label", "desc"); + createGroup("/organization/" + groupName1, groupName2, "label", "desc"); + + createMembership(newUserName, groupName2, membershipType); + assertEquals("We expect to find single membership for user " + newUserName, 1, + mHandler.findMembershipsByUser(newUserName).size()); + + // can not remove group till children exist + try + { + gHandler.removeGroup(gHandler.findGroupById("/organization/group1"), true); + fail(""); + } + catch (Exception e) + { + } + + gHandler.removeGroup(gHandler.findGroupById("/organization/group1/group2"), true); + Group group = gHandler.removeGroup(gHandler.findGroupById("/organization/group1"), true); + + assertNull(gHandler.findGroupById("/organization/group1")); + assertNull(gHandler.findGroupById("/organization/group1/group2")); + + gHandler.removeGroup(gHandler.findGroupById("/" + groupName2), true); + assertEquals("We expect to find no membership for user " + newUserName, 0, + mHandler.findMembershipsByUser(newUserName).size()); + + // try to remove not exited group. Exception should be thrown + try + { + gHandler.removeGroup(group, true); + fail("Exception should be thrown"); + } + catch (Exception e) + { + } + + // create in root + createGroup(null, groupName1, "label", "desc"); + createGroup("/" + groupName1, groupName2, "label", "desc"); + + gHandler.removeGroup(gHandler.findGroupById("/" + groupName1 + "/" + groupName2), true); + gHandler.removeGroup(gHandler.findGroupById("/" + groupName1), true); + assertNull(gHandler.findGroupById("/" + groupName1)); + assertNull(gHandler.findGroupById("/" + groupName1 + "/" + groupName2)); + + // Check the listener's counters + assertEquals(5, listener.preSaveNew); + assertEquals(5, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(5, listener.preDelete); + assertEquals(5, listener.postDelete); + } + + /** + * Add child. + */ + public void testAddChild() throws Exception + { + Group parent = createGroupInstance(groupName1); + Group child = createGroupInstance(groupName2); + + // try to add child to not existed parent group + try + { + gHandler.addChild(parent, child, false); + fail("Exception should be thrown."); + } + catch (Exception e) + { + } + + // add parent group + gHandler.addChild(null, parent, false); + assertNotNull(gHandler.findGroupById("/" + groupName1)); + + // add child group + gHandler.addChild(parent, child, false); + assertNotNull(gHandler.findGroupById("/" + groupName1 + "/" + groupName2)); + + // Check the listener's counters + assertEquals(2, listener.preSaveNew); + assertEquals(2, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(2, listener.preDelete); + assertEquals(2, listener.postDelete); + } + + /** + * Test add new group same name with existing one + * @throws Exception + */ + public void testAddDuplicateChild() throws Exception { + // Create parent group + String parentName = "testAddDuplicateChild_GroupParent"; + Group groupParent = gHandler.createGroupInstance(); + groupParent.setGroupName(parentName); + groupParent.setLabel("GroupParent Label"); + groupParent.setDescription("This is description"); + gHandler.addChild(null, groupParent, true); + assertEquals(gHandler.findGroupById(groupParent.getId()).getLabel(), "GroupParent Label"); + + /* Create a child group with name: Group1 */ + Group groupChild1 = gHandler.createGroupInstance(); + groupChild1.setGroupName("testAddDuplicateChild"); + groupChild1.setLabel("Group1 Label"); + gHandler.addChild(groupParent, groupChild1, true); + assertEquals(gHandler.findGroupById(groupChild1.getId()).getLabel(), "Group1 Label"); + + try { + // Add new child same name with existing one + Group groupChild2 = gHandler.createGroupInstance(); + groupChild2.setGroupName("testAddDuplicateChild"); + groupChild2.setLabel("Group2 Label"); + gHandler.addChild(groupParent, groupChild2, true); + + fail("Exception should be thrown because group child is existing"); + } catch (Exception ex) { + + } + // Label of the existing group is not updated + assertEquals(gHandler.findGroupById(groupChild1.getId()).getLabel(), "Group1 Label"); + + // remove child group + gHandler.removeGroup(gHandler.findGroupById(groupChild1.getId()),true); + // remove parent group + gHandler.removeGroup(gHandler.findGroupById(groupParent.getId()),true); + } + + /** + * Create group. + */ + @SuppressWarnings("deprecation") + public void testCreateGroup() throws Exception + { + Group group = gHandler.createGroupInstance(); + group.setGroupName(groupName1); + group.setLabel("label"); + gHandler.createGroup(group, true); + + assertNotNull(gHandler.findGroupById("/" + groupName1)); + } + + /** + * Save group. + */ + public void testSaveGroup() throws Exception + { + createGroup(null, groupName1, "label", "desc"); + + // set new description + Group g = gHandler.findGroupById("/" + groupName1); + g.setDescription("newDesc"); + gHandler.saveGroup(g, true); + + // check if group has new description + g = gHandler.findGroupById("/" + groupName1); + assertEquals(g.getDescription(), "newDesc"); + + // Check the listener's counters + assertEquals(1, listener.preSaveNew); + assertEquals(1, listener.postSaveNew); + assertEquals(1, listener.preSave); + assertEquals(1, listener.postSave); + assertEquals(0, listener.preDelete); + assertEquals(0, listener.postDelete); + } + + /** + * Test get listeners. + */ + public void testGetListeners() throws Exception + { + if (gHandler instanceof GroupEventListenerHandler) + { + List list = ((GroupEventListenerHandler) gHandler).getGroupListeners(); + try + { + list.clear(); + fail("We are not supposed to be able to change list of listeners."); + } + catch (Exception e) + { + } + } + } + + private static class MyGroupEventListener extends GroupEventListener + { + public int preSaveNew, postSaveNew; + public int preSave, postSave; + public int preDelete, postDelete; + + @Override + public void preSave(Group group, boolean isNew) throws Exception + { + if (group == null) + return; + if (isNew) + preSaveNew++; + else + preSave++; + } + + @Override + public void postSave(Group group, boolean isNew) throws Exception + { + if (group == null) + return; + if (isNew) + postSaveNew++; + else + postSave++; + } + + @Override + public void preDelete(Group group) throws Exception + { + if (group == null) + return; + preDelete++; + } + + @Override + public void postDelete(Group group) throws Exception + { + if (group == null) + return; + postDelete++; + } + } + + public void testFindGroupByMembership() throws Exception + { + createMembership(userName, groupName1, membershipType); + createGroup(null, groupName2, "label", "desc"); + mtHandler.findMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE); + mHandler.linkMembership(uHandler.findUserByName(userName), gHandler.findGroupById("/" + groupName2), + mtHandler.findMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE), false); + Collection groups = gHandler.findGroupByMembership(userName, membershipType); + assertNotNull(groups); + assertEquals(1, groups.size()); + assertEquals(groupName1, groups.iterator().next().getGroupName()); + + groups = gHandler.findGroupByMembership(userName, MembershipTypeHandler.ANY_MEMBERSHIP_TYPE); + assertNotNull(groups); + assertEquals(1, groups.size()); + assertEquals(groupName2, groups.iterator().next().getGroupName()); + } + + public void testResolveGroupByMembership() throws Exception + { + createMembership(userName, groupName1, membershipType); + createGroup(null, groupName2, "label", "desc"); + mHandler.linkMembership(uHandler.findUserByName(userName), gHandler.findGroupById("/" + groupName2), + mtHandler.findMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE), false); + Collection groups = gHandler.resolveGroupByMembership(userName, membershipType); + assertNotNull(groups); + assertEquals(2, groups.size()); + Set groupNames = new HashSet(); + for (Group g : groups) + { + groupNames.add(g.getGroupName()); + } + assertTrue(groupNames.contains(groupName1)); + assertTrue(groupNames.contains(groupName2)); + + groups = gHandler.resolveGroupByMembership(userName, MembershipTypeHandler.ANY_MEMBERSHIP_TYPE); + assertNotNull(groups); + assertEquals(1, groups.size()); + assertEquals(groupName2, groups.iterator().next().getGroupName()); + } +} diff --git a/component/identity/src/test/java/org/exoplatform/services/tck/organization/TestMembershipHandler.java b/component/identity/src/test/java/org/exoplatform/services/tck/organization/TestMembershipHandler.java new file mode 100644 index 0000000000..98971030ca --- /dev/null +++ b/component/identity/src/test/java/org/exoplatform/services/tck/organization/TestMembershipHandler.java @@ -0,0 +1,1102 @@ +/* + * Copyright (C) 2003-2008 eXo Platform SAS. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see. + */ +package org.exoplatform.services.tck.organization; + +import org.exoplatform.commons.utils.ListAccess; +import org.exoplatform.commons.utils.PageList; +import org.exoplatform.services.organization.Group; +import org.exoplatform.services.organization.Membership; +import org.exoplatform.services.organization.MembershipEventListener; +import org.exoplatform.services.organization.MembershipEventListenerHandler; +import org.exoplatform.services.organization.MembershipType; +import org.exoplatform.services.organization.MembershipTypeHandler; +import org.exoplatform.services.organization.User; + +import java.util.Collection; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Created by The eXo Platform SAS. + * + * @author Anatoliy Bazko + * @version $Id: TestMembershipImpl.java 111 2008-11-11 11:11:11Z $ + */ +public class TestMembershipHandler extends AbstractOrganizationServiceTest +{ + private MyMembershipEventListener listener; + + @Override + public void setUp() throws Exception + { + super.setUp(); + listener = new MyMembershipEventListener(); + mHandler.addMembershipEventListener(listener); + } + + @Override + public void tearDown() throws Exception + { + mHandler.removeMembershipEventListener(listener); + super.tearDown(); + } + + /** + * Find membership. + */ + public void testFindMembership() throws Exception + { + createMembership(userName, groupName1, membershipType); + + Membership m = mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, membershipType); + assertNotNull(m); + assertEquals(membershipType, m.getMembershipType()); + assertNotNull(m = mHandler.findMembership(m.getId())); + assertEquals(membershipType, m.getMembershipType()); + + // try to find not existed membership. We are supposed to get Exception + try + { + assertNull(mHandler.findMembership("not-existed-id")); + fail("Exception should be thrown"); + } + catch (Exception e) + { + + } + + mHandler.linkMembership(uHandler.findUserByName(userName), gHandler.findGroupById("/" + groupName1), + mtHandler.findMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE), true); + + m = + mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, + MembershipTypeHandler.ANY_MEMBERSHIP_TYPE); + assertNotNull(m); + assertEquals(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE, m.getMembershipType()); + assertNotNull(m = mHandler.findMembership(m.getId())); + assertEquals(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE, m.getMembershipType()); + + // Check the listener's counters + assertEquals(2, listener.preSaveNew); + assertEquals(2, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(0, listener.preDelete); + assertEquals(0, listener.postDelete); + } + + /** + * Find membership by user and group. + */ + public void testFindMembershipByUserGroupAndType() throws Exception + { + Membership m = mHandler.findMembershipByUserGroupAndType("marry", "/platform/users", "member"); + + assertNotNull(m); + assertEquals(m.getGroupId(), "/platform/users"); + assertEquals(m.getMembershipType(), "member"); + assertEquals(m.getUserName(), "marry"); + + // try to find not existed membership. We are supposed to get null instead of Exception + try + { + assertNull(mHandler.findMembershipByUserGroupAndType(userName, "/platform/users", "member")); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + try + { + assertNull(mHandler.findMembershipByUserGroupAndType("marry", "/" + groupName1, "member")); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + try + { + assertNull(mHandler.findMembershipByUserGroupAndType("marry", "/platform/users", membershipType)); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + createMembership(userName, groupName1, membershipType); + + assertNotNull(m = mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, membershipType)); + + mHandler.removeMembership(m.getId(), true); + + assertNull(mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, membershipType)); + + createMembership(userName + "2", groupName2, membershipType + "2"); + + assertNotNull(mHandler.findMembershipByUserGroupAndType(userName + "2", "/" + groupName2, membershipType + "2")); + + mHandler.removeMembershipByUser(userName + "2", true); + + assertNull(mHandler.findMembershipByUserGroupAndType(userName + "2", "/" + groupName2, membershipType + "2")); + + createMembership(userName + "3", groupName2 + "3", membershipType + "3"); + + assertNotNull(mHandler.findMembershipByUserGroupAndType(userName + "3", "/" + groupName2 + "3", membershipType + + "3")); + + uHandler.removeUser(userName + "3", false); + + assertNull(mHandler + .findMembershipByUserGroupAndType(userName + "3", "/" + groupName2 + "3", membershipType + "3")); + + createMembership(userName + "4", groupName2 + "4", membershipType + "4"); + + assertNotNull(mHandler.findMembershipByUserGroupAndType(userName + "4", "/" + groupName2 + "4", membershipType + + "4")); + + gHandler.removeGroup(gHandler.findGroupById("/" + groupName2 + "4"), false); + + assertNull(mHandler + .findMembershipByUserGroupAndType(userName + "4", "/" + groupName2 + "4", membershipType + "4")); + + // Check the listener's counters + assertEquals(4, listener.preSaveNew); + assertEquals(4, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(2, listener.preDelete); + assertEquals(2, listener.postDelete); + } + + /** + * Test find all membershipTypes linked to this group + * @throws Exception + */ + public void testFindMembershipTypesByGroup() throws Exception { + + Group users, testGroup = null; + try { + createUser(userName); + assertEquals(1, mHandler.findMembershipTypesByGroup("/platform/users").size()); + + testGroup = gHandler.createGroupInstance(); + testGroup.setGroupName("testGroup"); + testGroup.setLabel("Testing Group"); + gHandler.addChild(null, testGroup, false); + + assertEquals(0, mHandler.findMembershipTypesByGroup("/testGroup").size()); + + mHandler.linkMembership(uHandler.findUserByName(userName), testGroup, + mtHandler.findMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE), true); + + assertEquals(1, mHandler.findMembershipTypesByGroup("/testGroup").size()); + + // Add another user + String ali = "ali"; + createUser(ali); + + mHandler.linkMembership(uHandler.findUserByName(ali), testGroup, + mtHandler.findMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE), true); + + assertEquals(1, mHandler.findMembershipTypesByGroup("/testGroup").size()); + + createMembershipType(membershipType, "desc"); + mHandler.linkMembership(uHandler.findUserByName(ali), testGroup, + mtHandler.findMembershipType(membershipType), true); + + assertEquals(2, mHandler.findMembershipTypesByGroup("/testGroup").size()); + + + Membership membership = mHandler.findMembershipByUserGroupAndType(userName,"/testGroup", MembershipTypeHandler.ANY_MEMBERSHIP_TYPE); + mHandler.removeMembership(membership.getId(), true); + + assertEquals(2, mHandler.findMembershipTypesByGroup("/testGroup").size()); + + // remove all memberships of ali + membership = mHandler.findMembershipByUserGroupAndType(ali,"/testGroup", membershipType); + mHandler.removeMembership(membership.getId(), true); + + // we have 2 membership types * and type + assertEquals(1, mHandler.findMembershipTypesByGroup("/testGroup").size()); + } catch (UnsupportedOperationException unsupportedOperationException) { + //Expected if this function was not implemented + } + } + + + /** + * Find membership by group. + */ + public void testFindMembershipsByGroup() throws Exception + { + Group g = gHandler.findGroupById("/platform/users"); + assertTrue(mHandler.findMembershipsByGroup(g).size() >= 4); + + // try to find for non-existing group + g = gHandler.createGroupInstance(); + g.setGroupName(groupName1); + g.setLabel("label"); + gHandler.addChild(null, g, false); + assertEquals(g.getId(), gHandler.findGroupById("/" + groupName1).getId()); + g = gHandler.removeGroup(g, false); + assertSizeEquals(0, mHandler.findMembershipsByGroup(g)); + + // Check the listener's counters + assertEquals(0, listener.preSaveNew); + assertEquals(0, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(0, listener.preDelete); + assertEquals(0, listener.postDelete); + } + + /** + * Find membership by group. + */ + public void testFindAllMembershipsByGroup() throws Exception + { + Group g = gHandler.findGroupById("/platform/users"); + ListAccess memberships = mHandler.findAllMembershipsByGroup(g); + assertTrue(memberships.getSize() >= 4); + + try + { + Membership[] m = memberships.load(0, 4); + assertEquals(4, m.length); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + try + { + Membership[] m = memberships.load(1, 2); + assertEquals(2, m.length); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + // test case removed from tck organization PLF-7379 + // no need to improve ldap and jdbc implementation : it will be removed on 5.0 + + /* try + { + memberships.load(1, 4); + fail("Exception should be thrown"); + } + catch (Exception e) + { + + }*/ + + // try to find for non-existing group + g = gHandler.createGroupInstance(); + g.setGroupName(groupName1); + g.setLabel("label"); + gHandler.addChild(null, g, false); + assertEquals(g.getId(), gHandler.findGroupById("/" + groupName1).getId()); + g = gHandler.removeGroup(g, true); + assertSizeEquals(0, mHandler.findMembershipsByGroup(g)); + + createMembership(userName, groupName1, membershipType); + + g = gHandler.findGroupById("/" + groupName1); + + assertSizeEquals(1, mHandler.findAllMembershipsByGroup(g)); + + Membership m; + assertNotNull(m = mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, membershipType)); + + mHandler.linkMembership(uHandler.findUserByName(userName), g, + mtHandler.findMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE), true); + + assertSizeEquals(2, mHandler.findAllMembershipsByGroup(g)); + + Set membershipTypes = new HashSet(); + for (Membership mem : mHandler.findAllMembershipsByGroup(g).load(0, 2)) + { + membershipTypes.add(mem.getMembershipType()); + } + assertTrue(membershipTypes.contains(membershipType)); + assertTrue(membershipTypes.contains(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE)); + + mHandler.removeMembership(m.getId(), true); + + assertSizeEquals(1, mHandler.findAllMembershipsByGroup(g)); + + assertNotNull(m = + mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, + MembershipTypeHandler.ANY_MEMBERSHIP_TYPE)); + + mHandler.removeMembership(m.getId(), true); + + assertSizeEquals(0, mHandler.findAllMembershipsByGroup(g)); + + createMembership(userName + "2", groupName2, membershipType + "2"); + + g = gHandler.findGroupById("/" + groupName2); + + assertSizeEquals(1, mHandler.findAllMembershipsByGroup(g)); + + mHandler.removeMembershipByUser(userName + "2", true); + + assertSizeEquals(0, mHandler.findAllMembershipsByGroup(g)); + + createMembership(userName + "3", groupName2 + "3", membershipType + "3"); + + g = gHandler.findGroupById("/" + groupName2 + "3"); + + assertSizeEquals(1, mHandler.findAllMembershipsByGroup(g)); + + uHandler.removeUser(userName + "3", false); + + assertSizeEquals(0, mHandler.findAllMembershipsByGroup(g)); + + createMembership(userName + "4", groupName2 + "4", membershipType + "4"); + + g = gHandler.findGroupById("/" + groupName2 + "4"); + + assertSizeEquals(1, mHandler.findAllMembershipsByGroup(g)); + + gHandler.removeGroup(gHandler.findGroupById("/" + groupName2 + "4"), false); + + assertSizeEquals(0, mHandler.findAllMembershipsByGroup(g)); + + // Check the listener's counters + assertEquals(5, listener.preSaveNew); + assertEquals(5, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(3, listener.preDelete); + assertEquals(3, listener.postDelete); + } + + /** + * Find all memberships by user. + */ + public void testFindMembershipsByUser() throws Exception + { + assertSizeEquals(3, mHandler.findMembershipsByUser("john")); + assertSizeEquals(0, mHandler.findMembershipsByUser("not-existed-user")); + + createMembership(userName, groupName1, membershipType); + + assertSizeEquals(1, mHandler.findMembershipsByUser(userName)); + + Membership m; + assertNotNull(m = mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, membershipType)); + + mHandler.linkMembership(uHandler.findUserByName(userName), gHandler.findGroupById("/" + groupName1), + mtHandler.findMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE), true); + + assertSizeEquals(2, mHandler.findMembershipsByUser(userName)); + + Set membershipTypes = new HashSet(); + for (Membership mem : mHandler.findMembershipsByUser(userName)) + { + membershipTypes.add(mem.getMembershipType()); + } + assertTrue(membershipTypes.contains(membershipType)); + assertTrue(membershipTypes.contains(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE)); + + mHandler.removeMembership(m.getId(), true); + + assertSizeEquals(1, mHandler.findMembershipsByUser(userName)); + + assertNotNull(m = + mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, + MembershipTypeHandler.ANY_MEMBERSHIP_TYPE)); + + mHandler.removeMembership(m.getId(), true); + + assertSizeEquals(0, mHandler.findMembershipsByUser(userName)); + + createMembership(userName + "2", groupName2, membershipType + "2"); + + assertSizeEquals(1, mHandler.findMembershipsByUser(userName + "2")); + + mHandler.removeMembershipByUser(userName + "2", true); + + assertSizeEquals(0, mHandler.findMembershipsByUser(userName + "2")); + + createMembership(userName + "3", groupName2 + "3", membershipType + "3"); + + assertSizeEquals(1, mHandler.findMembershipsByUser(userName + "3")); + + uHandler.removeUser(userName + "3", false); + + assertSizeEquals(0, mHandler.findMembershipsByUser(userName + "3")); + + createMembership(userName + "4", groupName2 + "4", membershipType + "4"); + + assertSizeEquals(1, mHandler.findMembershipsByUser(userName + "4")); + + gHandler.removeGroup(gHandler.findGroupById("/" + groupName2 + "4"), false); + + assertSizeEquals(0, mHandler.findMembershipsByUser(userName + "4")); + + // Check the listener's counters + assertEquals(5, listener.preSaveNew); + assertEquals(5, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(3, listener.preDelete); + assertEquals(3, listener.postDelete); + } + + /** + * Find all membership by user and group. + */ + public void testFindMembershipsByUserAndGroup() throws Exception + { + assertSizeEquals(1, mHandler.findMembershipsByUserAndGroup("john", "/platform/users")); + + // try to find not existed membership. We are supposed to get null instead of Exception + try + { + assertSizeEquals(0, mHandler.findMembershipsByUserAndGroup("non-existed-john", "/platform/users")); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + try + { + assertSizeEquals(0, mHandler.findMembershipsByUserAndGroup("john", "/non-existed-group")); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + createMembership(userName, groupName1, membershipType); + + assertSizeEquals(1, mHandler.findMembershipsByUserAndGroup(userName, "/" + groupName1)); + + Membership m = mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, membershipType); + assertNotNull(m); + assertEquals(membershipType, m.getMembershipType()); + + mHandler.linkMembership(uHandler.findUserByName(userName), gHandler.findGroupById("/" + groupName1), + mtHandler.findMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE), true); + + assertSizeEquals(2, mHandler.findMembershipsByUserAndGroup(userName, "/" + groupName1)); + + Set membershipTypes = new HashSet(); + for (Membership mem : mHandler.findMembershipsByUserAndGroup(userName, "/" + groupName1)) + { + membershipTypes.add(mem.getMembershipType()); + } + assertTrue(membershipTypes.contains(membershipType)); + assertTrue(membershipTypes.contains(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE)); + + m = mHandler.removeMembership(m.getId(), true); + assertNotNull(m); + assertEquals(membershipType, m.getMembershipType()); + + assertSizeEquals(1, mHandler.findMembershipsByUserAndGroup(userName, "/" + groupName1)); + + m = + mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, + MembershipTypeHandler.ANY_MEMBERSHIP_TYPE); + assertNotNull(m); + assertEquals(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE, m.getMembershipType()); + + m = mHandler.removeMembership(m.getId(), true); + + assertNotNull(m); + assertEquals(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE, m.getMembershipType()); + + assertSizeEquals(0, mHandler.findMembershipsByUserAndGroup(userName, "/" + groupName1)); + + createMembership(userName + "2", groupName2, membershipType + "2"); + + assertSizeEquals(1, mHandler.findMembershipsByUserAndGroup(userName + "2", "/" + groupName2)); + + mHandler.removeMembershipByUser(userName + "2", true); + + assertSizeEquals(0, mHandler.findMembershipsByUserAndGroup(userName + "2", "/" + groupName2)); + + createMembership(userName + "3", groupName2 + "3", membershipType + "3"); + + assertSizeEquals(1, mHandler.findMembershipsByUserAndGroup(userName + "3", "/" + groupName2 + "3")); + + uHandler.removeUser(userName + "3", false); + + assertSizeEquals(0, mHandler.findMembershipsByUserAndGroup(userName + "3", "/" + groupName2 + "3")); + + createMembership(userName + "4", groupName2 + "4", membershipType + "4"); + + assertSizeEquals(1, mHandler.findMembershipsByUserAndGroup(userName + "4", "/" + groupName2 + "4")); + + gHandler.removeGroup(gHandler.findGroupById("/" + groupName2 + "4"), false); + + assertSizeEquals(0, mHandler.findMembershipsByUserAndGroup(userName + "4", "/" + groupName2 + "4")); + + // Check the listener's counters + assertEquals(5, listener.preSaveNew); + assertEquals(5, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(3, listener.preDelete); + assertEquals(3, listener.postDelete); + } + + /** + * Link membership. + */ + public void testLinkMembership() throws Exception + { + createUser(userName); + createGroup(null, groupName1, "label", "desc"); + createMembershipType(membershipType, "desc"); + + // link membership + mHandler.linkMembership(uHandler.findUserByName(userName), gHandler.findGroupById("/" + groupName1), + mtHandler.findMembershipType(membershipType), true); + + Membership m = mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, membershipType); + assertNotNull(m); + + // try to create already existed membership. Exception should not be thrown + try + { + mHandler.linkMembership(uHandler.findUserByName(userName), gHandler.findGroupById("/" + groupName1), + mtHandler.findMembershipType(membershipType), true); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + // we expect only 1 membership record + assertEquals(1, mHandler.findMembershipsByUser(userName).size()); + + // test deprecated memthod create membership + mHandler.removeMembership(m.getId(), true); + mHandler.createMembership(m, true); + m = mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, membershipType); + assertNotNull(m); + + // try to link membership with not existed entries. We are supposed to get Exception + Group group = createGroupInstance("not-existed-group"); + try + { + mHandler.linkMembership(uHandler.findUserByName(userName), group, + mtHandler.findMembershipType(membershipType), true); + fail("Exception should be thrown"); + } + catch (Exception e) + { + } + + User user = uHandler.createUserInstance("not-existed-user"); + try + { + mHandler.linkMembership(user, gHandler.findGroupById("/" + groupName1), + mtHandler.findMembershipType(membershipType), true); + fail("Exception should be thrown"); + } + catch (Exception e) + { + } + + MembershipType mt = mtHandler.createMembershipTypeInstance(); + mt.setName("not-existed-mt"); + try + { + mHandler.linkMembership(uHandler.findUserByName(userName), gHandler.findGroupById("/" + groupName1), mt, true); + fail("Exception should be thrown"); + } + catch (Exception e) + { + } + + try + { + mHandler.linkMembership(uHandler.findUserByName(userName), null, mtHandler.findMembershipType(membershipType), + true); + fail("Exception should be thrown"); + } + catch (Exception e) + { + } + + try + { + mHandler.linkMembership(null, gHandler.findGroupById("/" + groupName1), + mtHandler.findMembershipType(membershipType), true); + fail("Exception should be thrown"); + } + catch (Exception e) + { + } + + try + { + mHandler.linkMembership(uHandler.findUserByName(userName), gHandler.findGroupById("/" + groupName1), null, + true); + fail("Exception should be thrown"); + } + catch (Exception e) + { + } + + mHandler.linkMembership(uHandler.findUserByName(userName), gHandler.findGroupById("/" + groupName1), + mtHandler.findMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE), true); + m = + mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, + MembershipTypeHandler.ANY_MEMBERSHIP_TYPE); + assertNotNull(m); + + // Check the listener's counters + assertEquals(3, listener.preSaveNew); + assertEquals(3, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(1, listener.preDelete); + assertEquals(1, listener.postDelete); + } + + /** + * Remove membership + */ + public void testRemoveMembership() throws Exception + { + + createMembership(userName, groupName1, membershipType); + Membership m = mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, membershipType); + + assertNotNull(m); + + m = mHandler.removeMembership(m.getId(), true); + assertEquals(m.getGroupId(), "/" + groupName1); + assertEquals(m.getMembershipType(), membershipType); + assertEquals(m.getUserName(), userName); + + assertNull(mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, membershipType)); + + // try to remove not existed membership. We are supposed to get "null" instead of Exception + try + { + assertNull(mHandler.removeMembership("not-existed-id", true)); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + // Check the listener's counters + assertEquals(1, listener.preSaveNew); + assertEquals(1, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(1, listener.preDelete); + assertEquals(1, listener.postDelete); + } + + /** + * Remove membership by user. + */ + public void testRemoveMembershipByUser() throws Exception + { + createMembership(userName, groupName1, membershipType); + assertNotNull(mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, membershipType)); + + mHandler.linkMembership(uHandler.findUserByName(userName), gHandler.findGroupById("/" + groupName1), + mtHandler.findMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE), true); + + assertNotNull(mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, + MembershipTypeHandler.ANY_MEMBERSHIP_TYPE)); + + Collection memberships; + assertSizeEquals(2, memberships = mHandler.removeMembershipByUser(userName, true)); + Set membershipNames = new HashSet(); + for (Membership m : memberships) + { + membershipNames.add(m.getMembershipType()); + } + assertTrue(membershipNames.contains(membershipType)); + assertTrue(membershipNames.contains(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE)); + + assertNull(mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, membershipType)); + assertNull(mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, + MembershipTypeHandler.ANY_MEMBERSHIP_TYPE)); + + assertSizeEquals(0, mHandler.findMembershipsByUserAndGroup(userName, "/" + groupName1)); + assertSizeEquals(0, mHandler.findMembershipsByUser(userName)); + + assertNull(mHandler.findMembershipByUserGroupAndType(userName, "/group", membershipType)); + + // try to remove memberships by not existed users. We are supposed to get empty list instead of Exception + try + { + assertSizeEquals(0, mHandler.removeMembershipByUser("not-existed-user", true)); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + // Check the listener's counters + assertEquals(2, listener.preSaveNew); + assertEquals(2, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(2, listener.preDelete); + assertEquals(2, listener.postDelete); + } + + /** + * Find group by membership. + */ + public void testFindGroupByMembership() throws Exception + { + createMembership(userName, groupName1, membershipType); + assertSizeEquals(1, gHandler.findGroupByMembership(userName, membershipType)); + + // try to find groups by not existed entries. We supposed to get empty list instead of Exception + try + { + assertSizeEquals(0, gHandler.findGroupByMembership("not-existed-john", membershipType)); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + mHandler.removeMembershipByUser(userName, true); + try + { + assertSizeEquals(0, gHandler.findGroupByMembership(userName, membershipType)); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + createMembership(userName + "2", groupName2, "foo"); + + assertSizeEquals(1, gHandler.findGroupByMembership(userName + "2", "foo")); + + uHandler.removeUser(userName + "2", false); + + try + { + assertSizeEquals(0, gHandler.findGroupByMembership(userName + "2", "foo")); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + // Check the listener's counters + assertEquals(2, listener.preSaveNew); + assertEquals(2, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(1, listener.preDelete); + assertEquals(1, listener.postDelete); + } + + /** + * Find groups of user. + */ + public void testFindGroupsOfUser() throws Exception + { + assertSizeEquals(3, gHandler.findGroupsOfUser("john")); + + // try to find groups by not existed entries. We supposed to get empty list instead of Exception + try + { + assertSizeEquals(0, gHandler.findGroupsOfUser("not-existed-james")); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + // Check the listener's counters + assertEquals(0, listener.preSaveNew); + assertEquals(0, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(0, listener.preDelete); + assertEquals(0, listener.postDelete); + } + + /** + * Find users by group. + */ + public void testFindUsersByGroupId() throws Exception + { + ListAccess users = uHandler.findUsersByGroupId("/platform/users"); + + assertTrue(users.getSize() >= 4); + + for (User u : users.load(0, users.getSize())) + { + User currentUrer = uHandler.findUserByName(u.getUserName()); + assertNotNull(currentUrer); + + assertEquals(currentUrer.getUserName(), u.getUserName()); + assertEquals(currentUrer.getFirstName(), u.getFirstName()); + assertEquals(currentUrer.getLastName(), u.getLastName()); + assertEquals(currentUrer.getEmail(), u.getEmail()); + assertEquals(currentUrer.getOrganizationId(), u.getOrganizationId()); + } + + // try to find users by not existed entries. We supposed to get empty list instead of Exception + try + { + assertSizeEquals(0, uHandler.findUsersByGroupId("/not-existed-group")); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + User[] allPage = users.load(0, 4); + User[] page1 = users.load(0, 2); + User[] page2 = users.load(2, 2); + + assertEquals(allPage[0].getUserName(), page1[0].getUserName()); + assertEquals(allPage[1].getUserName(), page1[1].getUserName()); + assertEquals(allPage[2].getUserName(), page2[0].getUserName()); + assertEquals(allPage[3].getUserName(), page2[1].getUserName()); + + try + { + users.load(0, 0); + } + catch (Exception e) + { + fail("Exception is not expected"); + } + + // try to load more than exist + users.load(1, 4); + + // Check the listener's counters + assertEquals(0, listener.preSaveNew); + assertEquals(0, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(0, listener.preDelete); + assertEquals(0, listener.postDelete); + } + + /** + * Find users by group. + */ + public void testFindUsersByGroup() throws Exception + { + PageList usersList = uHandler.findUsersByGroup("/platform/users"); + + assertTrue(usersList.getAll().size() >= 4); + + for (User u : usersList.getAll()) + { + User currentUrer = uHandler.findUserByName(u.getUserName()); + assertNotNull(currentUrer); + + assertEquals(currentUrer.getUserName(), u.getUserName()); + assertEquals(currentUrer.getFirstName(), u.getFirstName()); + assertEquals(currentUrer.getLastName(), u.getLastName()); + assertEquals(currentUrer.getEmail(), u.getEmail()); + assertEquals(currentUrer.getOrganizationId(), u.getOrganizationId()); + } + + // try to find users by not existed entries. We supposed to get empty list instead of Exception + try + { + assertSizeEquals(0, uHandler.findUsersByGroup("/not-existed-group").getAll()); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + // Check the listener's counters + assertEquals(0, listener.preSaveNew); + assertEquals(0, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(0, listener.preDelete); + assertEquals(0, listener.postDelete); + } + + /** + * Remove membership type. + */ + public void testRemoveMembershipType() throws Exception + { + createMembership(userName, groupName1, membershipType); + + mtHandler.removeMembershipType("type", true); + assertNull(mtHandler.findMembershipType("type")); + assertNull(mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, membershipType)); + + // Check the listener's counters + assertEquals(1, listener.preSaveNew); + assertEquals(1, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(0, listener.preDelete); + assertEquals(0, listener.postDelete); + } + + /** + * Remove group. + */ + public void testRemoveGroup() throws Exception + { + createMembership(userName, groupName1, membershipType); + + gHandler.removeGroup(gHandler.findGroupById("/" + groupName1), true); + + assertNull(gHandler.findGroupById("/" + groupName1)); + assertNull(mHandler.findMembershipByUserGroupAndType(userName, "/" + groupName1, membershipType)); + + // try to remove not existed groups. We are supposed to get Exception + try + { + Group group = createGroupInstance("not-existed-group"); + + gHandler.removeGroup(group, true); + + fail("Exception should be thrown"); + } + catch (Exception e) + { + } + + try + { + gHandler.removeGroup(null, true); + fail("Exception should be thrown"); + } + catch (Exception e) + { + } + + // Check the listener's counters + assertEquals(1, listener.preSaveNew); + assertEquals(1, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(0, listener.preDelete); + assertEquals(0, listener.postDelete); + } + + /** + * Remove user. + */ + public void testRemoveUser() throws Exception + { + String userName = "testRemoveUser"; + createMembership(userName, groupName1, membershipType); + + uHandler.removeUser(userName, true); + + // Check the listener's counters + assertEquals(1, listener.preSaveNew); + assertEquals(1, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(0, listener.preDelete); + assertEquals(0, listener.postDelete); + } + + /** + * Test get listeners. + */ + public void testGetListeners() throws Exception + { + if (mHandler instanceof MembershipEventListenerHandler) + { + List list = ((MembershipEventListenerHandler)mHandler).getMembershipListeners(); + try + { + list.clear(); + fail("We are not supposed to change list of listners"); + } + catch (Exception e) + { + } + } + } + + private static class MyMembershipEventListener extends MembershipEventListener + { + public int preSaveNew, postSaveNew; + + public int preSave, postSave; + + public int preDelete, postDelete; + + @Override + public void preSave(Membership m, boolean isNew) throws Exception + { + if (m == null) + return; + if (!m.getMembershipType().startsWith("type") && !m.getMembershipType().equals("foo") + && !m.getMembershipType().equals(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE)) + { + throw new Exception("Unexpected membership type, it should be 'type' or '" + + MembershipTypeHandler.ANY_MEMBERSHIP_TYPE + "' but was " + m.getMembershipType()); + } + if (isNew) + preSaveNew++; + else + preSave++; + } + + @Override + public void postSave(Membership m, boolean isNew) throws Exception + { + if (m == null) + return; + if (isNew) + postSaveNew++; + else + postSave++; + } + + @Override + public void preDelete(Membership m) throws Exception + { + if (m == null) + return; + preDelete++; + } + + @Override + public void postDelete(Membership m) throws Exception + { + if (m == null) + return; + postDelete++; + } + } +} diff --git a/component/identity/src/test/java/org/exoplatform/services/tck/organization/TestMembershipTypeHandler.java b/component/identity/src/test/java/org/exoplatform/services/tck/organization/TestMembershipTypeHandler.java new file mode 100644 index 0000000000..1fb0677fb8 --- /dev/null +++ b/component/identity/src/test/java/org/exoplatform/services/tck/organization/TestMembershipTypeHandler.java @@ -0,0 +1,417 @@ +/** + * + */ +/* + * Copyright (C) 2003-2007 eXo Platform SAS. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see. + */ +package org.exoplatform.services.tck.organization; + +import org.exoplatform.services.organization.MembershipType; +import org.exoplatform.services.organization.MembershipTypeEventListener; +import org.exoplatform.services.organization.MembershipTypeEventListenerHandler; +import org.exoplatform.services.organization.MembershipTypeHandler; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by The eXo Platform SAS. + * + * @author Anatoliy Bazko + * @version $Id: TestMembershipTypeHandlerImpl.java 111 2008-11-11 11:11:11Z $ + */ +public class TestMembershipTypeHandler extends AbstractOrganizationServiceTest +{ + private MyMembershipTypeEventListener listener; + + @Override + public void setUp() throws Exception + { + super.setUp(); + listener = new MyMembershipTypeEventListener(); + mtHandler.addMembershipTypeEventListener(listener); + } + + @Override + public void tearDown() throws Exception + { + mtHandler.removeMembershipTypeEventListener(listener); + super.tearDown(); + } + + /** + * Find membership type. + */ + public void testFindMembershipType() throws Exception + { + MembershipType mt = mtHandler.findMembershipType("manager"); + assertNotNull(mt); + assertEquals(mt.getName(), "manager"); + assertEquals(mt.getDescription(), "manager membership type"); + + // try to find not existed membership type + assertNull(mtHandler.findMembershipType("manager_")); + + // Check the listener's counters + assertEquals(0, listener.preSaveNew); + assertEquals(0, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(0, listener.preDelete); + assertEquals(0, listener.postDelete); + } + + /** + * Find membership types. + */ + public void testFindMembershipTypes() throws Exception + { + int initSize = mtHandler.findMembershipTypes().size(); + assertEquals(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE, mtHandler.findMembershipTypes().iterator() + .next().getName()); + + assertTrue(initSize >= 4); + + createMembershipType("type2", "membership type2"); + assertSizeEquals(initSize + 1, mtHandler.findMembershipTypes()); + + mtHandler.removeMembershipType("type2", true); + assertSizeEquals(initSize, mtHandler.findMembershipTypes()); + + // Check the listener's counters + assertEquals(1, listener.preSaveNew); + assertEquals(1, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(1, listener.preDelete); + assertEquals(1, listener.postDelete); + } + + /** + * Remove membership type. + */ + public void testRemoveMembershipType() throws Exception + { + createMembership(userName, groupName1, membershipType); + assertEquals("We expect to find single membership for user " + userName, 1, + mHandler.findMembershipsByUser(userName).size()); + + MembershipType mt = mtHandler.removeMembershipType("type", true); + assertEquals(mt.getName(), membershipType); + assertNull(mtHandler.findMembershipType("type")); + assertEquals("We expect to find no membership for user " + userName, 0, mHandler.findMembershipsByUser(userName) + .size()); + + // try to remove not existed membership type. We are supposed to get "null" instead of Exception + try + { + assertNull(mtHandler.removeMembershipType("not-existed-mt", true)); + fail("Exception should be thrown"); + } + catch (Exception e) + { + } + + assertNotNull(mtHandler.findMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE)); + + try + { + mtHandler.removeMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE, true); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + assertNull(mtHandler.findMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE)); + try + { + assertNull(mtHandler.removeMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE, true)); + fail("Exception should be thrown"); + } + catch (Exception e) + { + } + assertNull(mtHandler.findMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE)); + + MembershipType anyMT = mtHandler.createMembershipTypeInstance(); + anyMT.setName(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE); + anyMT.setDescription("desc"); + mtHandler.createMembershipType(anyMT, true); + + assertNotNull(mtHandler.findMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE)); + + // Check the listener's counters + assertEquals(2, listener.preSaveNew); + assertEquals(2, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(2, listener.preDelete); + assertEquals(2, listener.postDelete); + } + + /** + * Save membership type. + */ + public void testSaveMembershipType() throws Exception + { + createMembershipType(membershipType, "desc"); + MembershipType mt = mtHandler.findMembershipType(membershipType); + + // change description + mt.setDescription("newDesc"); + mtHandler.saveMembershipType(mt, true); + + mt = mtHandler.findMembershipType(membershipType); + assertEquals(mt.getDescription(), "newDesc"); + + MembershipType anyMT = mtHandler.findMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE); + assertNotNull(anyMT); + + try + { + mtHandler.saveMembershipType(anyMT, true); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + // Check the listener's counters + assertEquals(1, listener.preSaveNew); + assertEquals(1, listener.postSaveNew); + assertEquals(2, listener.preSave); + assertEquals(2, listener.postSave); + assertEquals(0, listener.preDelete); + assertEquals(0, listener.postDelete); + } + + public void testCreateMembershipType() throws Exception + { + createMembershipType(membershipType, "desc"); + MembershipType mt = mtHandler.findMembershipType(membershipType); + assertNotNull(mt); + + try + { + mtHandler.removeMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE, true); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + assertNull(mtHandler.findMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE)); + + MembershipType anyMT = mtHandler.createMembershipTypeInstance(); + anyMT.setName(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE); + anyMT.setDescription("desc"); + try + { + mtHandler.createMembershipType(anyMT, true); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + assertNotNull(mtHandler.findMembershipType(MembershipTypeHandler.ANY_MEMBERSHIP_TYPE)); + + // Check the listener's counters + assertEquals(2, listener.preSaveNew); + assertEquals(2, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(1, listener.preDelete); + assertEquals(1, listener.postDelete); + } + + /** + * Test get listeners. + */ + public void testGetListeners() throws Exception + { + if (mtHandler instanceof MembershipTypeEventListenerHandler) + { + List list = + ((MembershipTypeEventListenerHandler)mtHandler).getMembershipTypeListeners(); + + try + { + list.clear(); + fail("We are not supposed to change list of listeners"); + } + catch (Exception e) + { + } + } + } + + /** + * Test events. + */ + public void testMembershipTypeEventListener() throws Exception + { + TesterMembershipTypeEventListener testListener = new TesterMembershipTypeEventListener(); + int currentSize = 1; + if (mtHandler instanceof MembershipTypeEventListenerHandler) + { + List list = + ((MembershipTypeEventListenerHandler)mtHandler).getMembershipTypeListeners(); + + currentSize = list.size(); + } + mtHandler.addMembershipTypeEventListener(testListener); + + if (mtHandler instanceof MembershipTypeEventListenerHandler) + { + List list = + ((MembershipTypeEventListenerHandler)mtHandler).getMembershipTypeListeners(); + + assertEquals(currentSize + 1, list.size()); + } + + // Create new membership type. In preSave event there is not recored in db. + createMembershipType(membershipType, "desc"); + + assertEquals(2, testListener.mtInEvent.size()); + assertEquals(2, testListener.mtInStorage.size()); + + // preSave Event + assertEquals(membershipType, testListener.mtInEvent.get(0).getName()); + assertNull(testListener.mtInStorage.get(0)); + + // postSave Event + assertEquals(membershipType, testListener.mtInEvent.get(1).getName()); + assertNotNull(testListener.mtInStorage.get(1)); + assertEquals(membershipType, testListener.mtInStorage.get(1).getName()); + + testListener.mtInEvent.clear(); + testListener.mtInStorage.clear(); + + // Modify membership type. In preSave event there is old record in storage. + MembershipType mt = mtHandler.findMembershipType(membershipType); + mt.setDescription("newDesc"); + + mtHandler.saveMembershipType(mt, true); + + // preSave Event + assertEquals(2, testListener.mtInEvent.size()); + assertEquals(2, testListener.mtInStorage.size()); + + assertEquals("newDesc", testListener.mtInEvent.get(0).getDescription()); + + // postSave Event + assertEquals("newDesc", testListener.mtInEvent.get(1).getDescription()); + assertEquals("newDesc", testListener.mtInStorage.get(1).getDescription()); + + testListener.mtInEvent.clear(); + testListener.mtInStorage.clear(); + + // Remove membership type. In preDelete Event there is still record in storage + mtHandler.removeMembershipType(membershipType, true); + + assertEquals(2, testListener.mtInEvent.size()); + assertEquals(2, testListener.mtInStorage.size()); + + // preDelete Event + assertEquals(membershipType, testListener.mtInEvent.get(0).getName()); + assertNotNull(testListener.mtInStorage.get(0)); + + // postDelete Event + assertEquals(membershipType, testListener.mtInEvent.get(1).getName()); + assertNull(testListener.mtInStorage.get(1)); + + testListener.mtInEvent.clear(); + testListener.mtInStorage.clear(); + + } + + private class TesterMembershipTypeEventListener extends MembershipTypeEventListener + { + List mtInEvent = new ArrayList(); + + List mtInStorage = new ArrayList(); + + public void preSave(MembershipType type, boolean isNew) throws Exception + { + mtInEvent.add(type); + mtInStorage.add(mtHandler.findMembershipType(type.getName())); + } + + public void postSave(MembershipType type, boolean isNew) throws Exception + { + mtInEvent.add(type); + mtInStorage.add(mtHandler.findMembershipType(type.getName())); + } + + public void preDelete(MembershipType type) throws Exception + { + mtInEvent.add(type); + mtInStorage.add(mtHandler.findMembershipType(type.getName())); + } + + public void postDelete(MembershipType type) throws Exception + { + mtInEvent.add(type); + mtInStorage.add(mtHandler.findMembershipType(type.getName())); + } + } + + private static class MyMembershipTypeEventListener extends MembershipTypeEventListener + { + public int preSaveNew, postSaveNew; + public int preSave, postSave; + public int preDelete, postDelete; + + @Override + public void preSave(MembershipType type, boolean isNew) throws Exception + { + if (type == null) + return; + if (isNew) + preSaveNew++; + else + preSave++; + } + + @Override + public void postSave(MembershipType type, boolean isNew) throws Exception + { + if (type == null) + return; + if (isNew) + postSaveNew++; + else + postSave++; + } + + @Override + public void preDelete(MembershipType type) throws Exception + { + if (type == null) + return; + preDelete++; + } + + @Override + public void postDelete(MembershipType type) throws Exception + { + if (type == null) + return; + postDelete++; + } + } +} diff --git a/component/identity/src/test/java/org/exoplatform/services/tck/organization/TestUserHandler.java b/component/identity/src/test/java/org/exoplatform/services/tck/organization/TestUserHandler.java new file mode 100644 index 0000000000..b592eb0415 --- /dev/null +++ b/component/identity/src/test/java/org/exoplatform/services/tck/organization/TestUserHandler.java @@ -0,0 +1,1208 @@ +/* + * Copyright (C) 2003-2008 eXo Platform SAS. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see. + */ +package org.exoplatform.services.tck.organization; + +import org.exoplatform.commons.utils.ListAccess; +import org.exoplatform.container.xml.InitParams; +import org.exoplatform.container.xml.ObjectParameter; +import org.exoplatform.services.organization.DisabledUserException; +import org.exoplatform.services.organization.Query; +import org.exoplatform.services.organization.User; +import org.exoplatform.services.organization.UserEventListener; +import org.exoplatform.services.organization.UserEventListenerHandler; +import org.exoplatform.services.organization.UserProfile; +import org.exoplatform.services.organization.UserStatus; +import org.exoplatform.services.organization.impl.NewUserConfig; +import org.exoplatform.services.organization.impl.NewUserEventListener; + +import java.util.Calendar; +import java.util.List; + +/** + * Created by The eXo Platform SAS. + * + * @author Anatoliy Bazko + * @version $Id: TestOrganizationService.java 111 2008-11-11 11:11:11Z $ + */ +public class TestUserHandler extends AbstractOrganizationServiceTest +{ + private MyUserEventListener listener; + + @Override + public void setUp() throws Exception + { + super.setUp(); + listener = new MyUserEventListener(); + uHandler.addUserEventListener(listener); + } + + @Override + public void tearDown() throws Exception + { + uHandler.removeUserEventListener(listener); + super.tearDown(); + } + + /** + * User authentication. + */ + public void testAuthenticate() throws Exception + { + createUser("testAuthenticate"); + + // authentication with existing user and correct password + assertTrue(uHandler.authenticate("testAuthenticate", "pwdADDSomeSaltToBeCompliantWithSomeIS00")); + + // unknown user authentication + assertFalse(uHandler.authenticate("testAuthenticate_", "pwdADDSomeSaltToBeCompliantWithSomeIS00")); + + // authentication with wrong password + assertFalse(uHandler.authenticate("testAuthenticate", "pwdADDSomeSaltToBeCompliantWithSomeIS00_")); + + boolean unsupportedOperation = false; + try + { + // Disable the user testAuthenticate + uHandler.setEnabled("testAuthenticate", false, true); + + try + { + uHandler.authenticate("testAuthenticate", "pwdADDSomeSaltToBeCompliantWithSomeIS00"); + fail("A DisabledUserException was expected"); + } + catch (DisabledUserException e) + { + // expected exception + } + + // Enable the user testAuthenticate + uHandler.setEnabled("testAuthenticate", true, true); + assertTrue(uHandler.authenticate("testAuthenticate", "pwdADDSomeSaltToBeCompliantWithSomeIS00")); + } + catch (UnsupportedOperationException e) + { + // This operation can be unsupported + unsupportedOperation = true; + } + + // Remove the user testAuthenticate + uHandler.removeUser("testAuthenticate", true); + + // The user testAuthenticate doesn't exist anymore thus the authentication should fail + assertFalse(uHandler.authenticate("testAuthenticate", "pwdADDSomeSaltToBeCompliantWithSomeIS00")); + + // Check the listener's counters + assertEquals(1, listener.preSaveNew); + assertEquals(1, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(unsupportedOperation ? 0 : 2, listener.preSetEnabled); + assertEquals(unsupportedOperation ? 0 : 2, listener.postSetEnabled); + assertEquals(1, listener.preDelete); + assertEquals(1, listener.postDelete); + } + + /** + * Find user by name. + */ + public void testFindUserByName() throws Exception + { + createUser("testFindUserByName"); + + // try to find existed user + User u = uHandler.findUserByName("demo"); + + assertNotNull(u); + assertEquals("demo@localhost", u.getEmail()); + assertEquals("Demo", u.getFirstName()); + assertEquals("exo", u.getLastName()); + assertEquals("demo", u.getUserName()); + assertTrue(u.isEnabled()); + + // try to find a non existing user. We are supposed to get "null" instead of Exception. + try + { + assertNull(uHandler.findUserByName("not-existed-user")); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + try + { + assertNull(uHandler.findUserByName("not-existed-user", UserStatus.ENABLED)); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + try + { + assertNull(uHandler.findUserByName("not-existed-user", UserStatus.DISABLED)); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + try + { + assertNull(uHandler.findUserByName("not-existed-user", UserStatus.ANY)); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + assertNotNull(uHandler.findUserByName("testFindUserByName")); + assertTrue(uHandler.findUserByName("testFindUserByName").isEnabled()); + assertNotNull(uHandler.findUserByName("testFindUserByName", UserStatus.ENABLED)); + assertTrue(uHandler.findUserByName("testFindUserByName", UserStatus.ENABLED).isEnabled()); + assertNotNull(uHandler.findUserByName("testFindUserByName", UserStatus.ANY)); + assertTrue(uHandler.findUserByName("testFindUserByName", UserStatus.ANY).isEnabled()); + + boolean unsupportedOperation = false; + try + { + // Disable the user testFindUserByName + uHandler.setEnabled("testFindUserByName", false, true); + + // We should not find the user testFindUserByName anymore from the normal method + assertNull(uHandler.findUserByName("testFindUserByName")); + assertNull(uHandler.findUserByName("testFindUserByName", UserStatus.ENABLED)); + // We should find it using the method that includes the disabled user account + assertNotNull(uHandler.findUserByName("testFindUserByName", UserStatus.ANY)); + assertFalse(uHandler.findUserByName("testFindUserByName", UserStatus.ANY).isEnabled()); + // We should find it using the method that queries only the disabled user account + assertNotNull(uHandler.findUserByName("testFindUserByName", UserStatus.DISABLED)); + assertFalse(uHandler.findUserByName("testFindUserByName", UserStatus.DISABLED).isEnabled()); + + // Enable the user testFindUserByName + uHandler.setEnabled("testFindUserByName", true, true); + + // We should find it again whatever the value of the parameter status + assertNotNull(uHandler.findUserByName("testFindUserByName")); + assertTrue(uHandler.findUserByName("testFindUserByName").isEnabled()); + assertNotNull(uHandler.findUserByName("testFindUserByName", UserStatus.ENABLED)); + assertTrue(uHandler.findUserByName("testFindUserByName", UserStatus.ENABLED).isEnabled()); + assertNotNull(uHandler.findUserByName("testFindUserByName", UserStatus.ANY)); + assertTrue(uHandler.findUserByName("testFindUserByName", UserStatus.ANY).isEnabled()); + // We should not find the user using the method that queries only the disabled user account + assertNull(uHandler.findUserByName("testFindUserByName", UserStatus.DISABLED)); + } + catch (UnsupportedOperationException e) + { + // This operation can be unsupported + unsupportedOperation = true; + } + + // Remove the user testFindUserByName + uHandler.removeUser("testFindUserByName", true); + + // try to find a user that doesn't exist anymore. We are supposed to get "null" instead of Exception. + try + { + assertNull(uHandler.findUserByName("testFindUserByName")); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + try + { + assertNull(uHandler.findUserByName("testFindUserByName", UserStatus.ENABLED)); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + try + { + assertNull(uHandler.findUserByName("testFindUserByName", UserStatus.DISABLED)); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + try + { + assertNull(uHandler.findUserByName("testFindUserByName", UserStatus.ANY)); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + // Check the listener's counters + assertEquals(1, listener.preSaveNew); + assertEquals(1, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(unsupportedOperation ? 0 : 2, listener.preSetEnabled); + assertEquals(unsupportedOperation ? 0 : 2, listener.postSetEnabled); + assertEquals(1, listener.preDelete); + assertEquals(1, listener.postDelete); + } + + /** + * Find users by query. + */ + public void testFindUsersByQuery() throws Exception + { + createUser("tolik"); + uHandler.authenticate("tolik", "pwdADDSomeSaltToBeCompliantWithSomeIS00"); + + Query query = new Query(); + query.setEmail("email@test"); + + // try to find user by email + assertSizeEquals(1, uHandler.findUsersByQuery(query), UserStatus.ENABLED); + assertSizeEquals(1, uHandler.findUsersByQuery(query, UserStatus.ENABLED), UserStatus.ENABLED); + assertSizeEquals(1, uHandler.findUsersByQuery(query, UserStatus.ANY), UserStatus.ANY); + + // try to find user by name with mask + query = new Query(); + query.setUserName("*tolik*"); + assertSizeEquals(1, uHandler.findUsersByQuery(query)); + + // try to find user by name with mask + query = new Query(); + query.setUserName("tol*"); + assertSizeEquals(1, uHandler.findUsersByQuery(query)); + + // try to find user by name with mask + query = new Query(); + query.setUserName("*lik"); + assertSizeEquals(1, uHandler.findUsersByQuery(query)); + + // try to find user by name explicitly + query = new Query(); + query.setUserName("tolik"); + assertSizeEquals(1, uHandler.findUsersByQuery(query)); + + // try to find user by part of name without mask + query = new Query(); + query.setUserName("tol"); + assertSizeEquals(1, uHandler.findUsersByQuery(query)); + + // try to find user by fist and last names + query = new Query(); + query.setFirstName("first"); + query.setLastName("last"); + assertSizeEquals(1, uHandler.findUsersByQuery(query)); + + String skipCISearchTests = System.getProperty("orgservice.test.configuration.skipCISearchTests"); + if (!"true".equals(skipCISearchTests)) + { + // try to find user by name explicitly, case insensitive search + query = new Query(); + query.setUserName("Tolik"); + assertSizeEquals(1, uHandler.findUsersByQuery(query)); + + // try to find user by fist and last names, case insensitive search + query = new Query(); + query.setFirstName("fiRst"); + query.setLastName("lasT"); + assertSizeEquals(1, uHandler.findUsersByQuery(query)); + } + + String skipDateTests = System.getProperty("orgservice.test.configuration.skipDateTests"); + if (!"true".equals(skipDateTests)) + { + // try to find user by login date + Calendar calc = Calendar.getInstance(); + calc.set(Calendar.YEAR, calc.get(Calendar.YEAR) - 1); + + query = new Query(); + query.setFromLoginDate(calc.getTime()); + query.setUserName("tolik"); + assertSizeEquals(1, uHandler.findUsersByQuery(query)); + + calc = Calendar.getInstance(); + calc.set(Calendar.YEAR, calc.get(Calendar.YEAR) + 1); + + query = new Query(); + query.setFromLoginDate(calc.getTime()); + assertSizeEquals(0, uHandler.findUsersByQuery(query)); + + calc = Calendar.getInstance(); + calc.set(Calendar.YEAR, calc.get(Calendar.YEAR) - 1); + + query = new Query(); + query.setToLoginDate(calc.getTime()); + assertSizeEquals(0, uHandler.findUsersByQuery(query)); + + calc = Calendar.getInstance(); + calc.set(Calendar.YEAR, calc.get(Calendar.YEAR) + 1); + + query = new Query(); + query.setToLoginDate(calc.getTime()); + query.setUserName("tolik"); + assertSizeEquals(1, uHandler.findUsersByQuery(query)); + } + + createUser("rolik"); + createUser("bolik"); + createUser("volik"); + + query = new Query(); + query.setUserName("olik"); + + ListAccess users = uHandler.findUsersByQuery(query); + + assertSizeEquals(4, users, UserStatus.ENABLED); + assertSizeEquals(4, uHandler.findUsersByQuery(query, UserStatus.ENABLED), UserStatus.ENABLED); + assertSizeEquals(4, uHandler.findUsersByQuery(query, UserStatus.ANY), UserStatus.ANY); + + User[] allPage = users.load(0, 4); + User[] page1 = users.load(0, 2); + User[] page2 = users.load(2, 2); + + assertEquals(allPage[0].getUserName(), page1[0].getUserName()); + assertEquals(allPage[1].getUserName(), page1[1].getUserName()); + assertEquals(allPage[2].getUserName(), page2[0].getUserName()); + assertEquals(allPage[3].getUserName(), page2[1].getUserName()); + + try + { + users.load(0, 0); + } + catch (Exception e) + { + fail("Exception is not expected"); + } + + // try to load more than exist + try + { + users.load(0, 5); + fail("Exception is expected"); + } + catch (Exception e) + { + } + + // try to load more than exist + try + { + users.load(1, 4); + fail("Exception is expected"); + } + catch (Exception e) + { + } + + boolean unsupportedOperation = false; + try + { + // Disable the user tolik + uHandler.setEnabled("tolik", false, true); + + assertSizeEquals(3, uHandler.findUsersByQuery(query), UserStatus.ENABLED); + assertSizeEquals(3, uHandler.findUsersByQuery(query, UserStatus.ENABLED), UserStatus.ENABLED); + assertSizeEquals(1, uHandler.findUsersByQuery(query, UserStatus.DISABLED), UserStatus.DISABLED); + assertSizeEquals(4, uHandler.findUsersByQuery(query, UserStatus.ANY), UserStatus.ANY); + + // Enable the user tolik + uHandler.setEnabled("tolik", true, true); + + assertSizeEquals(4, uHandler.findUsersByQuery(query), UserStatus.ENABLED); + assertSizeEquals(4, uHandler.findUsersByQuery(query, UserStatus.ENABLED), UserStatus.ENABLED); + assertSizeEquals(0, uHandler.findUsersByQuery(query, UserStatus.DISABLED), UserStatus.DISABLED); + assertSizeEquals(4, uHandler.findUsersByQuery(query, UserStatus.ANY), UserStatus.ANY); + } + catch (UnsupportedOperationException e) + { + // This operation can be unsupported + unsupportedOperation = true; + } + + // Remove the user tolik + uHandler.removeUser("tolik", true); + + assertSizeEquals(3, uHandler.findUsersByQuery(query), UserStatus.ENABLED); + assertSizeEquals(3, uHandler.findUsersByQuery(query, UserStatus.ENABLED), UserStatus.ENABLED); + assertSizeEquals(3, uHandler.findUsersByQuery(query, UserStatus.ANY), UserStatus.ANY); + + + // Check the listener's counters + assertEquals(4, listener.preSaveNew); + assertEquals(4, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(unsupportedOperation ? 0 : 2, listener.preSetEnabled); + assertEquals(unsupportedOperation ? 0 : 2, listener.postSetEnabled); + assertEquals(1, listener.preDelete); + assertEquals(1, listener.postDelete); + } + + /** + * Find users. + */ + public void testFindUsers() throws Exception + { + createUser("tolik"); + uHandler.authenticate("tolik", "pwdADDSomeSaltToBeCompliantWithSomeIS00"); + + Query query = new Query(); + query.setEmail("email@test"); + + // try to find user by email + assertSizeEquals(1, uHandler.findUsers(query).getAll()); + + // try to find user by name with mask + query = new Query(); + query.setUserName("*tolik*"); + assertSizeEquals(1, uHandler.findUsers(query).getAll()); + + // try to find user by name with mask + query = new Query(); + query.setUserName("tol*"); + assertSizeEquals(1, uHandler.findUsers(query).getAll()); + + // try to find user by name with mask + query = new Query(); + query.setUserName("*lik"); + assertSizeEquals(1, uHandler.findUsers(query).getAll()); + + // try to find user by name explicitly + query = new Query(); + query.setUserName("tolik"); + assertSizeEquals(1, uHandler.findUsers(query).getAll()); + + // try to find user by part of name without mask + query = new Query(); + query.setUserName("tol"); + assertSizeEquals(1, uHandler.findUsers(query).getAll()); + + // try to find user by fist and last names + query = new Query(); + query.setFirstName("first"); + query.setLastName("last"); + assertSizeEquals(1, uHandler.findUsers(query).getAll()); + + String skipCISearchTests = System.getProperty("orgservice.test.configuration.skipCISearchTests"); + if (!"true".equals(skipCISearchTests)) + { + // try to find user by name explicitly, case insensitive search + query = new Query(); + query.setUserName("Tolik"); + assertSizeEquals(1, uHandler.findUsers(query).getAll()); + + // try to find user by fist and last names, case insensitive search + query = new Query(); + query.setFirstName("fiRst"); + query.setLastName("lasT"); + assertSizeEquals(1, uHandler.findUsers(query).getAll()); + } + + String skipDateTests = System.getProperty("orgservice.test.configuration.skipDateTests"); + if (!"true".equals(skipDateTests)) + { + // try to find user by login date + Calendar calc = Calendar.getInstance(); + calc.set(Calendar.YEAR, calc.get(Calendar.YEAR) - 1); + + query = new Query(); + query.setFromLoginDate(calc.getTime()); + query.setUserName("tolik"); + assertSizeEquals(1, uHandler.findUsers(query).getAll()); + + calc = Calendar.getInstance(); + calc.set(Calendar.YEAR, calc.get(Calendar.YEAR) + 1); + + query = new Query(); + query.setFromLoginDate(calc.getTime()); + assertSizeEquals(0, uHandler.findUsers(query).getAll()); + + calc = Calendar.getInstance(); + calc.set(Calendar.YEAR, calc.get(Calendar.YEAR) - 1); + + query = new Query(); + query.setToLoginDate(calc.getTime()); + assertSizeEquals(0, uHandler.findUsers(query).getAll()); + + calc = Calendar.getInstance(); + calc.set(Calendar.YEAR, calc.get(Calendar.YEAR) + 1); + + query = new Query(); + query.setToLoginDate(calc.getTime()); + query.setUserName("tolik"); + assertSizeEquals(1, uHandler.findUsers(query).getAll()); + } + } + + /** + * Get users page list. + */ + public void testGetUserPageList() throws Exception + { + assertTrue(uHandler.getUserPageList(10).getAll().size() >= 4); + } + + /** + * Find all users. + */ + public void testFindAllUsers() throws Exception + { + String userName = "testFindAllUsers"; + createUser(userName); + + int initSize = uHandler.findAllUsers().getSize(); + assertTrue(initSize >= 5); + assertSizeEquals(initSize, uHandler.findAllUsers(UserStatus.ENABLED), UserStatus.ENABLED); + assertSizeEquals(initSize, uHandler.findAllUsers(UserStatus.ANY), UserStatus.ANY); + + ListAccess users = uHandler.findAllUsers(); + User[] allPage = users.load(0, 4); + User[] page1 = users.load(0, 2); + User[] page2 = users.load(2, 2); + + assertEquals(allPage[0].getUserName(), page1[0].getUserName()); + assertEquals(allPage[1].getUserName(), page1[1].getUserName()); + assertEquals(allPage[2].getUserName(), page2[0].getUserName()); + assertEquals(allPage[3].getUserName(), page2[1].getUserName()); + + try + { + users.load(0, 0); + } + catch (Exception e) + { + fail("Exception is not expected"); + } + + // try to load more than exist + users.load(0, 6); + + boolean unsupportedOperation = false; + int initialEnabledSize = uHandler.findAllUsers(UserStatus.ENABLED).getSize(); + int initialDisabledSize = uHandler.findAllUsers(UserStatus.DISABLED).getSize(); + try + { + // Disable the user + uHandler.setEnabled(userName, false, true); + + assertSizeEquals(initialEnabledSize - 1, uHandler.findAllUsers(), UserStatus.ENABLED); + assertSizeEquals(initialEnabledSize - 1, uHandler.findAllUsers(UserStatus.ENABLED), UserStatus.ENABLED); + assertSizeEquals(initialDisabledSize + 1, uHandler.findAllUsers(UserStatus.DISABLED), UserStatus.DISABLED); + assertSizeEquals(initialDisabledSize + initialEnabledSize, uHandler.findAllUsers(UserStatus.ANY), UserStatus.ANY); + + // Enable the user + uHandler.setEnabled(userName, true, true); + + assertSizeEquals(initialEnabledSize, uHandler.findAllUsers(), UserStatus.ENABLED); + assertSizeEquals(initialEnabledSize, uHandler.findAllUsers(UserStatus.ENABLED), UserStatus.ENABLED); + assertSizeEquals(initialDisabledSize, uHandler.findAllUsers(UserStatus.DISABLED), UserStatus.DISABLED); + assertSizeEquals(initialDisabledSize + initialEnabledSize, uHandler.findAllUsers(UserStatus.ANY), UserStatus.ANY); + } + catch (UnsupportedOperationException e) + { + // This operation can be unsupported + unsupportedOperation = true; + } + + // Remove the user + uHandler.removeUser(userName, true); + + initialEnabledSize = uHandler.findAllUsers(UserStatus.ENABLED).getSize(); + + assertSizeEquals(initialEnabledSize, uHandler.findAllUsers(), UserStatus.ENABLED); + assertSizeEquals(initialEnabledSize, uHandler.findAllUsers(UserStatus.ENABLED), UserStatus.ENABLED); + assertSizeEquals(initialEnabledSize, uHandler.findAllUsers(UserStatus.ANY), UserStatus.ANY); + + // Check the listener's counters + assertEquals(1, listener.preSaveNew); + assertEquals(1, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(unsupportedOperation ? 0 : 2, listener.preSetEnabled); + assertEquals(unsupportedOperation ? 0 : 2, listener.postSetEnabled); + assertEquals(1, listener.preDelete); + assertEquals(1, listener.postDelete); + } + + /** + * Remove user. + */ + public void testRemoveUser() throws Exception + { + createMembership(userName, groupName2, membershipType); + + assertEquals("We expect to find single membership for user " + userName, 1, + mHandler.findMembershipsByUser(userName).size()); + + assertNotNull(uHandler.removeUser(userName, true)); + + assertNull(upHandler.findUserProfileByName(userName)); + assertEquals("We expect to find no membership for user " + userName, 0, mHandler.findMembershipsByUser(userName) + .size()); + + // try to find user after remove. We are supposed to get "null" instead of exception + try + { + assertNull(uHandler.findUserByName(userName + "_")); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + } + + /** + * Save user. + */ + public void testSaveUser() throws Exception + { + String userName = "testSaveUser"; + createUser(userName); + + String newEmail = "new@Email"; + String displayName = "name"; + + // change email and check + User u = uHandler.findUserByName(userName); + u.setEmail(newEmail); + + uHandler.saveUser(u, true); + + u = uHandler.findUserByName(userName); + assertEquals(newEmail, u.getEmail()); + assertEquals(u.getDisplayName(), u.getFirstName() + " " + u.getLastName()); + + u.setDisplayName(displayName); + uHandler.saveUser(u, true); + + u = uHandler.findUserByName(userName); + assertEquals(displayName, u.getDisplayName()); + + boolean unsupportedOperation = false; + try + { + // Disable the user + u = uHandler.setEnabled(userName, false, true); + u.setDisplayName(displayName + "new-value"); + try + { + uHandler.saveUser(u, true); + fail("A DisabledUserException was expected"); + } + catch (DisabledUserException e) + { + // expected issue + } + + // Enable the user + u = uHandler.setEnabled(userName, true, true); + u.setDisplayName(displayName + "new-value"); + uHandler.saveUser(u, true); + + u = uHandler.findUserByName(userName); + assertEquals(displayName + "new-value", u.getDisplayName()); + } + catch (UnsupportedOperationException e) + { + // This operation can be unsupported + unsupportedOperation = true; + } + + // Remove the user + uHandler.removeUser(userName, true); + + // Check the listener's counters + assertEquals(1, listener.preSaveNew); + assertEquals(1, listener.postSaveNew); + assertEquals(unsupportedOperation ? 2 : 3, listener.preSave); + assertEquals(unsupportedOperation ? 2 : 3, listener.postSave); + assertEquals(unsupportedOperation ? 0 : 2, listener.preSetEnabled); + assertEquals(unsupportedOperation ? 0 : 2, listener.postSetEnabled); + assertEquals(1, listener.preDelete); + assertEquals(1, listener.postDelete); + } + + /** + * Change password. + */ + public void testChangePassword() throws Exception + { + createUser("testChangePassword"); + + // authentication with existing user and correct password + assertTrue(uHandler.authenticate("testChangePassword", "pwdADDSomeSaltToBeCompliantWithSomeIS00")); + + // authentication with wrong password + assertFalse(uHandler.authenticate("testChangePassword", "pwdADDSomeSaltToBeCompliantWithSomeIS00_")); + + User u = uHandler.findUserByName("testChangePassword"); + u.setPassword("pwdADDSomeSaltToBeCompliantWithSomeIS00_"); + uHandler.saveUser(u, true); + + // authentication with existing user and correct password + assertTrue(uHandler.authenticate("testChangePassword", "pwdADDSomeSaltToBeCompliantWithSomeIS00_")); + + // authentication with wrong password + assertFalse(uHandler.authenticate("testChangePassword", "pwdADDSomeSaltToBeCompliantWithSomeIS00")); + + boolean unsupportedOperation = false; + try + { + // Disable the user + u = uHandler.setEnabled("testChangePassword", false, true); + u.setPassword("pwdADDSomeSaltToBeCompliantWithSomeIS00"); + + try + { + uHandler.saveUser(u, true); + fail("A DisabledUserException was expected"); + } + catch (DisabledUserException e) + { + // expected issue + } + + try + { + // authentication with existing user and correct password + uHandler.authenticate("testChangePassword", "pwdADDSomeSaltToBeCompliantWithSomeIS00_"); + fail("A DisabledUserException was expected"); + } + catch (DisabledUserException e) + { + // expected issue + } + + try + { + // authentication with wrong password + uHandler.authenticate("testChangePassword", "pwdADDSomeSaltToBeCompliantWithSomeIS00"); + fail("A DisabledUserException was expected"); + } + catch (DisabledUserException e) + { + // expected issue + } + + // Disable the user + u = uHandler.setEnabled("testChangePassword", true, true); + u.setPassword("pwdADDSomeSaltToBeCompliantWithSomeIS00"); + uHandler.saveUser(u, true); + + // authentication with existing user and correct password + assertTrue(uHandler.authenticate("testChangePassword", "pwdADDSomeSaltToBeCompliantWithSomeIS00")); + + // authentication with wrong password + assertFalse(uHandler.authenticate("testChangePassword", "pwdADDSomeSaltToBeCompliantWithSomeIS00_")); + } + catch (UnsupportedOperationException e) + { + // This operation can be unsupported + unsupportedOperation = true; + } + + // Remove the user + uHandler.removeUser("testChangePassword", true); + + // Check the listener's counters + assertEquals(1, listener.preSaveNew); + assertEquals(1, listener.postSaveNew); + assertEquals(unsupportedOperation ? 1 : 2, listener.preSave); + assertEquals(unsupportedOperation ? 1 : 2, listener.postSave); + assertEquals(unsupportedOperation ? 0 : 2, listener.preSetEnabled); + assertEquals(unsupportedOperation ? 0 : 2, listener.postSetEnabled); + assertEquals(1, listener.preDelete); + assertEquals(1, listener.postDelete); + } + + /** + * Create user. + */ + public void testCreateUser() throws Exception + { + User u = uHandler.createUserInstance(userName); + u.setEmail("email@test"); + u.setFirstName("first"); + u.setLastName("last"); + u.setPassword("pwdADDSomeSaltToBeCompliantWithSomeIS00"); + uHandler.createUser(u, true); + + // check if user exists + assertNotNull(uHandler.findUserByName(userName)); + + // Check the listener's counters + assertEquals(1, listener.preSaveNew); + assertEquals(1, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(0, listener.preSetEnabled); + assertEquals(0, listener.postSetEnabled); + assertEquals(0, listener.preDelete); + assertEquals(0, listener.postDelete); + } + + public void testFindUsersByGroupId() throws Exception + { + createMembership(userName, groupName2, membershipType); + + String groupId = "/" + groupName2; + assertSizeEquals(1, uHandler.findUsersByGroupId(groupId), UserStatus.ENABLED); + assertSizeEquals(1, uHandler.findUsersByGroupId(groupId, UserStatus.ENABLED), UserStatus.ENABLED); + assertSizeEquals(1, uHandler.findUsersByGroupId(groupId, UserStatus.ANY), UserStatus.ANY); + + boolean unsupportedOperation = false; + try + { + // Disable the user + uHandler.setEnabled(userName, false, true); + + assertSizeEquals(0, uHandler.findUsersByGroupId(groupId), UserStatus.ENABLED); + assertSizeEquals(0, uHandler.findUsersByGroupId(groupId, UserStatus.ENABLED), UserStatus.ENABLED); + assertSizeEquals(1, uHandler.findUsersByGroupId(groupId, UserStatus.DISABLED), UserStatus.DISABLED); + assertSizeEquals(1, uHandler.findUsersByGroupId(groupId, UserStatus.ANY), UserStatus.ANY); + + // Enable the user + uHandler.setEnabled(userName, true, true); + + assertSizeEquals(1, uHandler.findUsersByGroupId(groupId), UserStatus.ENABLED); + assertSizeEquals(1, uHandler.findUsersByGroupId(groupId, UserStatus.ENABLED), UserStatus.ENABLED); + assertSizeEquals(0, uHandler.findUsersByGroupId(groupId, UserStatus.DISABLED), UserStatus.DISABLED); + assertSizeEquals(1, uHandler.findUsersByGroupId(groupId, UserStatus.ANY), UserStatus.ANY); + } + catch (UnsupportedOperationException e) + { + // This operation can be unsupported + unsupportedOperation = true; + } + + // Remove the user + uHandler.removeUser(userName, true); + + assertSizeEquals(0, uHandler.findUsersByGroupId(groupId), UserStatus.ENABLED); + assertSizeEquals(0, uHandler.findUsersByGroupId(groupId, UserStatus.ENABLED), UserStatus.ENABLED); + assertSizeEquals(0, uHandler.findUsersByGroupId(groupId, UserStatus.ANY), UserStatus.ANY); + + // Check the listener's counters + assertEquals(1, listener.preSaveNew); + assertEquals(1, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(unsupportedOperation ? 0 : 2, listener.preSetEnabled); + assertEquals(unsupportedOperation ? 0 : 2, listener.postSetEnabled); + assertEquals(1, listener.preDelete); + assertEquals(1, listener.postDelete); + } + + /** + * Test get listeners. + */ + public void testGetListeners() throws Exception + { + if (uHandler instanceof UserEventListenerHandler) + { + List list = ((UserEventListenerHandler) uHandler).getUserListeners(); + try + { + // check if we able to modify the list of listeners + list.clear(); + fail("Exception should not be thrown"); + } + catch (Exception e) + { + } + } + } + + public void testSetEnabled() throws Exception + { + try + { + // Trying to disable a non existing user should not throw any exception + assertNull(uHandler.setEnabled("foo", false, true)); + } + catch (UnsupportedOperationException e) + { + // This operation can be unsupported, the unit test will be ignored + return; + } + createUser("testSetEnabled"); + + // Trying to disable an existing user should return the corresponding user + User user = uHandler.setEnabled("testSetEnabled", false, true); + + assertNotNull(user); + assertEquals("testSetEnabled", user.getUserName()); + assertFalse(user.isEnabled()); + + // Trying to disable an user already disabled + user = uHandler.setEnabled("testSetEnabled", false, true); + + assertNotNull(user); + assertEquals("testSetEnabled", user.getUserName()); + assertFalse(user.isEnabled()); + + // Trying to enable the user + user = uHandler.setEnabled("testSetEnabled", true, true); + + assertNotNull(user); + assertEquals("testSetEnabled", user.getUserName()); + assertTrue(user.isEnabled()); + + // Trying to enable an user already enabled + user = uHandler.setEnabled("testSetEnabled", true, true); + + assertNotNull(user); + assertEquals("testSetEnabled", user.getUserName()); + assertTrue(user.isEnabled()); + + // Remove the user testSetEnabled + uHandler.removeUser("testSetEnabled", true); + assertNull(uHandler.setEnabled("testSetEnabled", false, true)); + + // Check the listener's counters + assertEquals(1, listener.preSaveNew); + assertEquals(1, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(2, listener.preSetEnabled); + assertEquals(2, listener.postSetEnabled); + assertEquals(1, listener.preDelete); + assertEquals(1, listener.postDelete); + } + + public void testPreDeleteUserEventListenerPreventRemoveUser() throws Exception + { + createMembership(userName, groupName2, membershipType); + createUserProfile(userName); + + assertEquals("Only one membership is expected for the user " + userName, 1, + mHandler.findMembershipsByUser(userName).size()); + + // We ensure that the UserProfile has been created properly + UserProfile up = upHandler.findUserProfileByName(userName); + assertNotNull(up); + assertEquals(userName, up.getUserName()); + assertEquals("value1", up.getAttribute("key1")); + assertEquals("value2", up.getAttribute("key2")); + + //Try to remove user + PreventDeleteUserListener preventDeleteUserListener = new PreventDeleteUserListener(); + uHandler.addUserEventListener(preventDeleteUserListener); + try + { + uHandler.removeUser(userName, true); + fail("Exception should be thrown"); + } + catch (Exception ex) + { + //Expect exception will be thrown + } + finally + { + uHandler.removeUserEventListener(preventDeleteUserListener); + } + + // Make sure that the user has not been removed + assertNotNull(uHandler.findUserByName(userName)); + + // Make sure that the membership has not been removed + assertEquals("Only one membership is expected for the user " + userName, 1, + mHandler.findMembershipsByUser(userName).size()); + + // Make sure that the UserProfile has not been removed + up = upHandler.findUserProfileByName(userName); + assertNotNull(up); + assertEquals(userName, up.getUserName()); + assertEquals("value1", up.getAttribute("key1")); + assertEquals("value2", up.getAttribute("key2")); + } + + public void testPreDeleteNewUserEventListener() throws Exception + { + createMembership(userName, groupName2, membershipType); + createUserProfile(userName); + + assertEquals("Only one membership is expected for the user " + userName, 1, + mHandler.findMembershipsByUser(userName).size()); + + // We ensure that the UserProfile has been created properly + UserProfile up = upHandler.findUserProfileByName(userName); + assertNotNull(up); + assertEquals(userName, up.getUserName()); + assertEquals("value1", up.getAttribute("key1")); + assertEquals("value2", up.getAttribute("key2")); + + //Try to remove user + ObjectParameter param = new ObjectParameter(); + param.setObject(new NewUserConfig()); + InitParams params = new InitParams(); + params.addParam(param); + NewUserEventListener newUserEventListener = new NewUserEventListener(params); + uHandler.addUserEventListener(newUserEventListener); + try + { + uHandler.removeUser(userName, true); + } + catch (Exception ex) + { + fail("Exception should not be thrown"); + } + finally + { + uHandler.removeUserEventListener(newUserEventListener); + } + + // Make sure that the user has been removed + assertNull(uHandler.findUserByName(userName)); + + // Make sure that the membership has been removed + assertEquals("the membership should be removed for the user " + userName, 0, + mHandler.findMembershipsByUser(userName).size()); + + // Make sure that the UserProfile has been removed + up = upHandler.findUserProfileByName(userName); + assertNull(up); + } + + public void testPreventRemoveUser() throws Exception + { + createMembership(userName, groupName2, membershipType); + createUserProfile(userName); + + assertEquals("Only one membership is expected for the user " + userName, 1, + mHandler.findMembershipsByUser(userName).size()); + + // We ensure that the UserProfile has been created properly + UserProfile up = upHandler.findUserProfileByName(userName); + assertNotNull(up); + assertEquals(userName, up.getUserName()); + assertEquals("value1", up.getAttribute("key1")); + assertEquals("value2", up.getAttribute("key2")); + + //Try to remove user + ObjectParameter param = new ObjectParameter(); + param.setObject(new NewUserConfig()); + InitParams params = new InitParams(); + params.addParam(param); + NewUserEventListener newUserEventListener = new NewUserEventListener(params); + PreventDeleteUserListener preventDeleteUserListener = new PreventDeleteUserListener(); + uHandler.addUserEventListener(newUserEventListener); + uHandler.addUserEventListener(preventDeleteUserListener); + try + { + uHandler.removeUser(userName, true); + fail("Exception should be thrown"); + } + catch (Exception ex) + { + //Expect exception will be thrown + } + finally + { + uHandler.removeUserEventListener(preventDeleteUserListener); + uHandler.removeUserEventListener(newUserEventListener); + } + + // Make sure that the user has not been removed + assertNotNull(uHandler.findUserByName(userName)); + + // Make sure that the membership has not been removed + assertEquals("Only one membership is expected for the user " + userName, 1, + mHandler.findMembershipsByUser(userName).size()); + + // Make sure that the UserProfile has not been removed + up = upHandler.findUserProfileByName(userName); + assertNotNull(up); + assertEquals(userName, up.getUserName()); + assertEquals("value1", up.getAttribute("key1")); + assertEquals("value2", up.getAttribute("key2")); + } + + private static class PreventDeleteUserListener extends UserEventListener + { + @Override + public void preDelete(User user) throws Exception + { + throw new Exception("You cannot to delete user"); + } + + @Override + public void postDelete(User user) throws Exception + { + fail("This method should not be execute because preDelete Event prevent remove user"); + } + } + + private static class MyUserEventListener extends UserEventListener + { + public int preSaveNew, postSaveNew; + public int preSave, postSave; + public int preDelete, postDelete; + public int preSetEnabled, postSetEnabled; + + @Override + public void preSave(User user, boolean isNew) throws Exception + { + if (user == null) + return; + if (isNew) + preSaveNew++; + else + preSave++; + } + + @Override + public void postSave(User user, boolean isNew) throws Exception + { + if (user == null) + return; + if (isNew) + postSaveNew++; + else + postSave++; + } + + @Override + public void preDelete(User user) throws Exception + { + if (user == null) + return; + preDelete++; + } + + @Override + public void postDelete(User user) throws Exception + { + if (user == null) + return; + postDelete++; + } + + @Override + public void preSetEnabled(User user) throws Exception + { + if (user == null) + return; + preSetEnabled++; + } + + @Override + public void postSetEnabled(User user) throws Exception + { + if (user == null) + return; + postSetEnabled++; + } + } +} diff --git a/component/identity/src/test/java/org/exoplatform/services/tck/organization/TestUserProfileHandler.java b/component/identity/src/test/java/org/exoplatform/services/tck/organization/TestUserProfileHandler.java new file mode 100644 index 0000000000..582f2a3ba1 --- /dev/null +++ b/component/identity/src/test/java/org/exoplatform/services/tck/organization/TestUserProfileHandler.java @@ -0,0 +1,280 @@ +/** + * + */ +/* + * Copyright (C) 2003-2007 eXo Platform SAS. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation; either version 3 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see. + */ +package org.exoplatform.services.tck.organization; + +import org.exoplatform.services.organization.UserProfile; +import org.exoplatform.services.organization.UserProfileEventListener; +import org.exoplatform.services.organization.UserProfileEventListenerHandler; + +import java.util.List; + +/** + * Created by The eXo Platform SAS. + * + * @author Anatoliy Bazko + * @version $Id: TestUserProfileHandlerImpl.java 111 2008-11-11 11:11:11Z $ + */ +public class TestUserProfileHandler extends AbstractOrganizationServiceTest +{ + private MyUserProfileEventListener listener; + + @Override + public void setUp() throws Exception + { + super.setUp(); + listener = new MyUserProfileEventListener(); + upHandler.addUserProfileEventListener(listener); + } + + @Override + public void tearDown() throws Exception + { + upHandler.removeUserProfileEventListener(listener); + super.tearDown(); + } + + /** + * Find user profile by name. + */ + public void testFindUserProfileByName() throws Exception + { + createUser(userName); + createUserProfile(userName); + + UserProfile up = upHandler.findUserProfileByName(userName); + assertNotNull(up); + assertEquals(userName, up.getUserName()); + assertEquals("value1", up.getAttribute("key1")); + assertEquals("value2", up.getAttribute("key2")); + + // try to find profile for not existed user. We are supposed to get "null" instead of Exception + try + { + assertNull(upHandler.findUserProfileByName(newUserName)); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + // try to find not existed profile. We are supposed to get "null" instead of Exception + createUser(newUserName); + try + { + assertNull(upHandler.findUserProfileByName(newUserName)); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + createUserProfile(newUserName); + + assertNotNull(upHandler.findUserProfileByName(newUserName)); + + upHandler.removeUserProfile(newUserName, true); + + assertNull(upHandler.findUserProfileByName(newUserName)); + + createUserProfile(newUserName); + + assertNotNull(upHandler.findUserProfileByName(newUserName)); + + uHandler.removeUser(newUserName, false); + + assertNull(upHandler.findUserProfileByName(newUserName)); + + // Check the listener's counters + assertEquals(3, listener.preSaveNew); + assertEquals(3, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(1, listener.preDelete); + assertEquals(1, listener.postDelete); + } + + /** + * Find user profiles. + */ + public void testFindUserProfiles() throws Exception + { + createUser(userName); + createUserProfile(userName); + + createUser(newUserName); + createUserProfile(newUserName); + + assertSizeEquals(2, upHandler.findUserProfiles()); + + upHandler.removeUserProfile(newUserName, true); + + assertSizeEquals(1, upHandler.findUserProfiles()); + + createUserProfile(newUserName); + + assertSizeEquals(2, upHandler.findUserProfiles()); + + uHandler.removeUser(newUserName, false); + + assertSizeEquals(1, upHandler.findUserProfiles()); + + // Check the listener's counters + assertEquals(3, listener.preSaveNew); + assertEquals(3, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(1, listener.preDelete); + assertEquals(1, listener.postDelete); + } + + /** + * Remove user profile. + */ + public void testRemoveUserProfile() throws Exception + { + createUser(userName); + createUserProfile(userName); + + UserProfile up = upHandler.removeUserProfile(userName, true); + assertNotNull(up); + assertEquals(up.getAttribute("key1"), "value1"); + assertEquals(up.getAttribute("key2"), "value2"); + assertNull(upHandler.findUserProfileByName("userP1")); + + // remove not existed profile. We are supposed to get "null" instead of Exception + try + { + assertNull(upHandler.removeUserProfile(newUserName, true)); + } + catch (Exception e) + { + fail("Exception should not be thrown"); + } + + // Check the listener's counters + assertEquals(1, listener.preSaveNew); + assertEquals(1, listener.postSaveNew); + assertEquals(0, listener.preSave); + assertEquals(0, listener.postSave); + assertEquals(1, listener.preDelete); + assertEquals(1, listener.postDelete); + } + + /** + * Save user profile. + */ + public void testSaveUserProfile() throws Exception + { + createUser(userName); + createUserProfile(userName); + + UserProfile up = upHandler.findUserProfileByName(userName); + up.setAttribute("key1", "value11"); + up.setAttribute("key2", null); + upHandler.saveUserProfile(up, true); + + up = upHandler.findUserProfileByName(userName); + assertEquals(up.getAttribute("key1"), "value11"); + assertNull(up.getAttribute("key2")); + + // try to save user profile for not existed user + try + { + up = upHandler.createUserProfileInstance(newUserName); + upHandler.saveUserProfile(up, true); + fail("Exception should be thrown"); + } + catch (Exception e) + { + } + + // Check the listener's counters + assertEquals(1, listener.preSaveNew); + assertEquals(1, listener.postSaveNew); + assertEquals(1, listener.preSave); + assertEquals(1, listener.postSave); + assertEquals(0, listener.preDelete); + assertEquals(0, listener.postDelete); + } + + /** + * Test get listeners. + */ + public void testGetListeners() throws Exception + { + if (upHandler instanceof UserProfileEventListenerHandler) + { + List list = ((UserProfileEventListenerHandler) upHandler).getUserProfileListeners(); + try + { + list.clear(); + fail("We should not able to modife list of listeners"); + } + catch (Exception e) + { + } + } + } + + private static class MyUserProfileEventListener extends UserProfileEventListener + { + public int preSaveNew, postSaveNew; + public int preSave, postSave; + public int preDelete, postDelete; + + @Override + public void preSave(UserProfile up, boolean isNew) throws Exception + { + if (up == null) + return; + if (isNew) + preSaveNew++; + else + preSave++; + } + + @Override + public void postSave(UserProfile up, boolean isNew) throws Exception + { + if (up == null) + return; + if (isNew) + postSaveNew++; + else + postSave++; + } + + @Override + public void preDelete(UserProfile up) throws Exception + { + if (up == null) + return; + preDelete++; + } + + @Override + public void postDelete(UserProfile up) throws Exception + { + if (up == null) + return; + postDelete++; + } + } +} diff --git a/component/portal/pom.xml b/component/portal/pom.xml index e93a40fc07..1582add5ce 100644 --- a/component/portal/pom.xml +++ b/component/portal/pom.xml @@ -33,7 +33,7 @@ 1.0 Gatein-portal used Rest endpoints - 0.64 + 0.65 @@ -57,7 +57,7 @@ exo.portal.component.resources - + com.googlecode.json-simple json-simple @@ -104,6 +104,8 @@ maven-surefire-plugin always + 1 + false alphabetical **/*TestSuite.java diff --git a/component/portal/src/main/java/io/meeds/portal/security/listener/DefaultUserMembershipListener.java b/component/portal/src/main/java/io/meeds/portal/security/listener/DefaultUserMembershipListener.java index 970542dd67..fcd9303426 100644 --- a/component/portal/src/main/java/io/meeds/portal/security/listener/DefaultUserMembershipListener.java +++ b/component/portal/src/main/java/io/meeds/portal/security/listener/DefaultUserMembershipListener.java @@ -22,7 +22,7 @@ import org.apache.commons.codec.binary.StringUtils; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang3.ArrayUtils; import org.exoplatform.container.ExoContainer; import org.exoplatform.services.listener.Event; diff --git a/component/portal/src/main/java/io/meeds/portal/security/service/SecuritySettingService.java b/component/portal/src/main/java/io/meeds/portal/security/service/SecuritySettingService.java index 58dd6bf1e7..58f107db12 100644 --- a/component/portal/src/main/java/io/meeds/portal/security/service/SecuritySettingService.java +++ b/component/portal/src/main/java/io/meeds/portal/security/service/SecuritySettingService.java @@ -22,7 +22,7 @@ import java.util.Arrays; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.commons.api.settings.SettingService; import org.exoplatform.commons.api.settings.SettingValue; diff --git a/component/portal/src/main/java/org/exoplatform/portal/config/DynamicPortalLayoutMatcherPlugin.java b/component/portal/src/main/java/org/exoplatform/portal/config/DynamicPortalLayoutMatcherPlugin.java index b572f887e2..8c09102cf8 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/config/DynamicPortalLayoutMatcherPlugin.java +++ b/component/portal/src/main/java/org/exoplatform/portal/config/DynamicPortalLayoutMatcherPlugin.java @@ -2,7 +2,7 @@ import java.io.InputStream; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.container.component.BaseComponentPlugin; import org.exoplatform.container.component.ComponentPlugin; diff --git a/component/portal/src/main/java/org/exoplatform/portal/config/MetaPortalConfigUpdateListener.java b/component/portal/src/main/java/org/exoplatform/portal/config/MetaPortalConfigUpdateListener.java index bd5cc23a68..8c40b2be74 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/config/MetaPortalConfigUpdateListener.java +++ b/component/portal/src/main/java/org/exoplatform/portal/config/MetaPortalConfigUpdateListener.java @@ -18,7 +18,7 @@ */ package org.exoplatform.portal.config; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.container.ExoContainerContext; import org.exoplatform.portal.config.model.PortalConfig; import org.exoplatform.services.listener.Event; diff --git a/component/portal/src/main/java/org/exoplatform/portal/config/NavigationCategoryService.java b/component/portal/src/main/java/org/exoplatform/portal/config/NavigationCategoryService.java index 764d5fc2c2..ad842b9f75 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/config/NavigationCategoryService.java +++ b/component/portal/src/main/java/org/exoplatform/portal/config/NavigationCategoryService.java @@ -19,7 +19,7 @@ import java.util.*; import java.util.concurrent.atomic.AtomicInteger; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; /** * A service to manage categories of Administration Menu diff --git a/component/portal/src/main/java/org/exoplatform/portal/config/NewPortalConfigListener.java b/component/portal/src/main/java/org/exoplatform/portal/config/NewPortalConfigListener.java index 148f8d0fd2..f39feea5a8 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/config/NewPortalConfigListener.java +++ b/component/portal/src/main/java/org/exoplatform/portal/config/NewPortalConfigListener.java @@ -30,8 +30,8 @@ import java.util.Set; import java.util.regex.Pattern; -import org.apache.commons.lang.StringEscapeUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.text.StringEscapeUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.commons.utils.IOUtil; import org.exoplatform.container.PortalContainer; @@ -641,7 +641,7 @@ public UnmarshalledObject getConfig(String portalName, .findFirst() .orElse(null); if (xml != null) { - xml = OWNER_PATTERN.matcher(xml).replaceAll(StringEscapeUtils.escapeXml(portalName)); + xml = OWNER_PATTERN.matcher(xml).replaceAll(StringEscapeUtils.escapeXml11(portalName)); try { return fromXML(portalType, portalName, xml, objectType); } catch (Exception e) { diff --git a/component/portal/src/main/java/org/exoplatform/portal/config/UserPortalConfigService.java b/component/portal/src/main/java/org/exoplatform/portal/config/UserPortalConfigService.java index d3916dbc19..994e8de909 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/config/UserPortalConfigService.java +++ b/component/portal/src/main/java/org/exoplatform/portal/config/UserPortalConfigService.java @@ -32,7 +32,7 @@ import java.util.Set; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.picocontainer.Startable; import org.exoplatform.commons.api.settings.SettingService; @@ -77,6 +77,8 @@ import org.exoplatform.services.organization.Group; import org.exoplatform.services.organization.OrganizationService; +import io.meeds.common.ContainerTransactional; + import jakarta.servlet.http.HttpServletRequest; /** @@ -732,6 +734,7 @@ public void deleteListenerElements(ComponentPlugin listener) { } } + @ContainerTransactional public void start() { try { if (newPortalConfigListener_ == null) { diff --git a/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/ContainerEntity.java b/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/ContainerEntity.java index 4ebc2dd8d7..a7df8d64d1 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/ContainerEntity.java +++ b/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/ContainerEntity.java @@ -22,7 +22,7 @@ import java.util.LinkedList; import java.util.List; -import javax.persistence.*; +import jakarta.persistence.*; import org.json.simple.JSONArray; import org.json.simple.JSONObject; diff --git a/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/DescriptionEntity.java b/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/DescriptionEntity.java index 8ae6f7bfd1..185a5aa89d 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/DescriptionEntity.java +++ b/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/DescriptionEntity.java @@ -22,7 +22,7 @@ import java.util.HashMap; import java.util.Map; -import javax.persistence.*; +import jakarta.persistence.*; import org.exoplatform.commons.api.persistence.ExoEntity; diff --git a/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/DescriptionState.java b/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/DescriptionState.java index 12b22e4bb0..0166fd52d3 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/DescriptionState.java +++ b/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/DescriptionState.java @@ -20,8 +20,8 @@ import java.io.Serializable; -import javax.persistence.Column; -import javax.persistence.Embeddable; +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; import org.exoplatform.commons.api.persistence.ExoEntity; diff --git a/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/NavigationEntity.java b/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/NavigationEntity.java index 348f908def..d4af18f1cc 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/NavigationEntity.java +++ b/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/NavigationEntity.java @@ -20,7 +20,7 @@ import java.io.Serializable; -import javax.persistence.*; +import jakarta.persistence.*; import org.exoplatform.commons.api.persistence.ExoEntity; import org.exoplatform.portal.mop.SiteType; diff --git a/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/NodeEntity.java b/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/NodeEntity.java index c655345e3a..bf66510462 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/NodeEntity.java +++ b/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/NodeEntity.java @@ -22,25 +22,25 @@ import java.util.ArrayList; import java.util.List; -import javax.persistence.CascadeType; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQueries; -import javax.persistence.NamedQuery; -import javax.persistence.OneToMany; -import javax.persistence.OneToOne; -import javax.persistence.OrderBy; -import javax.persistence.PreRemove; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.NamedQueries; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.OneToMany; +import jakarta.persistence.OneToOne; +import jakarta.persistence.OrderBy; +import jakarta.persistence.PreRemove; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; import org.exoplatform.commons.api.persistence.ExoEntity; import org.exoplatform.container.ExoContainer; diff --git a/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/PageEntity.java b/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/PageEntity.java index c26c3df480..38115870fc 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/PageEntity.java +++ b/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/PageEntity.java @@ -22,20 +22,20 @@ import java.util.LinkedList; import java.util.List; -import javax.persistence.Column; -import javax.persistence.Entity; -import javax.persistence.EnumType; -import javax.persistence.Enumerated; -import javax.persistence.FetchType; -import javax.persistence.GeneratedValue; -import javax.persistence.GenerationType; -import javax.persistence.Id; -import javax.persistence.JoinColumn; -import javax.persistence.ManyToOne; -import javax.persistence.NamedQuery; -import javax.persistence.SequenceGenerator; -import javax.persistence.Table; -import javax.persistence.Transient; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.EnumType; +import jakarta.persistence.Enumerated; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import jakarta.persistence.Transient; import org.json.simple.JSONArray; import org.json.simple.JSONObject; diff --git a/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/PermissionEntity.java b/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/PermissionEntity.java index 5c8443ec33..0f02492d5d 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/PermissionEntity.java +++ b/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/PermissionEntity.java @@ -20,7 +20,7 @@ import java.io.Serializable; -import javax.persistence.*; +import jakarta.persistence.*; import org.exoplatform.commons.api.persistence.ExoEntity; diff --git a/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/SiteEntity.java b/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/SiteEntity.java index 280b70c6cf..c663c62cf2 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/SiteEntity.java +++ b/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/SiteEntity.java @@ -21,7 +21,7 @@ import java.util.LinkedList; import java.util.List; -import javax.persistence.*; +import jakarta.persistence.*; import org.json.simple.JSONArray; import org.json.simple.JSONObject; diff --git a/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/WindowEntity.java b/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/WindowEntity.java index 3ab0f4a04e..995979ddce 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/WindowEntity.java +++ b/component/portal/src/main/java/org/exoplatform/portal/jdbc/entity/WindowEntity.java @@ -20,22 +20,29 @@ import java.io.Serializable; -import javax.persistence.*; - -import org.hibernate.annotations.Type; import org.json.simple.JSONObject; import org.exoplatform.commons.api.persistence.ExoEntity; +import jakarta.persistence.Basic; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.Lob; +import jakarta.persistence.NamedQuery; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; + @Entity(name = "GateInWindow") @ExoEntity @Table(name = "PORTAL_WINDOWS") -@NamedQueries({ - @NamedQuery(name = "WindowEntity.findByIds", query = "SELECT w FROM GateInWindow w WHERE w.id IN (:ids)"), - @NamedQuery(name = "WindowEntity.findByContentIds", query = "SELECT w.id FROM GateInWindow w WHERE w.contentId IN (:contentIds)"), - @NamedQuery(name = "WindowEntity.updateContentId", query = "UPDATE GateInWindow SET contentId = :newContentId WHERE contentId = :oldContentId"), - @NamedQuery(name = "WindowEntity.deleteByContentId", query = "DELETE GateInWindow WHERE contentId = :contentId"), -}) +@NamedQuery(name = "WindowEntity.findByIds", query = "SELECT w FROM GateInWindow w WHERE w.id IN (:ids)") +@NamedQuery(name = "WindowEntity.findByContentIds", query = "SELECT w.id FROM GateInWindow w WHERE w.contentId IN (:contentIds)") +@NamedQuery(name = "WindowEntity.updateContentId", query = "UPDATE GateInWindow SET contentId = :newContentId WHERE contentId = :oldContentId") +@NamedQuery(name = "WindowEntity.deleteByContentId", query = "DELETE GateInWindow WHERE contentId = :contentId") public class WindowEntity extends ComponentEntity implements Serializable { private static final long serialVersionUID = 6633792468705838255L; @@ -83,7 +90,6 @@ public class WindowEntity extends ComponentEntity implements Serializable { private String contentId; @Lob - @Type(type = "org.hibernate.type.MaterializedBlobType") @Column(name = "CUSTOMIZATION", length = 10000) @Basic(fetch = FetchType.LAZY) private byte[] customization; diff --git a/component/portal/src/main/java/org/exoplatform/portal/mop/dao/ContainerDAOImpl.java b/component/portal/src/main/java/org/exoplatform/portal/mop/dao/ContainerDAOImpl.java index fc172f0e2c..703200541c 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/mop/dao/ContainerDAOImpl.java +++ b/component/portal/src/main/java/org/exoplatform/portal/mop/dao/ContainerDAOImpl.java @@ -3,7 +3,7 @@ import java.util.Collections; import java.util.List; -import javax.persistence.TypedQuery; +import jakarta.persistence.TypedQuery; import org.exoplatform.portal.jdbc.entity.ContainerEntity; diff --git a/component/portal/src/main/java/org/exoplatform/portal/mop/dao/DescriptionDAOImpl.java b/component/portal/src/main/java/org/exoplatform/portal/mop/dao/DescriptionDAOImpl.java index 9c83470f26..bd242f34c2 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/mop/dao/DescriptionDAOImpl.java +++ b/component/portal/src/main/java/org/exoplatform/portal/mop/dao/DescriptionDAOImpl.java @@ -2,8 +2,8 @@ import java.util.Map; -import javax.persistence.NoResultException; -import javax.persistence.TypedQuery; +import jakarta.persistence.NoResultException; +import jakarta.persistence.TypedQuery; import org.exoplatform.commons.api.persistence.ExoTransactional; import org.exoplatform.commons.persistence.impl.GenericDAOJPAImpl; diff --git a/component/portal/src/main/java/org/exoplatform/portal/mop/dao/NavigationDAOImpl.java b/component/portal/src/main/java/org/exoplatform/portal/mop/dao/NavigationDAOImpl.java index 8cd0921c92..1c8bea422e 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/mop/dao/NavigationDAOImpl.java +++ b/component/portal/src/main/java/org/exoplatform/portal/mop/dao/NavigationDAOImpl.java @@ -18,8 +18,8 @@ */ package org.exoplatform.portal.mop.dao; -import javax.persistence.NoResultException; -import javax.persistence.TypedQuery; +import jakarta.persistence.NoResultException; +import jakarta.persistence.TypedQuery; import org.exoplatform.commons.api.persistence.ExoTransactional; import org.exoplatform.commons.persistence.impl.GenericDAOJPAImpl; diff --git a/component/portal/src/main/java/org/exoplatform/portal/mop/dao/NodeDAOImpl.java b/component/portal/src/main/java/org/exoplatform/portal/mop/dao/NodeDAOImpl.java index 3f1f55d459..57b6c4a2d5 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/mop/dao/NodeDAOImpl.java +++ b/component/portal/src/main/java/org/exoplatform/portal/mop/dao/NodeDAOImpl.java @@ -20,7 +20,7 @@ import java.util.List; -import javax.persistence.TypedQuery; +import jakarta.persistence.TypedQuery; import org.exoplatform.commons.persistence.impl.GenericDAOJPAImpl; import org.exoplatform.portal.jdbc.entity.NodeEntity; diff --git a/component/portal/src/main/java/org/exoplatform/portal/mop/dao/PageDAOImpl.java b/component/portal/src/main/java/org/exoplatform/portal/mop/dao/PageDAOImpl.java index 5a179dac58..9b4ed2e15f 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/mop/dao/PageDAOImpl.java +++ b/component/portal/src/main/java/org/exoplatform/portal/mop/dao/PageDAOImpl.java @@ -3,15 +3,15 @@ import java.util.LinkedList; import java.util.List; -import javax.persistence.EntityManager; -import javax.persistence.NoResultException; -import javax.persistence.Query; -import javax.persistence.TypedQuery; -import javax.persistence.criteria.CriteriaBuilder; -import javax.persistence.criteria.CriteriaQuery; -import javax.persistence.criteria.Join; -import javax.persistence.criteria.Predicate; -import javax.persistence.criteria.Root; +import jakarta.persistence.EntityManager; +import jakarta.persistence.NoResultException; +import jakarta.persistence.Query; +import jakarta.persistence.TypedQuery; +import jakarta.persistence.criteria.CriteriaBuilder; +import jakarta.persistence.criteria.CriteriaQuery; +import jakarta.persistence.criteria.Join; +import jakarta.persistence.criteria.Predicate; +import jakarta.persistence.criteria.Root; import org.gatein.api.common.Pagination; import org.gatein.api.page.PageQuery; diff --git a/component/portal/src/main/java/org/exoplatform/portal/mop/dao/PermissionDAOImpl.java b/component/portal/src/main/java/org/exoplatform/portal/mop/dao/PermissionDAOImpl.java index 14bd29acf1..0da4743368 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/mop/dao/PermissionDAOImpl.java +++ b/component/portal/src/main/java/org/exoplatform/portal/mop/dao/PermissionDAOImpl.java @@ -2,8 +2,8 @@ import java.util.*; -import javax.persistence.Query; -import javax.persistence.TypedQuery; +import jakarta.persistence.Query; +import jakarta.persistence.TypedQuery; import org.exoplatform.commons.api.persistence.ExoTransactional; import org.exoplatform.commons.persistence.impl.GenericDAOJPAImpl; diff --git a/component/portal/src/main/java/org/exoplatform/portal/mop/dao/SiteDAOImpl.java b/component/portal/src/main/java/org/exoplatform/portal/mop/dao/SiteDAOImpl.java index 072096b439..c439d1e41a 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/mop/dao/SiteDAOImpl.java +++ b/component/portal/src/main/java/org/exoplatform/portal/mop/dao/SiteDAOImpl.java @@ -6,10 +6,10 @@ import java.util.List; import java.util.Map; -import javax.persistence.NoResultException; -import javax.persistence.TypedQuery; +import jakarta.persistence.NoResultException; +import jakarta.persistence.TypedQuery; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.commons.api.persistence.ExoTransactional; import org.exoplatform.portal.jdbc.entity.SiteEntity; diff --git a/component/portal/src/main/java/org/exoplatform/portal/mop/dao/WindowDAOImpl.java b/component/portal/src/main/java/org/exoplatform/portal/mop/dao/WindowDAOImpl.java index fd41499946..e441375e48 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/mop/dao/WindowDAOImpl.java +++ b/component/portal/src/main/java/org/exoplatform/portal/mop/dao/WindowDAOImpl.java @@ -3,8 +3,8 @@ import java.util.Collections; import java.util.List; -import javax.persistence.Query; -import javax.persistence.TypedQuery; +import jakarta.persistence.Query; +import jakarta.persistence.TypedQuery; import org.gatein.api.common.Pagination; diff --git a/component/portal/src/main/java/org/exoplatform/portal/mop/rest/EntityBuilder.java b/component/portal/src/main/java/org/exoplatform/portal/mop/rest/EntityBuilder.java index 11e948a1ac..2ef246cf1a 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/mop/rest/EntityBuilder.java +++ b/component/portal/src/main/java/org/exoplatform/portal/mop/rest/EntityBuilder.java @@ -25,7 +25,7 @@ import java.util.Map; import java.util.stream.Collectors; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.container.PortalContainer; import org.exoplatform.portal.config.UserACL; diff --git a/component/portal/src/main/java/org/exoplatform/portal/mop/rest/NavigationRest.java b/component/portal/src/main/java/org/exoplatform/portal/mop/rest/NavigationRest.java index 1768061afb..d42841adba 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/mop/rest/NavigationRest.java +++ b/component/portal/src/main/java/org/exoplatform/portal/mop/rest/NavigationRest.java @@ -17,7 +17,7 @@ import javax.ws.rs.core.Response; import org.exoplatform.portal.mop.rest.model.UserNodeRestEntity; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.json.JSONException; import org.json.JSONObject; diff --git a/component/portal/src/main/java/org/exoplatform/portal/mop/service/NavigationServiceImpl.java b/component/portal/src/main/java/org/exoplatform/portal/mop/service/NavigationServiceImpl.java index 7855f6a068..ebb816b503 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/mop/service/NavigationServiceImpl.java +++ b/component/portal/src/main/java/org/exoplatform/portal/mop/service/NavigationServiceImpl.java @@ -21,7 +21,7 @@ import java.util.Arrays; import java.util.Iterator; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.portal.mop.EventType; import org.exoplatform.portal.mop.SiteKey; diff --git a/component/portal/src/main/java/org/exoplatform/portal/mop/storage/LayoutStorage.java b/component/portal/src/main/java/org/exoplatform/portal/mop/storage/LayoutStorage.java index c274e551f8..eccdb7158d 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/mop/storage/LayoutStorage.java +++ b/component/portal/src/main/java/org/exoplatform/portal/mop/storage/LayoutStorage.java @@ -33,7 +33,7 @@ import java.util.Set; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.parser.JSONParser; diff --git a/component/portal/src/main/java/org/exoplatform/portal/mop/storage/SiteStorageImpl.java b/component/portal/src/main/java/org/exoplatform/portal/mop/storage/SiteStorageImpl.java index 57653f1d1f..6ec973cf9d 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/mop/storage/SiteStorageImpl.java +++ b/component/portal/src/main/java/org/exoplatform/portal/mop/storage/SiteStorageImpl.java @@ -35,7 +35,7 @@ import java.util.Arrays; import java.util.Date; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.commons.file.model.FileItem; import org.exoplatform.commons.file.services.FileService; import org.exoplatform.portal.mop.SiteFilter; diff --git a/component/portal/src/main/java/org/exoplatform/portal/rest/MembershipTypeRestResourcesV1.java b/component/portal/src/main/java/org/exoplatform/portal/rest/MembershipTypeRestResourcesV1.java index 26961e12d4..990b325bd3 100644 --- a/component/portal/src/main/java/org/exoplatform/portal/rest/MembershipTypeRestResourcesV1.java +++ b/component/portal/src/main/java/org/exoplatform/portal/rest/MembershipTypeRestResourcesV1.java @@ -14,7 +14,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.portal.config.UserACL; import org.exoplatform.services.organization.MembershipType; diff --git a/component/portal/src/main/java/org/exoplatform/settings/rest/SettingResource.java b/component/portal/src/main/java/org/exoplatform/settings/rest/SettingResource.java index 3cb81e5e31..96233aa9eb 100644 --- a/component/portal/src/main/java/org/exoplatform/settings/rest/SettingResource.java +++ b/component/portal/src/main/java/org/exoplatform/settings/rest/SettingResource.java @@ -5,7 +5,7 @@ import io.swagger.v3.oas.annotations.responses.ApiResponse; import io.swagger.v3.oas.annotations.responses.ApiResponses; import io.swagger.v3.oas.annotations.tags.Tag; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.commons.api.settings.SettingService; import org.exoplatform.commons.api.settings.SettingValue; import org.exoplatform.commons.api.settings.data.Context; diff --git a/component/portal/src/test/java/org/exoplatform/portal/mop/dao/ContainerDAOTest.java b/component/portal/src/test/java/org/exoplatform/portal/mop/dao/ContainerDAOTest.java index b15f1d7282..0878dad09a 100644 --- a/component/portal/src/test/java/org/exoplatform/portal/mop/dao/ContainerDAOTest.java +++ b/component/portal/src/test/java/org/exoplatform/portal/mop/dao/ContainerDAOTest.java @@ -3,7 +3,7 @@ import java.util.Arrays; import java.util.List; -import javax.persistence.EntityTransaction; +import jakarta.persistence.EntityTransaction; import org.exoplatform.commons.persistence.impl.EntityManagerService; import org.exoplatform.portal.jdbc.entity.ContainerEntity; diff --git a/component/portal/src/test/java/org/exoplatform/portal/mop/dao/NavigationDAOTest.java b/component/portal/src/test/java/org/exoplatform/portal/mop/dao/NavigationDAOTest.java index 9c17216046..2ec6c678a2 100644 --- a/component/portal/src/test/java/org/exoplatform/portal/mop/dao/NavigationDAOTest.java +++ b/component/portal/src/test/java/org/exoplatform/portal/mop/dao/NavigationDAOTest.java @@ -3,7 +3,7 @@ import java.util.Arrays; import java.util.List; -import javax.persistence.EntityTransaction; +import jakarta.persistence.EntityTransaction; import org.exoplatform.commons.persistence.impl.EntityManagerService; import org.exoplatform.portal.jdbc.entity.NavigationEntity; diff --git a/component/portal/src/test/java/org/exoplatform/portal/mop/dao/WindowDAOTest.java b/component/portal/src/test/java/org/exoplatform/portal/mop/dao/WindowDAOTest.java index c4388c635d..8e07c5df51 100644 --- a/component/portal/src/test/java/org/exoplatform/portal/mop/dao/WindowDAOTest.java +++ b/component/portal/src/test/java/org/exoplatform/portal/mop/dao/WindowDAOTest.java @@ -3,7 +3,7 @@ import java.util.Arrays; import java.util.List; -import javax.persistence.EntityTransaction; +import jakarta.persistence.EntityTransaction; import org.gatein.api.common.Pagination; diff --git a/component/portal/src/test/java/org/exoplatform/portal/mop/storage/TestModelStorage.java b/component/portal/src/test/java/org/exoplatform/portal/mop/storage/TestModelStorage.java index b362d05e41..10443af348 100644 --- a/component/portal/src/test/java/org/exoplatform/portal/mop/storage/TestModelStorage.java +++ b/component/portal/src/test/java/org/exoplatform/portal/mop/storage/TestModelStorage.java @@ -4,7 +4,7 @@ import java.util.HashSet; import java.util.List; -import javax.persistence.EntityTransaction; +import jakarta.persistence.EntityTransaction; import org.exoplatform.commons.persistence.impl.EntityManagerService; import org.exoplatform.component.test.ConfigurationUnit; diff --git a/component/scripting/src/main/java/org/exoplatform/groovyscript/GroovyTemplate.java b/component/scripting/src/main/java/org/exoplatform/groovyscript/GroovyTemplate.java index 279dbdd410..0f55196261 100644 --- a/component/scripting/src/main/java/org/exoplatform/groovyscript/GroovyTemplate.java +++ b/component/scripting/src/main/java/org/exoplatform/groovyscript/GroovyTemplate.java @@ -18,7 +18,7 @@ */ package org.exoplatform.groovyscript; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.services.log.Log; import org.exoplatform.services.log.ExoLogger; diff --git a/component/scripting/src/main/java/org/exoplatform/groovyscript/text/TemplateService.java b/component/scripting/src/main/java/org/exoplatform/groovyscript/text/TemplateService.java index 50808c9ca0..8358dd0461 100644 --- a/component/scripting/src/main/java/org/exoplatform/groovyscript/text/TemplateService.java +++ b/component/scripting/src/main/java/org/exoplatform/groovyscript/text/TemplateService.java @@ -47,7 +47,7 @@ import org.exoplatform.services.cache.CacheService; import org.exoplatform.services.cache.ExoCache; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.gatein.common.io.IOTools; import org.exoplatform.services.log.Log; import org.exoplatform.services.log.ExoLogger; diff --git a/component/test/core/pom.xml b/component/test/core/pom.xml index aab7daa399..d0f3fe2859 100644 --- a/component/test/core/pom.xml +++ b/component/test/core/pom.xml @@ -50,6 +50,30 @@ exo.ws.testframework + + + org.springframework.boot + spring-boot-starter-test + + + com.vaadin.external.google + android-json + + + org.junit.jupiter + junit-jupiter-engine + + + org.junit.vintage + junit-vintage-engine + + + + + org.springframework.security + spring-security-test + + diff --git a/component/common/src/test/resources/META-INF/persistence.xml b/component/test/core/src/main/resources/META-INF/persistence.xml similarity index 71% rename from component/common/src/test/resources/META-INF/persistence.xml rename to component/test/core/src/main/resources/META-INF/persistence.xml index 2526e83bad..5cdcb2c4f7 100644 --- a/component/common/src/test/resources/META-INF/persistence.xml +++ b/component/test/core/src/main/resources/META-INF/persistence.xml @@ -2,20 +2,22 @@ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_2.xsd" version="2.2"> - + org.hibernate.jpa.HibernatePersistenceProvider java:/comp/env/exo-jpa_portal false + NONE - + - + + \ No newline at end of file diff --git a/component/test/core/src/main/resources/conf/base-root-configuration.xml b/component/test/core/src/main/resources/conf/base-root-configuration.xml index 2efae37df5..b3271f7208 100644 --- a/component/test/core/src/main/resources/conf/base-root-configuration.xml +++ b/component/test/core/src/main/resources/conf/base-root-configuration.xml @@ -66,7 +66,7 @@ ref-addresses ref-addresses - + @@ -91,7 +91,7 @@ ref-addresses ref-addresses - + diff --git a/component/test/core/src/main/resources/logback-test.xml b/component/test/core/src/main/resources/logback-test.xml index c89c2984f7..8978c9f280 100644 --- a/component/test/core/src/main/resources/logback-test.xml +++ b/component/test/core/src/main/resources/logback-test.xml @@ -27,7 +27,8 @@ - + + diff --git a/component/web/api/pom.xml b/component/web/api/pom.xml index f34792724b..ea9f63ff83 100644 --- a/component/web/api/pom.xml +++ b/component/web/api/pom.xml @@ -30,7 +30,7 @@ Various API supported by GateIn related to web serving - 0.34 + 0.36 diff --git a/component/web/resources/src/main/java/org/exoplatform/portal/resource/SkinConfigPlugin.java b/component/web/resources/src/main/java/org/exoplatform/portal/resource/SkinConfigPlugin.java index 48d969b3db..ac8fb35f38 100644 --- a/component/web/resources/src/main/java/org/exoplatform/portal/resource/SkinConfigPlugin.java +++ b/component/web/resources/src/main/java/org/exoplatform/portal/resource/SkinConfigPlugin.java @@ -3,7 +3,7 @@ import java.util.ArrayList; import java.util.List; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.container.PortalContainer; import org.exoplatform.container.RootContainer.PortalContainerPostInitTask; diff --git a/component/web/resources/src/main/java/org/exoplatform/portal/resource/SkinService.java b/component/web/resources/src/main/java/org/exoplatform/portal/resource/SkinService.java index 722db8286a..517dc1c0e2 100644 --- a/component/web/resources/src/main/java/org/exoplatform/portal/resource/SkinService.java +++ b/component/web/resources/src/main/java/org/exoplatform/portal/resource/SkinService.java @@ -19,7 +19,7 @@ package org.exoplatform.portal.resource; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.commons.cache.future.FutureMap; import org.exoplatform.commons.cache.future.Loader; import org.exoplatform.commons.utils.BinaryOutput; diff --git a/component/web/resources/src/main/java/org/exoplatform/web/application/RequireJS.java b/component/web/resources/src/main/java/org/exoplatform/web/application/RequireJS.java index 88aa60ef38..4fe80a789f 100644 --- a/component/web/resources/src/main/java/org/exoplatform/web/application/RequireJS.java +++ b/component/web/resources/src/main/java/org/exoplatform/web/application/RequireJS.java @@ -26,7 +26,7 @@ import java.util.Set; import java.util.regex.Matcher; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import org.exoplatform.web.application.javascript.JavascriptConfigService; diff --git a/component/web/resources/src/main/java/org/exoplatform/web/application/javascript/JavascriptConfigService.java b/component/web/resources/src/main/java/org/exoplatform/web/application/javascript/JavascriptConfigService.java index 54fc085b58..5c7cf17225 100644 --- a/component/web/resources/src/main/java/org/exoplatform/web/application/javascript/JavascriptConfigService.java +++ b/component/web/resources/src/main/java/org/exoplatform/web/application/javascript/JavascriptConfigService.java @@ -39,7 +39,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.commons.utils.CompositeReader; import org.exoplatform.commons.utils.PropertyManager; import org.exoplatform.container.ExoContainerContext; diff --git a/component/web/resources/src/test/java/org/exoplatform/portal/resource/TestJavascriptConfigService.java b/component/web/resources/src/test/java/org/exoplatform/portal/resource/TestJavascriptConfigService.java index 6f3f4d19ad..46f873cac3 100644 --- a/component/web/resources/src/test/java/org/exoplatform/portal/resource/TestJavascriptConfigService.java +++ b/component/web/resources/src/test/java/org/exoplatform/portal/resource/TestJavascriptConfigService.java @@ -38,7 +38,7 @@ import org.exoplatform.web.controller.QualifiedName; import org.exoplatform.web.controller.router.URIWriter; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.gatein.common.io.IOTools; import org.gatein.portal.controller.resource.ResourceId; import org.gatein.portal.controller.resource.ResourceScope; diff --git a/component/web/security/src/main/java/org/exoplatform/web/security/codec/CodecInitializer.java b/component/web/security/src/main/java/org/exoplatform/web/security/codec/CodecInitializer.java index 0f3584ca01..b5f6342bf7 100644 --- a/component/web/security/src/main/java/org/exoplatform/web/security/codec/CodecInitializer.java +++ b/component/web/security/src/main/java/org/exoplatform/web/security/codec/CodecInitializer.java @@ -39,7 +39,7 @@ import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.picocontainer.Startable; import org.exoplatform.commons.api.settings.SettingService; diff --git a/component/web/security/src/main/java/org/exoplatform/web/security/jpa/TokenDAOImpl.java b/component/web/security/src/main/java/org/exoplatform/web/security/jpa/TokenDAOImpl.java index aaa49c24b4..711f400259 100644 --- a/component/web/security/src/main/java/org/exoplatform/web/security/jpa/TokenDAOImpl.java +++ b/component/web/security/src/main/java/org/exoplatform/web/security/jpa/TokenDAOImpl.java @@ -3,9 +3,9 @@ import org.exoplatform.commons.api.persistence.ExoTransactional; import org.exoplatform.commons.persistence.impl.GenericDAOJPAImpl; -import javax.persistence.NoResultException; -import javax.persistence.Query; -import javax.persistence.TypedQuery; +import jakarta.persistence.NoResultException; +import jakarta.persistence.Query; +import jakarta.persistence.TypedQuery; import java.util.List; public class TokenDAOImpl extends GenericDAOJPAImpl implements TokenDAO { diff --git a/component/web/security/src/main/java/org/exoplatform/web/security/jpa/TokenEntity.java b/component/web/security/src/main/java/org/exoplatform/web/security/jpa/TokenEntity.java index b38d00993d..1d739b3951 100644 --- a/component/web/security/src/main/java/org/exoplatform/web/security/jpa/TokenEntity.java +++ b/component/web/security/src/main/java/org/exoplatform/web/security/jpa/TokenEntity.java @@ -2,7 +2,7 @@ import org.exoplatform.commons.api.persistence.ExoEntity; -import javax.persistence.*; +import jakarta.persistence.*; import java.io.Serializable; import java.util.Date; diff --git a/component/web/server/src/main/java/org/exoplatform/web/handler/UploadHandler.java b/component/web/server/src/main/java/org/exoplatform/web/handler/UploadHandler.java index 18114d2aa5..1536ede41f 100644 --- a/component/web/server/src/main/java/org/exoplatform/web/handler/UploadHandler.java +++ b/component/web/server/src/main/java/org/exoplatform/web/handler/UploadHandler.java @@ -22,7 +22,7 @@ import java.io.Writer; import java.net.URLEncoder; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.gatein.common.text.EntityEncoder; import org.exoplatform.container.ExoContainerContext; diff --git a/web/portal/src/main/webapp/WEB-INF/conf/organization/idm-configuration.xml b/web/portal/src/main/webapp/WEB-INF/conf/organization/idm-configuration.xml index 0fd96edd5c..e8bda32bd6 100644 --- a/web/portal/src/main/webapp/WEB-INF/conf/organization/idm-configuration.xml +++ b/web/portal/src/main/webapp/WEB-INF/conf/organization/idm-configuration.xml @@ -68,7 +68,7 @@ --> - + diff --git a/web/portal/src/main/webapp/groovy/portal/webui/application/UIPortlet.gtmpl b/web/portal/src/main/webapp/groovy/portal/webui/application/UIPortlet.gtmpl index 6a647e7755..6f30f18f3c 100644 --- a/web/portal/src/main/webapp/groovy/portal/webui/application/UIPortlet.gtmpl +++ b/web/portal/src/main/webapp/groovy/portal/webui/application/UIPortlet.gtmpl @@ -166,7 +166,7 @@ cssStyle += "height: "+ windowHeight +";"; } String hiddenClass = ""; - if(portletContent == null || org.apache.commons.lang.StringUtils.isBlank(portletContent.toString())) { + if(portletContent == null || org.apache.commons.lang3.StringUtils.isBlank(portletContent.toString())) { hiddenClass = "hidden"; } %> diff --git a/web/portal/src/main/webapp/groovy/portal/webui/workspace/UIPortalApplication.gtmpl b/web/portal/src/main/webapp/groovy/portal/webui/workspace/UIPortalApplication.gtmpl index 4c551512ea..0f97e308ca 100644 --- a/web/portal/src/main/webapp/groovy/portal/webui/workspace/UIPortalApplication.gtmpl +++ b/web/portal/src/main/webapp/groovy/portal/webui/workspace/UIPortalApplication.gtmpl @@ -7,7 +7,7 @@ import java.util.ArrayList; import java.util.Enumeration; import org.json.JSONArray; - import org.apache.commons.lang.StringUtils; + import org.apache.commons.lang3.StringUtils; import org.exoplatform.container.PortalContainer; import org.exoplatform.webui.core.UIComponent; import org.exoplatform.portal.application.PortalRequestContext ; diff --git a/web/portal/src/main/webapp/groovy/webui/organization/account/UIUserGroupSelector.gtmpl b/web/portal/src/main/webapp/groovy/webui/organization/account/UIUserGroupSelector.gtmpl index 0ec8505394..15ab1b3002 100644 --- a/web/portal/src/main/webapp/groovy/webui/organization/account/UIUserGroupSelector.gtmpl +++ b/web/portal/src/main/webapp/groovy/webui/organization/account/UIUserGroupSelector.gtmpl @@ -1,7 +1,7 @@ <% import org.exoplatform.services.organization.User; import org.exoplatform.webui.core.UIPopupWindow; - import org.apache.commons.lang.StringEscapeUtils; + import org.apache.commons.text.StringEscapeUtils; uiform.begin() def rcontext = _ctx.getRequestContext(); boolean isMulti = uicomponent.getMulti(); @@ -91,8 +91,8 @@ <% } %> <% String userName = data.getUserName() != null ? data.getUserName() : ""; - String firstName = data.getFirstName() != null ? StringEscapeUtils.escapeHtml(data.getFirstName()) : ""; - String lastName = data.getLastName() != null ? StringEscapeUtils.escapeHtml(data.getLastName()) : ""; + String firstName = data.getFirstName() != null ? StringEscapeUtils.escapeHtml4(data.getFirstName()) : ""; + String lastName = data.getLastName() != null ? StringEscapeUtils.escapeHtml4(data.getLastName()) : ""; String userEmail = data.getEmail() != null ? data.getEmail() : ""; %> <%= userName %> diff --git a/web/portal/src/main/webapp/groovy/webui/organization/account/UIUserSelector.gtmpl b/web/portal/src/main/webapp/groovy/webui/organization/account/UIUserSelector.gtmpl index 3957ca15fc..a8d534ce42 100644 --- a/web/portal/src/main/webapp/groovy/webui/organization/account/UIUserSelector.gtmpl +++ b/web/portal/src/main/webapp/groovy/webui/organization/account/UIUserSelector.gtmpl @@ -1,7 +1,7 @@ <% import org.exoplatform.services.organization.User; import org.exoplatform.webui.core.UIPopupWindow; - import org.apache.commons.lang.StringEscapeUtils; + import org.apache.commons.text.StringEscapeUtils; uiform.begin() def rcontext = _ctx.getRequestContext(); boolean isMulti = uicomponent.getMulti(); @@ -78,8 +78,8 @@ <% } %> <% String userName = data.getUserName() != null ? data.getUserName() : ""; - String firstName = data.getFirstName() != null ? StringEscapeUtils.escapeHtml(data.getFirstName()) : ""; - String lastName = data.getLastName() != null ? StringEscapeUtils.escapeHtml(data.getLastName()) : ""; + String firstName = data.getFirstName() != null ? StringEscapeUtils.escapeHtml4(data.getFirstName()) : ""; + String lastName = data.getLastName() != null ? StringEscapeUtils.escapeHtml4(data.getLastName()) : ""; String userEmail = data.getEmail() != null ? data.getEmail() : ""; %> <%= userName %> diff --git a/webui/core/pom.xml b/webui/core/pom.xml index 1bec02ed30..0cc6fbdf63 100644 --- a/webui/core/pom.xml +++ b/webui/core/pom.xml @@ -29,7 +29,7 @@ GateIn Portal WebUI Core - 0.13 + 0.14 diff --git a/webui/core/src/main/java/org/exoplatform/webui/form/UIFormStringInput.java b/webui/core/src/main/java/org/exoplatform/webui/form/UIFormStringInput.java index a2e6746a0b..4a89eb9f1a 100644 --- a/webui/core/src/main/java/org/exoplatform/webui/form/UIFormStringInput.java +++ b/webui/core/src/main/java/org/exoplatform/webui/form/UIFormStringInput.java @@ -21,7 +21,7 @@ import java.io.Writer; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.commons.serialization.api.annotations.Serialized; import org.exoplatform.commons.utils.HTMLEntityEncoder; import org.exoplatform.webui.application.WebuiRequestContext; diff --git a/webui/core/src/main/java/org/exoplatform/webui/utils/TimeConvertUtils.java b/webui/core/src/main/java/org/exoplatform/webui/utils/TimeConvertUtils.java index 7908e58721..2b12e1ca60 100644 --- a/webui/core/src/main/java/org/exoplatform/webui/utils/TimeConvertUtils.java +++ b/webui/core/src/main/java/org/exoplatform/webui/utils/TimeConvertUtils.java @@ -23,7 +23,7 @@ import java.util.Locale; import java.util.ResourceBundle; -import org.apache.commons.lang.ArrayUtils; +import org.apache.commons.lang3.ArrayUtils; import org.exoplatform.container.ExoContainerContext; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; diff --git a/webui/portal/src/main/java/org/exoplatform/portal/application/localization/DefaultLocalePolicyService.java b/webui/portal/src/main/java/org/exoplatform/portal/application/localization/DefaultLocalePolicyService.java index 720f50c280..cbbf949f43 100644 --- a/webui/portal/src/main/java/org/exoplatform/portal/application/localization/DefaultLocalePolicyService.java +++ b/webui/portal/src/main/java/org/exoplatform/portal/application/localization/DefaultLocalePolicyService.java @@ -26,7 +26,7 @@ import java.util.Set; import java.util.stream.Collectors; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.container.xml.InitParams; import org.exoplatform.services.resources.LocaleConfig; diff --git a/webui/portal/src/main/java/org/exoplatform/portal/page/PageTemplateService.java b/webui/portal/src/main/java/org/exoplatform/portal/page/PageTemplateService.java index 1c83aebffe..2a40ef5165 100644 --- a/webui/portal/src/main/java/org/exoplatform/portal/page/PageTemplateService.java +++ b/webui/portal/src/main/java/org/exoplatform/portal/page/PageTemplateService.java @@ -5,7 +5,7 @@ import java.util.Collections; import java.util.List; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.webui.core.model.SelectItemCategory; import org.exoplatform.webui.core.model.SelectItemOption; diff --git a/webui/portal/src/main/java/org/exoplatform/portal/url/PortalURLContext.java b/webui/portal/src/main/java/org/exoplatform/portal/url/PortalURLContext.java index 117ad5f747..39d299eeb9 100644 --- a/webui/portal/src/main/java/org/exoplatform/portal/url/PortalURLContext.java +++ b/webui/portal/src/main/java/org/exoplatform/portal/url/PortalURLContext.java @@ -27,7 +27,7 @@ import jakarta.servlet.http.HttpServletRequest; import org.apache.commons.codec.binary.StringUtils; -import org.apache.commons.lang3.StringEscapeUtils; +import org.apache.commons.text.StringEscapeUtils; import org.exoplatform.commons.utils.I18N; import org.exoplatform.container.ExoContainerContext; import org.exoplatform.portal.application.PortalRequestHandler; diff --git a/webui/portal/src/main/java/org/exoplatform/portal/webui/workspace/UIPortalApplication.java b/webui/portal/src/main/java/org/exoplatform/portal/webui/workspace/UIPortalApplication.java index 5592305cae..30f5f02d30 100644 --- a/webui/portal/src/main/java/org/exoplatform/portal/webui/workspace/UIPortalApplication.java +++ b/webui/portal/src/main/java/org/exoplatform/portal/webui/workspace/UIPortalApplication.java @@ -20,7 +20,7 @@ package org.exoplatform.portal.webui.workspace; import org.apache.commons.collections.CollectionUtils; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.commons.api.settings.SettingService; import org.exoplatform.commons.api.settings.SettingValue; diff --git a/webui/portal/src/main/java/org/exoplatform/portal/webui/workspace/UIStandaloneApplication.java b/webui/portal/src/main/java/org/exoplatform/portal/webui/workspace/UIStandaloneApplication.java index e2f7ea22d7..e6d229ee48 100644 --- a/webui/portal/src/main/java/org/exoplatform/portal/webui/workspace/UIStandaloneApplication.java +++ b/webui/portal/src/main/java/org/exoplatform/portal/webui/workspace/UIStandaloneApplication.java @@ -30,7 +30,7 @@ import java.util.Map; import java.util.Set; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.exoplatform.portal.application.StandaloneAppRequestContext; import org.exoplatform.portal.resource.Skin; import org.exoplatform.portal.resource.SkinService; From f81ac726dbce58ad44b6567a67a7a02a81e122e8 Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Mon, 25 Dec 2023 14:28:55 +0100 Subject: [PATCH 02/14] feat: Add Spring Integration Framework Sample and Tests - Meeds-io/MIPs#57 --- .../kernel/PortalApplicationContext.java | 249 ---------------- .../api/persistence/DataInitializer.java | 12 +- .../commons/api/persistence/ExoEntity.java | 5 + .../api/persistence/ExoEntityProcessor.java | 9 +- component/common/pom.xml | 2 +- .../meeds/common/ContainerTransactional.java | 0 ...omIdentifierGeneratorStrategyProvider.java | 14 + .../io/meeds/spring/integration/Exclude.java | 37 +++ .../integration/PortalApplicationContext.java | 84 ++++++ .../integration/kernel/KernelIntegration.java | 266 ++++++++++++++++++ .../kernel/SharedSpringBeanRegistry.java | 58 ++++ .../PersistenceUnitIntegration.java | 55 ++++ .../GrantedAuthorityDefaults.java | 0 .../web/configuration/WebConfiguration.java | 0 .../WebSecurityConfiguration.java | 0 .../web/filter/PortalIdentityFilter.java | 0 .../web/filter/PortalTransactionFilter.java | 0 .../security/PortalAuthenticationManager.java | 0 .../impl/EntityManagerService.java | 9 +- .../persistence/impl/GenericDAOJPAImpl.java | 9 +- .../impl/JPADatasourceEntityScanner.java | 38 +-- .../impl/LiquibaseDataInitializer.java | 83 +++--- .../main/resources/META-INF/persistence.xml | 1 - .../SpringBeanFactoryPostProcessor.java | 73 +++++ .../integration/mapper/EntityMapper.java | 43 +++ .../spring/integration/model/TestModel.java | 34 +++ .../service/TestExcludedService.java | 42 +++ .../integration/service/TestService.java | 50 ++++ .../integration/storage/TestStorage.java | 42 +++ .../test/SpringIntegrationTest.java | 116 ++++++++ .../spring/integration/test/dao/TestDao.java | 28 ++ .../integration/test/entity/TestEntity.java | 55 ++++ .../db/changelog/test-rdbms.db.changelog.xml | 17 ++ .../src/test/resources/jpa-entities.idx | 1 + .../io/meeds/kernel/test/KernelExtension.java | 34 +++ .../component/test/AbstractKernelTest.java | 32 ++- .../main/resources/META-INF/persistence.xml | 2 - .../core/src/main/resources/logback-test.xml | 6 +- 38 files changed, 1174 insertions(+), 332 deletions(-) delete mode 100644 component/api/src/main/java/io/meeds/spring/integration/kernel/PortalApplicationContext.java rename component/{api => common}/src/main/java/io/meeds/common/ContainerTransactional.java (100%) create mode 100644 component/common/src/main/java/io/meeds/spring/integration/Exclude.java create mode 100644 component/common/src/main/java/io/meeds/spring/integration/PortalApplicationContext.java create mode 100644 component/common/src/main/java/io/meeds/spring/integration/kernel/KernelIntegration.java create mode 100644 component/common/src/main/java/io/meeds/spring/integration/kernel/SharedSpringBeanRegistry.java create mode 100644 component/common/src/main/java/io/meeds/spring/integration/persistence/PersistenceUnitIntegration.java rename component/{api => common}/src/main/java/io/meeds/spring/integration/web/configuration/GrantedAuthorityDefaults.java (100%) rename component/{api => common}/src/main/java/io/meeds/spring/integration/web/configuration/WebConfiguration.java (100%) rename component/{api => common}/src/main/java/io/meeds/spring/integration/web/configuration/WebSecurityConfiguration.java (100%) rename component/{api => common}/src/main/java/io/meeds/spring/integration/web/filter/PortalIdentityFilter.java (100%) rename component/{api => common}/src/main/java/io/meeds/spring/integration/web/filter/PortalTransactionFilter.java (100%) rename component/{api => common}/src/main/java/io/meeds/spring/integration/web/security/PortalAuthenticationManager.java (100%) create mode 100644 component/common/src/test/java/io/meeds/spring/integration/SpringBeanFactoryPostProcessor.java create mode 100644 component/common/src/test/java/io/meeds/spring/integration/mapper/EntityMapper.java create mode 100644 component/common/src/test/java/io/meeds/spring/integration/model/TestModel.java create mode 100644 component/common/src/test/java/io/meeds/spring/integration/service/TestExcludedService.java create mode 100644 component/common/src/test/java/io/meeds/spring/integration/service/TestService.java create mode 100644 component/common/src/test/java/io/meeds/spring/integration/storage/TestStorage.java create mode 100644 component/common/src/test/java/io/meeds/spring/integration/test/SpringIntegrationTest.java create mode 100644 component/common/src/test/java/io/meeds/spring/integration/test/dao/TestDao.java create mode 100644 component/common/src/test/java/io/meeds/spring/integration/test/entity/TestEntity.java create mode 100644 component/common/src/test/resources/db/changelog/test-rdbms.db.changelog.xml create mode 100644 component/common/src/test/resources/jpa-entities.idx create mode 100644 component/test/core/src/main/java/io/meeds/kernel/test/KernelExtension.java diff --git a/component/api/src/main/java/io/meeds/spring/integration/kernel/PortalApplicationContext.java b/component/api/src/main/java/io/meeds/spring/integration/kernel/PortalApplicationContext.java deleted file mode 100644 index b155d2327a..0000000000 --- a/component/api/src/main/java/io/meeds/spring/integration/kernel/PortalApplicationContext.java +++ /dev/null @@ -1,249 +0,0 @@ -/** - * This file is part of the Meeds project (https://meeds.io/). - * - * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package io.meeds.spring.integration.kernel; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.Set; -import java.util.stream.Stream; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; -import org.springframework.context.annotation.Configuration; -import org.springframework.stereotype.Component; -import org.springframework.stereotype.Service; - -import org.exoplatform.container.PortalContainer; -import org.exoplatform.container.RootContainer.PortalContainerPostInitTask; -import org.exoplatform.container.spi.ComponentAdapter; - -import jakarta.servlet.ServletContext; - -/** - * A class to let Spring context initialized once the - * {@link PortalContainer}finishes startup to bring all defined Kernel Services - * into Spring Context in order to be able to use annotations to inject Kernel - * services - */ -public class PortalApplicationContext extends AnnotationConfigServletWebServerApplicationContext { - - private static final Logger LOG = LoggerFactory.getLogger(PortalApplicationContext.class); - - private static final Set KERNEL_BEAN_NAMES = new HashSet<>(); - - private static final Map BEAN_NAMES = new HashMap<>(); - - private static final Map BEAN_DEFINITIONS = new HashMap<>(); - - private ServletContext servletContext; - - public PortalApplicationContext(ServletContext servletContext, DefaultListableBeanFactory beanFactory) { - super(beanFactory); - this.servletContext = servletContext; - } - - @Override - protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { - PortalContainer.addInitTask(servletContext, new PortalContainerPostInitTask() { - @Override - public void execute(ServletContext context, PortalContainer portalContainer) { - // Register beans in Spring - BeanDefinitionRegistry beanRegistry = (BeanDefinitionRegistry) beanFactory; - registerKernelComponentsAsBeans(beanRegistry, portalContainer); - registerSharedBeans(beanRegistry); - // Continue startup of Spring - initSpringContext(beanFactory); - // Register Spring Beans in Kernel Container - registerBeansInKernelContainer(portalContainer); - } - - }, "portal"); - } - - @Override - protected void finishRefresh() { - // Override to not be invoked in Context startup time - } - - private void initSpringContext(ConfigurableListableBeanFactory beanFactory) { - long start = System.currentTimeMillis(); - LOG.info("Start Spring context initialization of webapp '{}'", servletContext.getServletContextName()); - PortalApplicationContext.super.finishBeanFactoryInitialization(beanFactory); - PortalApplicationContext.super.finishRefresh(); - LOG.info("Spring context '{}' initialized in {}ms", - servletContext.getServletContextName(), - System.currentTimeMillis() - start); - } - - private void registerBeansInKernelContainer(PortalContainer portalContainer) { - Stream.of(getBeanDefinitionNames()) - .filter(beanName -> portalContainer.getComponentAdapter(beanName) == null) - .filter(beanName -> !KERNEL_BEAN_NAMES.contains(beanName)) - .forEach(beanName -> { - Object bean = getBean(beanName); - BeanDefinition beanDefinition = getBeanDefinition(beanName); - String beanClassName = beanDefinition.getBeanClassName(); - Class beanClass = getBeanClass(portalContainer, beanClassName, bean); - if (beanClass != null) { - LOG.debug("Register Spring Bean '{}' as Kernel service", beanClass.getName()); - portalContainer.registerComponentInstance(beanClass, bean); - BEAN_NAMES.put(beanName, beanClass.getName()); - BEAN_DEFINITIONS.put(beanClass.getName(), beanDefinition); - } - }); - } - - private void registerSharedBeans(BeanDefinitionRegistry beanRegistry) { - BEAN_NAMES.forEach((beanName, beanClassName) -> beanRegistry.registerBeanDefinition(beanName, - BEAN_DEFINITIONS.get(beanClassName))); - } - - @SuppressWarnings({ - "unchecked", "rawtypes" - }) - private List registerKernelComponentsAsBeans(BeanDefinitionRegistry beanRegistry, PortalContainer portalContainer) { - List beanClasses = portalContainer.getComponentAdapters() - .stream() - .filter(adapter -> !BEAN_DEFINITIONS.containsKey(adapter.getComponentKey())) - .map(adapter -> this.beanNameToClass(adapter, portalContainer)) - .filter(Objects::nonNull) - .distinct() - .toList(); - - return beanClasses.stream() - // Avoid having two instances inheriting from the same API - // interface to not get NoUniqueBeanDefinitionException - .filter(c -> { - Class ec = beanClasses.stream() - .filter(oc -> !oc.equals(c) && oc.isAssignableFrom(c)) - .findFirst() - .orElse(null); - if (ec != null || KERNEL_BEAN_NAMES.contains(c.getName())) { - LOG.debug("Kernel Service '{}' is already defined by other Service with Key {}. Registration will be ignored.", - c.getName(), - ec.getName()); - return false; - } else { - KERNEL_BEAN_NAMES.add(c.getName()); - return true; - } - }) - .map(beanClassName -> registerBean(portalContainer, beanRegistry, beanClassName)) - .filter(Objects::nonNull) - .toList(); - } - - private Class registerBean(PortalContainer portalContainer, BeanDefinitionRegistry beanRegistry, Class beanClass) { - RootBeanDefinition beanDefinition = createBeanDefinition(beanClass, portalContainer); - beanRegistry.registerBeanDefinition(beanClass.getName(), beanDefinition); - LOG.debug("Kernel service '{}' injected in Spring context", beanClass.getName()); - return beanClass; - } - - private RootBeanDefinition createBeanDefinition(Class keyClass, PortalContainer portalContainer) { - RootBeanDefinition beanDefinition = new RootBeanDefinition(keyClass, - () -> { - T instance = - portalContainer.getComponentInstanceOfType(keyClass); - LOG.debug("Getting Kernel Service '{}' to inject in Spring context. Found = {}", - keyClass, - instance != null); - return instance; - }); - beanDefinition.setLazyInit(true); - beanDefinition.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); - return beanDefinition; - } - - private Class getBeanClass(PortalContainer portalContainer, - String beanClassName, - Object bean) { - try { - if (StringUtils.isBlank(beanClassName) || bean == null) { - return null; - } - Class beanClass = portalContainer.getPortalClassLoader().loadClass(beanClassName); - if (bean.getClass().isAssignableFrom(beanClass) - && (isBeanClassValid(beanClass) || isBeanClassValid(bean.getClass())) - && !isConfigurationBean(beanClass) - && !isConfigurationBean(bean.getClass()) - && (isServiceBean(beanClass) || isServiceBean(bean.getClass()))) { - if (isServiceBean(bean.getClass())) { - return bean.getClass(); - } else { - return beanClass; - } - } else { - LOG.debug("Ignore registering Spring Bean '{}/{}' as Kernel service", - beanClassName, - bean.getClass().getName()); - return null; - } - } catch (ClassNotFoundException e) { - LOG.debug("Ignore registering Spring Bean '{}' as Kernel service since it's class is not found in shared ClassLoader", - beanClassName); - return null; - } - } - - private boolean isConfigurationBean(Class beanClass) { - return beanClass.isAnnotationPresent(Configuration.class) || beanClass.isAnnotationPresent(SpringBootApplication.class); - } - - private boolean isBeanClassValid(Class beanClass) { - return !StringUtils.contains(beanClass.getName(), "org.springframework") - && !StringUtils.startsWith(beanClass.getName(), "java") - && !StringUtils.startsWith(beanClass.getName(), "jdk") - && !StringUtils.contains(beanClass.getName(), "$"); - } - - private boolean isServiceBean(Class beanClass) { - return beanClass.isAnnotationPresent(Component.class) || beanClass.isAnnotationPresent(Service.class); - } - - @SuppressWarnings("rawtypes") - private Class beanNameToClass(ComponentAdapter adapter, PortalContainer portalContainer) { - Object key = adapter.getComponentKey(); - if (key instanceof Class keyClass) { - return keyClass; - } else { - if (key instanceof String keyString) { - try { - return portalContainer.getPortalClassLoader().loadClass(keyString); - } catch (ClassNotFoundException e) { - return adapter.getComponentImplementation(); - } - } else { - return key.getClass(); - } - } - } - -} \ No newline at end of file diff --git a/component/api/src/main/java/org/exoplatform/commons/api/persistence/DataInitializer.java b/component/api/src/main/java/org/exoplatform/commons/api/persistence/DataInitializer.java index 839c787058..6c0dcb2796 100644 --- a/component/api/src/main/java/org/exoplatform/commons/api/persistence/DataInitializer.java +++ b/component/api/src/main/java/org/exoplatform/commons/api/persistence/DataInitializer.java @@ -1,10 +1,18 @@ package org.exoplatform.commons.api.persistence; +import javax.sql.DataSource; + /** * Interface for data initialization */ public interface DataInitializer { - public void initData(); - public void initData(String datasourceName); + void initData(); + + void initData(String datasourceName); + + default DataSource getDatasource() { + throw new UnsupportedOperationException(); + } + } diff --git a/component/api/src/main/java/org/exoplatform/commons/api/persistence/ExoEntity.java b/component/api/src/main/java/org/exoplatform/commons/api/persistence/ExoEntity.java index 16f66f6637..7b8c849e1e 100644 --- a/component/api/src/main/java/org/exoplatform/commons/api/persistence/ExoEntity.java +++ b/component/api/src/main/java/org/exoplatform/commons/api/persistence/ExoEntity.java @@ -28,8 +28,13 @@ * * @author Benoit de Chateauvieux * @version $Revision$ + * @deprecated Define the list of entities inside the file + * 'src/main/resources/jpa-entities.idx' in order to include + * the JPA entities in Hibernate Mappings list rather than using this annotation + * which may not be interpreted when executing tests using IDE. */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) +@Deprecated(since = "1.6.0") public @interface ExoEntity { } diff --git a/component/api/src/main/java/org/exoplatform/commons/api/persistence/ExoEntityProcessor.java b/component/api/src/main/java/org/exoplatform/commons/api/persistence/ExoEntityProcessor.java index c86de66011..b22d2f736c 100644 --- a/component/api/src/main/java/org/exoplatform/commons/api/persistence/ExoEntityProcessor.java +++ b/component/api/src/main/java/org/exoplatform/commons/api/persistence/ExoEntityProcessor.java @@ -43,8 +43,15 @@ */ @SupportedAnnotationTypes("org.exoplatform.commons.api.persistence.ExoEntity") public class ExoEntityProcessor extends AbstractProcessor { + + /** + * @deprecated will be removed to ease the path of entities listing + */ + @Deprecated(forRemoval = true, since = "1.6.0") + public static final String DEPRECATED_ENTITIES_IDX_PATH = "exo-jpa-entities/entities.idx"; + /** Path to the generated entities.idx file **/ - public static final String ENTITIES_IDX_PATH = "exo-jpa-entities/entities.idx"; + public static final String ENTITIES_IDX_PATH = "jpa-entities.idx"; @Override public boolean process(Set annotations, RoundEnvironment roundEnv) { // NOSONAR diff --git a/component/common/pom.xml b/component/common/pom.xml index 25e3229f3f..ace0453376 100644 --- a/component/common/pom.xml +++ b/component/common/pom.xml @@ -28,7 +28,7 @@ GateIn Portal Component Common - 0.40 + 0.41 diff --git a/component/api/src/main/java/io/meeds/common/ContainerTransactional.java b/component/common/src/main/java/io/meeds/common/ContainerTransactional.java similarity index 100% rename from component/api/src/main/java/io/meeds/common/ContainerTransactional.java rename to component/common/src/main/java/io/meeds/common/ContainerTransactional.java diff --git a/component/common/src/main/java/io/meeds/common/persistence/HibernateCustomIdentifierGeneratorStrategyProvider.java b/component/common/src/main/java/io/meeds/common/persistence/HibernateCustomIdentifierGeneratorStrategyProvider.java index 593db30031..4ccd72bcb9 100644 --- a/component/common/src/main/java/io/meeds/common/persistence/HibernateCustomIdentifierGeneratorStrategyProvider.java +++ b/component/common/src/main/java/io/meeds/common/persistence/HibernateCustomIdentifierGeneratorStrategyProvider.java @@ -23,12 +23,26 @@ import org.hibernate.id.IdentityGenerator; import org.hibernate.id.enhanced.SequenceStyleGenerator; +import org.hibernate.id.factory.spi.GenerationTypeStrategyRegistration; import org.hibernate.jpa.spi.IdentifierGeneratorStrategyProvider; import org.exoplatform.commons.utils.PropertyManager; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; +import jakarta.persistence.GenerationType; + +/** + * This is used as workaround for Hibernate 6 change behavior with MySQL Dialect to + * interpret {@link GenerationType#AUTO} as {@link SequenceStyleGenerator} + * instead of {@link IdentityGenerator}. When supporting multiple RDBMS, this + * behavior is inconvenient, thus it's simply overridden here. + * + * This method of customization was deprecated while in Hibernate 6.3 the replacement using + * {@link GenerationTypeStrategyRegistration} doesn't work yet. + * + * Thus it will be considered when the Hibernate Engine will support the proposed replacement. + */ public class HibernateCustomIdentifierGeneratorStrategyProvider implements IdentifierGeneratorStrategyProvider { private static final Log LOG = ExoLogger.getLogger(HibernateCustomIdentifierGeneratorStrategyProvider.class); diff --git a/component/common/src/main/java/io/meeds/spring/integration/Exclude.java b/component/common/src/main/java/io/meeds/spring/integration/Exclude.java new file mode 100644 index 0000000000..608d66892a --- /dev/null +++ b/component/common/src/main/java/io/meeds/spring/integration/Exclude.java @@ -0,0 +1,37 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.integration; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.stereotype.Service; + +/** + * An annotation to use to exclude sharing Spring Beans in Kernel Container. All + * Beans of type {@link Service} will be automatically injected in Kernel + * Container and shared with other Spring contexts unless this annotation is + * added to exclude the service + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Exclude { +} diff --git a/component/common/src/main/java/io/meeds/spring/integration/PortalApplicationContext.java b/component/common/src/main/java/io/meeds/spring/integration/PortalApplicationContext.java new file mode 100644 index 0000000000..baab94c7d9 --- /dev/null +++ b/component/common/src/main/java/io/meeds/spring/integration/PortalApplicationContext.java @@ -0,0 +1,84 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.integration; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; +import org.springframework.context.support.AbstractApplicationContext; + +import org.exoplatform.container.PortalContainer; +import org.exoplatform.container.RootContainer.PortalContainerPostInitTask; + +import io.meeds.spring.integration.kernel.KernelIntegration; + +import jakarta.servlet.ServletContext; + +/** + * A class to let Spring context initialized once the + * {@link PortalContainer}finishes startup to bring all defined Kernel Services + * into Spring Context in order to be able to use annotations to inject Kernel + * services + */ +public class PortalApplicationContext extends AnnotationConfigServletWebServerApplicationContext { + + private static final Logger LOG = LoggerFactory.getLogger(PortalApplicationContext.class); + + private ServletContext servletContext; + + public PortalApplicationContext(ServletContext servletContext, DefaultListableBeanFactory beanFactory) { + super(beanFactory); + this.servletContext = servletContext; + } + + @Override + @SuppressWarnings("resource") + protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { + PortalContainer.addInitTask(servletContext, new PortalContainerPostInitTask() { + @Override + public void execute(ServletContext context, PortalContainer portalContainer) { + // Register beans in Spring + AbstractApplicationContext springApplicationContext = PortalApplicationContext.this; + KernelIntegration.integrateSpringContext(portalContainer, + (BeanDefinitionRegistry) beanFactory, + springApplicationContext::getBeanDefinitionNames, + springApplicationContext::getBean, + () -> finishSpringContextStartup(beanFactory)); + } + }, "portal"); + } + + @Override + protected void finishRefresh() { + // Override to not be invoked in Context startup time + } + + private void finishSpringContextStartup(ConfigurableListableBeanFactory beanFactory) { + long start = System.currentTimeMillis(); + LOG.info("Start Spring context initialization of webapp '{}'", servletContext.getServletContextName()); + PortalApplicationContext.super.finishBeanFactoryInitialization(beanFactory); + PortalApplicationContext.super.finishRefresh(); + LOG.info("Spring context '{}' initialized in {}ms", + servletContext.getServletContextName(), + System.currentTimeMillis() - start); + } + +} diff --git a/component/common/src/main/java/io/meeds/spring/integration/kernel/KernelIntegration.java b/component/common/src/main/java/io/meeds/spring/integration/kernel/KernelIntegration.java new file mode 100644 index 0000000000..4238a2c7ca --- /dev/null +++ b/component/common/src/main/java/io/meeds/spring/integration/kernel/KernelIntegration.java @@ -0,0 +1,266 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.integration.kernel; + +import static io.meeds.spring.integration.kernel.SharedSpringBeanRegistry.hasBeanDefinition; +import static io.meeds.spring.integration.kernel.SharedSpringBeanRegistry.registerSpringBeansState; + +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.event.Level; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Service; + +import org.exoplatform.commons.utils.PropertyManager; +import org.exoplatform.container.PortalContainer; +import org.exoplatform.container.spi.ComponentAdapter; + +import io.meeds.spring.integration.Exclude; + +/** + * A class to let Spring context initialized once the + * {@link PortalContainer}finishes startup to bring all defined Kernel Services + * into Spring Context in order to be able to use annotations to inject Kernel + * services + */ +public class KernelIntegration { + + private static final Logger LOG = LoggerFactory.getLogger(KernelIntegration.class); + + private static final Set KERNEL_BEAN_NAMES = new HashSet<>(); + + private KernelIntegration() { + } + + public static void integrateSpringContext(PortalContainer portalContainer, + BeanDefinitionRegistry beanRegistry, + Supplier getBeanDefinitionNamesSupplier, + Function getBeanFunction, + Runnable finishSpringContextStartup) { + registerKernelComponentsAsSpringBeans(portalContainer, beanRegistry); + if (finishSpringContextStartup != null) { + LOG.info("Continue Spring context startup with integrated Kernel Services"); + // Continue startup of Spring + finishSpringContextStartup.run(); + // Register Spring Beans in Kernel Container + LOG.info("Inject Spring Beans into Kernel after finishing startup into kernel"); + registerSpringBeansAsKernelComponents(portalContainer, + beanRegistry, + getBeanDefinitionNamesSupplier, + getBeanFunction); + } + } + + public static void registerKernelComponentsAsSpringBeans(PortalContainer portalContainer, + BeanDefinitionRegistry beanRegistry) { + LOG.info("Injecting Kernel components into Spring context as beans"); + injectKernelComponentsAsBeans(beanRegistry, portalContainer); + registerSpringBeansState(beanRegistry); + } + + public static void registerSpringBeanAsKernelComponent(PortalContainer container, + BeanDefinitionRegistry beanRegistry, + String beanName, + Object bean) { + registerSpringBeansAsKernelComponents(container, beanRegistry, new String[] { beanName }, name -> bean); + } + + private static void registerSpringBeansAsKernelComponents(PortalContainer portalContainer, + BeanDefinitionRegistry beanRegistry, + Supplier getBeanDefinitionNamesSupplier, + Function getBeanFunction) { + String[] springBeanDefinitionNames = getBeanDefinitionNamesSupplier.get(); + registerSpringBeansAsKernelComponents(portalContainer, beanRegistry, springBeanDefinitionNames, getBeanFunction); + } + + private static void registerSpringBeansAsKernelComponents(PortalContainer portalContainer, + BeanDefinitionRegistry beanRegistry, + String[] beanDefinitionNames, + Function getBeanFunction) { + Stream.of(beanDefinitionNames) + .filter(beanName -> portalContainer.getComponentAdapter(beanName) == null) + .filter(beanName -> !isBeanRegisteredInKernel(beanName)) + .forEach(beanName -> { + BeanDefinition beanDefinition = getBeanDefinition(beanRegistry, beanName); + if (beanDefinition == null) { + LOG.debug("Bean {} seems to have an empty definition name", beanName); + } else { + String beanClassName = beanDefinition.getBeanClassName(); + Object bean = getBeanFunction.apply(beanName); + Class beanClass = getBeanClass(portalContainer, beanClassName, bean); + if (beanClass != null && portalContainer.getComponentInstanceOfType(beanClass) == null) { + LOG.atLevel(PropertyManager.isDevelopping() ? Level.INFO : Level.DEBUG) + .log("Register Spring Bean '{}' as Kernel service", beanClass.getName()); + portalContainer.registerComponentInstance(beanClass, bean); + SharedSpringBeanRegistry.registerBeanDefinition(beanName, beanClass, beanDefinition); + } + } + }); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private static void injectKernelComponentsAsBeans(BeanDefinitionRegistry beanRegistry, PortalContainer portalContainer) { + List beanClasses = portalContainer.getComponentAdapters() + .stream() + .filter(adapter -> !hasBeanDefinition(adapter.getComponentKey().toString())) + .map(adapter -> beanNameToClass(adapter, portalContainer)) + .filter(Objects::nonNull) + .distinct() + .toList(); + + beanClasses.stream() + // Avoid having two instances inheriting from the same API + // interface to not get NoUniqueBeanDefinitionException + .filter(c -> { + Class ec = beanClasses.stream() + .filter(oc -> !oc.equals(c) && oc.isAssignableFrom(c)) + .findFirst() + .orElse(null); + if (ec != null || KERNEL_BEAN_NAMES.contains(c.getName())) { + LOG.debug("Kernel Service '{}' is already defined by other Service with Key {}. Registration will be ignored.", + c.getName(), + ec.getName()); + return false; + } else { + KERNEL_BEAN_NAMES.add(c.getName()); + return true; + } + }) + .forEach(beanClassName -> registerBean(portalContainer, beanRegistry, beanClassName)); + } + + private static Class registerBean(PortalContainer portalContainer, BeanDefinitionRegistry beanRegistry, Class beanClass) { + RootBeanDefinition beanDefinition = createBeanDefinition(beanClass, portalContainer); + beanRegistry.registerBeanDefinition(beanClass.getName(), beanDefinition); + LOG.debug("Kernel service '{}' injected in Spring context", beanClass.getName()); + return beanClass; + } + + private static RootBeanDefinition createBeanDefinition(Class keyClass, PortalContainer portalContainer) { + RootBeanDefinition beanDefinition = new RootBeanDefinition(keyClass, + () -> { + T instance = + portalContainer.getComponentInstanceOfType(keyClass); + LOG.debug("Getting Kernel Service '{}' to inject in Spring context. Found = {}", + keyClass, + instance != null); + return instance; + }); + beanDefinition.setLazyInit(true); + beanDefinition.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); + return beanDefinition; + } + + private static Class getBeanClass(PortalContainer portalContainer, + String beanClassName, + Object bean) { + try { + if (StringUtils.isBlank(beanClassName) || bean == null) { + return null; + } + Class beanClass = portalContainer.getPortalClassLoader().loadClass(beanClassName); + if (bean.getClass().isAssignableFrom(beanClass) + && (isBeanClassValid(beanClass) || isBeanClassValid(bean.getClass())) + && !isConfigurationBean(beanClass) + && !isConfigurationBean(bean.getClass()) + && (isServiceBean(beanClass) || isServiceBean(bean.getClass())) + && (!isServiceBeanExcluded(beanClass) || !isServiceBeanExcluded(bean.getClass()))) { + if (isServiceBean(bean.getClass())) { + return bean.getClass(); + } else { + return beanClass; + } + } else { + LOG.debug("Ignore registering Spring Bean '{}/{}' as Kernel service", + beanClassName, + bean.getClass().getName()); + return null; + } + } catch (ClassNotFoundException e) { + LOG.warn("Ignore registering Spring Bean '{}' as Kernel service since it's class is not found in shared ClassLoader", + beanClassName); + return null; + } + } + + private static BeanDefinition getBeanDefinition(BeanDefinitionRegistry beanRegistry, String beanName) { + try { + return beanRegistry.getBeanDefinition(beanName); + } catch (NoSuchBeanDefinitionException e) { + LOG.debug("Can't find Bean with name {}. Ignore adding it into Kernel Container", beanName); + return null; + } + } + + private static boolean isConfigurationBean(Class beanClass) { + return beanClass.isAnnotationPresent(Configuration.class) || beanClass.isAnnotationPresent(SpringBootApplication.class); + } + + private static boolean isBeanClassValid(Class beanClass) { + return !StringUtils.contains(beanClass.getName(), "org.springframework") + && !StringUtils.startsWith(beanClass.getName(), "java") + && !StringUtils.startsWith(beanClass.getName(), "jdk") + && !StringUtils.contains(beanClass.getName(), "$"); + } + + private static boolean isServiceBean(Class beanClass) { + return beanClass.isAnnotationPresent(Service.class); + } + + private static boolean isServiceBeanExcluded(Class beanClass) { + return beanClass.isAnnotationPresent(Exclude.class); + } + + @SuppressWarnings("rawtypes") + private static Class beanNameToClass(ComponentAdapter adapter, PortalContainer portalContainer) { + Object key = adapter.getComponentKey(); + if (key instanceof Class keyClass) { + return keyClass; + } else { + if (key instanceof String keyString) { + try { + return portalContainer.getPortalClassLoader().loadClass(keyString); + } catch (ClassNotFoundException e) { + return adapter.getComponentImplementation(); + } + } else { + return key.getClass(); + } + } + } + + private static boolean isBeanRegisteredInKernel(String beanName) { + return KERNEL_BEAN_NAMES.contains(beanName); + } + +} diff --git a/component/common/src/main/java/io/meeds/spring/integration/kernel/SharedSpringBeanRegistry.java b/component/common/src/main/java/io/meeds/spring/integration/kernel/SharedSpringBeanRegistry.java new file mode 100644 index 0000000000..3fe4df6924 --- /dev/null +++ b/component/common/src/main/java/io/meeds/spring/integration/kernel/SharedSpringBeanRegistry.java @@ -0,0 +1,58 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.integration.kernel; + +import java.util.HashMap; +import java.util.Map; + +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; + +public class SharedSpringBeanRegistry { + + private static final Map BEAN_NAMES = new HashMap<>(); + + private static final Map BEAN_DEFINITIONS = new HashMap<>(); + + private SharedSpringBeanRegistry() { + } + + public static void registerBeanDefinition(String beanName, Class beanClass, BeanDefinition beanDefinition) { + BEAN_NAMES.put(beanName, beanClass.getName()); + BEAN_DEFINITIONS.put(beanClass.getName(), beanDefinition); + } + + public static Map getBeanNames() { + return BEAN_NAMES; + } + + public static BeanDefinition getBeanDefinition(String beanName) { + return BEAN_DEFINITIONS.get(beanName); + } + + public static boolean hasBeanDefinition(String beanName) { + return BEAN_DEFINITIONS.containsKey(beanName); + } + + public static void registerSpringBeansState(BeanDefinitionRegistry beanRegistry) { + getBeanNames().forEach((beanName, beanClassName) -> beanRegistry.registerBeanDefinition(beanName, + getBeanDefinition(beanClassName))); + } + +} diff --git a/component/common/src/main/java/io/meeds/spring/integration/persistence/PersistenceUnitIntegration.java b/component/common/src/main/java/io/meeds/spring/integration/persistence/PersistenceUnitIntegration.java new file mode 100644 index 0000000000..75eae7ec4b --- /dev/null +++ b/component/common/src/main/java/io/meeds/spring/integration/persistence/PersistenceUnitIntegration.java @@ -0,0 +1,55 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.integration.persistence; + +import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import org.exoplatform.commons.persistence.impl.EntityManagerService; +import org.exoplatform.commons.persistence.impl.LiquibaseDataInitializer; +import org.exoplatform.container.PortalContainer; + +import jakarta.persistence.EntityManagerFactory; +import liquibase.integration.spring.SpringLiquibase; + +@Configuration +public class PersistenceUnitIntegration { + + @Bean + public EntityManagerFactory entityManagerFactory() { + EntityManagerService entityManagerService = PortalContainer.getInstance() + .getComponentInstanceOfType(EntityManagerService.class); + return entityManagerService.getEntityManagerFactory(); + } + + @Bean + public SpringLiquibase liquibase(LiquibaseProperties liquibaseProperties) { + LiquibaseDataInitializer liquibaseDataInitializer = PortalContainer.getInstance() + .getComponentInstanceOfType(LiquibaseDataInitializer.class); + SpringLiquibase liquibase = new SpringLiquibase(); + liquibase.setDataSource(liquibaseDataInitializer.getDatasource()); + liquibase.setChangeLog(liquibaseProperties.getChangeLog()); + liquibase.setContexts(liquibaseProperties.getContexts()); + liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema()); + liquibase.setDropFirst(liquibaseProperties.isDropFirst()); + liquibase.setShouldRun(liquibaseProperties.isEnabled()); + return liquibase; + } + +} diff --git a/component/api/src/main/java/io/meeds/spring/integration/web/configuration/GrantedAuthorityDefaults.java b/component/common/src/main/java/io/meeds/spring/integration/web/configuration/GrantedAuthorityDefaults.java similarity index 100% rename from component/api/src/main/java/io/meeds/spring/integration/web/configuration/GrantedAuthorityDefaults.java rename to component/common/src/main/java/io/meeds/spring/integration/web/configuration/GrantedAuthorityDefaults.java diff --git a/component/api/src/main/java/io/meeds/spring/integration/web/configuration/WebConfiguration.java b/component/common/src/main/java/io/meeds/spring/integration/web/configuration/WebConfiguration.java similarity index 100% rename from component/api/src/main/java/io/meeds/spring/integration/web/configuration/WebConfiguration.java rename to component/common/src/main/java/io/meeds/spring/integration/web/configuration/WebConfiguration.java diff --git a/component/api/src/main/java/io/meeds/spring/integration/web/configuration/WebSecurityConfiguration.java b/component/common/src/main/java/io/meeds/spring/integration/web/configuration/WebSecurityConfiguration.java similarity index 100% rename from component/api/src/main/java/io/meeds/spring/integration/web/configuration/WebSecurityConfiguration.java rename to component/common/src/main/java/io/meeds/spring/integration/web/configuration/WebSecurityConfiguration.java diff --git a/component/api/src/main/java/io/meeds/spring/integration/web/filter/PortalIdentityFilter.java b/component/common/src/main/java/io/meeds/spring/integration/web/filter/PortalIdentityFilter.java similarity index 100% rename from component/api/src/main/java/io/meeds/spring/integration/web/filter/PortalIdentityFilter.java rename to component/common/src/main/java/io/meeds/spring/integration/web/filter/PortalIdentityFilter.java diff --git a/component/api/src/main/java/io/meeds/spring/integration/web/filter/PortalTransactionFilter.java b/component/common/src/main/java/io/meeds/spring/integration/web/filter/PortalTransactionFilter.java similarity index 100% rename from component/api/src/main/java/io/meeds/spring/integration/web/filter/PortalTransactionFilter.java rename to component/common/src/main/java/io/meeds/spring/integration/web/filter/PortalTransactionFilter.java diff --git a/component/api/src/main/java/io/meeds/spring/integration/web/security/PortalAuthenticationManager.java b/component/common/src/main/java/io/meeds/spring/integration/web/security/PortalAuthenticationManager.java similarity index 100% rename from component/api/src/main/java/io/meeds/spring/integration/web/security/PortalAuthenticationManager.java rename to component/common/src/main/java/io/meeds/spring/integration/web/security/PortalAuthenticationManager.java diff --git a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/EntityManagerService.java b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/EntityManagerService.java index 57ae3d26c2..641945ce5d 100644 --- a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/EntityManagerService.java +++ b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/EntityManagerService.java @@ -27,11 +27,13 @@ import org.exoplatform.services.log.Log; import org.hibernate.cfg.AvailableSettings; +import org.hibernate.cfg.JdbcSettings; import jakarta.persistence.EntityManager; import jakarta.persistence.EntityManagerFactory; import jakarta.persistence.EntityTransaction; import jakarta.persistence.Persistence; +import lombok.Getter; import java.lang.reflect.Field; import java.util.ArrayList; @@ -56,6 +58,7 @@ public class EntityManagerService implements ComponentRequestLifecycle { private static final String EXO_JPA_DATASOURCE_NAME = "exo.jpa.datasource.name"; private static final String EXO_PREFIX_FOR_HIB_SETTINGS = "exo.jpa."; + @Getter protected EntityManagerFactory entityManagerFactory; protected Properties properties; @@ -77,7 +80,7 @@ public EntityManagerService(EntityManagerFactory managerFactory, Properties prop // Setting datasource JNDI name. Get it directly from eXo global properties so it is not overridable by addons. String datasourceName = PropertyManager.getProperty(EXO_JPA_DATASOURCE_NAME); if (StringUtils.isNotBlank(datasourceName)) { - properties.put(AvailableSettings.JPA_NON_JTA_DATASOURCE, datasourceName); + properties.put(JdbcSettings.JAKARTA_NON_JTA_DATASOURCE, datasourceName); LOGGER.info("EntityManagerFactory [{}] - Creating with datasource {}.", getPersistenceUnitName(), datasourceName); } else { LOGGER.info("EntityManagerFactory [{}] - Creating with default datasource.", getPersistenceUnitName()); @@ -97,11 +100,11 @@ public EntityManagerService(EntityManagerFactory managerFactory, Properties prop } public String getDatasourceName() { - return (String) properties.get(AvailableSettings.JPA_NON_JTA_DATASOURCE); + return (String) properties.get(JdbcSettings.JAKARTA_NON_JTA_DATASOURCE); } public void setDatasourceName(String datasourceName) { - properties.put(AvailableSettings.JPA_NON_JTA_DATASOURCE, datasourceName); + properties.put(JdbcSettings.JAKARTA_NON_JTA_DATASOURCE, datasourceName); } private List getHibernateAvailableSettings() { diff --git a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/GenericDAOJPAImpl.java b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/GenericDAOJPAImpl.java index abffcc7780..23109af286 100644 --- a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/GenericDAOJPAImpl.java +++ b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/GenericDAOJPAImpl.java @@ -37,12 +37,13 @@ * @author Trong Tran * @version $Revision$ * @param Entity type - * @param Identity of the entity + * @param Identity of the entity */ -public class GenericDAOJPAImpl implements GenericDAO { +public class GenericDAOJPAImpl implements GenericDAO { protected Class modelClass; + @SuppressWarnings("unchecked") public GenericDAOJPAImpl() { ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass(); this.modelClass = (Class) genericSuperclass.getActualTypeArguments()[0]; @@ -62,8 +63,8 @@ public Long count() { } @Override - public E find(ID id) { - return (E) getEntityManager().find(modelClass, id); + public E find(I id) { + return getEntityManager().find(modelClass, id); } /** diff --git a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/JPADatasourceEntityScanner.java b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/JPADatasourceEntityScanner.java index 1ac4491813..a0c95a4c45 100644 --- a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/JPADatasourceEntityScanner.java +++ b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/JPADatasourceEntityScanner.java @@ -56,26 +56,13 @@ public URL getRootUrl() { return environment.getRootUrl(); } + @SuppressWarnings("removal") @Override public List getNonRootUrls() { List nonRootUrls = new ArrayList<>(); String rootPath = environment.getRootUrl().getPath(); - try { - Enumeration entityFiles = getClass().getClassLoader().getResources(ExoEntityProcessor.ENTITIES_IDX_PATH); - while (entityFiles.hasMoreElements()) { - URL url = entityFiles.nextElement(); - url = new URL(url.toExternalForm() - .replace("!/" + ExoEntityProcessor.ENTITIES_IDX_PATH, "") - .replace("jar:", "") - .replace(ExoEntityProcessor.ENTITIES_IDX_PATH, "")); - if (url.getPath().startsWith(rootPath)) { - continue; - } - nonRootUrls.add(url); - } - } catch (IOException e) { - throw new IllegalStateException("Can't access class path loader resources", e); - } + addPaths(nonRootUrls, rootPath, ExoEntityProcessor.DEPRECATED_ENTITIES_IDX_PATH); + addPaths(nonRootUrls, rootPath, ExoEntityProcessor.ENTITIES_IDX_PATH); return nonRootUrls; } @@ -92,4 +79,23 @@ public List getExplicitlyListedClassNames() { return super.scan(environmentWrapper, options, params); } + private void addPaths(List nonRootUrls, String rootPath, String entitiesIdxPath) { + try { + Enumeration entityFiles = getClass().getClassLoader().getResources(entitiesIdxPath); + while (entityFiles.hasMoreElements()) { + URL url = entityFiles.nextElement(); + url = new URL(url.toExternalForm() + .replace("!/" + entitiesIdxPath, "") + .replace("jar:", "") + .replace(entitiesIdxPath, "")); + if (url.getPath().startsWith(rootPath)) { + continue; + } + nonRootUrls.add(url); + } + } catch (IOException e) { + throw new IllegalStateException("Can't access class path loader resources", e); + } + } + } diff --git a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/LiquibaseDataInitializer.java b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/LiquibaseDataInitializer.java index 1e913a2646..d37908ac6e 100644 --- a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/LiquibaseDataInitializer.java +++ b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/LiquibaseDataInitializer.java @@ -2,7 +2,6 @@ import java.sql.SQLException; import java.util.ArrayList; -import java.util.Arrays; import java.util.List; import java.util.Map; @@ -26,44 +25,52 @@ import liquibase.database.Database; import liquibase.database.DatabaseFactory; import liquibase.database.jvm.JdbcConnection; -import liquibase.exception.DatabaseException; import liquibase.exception.LiquibaseException; import liquibase.resource.ClassLoaderResourceAccessor; import liquibase.ui.LoggerUIService; /** - * Startable service to initialize all the data with Liquibase. - * Changelog files are added by external plugins. + * Startable service to initialize all the data with Liquibase. Changelog files + * are added by external plugins. */ public class LiquibaseDataInitializer implements Startable, DataInitializer { - private static final Log LOG = ExoLogger.getLogger(LiquibaseDataInitializer.class); + private static final Log LOG = ExoLogger.getLogger(LiquibaseDataInitializer.class); - public static final String LIQUIBASE_DATASOURCE_PARAM_NAME = "liquibase.datasource"; - public static final String LIQUIBASE_CONTEXTS_PARAM_NAME = "liquibase.contexts"; - public static final String LIQUIBASE_DEFAULT_CONTEXTS = "production"; + public static final String LIQUIBASE_DATASOURCE_PARAM_NAME = "liquibase.datasource"; - private String datasourceName; + public static final String LIQUIBASE_CONTEXTS_PARAM_NAME = "liquibase.contexts"; - private String liquibaseContexts; + public static final String LIQUIBASE_DEFAULT_CONTEXTS = "production"; - private List changeLogsPlugins = new ArrayList<>(); + private String datasourceName; - public LiquibaseDataInitializer(InitialContextInitializer initialContextInitializer, InitParams initParams) { + private String liquibaseContexts; + + private List changeLogsPlugins = new ArrayList<>(); + + public LiquibaseDataInitializer(InitialContextInitializer initialContextInitializer, // NOSONAR + // added + // for + // dependency + // injection + // and + // prioritization + InitParams initParams) { this(initParams); } public LiquibaseDataInitializer(InitParams initParams) { if (initParams == null) { - throw new IllegalArgumentException("No InitParams found for LiquibaseDataInitializer service. The datasource name (" - + LIQUIBASE_DATASOURCE_PARAM_NAME + ") should be defined at least."); + throw new IllegalArgumentException("No InitParams found for LiquibaseDataInitializer service. The datasource name (" + + LIQUIBASE_DATASOURCE_PARAM_NAME + ") should be defined at least."); } ValueParam liquibaseDatasourceNameParam = initParams.getValueParam(LIQUIBASE_DATASOURCE_PARAM_NAME); if (liquibaseDatasourceNameParam != null && liquibaseDatasourceNameParam.getValue() != null) { datasourceName = liquibaseDatasourceNameParam.getValue(); } else { - throw new IllegalArgumentException("Datasource name for LiquibaseDataInitializer must be defined in the init params (" - + LIQUIBASE_DATASOURCE_PARAM_NAME + ")"); + throw new IllegalArgumentException("Datasource name for LiquibaseDataInitializer must be defined in the init params (" + + LIQUIBASE_DATASOURCE_PARAM_NAME + ")"); } ValueParam liquibaseContextsParam = initParams.getValueParam(LIQUIBASE_CONTEXTS_PARAM_NAME); @@ -99,13 +106,13 @@ public void setContexts(String liquibaseContexts) { /** * Add a changelogs plugin + * * @param changeLogsPlugin Changelogs plugin to add */ public void addChangeLogsPlugin(ChangeLogsPlugin changeLogsPlugin) { this.changeLogsPlugins.add(changeLogsPlugin); } - @Override public void start() { initData(); @@ -116,6 +123,11 @@ public void stop() { // Nothing to stop } + @Override + public DataSource getDatasource() { + return getDatasource(datasourceName); + } + /** * Initialize the data with Liquibase with the default datasource. */ @@ -125,15 +137,15 @@ public void initData() { } /** - * Initialize the data with Liquibase with the given datasource. - * Iterates over all the changelogs injected by the change logs plugins and executes them. + * Initialize the data with Liquibase with the given datasource. Iterates over + * all the changelogs injected by the change logs plugins and executes them. */ @Override - public void initData(String datasourceName) { + public void initData(String datasourceName) { // NOSONAR if (changeLogsPlugins.isEmpty()) { LOG.info("No data to initialize with Liquibase"); } else { - LOG.info("Starting data initialization with Liquibase with datasource {0}", datasourceName); + LOG.info("Starting data initialization with Liquibase with datasource {}", datasourceName); DataSource datasource = getDatasource(datasourceName); for (ChangeLogsPlugin changeLogsPlugin : this.changeLogsPlugins) { @@ -167,35 +179,29 @@ public void initData(String datasourceName) { /** * Apply changelogs with Liquibase + * * @param datasource * @param changelogsPath */ protected void applyChangeLog(DataSource datasource, String changelogsPath) { - Database database = null; - try { - database = DatabaseFactory.getInstance() - .findCorrectDatabaseImplementation(new JdbcConnection(datasource.getConnection())); - // Adding "RANK" in data base reserved words as a workaround until Liquibase metadata reserved keywords update. - database.addReservedWords(Arrays.asList("RANK")); - Liquibase liquibase = new Liquibase(changelogsPath, new ClassLoaderResourceAccessor(), database); - liquibase.update(liquibaseContexts); + try (Database database = DatabaseFactory.getInstance() + .findCorrectDatabaseImplementation(new JdbcConnection(datasource.getConnection()))) { + try (Liquibase liquibase = new Liquibase(changelogsPath, new ClassLoaderResourceAccessor(), database)) { + liquibase.update(liquibaseContexts); + } } catch (SQLException e) { - LOG.error("Error while getting a JDBC connection from datasource " + datasourceName + " - Cause : " + e.getMessage(), e); + LOG.error("Error while getting a JDBC connection from datasource {}", datasourceName, e); } catch (LiquibaseException e) { - LOG.error("Error while applying liquibase changelogs " + changelogsPath + " - Cause : " + e.getMessage(), e); - } finally { - if(database != null) { - try { - database.close(); - } catch (DatabaseException e) { - LOG.error("Error while closing database connection - Cause : " + e.getMessage(), e); - } + // AutoCommit, database connection is auto closed + if (!(e.getCause() instanceof SQLException sqlException && StringUtils.contains(sqlException.getMessage(), "Connection is closed"))) { + LOG.error("Error while applying liquibase changelogs {}", changelogsPath, e); } } } /** * Lookup for a datasource with the given name + * * @param datasourceName Name of the datasource to retrieve * @return The datasource with the given name */ @@ -212,4 +218,5 @@ protected DataSource getDatasource(String datasourceName) { return dataSource; } + } diff --git a/component/common/src/main/resources/META-INF/persistence.xml b/component/common/src/main/resources/META-INF/persistence.xml index 326587d14d..7236000262 100644 --- a/component/common/src/main/resources/META-INF/persistence.xml +++ b/component/common/src/main/resources/META-INF/persistence.xml @@ -10,7 +10,6 @@ - diff --git a/component/common/src/test/java/io/meeds/spring/integration/SpringBeanFactoryPostProcessor.java b/component/common/src/test/java/io/meeds/spring/integration/SpringBeanFactoryPostProcessor.java new file mode 100644 index 0000000000..c21d826f19 --- /dev/null +++ b/component/common/src/test/java/io/meeds/spring/integration/SpringBeanFactoryPostProcessor.java @@ -0,0 +1,73 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.integration; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +import org.exoplatform.container.PortalContainer; + +import io.meeds.spring.integration.kernel.KernelIntegration; + +@Component +public class SpringBeanFactoryPostProcessor implements BeanFactoryPostProcessor, BeanPostProcessor, ApplicationContextAware { + + private static final Logger LOG = LoggerFactory.getLogger(SpringBeanFactoryPostProcessor.class); + + private PortalContainer container; + + private ApplicationContext applicationContext; + + private BeanDefinitionRegistry beanRegistry; + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + if (applicationContext == null) { + LOG.error("Application context seems to be empty. Can't integrate Spring with Kernel"); + return; + } + LOG.info("Integrating Spring Context with Container. Application name = {}", applicationContext.getApplicationName()); + this.container = PortalContainer.getInstance(); + this.beanRegistry = (BeanDefinitionRegistry) beanFactory; + KernelIntegration.registerKernelComponentsAsSpringBeans(container, beanRegistry); + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + KernelIntegration.registerSpringBeanAsKernelComponent(container, + beanRegistry, + beanName, + bean); + return bean; + } + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + +} diff --git a/component/common/src/test/java/io/meeds/spring/integration/mapper/EntityMapper.java b/component/common/src/test/java/io/meeds/spring/integration/mapper/EntityMapper.java new file mode 100644 index 0000000000..a2e60b3543 --- /dev/null +++ b/component/common/src/test/java/io/meeds/spring/integration/mapper/EntityMapper.java @@ -0,0 +1,43 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.integration.mapper; + +import io.meeds.spring.integration.model.TestModel; +import io.meeds.spring.integration.test.entity.TestEntity; + +public class EntityMapper { + + private EntityMapper() { + } + + public static TestEntity toEntity(TestModel model) { + if (model == null) { + return null; + } + return new TestEntity(model.getId(), model.getText()); + } + + public static TestModel fromEntity(TestEntity entity) { + if (entity == null) { + return null; + } + return new TestModel(entity.getId(), entity.getText()); + } + +} diff --git a/component/common/src/test/java/io/meeds/spring/integration/model/TestModel.java b/component/common/src/test/java/io/meeds/spring/integration/model/TestModel.java new file mode 100644 index 0000000000..689f5f402f --- /dev/null +++ b/component/common/src/test/java/io/meeds/spring/integration/model/TestModel.java @@ -0,0 +1,34 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.integration.model; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TestModel { + + private Long id; + + private String text; + +} diff --git a/component/common/src/test/java/io/meeds/spring/integration/service/TestExcludedService.java b/component/common/src/test/java/io/meeds/spring/integration/service/TestExcludedService.java new file mode 100644 index 0000000000..95dd2e7453 --- /dev/null +++ b/component/common/src/test/java/io/meeds/spring/integration/service/TestExcludedService.java @@ -0,0 +1,42 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.integration.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import io.meeds.spring.integration.Exclude; +import io.meeds.spring.integration.model.TestModel; +import io.meeds.spring.integration.storage.TestStorage; + +@Service +@Exclude +public class TestExcludedService { + + @Autowired + private TestStorage storage; + + public TestModel save(TestModel model) { + if (model == null) { + throw new IllegalArgumentException("TestModel is mandatory"); + } + return storage.save(model); + } + +} diff --git a/component/common/src/test/java/io/meeds/spring/integration/service/TestService.java b/component/common/src/test/java/io/meeds/spring/integration/service/TestService.java new file mode 100644 index 0000000000..fefc2ff148 --- /dev/null +++ b/component/common/src/test/java/io/meeds/spring/integration/service/TestService.java @@ -0,0 +1,50 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.integration.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import org.exoplatform.commons.api.settings.SettingService; + +import io.meeds.spring.integration.model.TestModel; +import io.meeds.spring.integration.storage.TestStorage; + +@Service +public class TestService { + + // Fake injection from Kernel for Testing Purpose + @Autowired + private SettingService settingService; + + @Autowired + private TestStorage storage; + + public TestModel save(TestModel model) { + if (model == null) { + throw new IllegalArgumentException("TestModel is mandatory"); + } + if (settingService == null) { + // Fake exception + throw new IllegalStateException("SettingService is null"); + } + return storage.save(model); + } + +} diff --git a/component/common/src/test/java/io/meeds/spring/integration/storage/TestStorage.java b/component/common/src/test/java/io/meeds/spring/integration/storage/TestStorage.java new file mode 100644 index 0000000000..e7cf5f816e --- /dev/null +++ b/component/common/src/test/java/io/meeds/spring/integration/storage/TestStorage.java @@ -0,0 +1,42 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.integration.storage; + +import static io.meeds.spring.integration.mapper.EntityMapper.*; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import io.meeds.spring.integration.model.TestModel; +import io.meeds.spring.integration.test.dao.TestDao; +import io.meeds.spring.integration.test.entity.TestEntity; + +@Component +public class TestStorage { + + @Autowired + private TestDao dao; + + public TestModel save(TestModel model) { + TestEntity entity = toEntity(model); + entity = dao.save(entity); + return fromEntity(entity); + } + +} diff --git a/component/common/src/test/java/io/meeds/spring/integration/test/SpringIntegrationTest.java b/component/common/src/test/java/io/meeds/spring/integration/test/SpringIntegrationTest.java new file mode 100644 index 0000000000..04a5bb4723 --- /dev/null +++ b/component/common/src/test/java/io/meeds/spring/integration/test/SpringIntegrationTest.java @@ -0,0 +1,116 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.integration.test; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import org.exoplatform.commons.api.settings.SettingService; +import org.exoplatform.jpa.CommonsDAOJPAImplTest; + +import io.meeds.kernel.test.KernelExtension; +import io.meeds.spring.integration.model.TestModel; +import io.meeds.spring.integration.service.TestExcludedService; +import io.meeds.spring.integration.service.TestService; +import io.meeds.spring.integration.storage.TestStorage; +import io.meeds.spring.integration.test.dao.TestDao; +import io.meeds.spring.integration.test.entity.TestEntity; + +@ExtendWith({ SpringExtension.class, KernelExtension.class }) +@SpringBootApplication(scanBasePackages = { + "io.meeds.spring.integration" +}) +@EnableJpaRepositories(basePackages = "io.meeds.spring.integration") +@EnableConfigurationProperties(LiquibaseProperties.class) +@TestPropertySource(properties = { + "spring.liquibase.change-log=classpath:db/changelog/test-rdbms.db.changelog.xml", +}) +public class SpringIntegrationTest extends CommonsDAOJPAImplTest { // NOSONAR + + @Autowired + private SettingService settingService; + + @Autowired + private TestDao testDao; + + @Autowired + private TestStorage testStorage; + + @Autowired + private TestService testService; + + @Autowired + private TestExcludedService testExcludedService; + + @Test + public void beansInjected() { + assertNotNull("Kernel Service not included in Spring context", settingService); + assertNotNull("Spring @Repository Bean not found", testDao); + assertNotNull("Spring @Component Bean not found", testStorage); + assertNotNull("Spring @Service Bean not found", testService); + assertNotNull("Spring @Service + @Exclude Bean not found", testExcludedService); + } + + @Test + public void daoBeanReady() { + TestEntity testEntity = testDao.save(new TestEntity(null, "test")); + assertNotNull(testEntity); + assertTrue(testEntity.getId() > 0); + assertEquals("test", testEntity.getText()); + } + + @Test + public void serviceBeanReady() { + TestModel testModel = testService.save(new TestModel(null, "test2")); + assertNotNull(testModel); + assertTrue(testModel.getId() > 0); + assertEquals("test2", testModel.getText()); + } + + @Test + public void daoNotRegisteredInKernel() { + TestDao daoComponent = getContainer().getComponentInstanceOfType(TestDao.class); + assertNull("DAO Layer shouldn't be accessible globally outside the curent module", daoComponent); + } + + @Test + public void storageNotRegisteredInKernel() { + TestStorage testStorageComponent = getContainer().getComponentInstanceOfType(TestStorage.class); + assertNull("Service Layer should be shared globally", testStorageComponent); + } + + @Test + public void excludedServiceNotRegisteredInKernel() { + TestExcludedService testExcludedServiceComponent = getContainer().getComponentInstanceOfType(TestExcludedService.class); + assertNull("Excluded Service Layer shouldn't be shared globally", testExcludedServiceComponent); + } + + @Test + public void serviceRegisteredInKernel() { + TestService testServiceComponent = getContainer().getComponentInstanceOfType(TestService.class); + assertNotNull("Service Layer should be shared globally", testServiceComponent); + } + +} diff --git a/component/common/src/test/java/io/meeds/spring/integration/test/dao/TestDao.java b/component/common/src/test/java/io/meeds/spring/integration/test/dao/TestDao.java new file mode 100644 index 0000000000..dc5411c6a4 --- /dev/null +++ b/component/common/src/test/java/io/meeds/spring/integration/test/dao/TestDao.java @@ -0,0 +1,28 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.integration.test.dao; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import io.meeds.spring.integration.test.entity.TestEntity; + +@Repository +public interface TestDao extends JpaRepository { +} diff --git a/component/common/src/test/java/io/meeds/spring/integration/test/entity/TestEntity.java b/component/common/src/test/java/io/meeds/spring/integration/test/entity/TestEntity.java new file mode 100644 index 0000000000..07e56fe909 --- /dev/null +++ b/component/common/src/test/java/io/meeds/spring/integration/test/entity/TestEntity.java @@ -0,0 +1,55 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2022 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.integration.test.entity; + +import java.io.Serializable; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.SequenceGenerator; +import jakarta.persistence.Table; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Entity(name = "Test") +@Table(name = "TEST") +@Data +@AllArgsConstructor +@NoArgsConstructor +public class TestEntity implements Serializable { + + private static final long serialVersionUID = -1657319966260461019L; + + @Id + @SequenceGenerator(name = "TEST_ID_SEQ", + sequenceName = "TEST_ID_SEQ", + allocationSize = 1) + @GeneratedValue(strategy = GenerationType.AUTO, + generator = "TEST_ID_SEQ") + @Column(name = "TEST_ID") + private Long id; + + @Column(name = "TEXT") + private String text; + +} diff --git a/component/common/src/test/resources/db/changelog/test-rdbms.db.changelog.xml b/component/common/src/test/resources/db/changelog/test-rdbms.db.changelog.xml new file mode 100644 index 0000000000..1643aafade --- /dev/null +++ b/component/common/src/test/resources/db/changelog/test-rdbms.db.changelog.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + diff --git a/component/common/src/test/resources/jpa-entities.idx b/component/common/src/test/resources/jpa-entities.idx new file mode 100644 index 0000000000..f081dcf626 --- /dev/null +++ b/component/common/src/test/resources/jpa-entities.idx @@ -0,0 +1 @@ +io.meeds.spring.integration.test.TestEntity \ No newline at end of file diff --git a/component/test/core/src/main/java/io/meeds/kernel/test/KernelExtension.java b/component/test/core/src/main/java/io/meeds/kernel/test/KernelExtension.java new file mode 100644 index 0000000000..5bc200f423 --- /dev/null +++ b/component/test/core/src/main/java/io/meeds/kernel/test/KernelExtension.java @@ -0,0 +1,34 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.kernel.test; + +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; + +import org.exoplatform.component.test.AbstractKernelTest; + +public class KernelExtension implements BeforeAllCallback { + + @Override + public void beforeAll(ExtensionContext context) { + Class testClass = context.getTestClass().orElseThrow(); + AbstractKernelTest.bootContainer(testClass); + } + +} diff --git a/component/test/core/src/main/java/org/exoplatform/component/test/AbstractKernelTest.java b/component/test/core/src/main/java/org/exoplatform/component/test/AbstractKernelTest.java index 5736704a79..9d57340fbb 100644 --- a/component/test/core/src/main/java/org/exoplatform/component/test/AbstractKernelTest.java +++ b/component/test/core/src/main/java/org/exoplatform/component/test/AbstractKernelTest.java @@ -74,6 +74,19 @@ protected AbstractKernelTest(String name) { super(name); } + public static PortalContainer bootContainer(Class clazz) { + // + bootstrap = new KernelBootstrap(Thread.currentThread().getContextClassLoader()); + + // Configure ourselves + bootstrap.addConfiguration(clazz); + // + bootstrap.boot(); + + // + return bootstrap.getContainer(); + } + public PortalContainer getContainer() { return bootstrap == null ? bootContainer() : bootstrap.getContainer(); } @@ -153,7 +166,7 @@ protected void beforeClass() { } } - private void forceStop() { + protected void forceStop() { RootContainer.getInstance().stop(); ExoContainerContext.setCurrentContainer(null); } @@ -172,21 +185,12 @@ protected void afterClass() { } private PortalContainer bootContainer() { - // - bootstrap = new KernelBootstrap(Thread.currentThread().getContextClassLoader()); - - // Configure ourselves - bootstrap.addConfiguration(getClass()); - // - bootstrap.boot(); - - // - PortalContainer portalContainer = bootstrap.getContainer(); - ExoContainerContext.setCurrentContainer(portalContainer); - return portalContainer; + PortalContainer container = bootContainer(getClass()); + ExoContainerContext.setCurrentContainer(container); + return container; } - private boolean isPortalContainerPresent() { + private static boolean isPortalContainerPresent() { return ExoContainerContext.getCurrentContainerIfPresent() != null && PortalContainer.getInstanceIfPresent() != null; } diff --git a/component/test/core/src/main/resources/META-INF/persistence.xml b/component/test/core/src/main/resources/META-INF/persistence.xml index 5cdcb2c4f7..a018082aac 100644 --- a/component/test/core/src/main/resources/META-INF/persistence.xml +++ b/component/test/core/src/main/resources/META-INF/persistence.xml @@ -10,13 +10,11 @@ - - diff --git a/component/test/core/src/main/resources/logback-test.xml b/component/test/core/src/main/resources/logback-test.xml index 8978c9f280..df3b0545f4 100644 --- a/component/test/core/src/main/resources/logback-test.xml +++ b/component/test/core/src/main/resources/logback-test.xml @@ -28,7 +28,11 @@ - + + + + + From 5f1172cf88c326a69e1269de9b8c1da05ae80cc2 Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Mon, 25 Dec 2023 16:22:21 +0100 Subject: [PATCH 03/14] feat: Split Spring Integrations into Modules - Meeds-io/MIPs#57 This change will allow to split Spring integrations with Kernel & Portal, so that addons can choose which integrations to apply. --- .../api/persistence/ExoEntityProcessor.java | 3 + .../io/meeds/spring/AvailableIntegration.java | 79 ++++++++++++++++ .../integration/PortalApplicationContext.java | 84 ----------------- .../jpa/PersistenceUnitIntegration.java | 38 ++++++++ .../kernel/SpringBeanFactoryInterceptor.java} | 22 ++--- .../annotation}/Exclude.java | 2 +- .../utils}/KernelIntegration.java | 8 +- .../utils}/SharedSpringBeanRegistry.java | 2 +- .../LiquibaseIntegration.java} | 15 +-- .../web/security/FilterConfiguration.java | 34 +++++++ .../security}/GrantedAuthorityDefaults.java | 2 +- .../security/PortalAuthenticationManager.java | 2 +- .../security}/PortalIdentityFilter.java | 2 +- .../security}/WebSecurityConfiguration.java | 4 +- .../transaction/FilterConfiguration.java} | 16 +--- .../transaction}/PortalTransactionFilter.java | 2 +- .../test/KernelIntegrationTest.java | 65 +++++++++++++ .../test/SpringIntegrationTest.java | 31 ++++--- .../test => module}/dao/TestDao.java | 4 +- .../test => module}/entity/TestEntity.java | 2 +- .../mapper/EntityMapper.java | 6 +- .../model/TestModel.java | 2 +- .../service/TestExcludedService.java | 8 +- .../service/TestService.java | 6 +- .../storage/TestStorage.java | 10 +- .../LiquibaseDataInitializerTestContext.java | 1 - component/test/core/pom.xml | 2 +- .../meeds/kernel/test/AbstractSpringTest.java | 93 +++++++++++++++++++ .../io/meeds/kernel/test/KernelExtension.java | 4 +- .../component/test/AbstractKernelTest.java | 32 +++---- .../component/test/BaseGateInTest.java | 1 + .../core/src/main/resources/logback-test.xml | 10 +- 32 files changed, 397 insertions(+), 195 deletions(-) create mode 100644 component/common/src/main/java/io/meeds/spring/AvailableIntegration.java delete mode 100644 component/common/src/main/java/io/meeds/spring/integration/PortalApplicationContext.java create mode 100644 component/common/src/main/java/io/meeds/spring/jpa/PersistenceUnitIntegration.java rename component/common/src/{test/java/io/meeds/spring/integration/SpringBeanFactoryPostProcessor.java => main/java/io/meeds/spring/kernel/SpringBeanFactoryInterceptor.java} (86%) rename component/common/src/main/java/io/meeds/spring/{integration => kernel/annotation}/Exclude.java (97%) rename component/common/src/main/java/io/meeds/spring/{integration/kernel => kernel/utils}/KernelIntegration.java (97%) rename component/common/src/main/java/io/meeds/spring/{integration/kernel => kernel/utils}/SharedSpringBeanRegistry.java (98%) rename component/common/src/main/java/io/meeds/spring/{integration/persistence/PersistenceUnitIntegration.java => liquibase/LiquibaseIntegration.java} (79%) create mode 100644 component/common/src/main/java/io/meeds/spring/web/security/FilterConfiguration.java rename component/common/src/main/java/io/meeds/spring/{integration/web/configuration => web/security}/GrantedAuthorityDefaults.java (96%) rename component/common/src/main/java/io/meeds/spring/{integration => }/web/security/PortalAuthenticationManager.java (99%) rename component/common/src/main/java/io/meeds/spring/{integration/web/filter => web/security}/PortalIdentityFilter.java (95%) rename component/common/src/main/java/io/meeds/spring/{integration/web/configuration => web/security}/WebSecurityConfiguration.java (96%) rename component/common/src/main/java/io/meeds/spring/{integration/web/configuration/WebConfiguration.java => web/transaction/FilterConfiguration.java} (71%) rename component/common/src/main/java/io/meeds/spring/{integration/web/filter => web/transaction}/PortalTransactionFilter.java (98%) create mode 100644 component/common/src/test/java/io/meeds/spring/integration/test/KernelIntegrationTest.java rename component/common/src/test/java/io/meeds/spring/{integration/test => module}/dao/TestDao.java (91%) rename component/common/src/test/java/io/meeds/spring/{integration/test => module}/entity/TestEntity.java (97%) rename component/common/src/test/java/io/meeds/spring/{integration => module}/mapper/EntityMapper.java (89%) rename component/common/src/test/java/io/meeds/spring/{integration => module}/model/TestModel.java (96%) rename component/common/src/test/java/io/meeds/spring/{integration => module}/service/TestExcludedService.java (86%) rename component/common/src/test/java/io/meeds/spring/{integration => module}/service/TestService.java (91%) rename component/common/src/test/java/io/meeds/spring/{integration => module}/storage/TestStorage.java (81%) create mode 100644 component/test/core/src/main/java/io/meeds/kernel/test/AbstractSpringTest.java diff --git a/component/api/src/main/java/org/exoplatform/commons/api/persistence/ExoEntityProcessor.java b/component/api/src/main/java/org/exoplatform/commons/api/persistence/ExoEntityProcessor.java index b22d2f736c..81e62d5395 100644 --- a/component/api/src/main/java/org/exoplatform/commons/api/persistence/ExoEntityProcessor.java +++ b/component/api/src/main/java/org/exoplatform/commons/api/persistence/ExoEntityProcessor.java @@ -23,6 +23,8 @@ import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +import javax.lang.model.SourceVersion; import javax.lang.model.element.Element; import javax.lang.model.element.TypeElement; import javax.tools.Diagnostic; @@ -42,6 +44,7 @@ * 7/22/15 */ @SupportedAnnotationTypes("org.exoplatform.commons.api.persistence.ExoEntity") +@SupportedSourceVersion(SourceVersion.RELEASE_17) public class ExoEntityProcessor extends AbstractProcessor { /** diff --git a/component/common/src/main/java/io/meeds/spring/AvailableIntegration.java b/component/common/src/main/java/io/meeds/spring/AvailableIntegration.java new file mode 100644 index 0000000000..8dac9a4d80 --- /dev/null +++ b/component/common/src/main/java/io/meeds/spring/AvailableIntegration.java @@ -0,0 +1,79 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring; + +import org.springframework.stereotype.Service; + +/** + * A class that group available Spring integrations made for Meeds Portal and + * Meeds Kernel applications. + * + * Those integrations aren't imported automatically to let each addon choose + * which Beans and integrations to adopt which is convinient to not force initialise + * not needed Beans in certain contexts + */ +public class AvailableIntegration { + + private AvailableIntegration() {} + + /** + * Used to bring Kernel components into Spring Context and vice versa + * add Spring {@link Service} layer Beans into Kernel and other Spring contexts + * to share the service layer + */ + public static final String KERNEL_MODULE = "io.meeds.spring.kernel"; + + /** + * Used when JPA entities are needed to be managed by Spring Data JPA and allows + * to reuse JPA PersistenceUnit defined globally in Kernel. + */ + public static final String JPA_MODULE = "io.meeds.spring.jpa"; + + /** + * Has to be used when Liquibase configuration and annotation are needed in an addon, + * otherwise, the parameter 'exclude = LiquibaseAutoConfiguration.class' has to be added in + * SpringBootApplication annotation to disable auto configuration of liquibase. + */ + public static final String LIQUIBASE_MODULE = "io.meeds.spring.liquibase"; + + /** + * Used to inject Meeds Portal Authentication and Authorization contexts to apply security + * on Spring REST/Controller endpoints. At the same time, this will allow to inject + * ConversationState.setCurrent as made in regular Portal REST endpoints. + */ + public static final String WEB_SECURITY_MODULE = "io.meeds.spring.web.security"; + + /** + * Used to start and end a transaction at each Spring REST/Controller call, same as used + * to be in regular Portal REST endpoints. + */ + public static final String WEB_TRANSACTION_MODULE = "io.meeds.spring.web.transaction"; + + /** + * Shortcut to list all available Meeds Portal and Kernel integration modules with Spring + */ + public static final String[] ALL_MODULES = { // NOSONAR + KERNEL_MODULE, + JPA_MODULE, + LIQUIBASE_MODULE, + WEB_SECURITY_MODULE, + WEB_TRANSACTION_MODULE + }; + +} diff --git a/component/common/src/main/java/io/meeds/spring/integration/PortalApplicationContext.java b/component/common/src/main/java/io/meeds/spring/integration/PortalApplicationContext.java deleted file mode 100644 index baab94c7d9..0000000000 --- a/component/common/src/main/java/io/meeds/spring/integration/PortalApplicationContext.java +++ /dev/null @@ -1,84 +0,0 @@ -/** - * This file is part of the Meeds project (https://meeds.io/). - * - * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package io.meeds.spring.integration; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; -import org.springframework.context.support.AbstractApplicationContext; - -import org.exoplatform.container.PortalContainer; -import org.exoplatform.container.RootContainer.PortalContainerPostInitTask; - -import io.meeds.spring.integration.kernel.KernelIntegration; - -import jakarta.servlet.ServletContext; - -/** - * A class to let Spring context initialized once the - * {@link PortalContainer}finishes startup to bring all defined Kernel Services - * into Spring Context in order to be able to use annotations to inject Kernel - * services - */ -public class PortalApplicationContext extends AnnotationConfigServletWebServerApplicationContext { - - private static final Logger LOG = LoggerFactory.getLogger(PortalApplicationContext.class); - - private ServletContext servletContext; - - public PortalApplicationContext(ServletContext servletContext, DefaultListableBeanFactory beanFactory) { - super(beanFactory); - this.servletContext = servletContext; - } - - @Override - @SuppressWarnings("resource") - protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { - PortalContainer.addInitTask(servletContext, new PortalContainerPostInitTask() { - @Override - public void execute(ServletContext context, PortalContainer portalContainer) { - // Register beans in Spring - AbstractApplicationContext springApplicationContext = PortalApplicationContext.this; - KernelIntegration.integrateSpringContext(portalContainer, - (BeanDefinitionRegistry) beanFactory, - springApplicationContext::getBeanDefinitionNames, - springApplicationContext::getBean, - () -> finishSpringContextStartup(beanFactory)); - } - }, "portal"); - } - - @Override - protected void finishRefresh() { - // Override to not be invoked in Context startup time - } - - private void finishSpringContextStartup(ConfigurableListableBeanFactory beanFactory) { - long start = System.currentTimeMillis(); - LOG.info("Start Spring context initialization of webapp '{}'", servletContext.getServletContextName()); - PortalApplicationContext.super.finishBeanFactoryInitialization(beanFactory); - PortalApplicationContext.super.finishRefresh(); - LOG.info("Spring context '{}' initialized in {}ms", - servletContext.getServletContextName(), - System.currentTimeMillis() - start); - } - -} diff --git a/component/common/src/main/java/io/meeds/spring/jpa/PersistenceUnitIntegration.java b/component/common/src/main/java/io/meeds/spring/jpa/PersistenceUnitIntegration.java new file mode 100644 index 0000000000..e2ef9a6a3d --- /dev/null +++ b/component/common/src/main/java/io/meeds/spring/jpa/PersistenceUnitIntegration.java @@ -0,0 +1,38 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.jpa; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import org.exoplatform.commons.persistence.impl.EntityManagerService; +import org.exoplatform.container.PortalContainer; + +import jakarta.persistence.EntityManagerFactory; + +@Configuration +public class PersistenceUnitIntegration { + + @Bean + public EntityManagerFactory entityManagerFactory() { + EntityManagerService entityManagerService = PortalContainer.getInstance() + .getComponentInstanceOfType(EntityManagerService.class); + return entityManagerService.getEntityManagerFactory(); + } + +} diff --git a/component/common/src/test/java/io/meeds/spring/integration/SpringBeanFactoryPostProcessor.java b/component/common/src/main/java/io/meeds/spring/kernel/SpringBeanFactoryInterceptor.java similarity index 86% rename from component/common/src/test/java/io/meeds/spring/integration/SpringBeanFactoryPostProcessor.java rename to component/common/src/main/java/io/meeds/spring/kernel/SpringBeanFactoryInterceptor.java index c21d826f19..41f2611b9f 100644 --- a/component/common/src/test/java/io/meeds/spring/integration/SpringBeanFactoryPostProcessor.java +++ b/component/common/src/main/java/io/meeds/spring/kernel/SpringBeanFactoryInterceptor.java @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package io.meeds.spring.integration; +package io.meeds.spring.kernel; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -31,12 +31,12 @@ import org.exoplatform.container.PortalContainer; -import io.meeds.spring.integration.kernel.KernelIntegration; +import io.meeds.spring.kernel.utils.KernelIntegration; @Component -public class SpringBeanFactoryPostProcessor implements BeanFactoryPostProcessor, BeanPostProcessor, ApplicationContextAware { +public class SpringBeanFactoryInterceptor implements BeanFactoryPostProcessor, BeanPostProcessor, ApplicationContextAware { - private static final Logger LOG = LoggerFactory.getLogger(SpringBeanFactoryPostProcessor.class); + private static final Logger LOG = LoggerFactory.getLogger(SpringBeanFactoryInterceptor.class); private PortalContainer container; @@ -44,12 +44,13 @@ public class SpringBeanFactoryPostProcessor implements BeanFactoryPostProcessor, private BeanDefinitionRegistry beanRegistry; + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - if (applicationContext == null) { - LOG.error("Application context seems to be empty. Can't integrate Spring with Kernel"); - return; - } LOG.info("Integrating Spring Context with Container. Application name = {}", applicationContext.getApplicationName()); this.container = PortalContainer.getInstance(); this.beanRegistry = (BeanDefinitionRegistry) beanFactory; @@ -65,9 +66,4 @@ public Object postProcessAfterInitialization(Object bean, String beanName) throw return bean; } - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; - } - } diff --git a/component/common/src/main/java/io/meeds/spring/integration/Exclude.java b/component/common/src/main/java/io/meeds/spring/kernel/annotation/Exclude.java similarity index 97% rename from component/common/src/main/java/io/meeds/spring/integration/Exclude.java rename to component/common/src/main/java/io/meeds/spring/kernel/annotation/Exclude.java index 608d66892a..70a0e94e54 100644 --- a/component/common/src/main/java/io/meeds/spring/integration/Exclude.java +++ b/component/common/src/main/java/io/meeds/spring/kernel/annotation/Exclude.java @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package io.meeds.spring.integration; +package io.meeds.spring.kernel.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; diff --git a/component/common/src/main/java/io/meeds/spring/integration/kernel/KernelIntegration.java b/component/common/src/main/java/io/meeds/spring/kernel/utils/KernelIntegration.java similarity index 97% rename from component/common/src/main/java/io/meeds/spring/integration/kernel/KernelIntegration.java rename to component/common/src/main/java/io/meeds/spring/kernel/utils/KernelIntegration.java index 4238a2c7ca..8b041f9e2b 100644 --- a/component/common/src/main/java/io/meeds/spring/integration/kernel/KernelIntegration.java +++ b/component/common/src/main/java/io/meeds/spring/kernel/utils/KernelIntegration.java @@ -15,10 +15,10 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package io.meeds.spring.integration.kernel; +package io.meeds.spring.kernel.utils; -import static io.meeds.spring.integration.kernel.SharedSpringBeanRegistry.hasBeanDefinition; -import static io.meeds.spring.integration.kernel.SharedSpringBeanRegistry.registerSpringBeansState; +import static io.meeds.spring.kernel.utils.SharedSpringBeanRegistry.hasBeanDefinition; +import static io.meeds.spring.kernel.utils.SharedSpringBeanRegistry.registerSpringBeansState; import java.util.HashSet; import java.util.List; @@ -45,7 +45,7 @@ import org.exoplatform.container.PortalContainer; import org.exoplatform.container.spi.ComponentAdapter; -import io.meeds.spring.integration.Exclude; +import io.meeds.spring.kernel.annotation.Exclude; /** * A class to let Spring context initialized once the diff --git a/component/common/src/main/java/io/meeds/spring/integration/kernel/SharedSpringBeanRegistry.java b/component/common/src/main/java/io/meeds/spring/kernel/utils/SharedSpringBeanRegistry.java similarity index 98% rename from component/common/src/main/java/io/meeds/spring/integration/kernel/SharedSpringBeanRegistry.java rename to component/common/src/main/java/io/meeds/spring/kernel/utils/SharedSpringBeanRegistry.java index 3fe4df6924..f0ca63969b 100644 --- a/component/common/src/main/java/io/meeds/spring/integration/kernel/SharedSpringBeanRegistry.java +++ b/component/common/src/main/java/io/meeds/spring/kernel/utils/SharedSpringBeanRegistry.java @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package io.meeds.spring.integration.kernel; +package io.meeds.spring.kernel.utils; import java.util.HashMap; import java.util.Map; diff --git a/component/common/src/main/java/io/meeds/spring/integration/persistence/PersistenceUnitIntegration.java b/component/common/src/main/java/io/meeds/spring/liquibase/LiquibaseIntegration.java similarity index 79% rename from component/common/src/main/java/io/meeds/spring/integration/persistence/PersistenceUnitIntegration.java rename to component/common/src/main/java/io/meeds/spring/liquibase/LiquibaseIntegration.java index 75eae7ec4b..2ea735905e 100644 --- a/component/common/src/main/java/io/meeds/spring/integration/persistence/PersistenceUnitIntegration.java +++ b/component/common/src/main/java/io/meeds/spring/liquibase/LiquibaseIntegration.java @@ -15,28 +15,21 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package io.meeds.spring.integration.persistence; +package io.meeds.spring.liquibase; import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; +import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import org.exoplatform.commons.persistence.impl.EntityManagerService; import org.exoplatform.commons.persistence.impl.LiquibaseDataInitializer; import org.exoplatform.container.PortalContainer; -import jakarta.persistence.EntityManagerFactory; import liquibase.integration.spring.SpringLiquibase; @Configuration -public class PersistenceUnitIntegration { - - @Bean - public EntityManagerFactory entityManagerFactory() { - EntityManagerService entityManagerService = PortalContainer.getInstance() - .getComponentInstanceOfType(EntityManagerService.class); - return entityManagerService.getEntityManagerFactory(); - } +@EnableConfigurationProperties(LiquibaseProperties.class) +public class LiquibaseIntegration { @Bean public SpringLiquibase liquibase(LiquibaseProperties liquibaseProperties) { diff --git a/component/common/src/main/java/io/meeds/spring/web/security/FilterConfiguration.java b/component/common/src/main/java/io/meeds/spring/web/security/FilterConfiguration.java new file mode 100644 index 0000000000..8e542e7e24 --- /dev/null +++ b/component/common/src/main/java/io/meeds/spring/web/security/FilterConfiguration.java @@ -0,0 +1,34 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * Copyright (C) 2020 - 2022 Meeds Association contact@meeds.io + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.web.security; + +import org.springframework.boot.web.servlet.FilterRegistrationBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class FilterConfiguration { + + @Bean + public FilterRegistrationBean identityFilter() { + FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); + registrationBean.setFilter(new PortalIdentityFilter()); + registrationBean.addUrlPatterns("/rest/*"); + registrationBean.setOrder(1); + return registrationBean; + } + +} diff --git a/component/common/src/main/java/io/meeds/spring/integration/web/configuration/GrantedAuthorityDefaults.java b/component/common/src/main/java/io/meeds/spring/web/security/GrantedAuthorityDefaults.java similarity index 96% rename from component/common/src/main/java/io/meeds/spring/integration/web/configuration/GrantedAuthorityDefaults.java rename to component/common/src/main/java/io/meeds/spring/web/security/GrantedAuthorityDefaults.java index 6eb05de606..44aee0e84d 100644 --- a/component/common/src/main/java/io/meeds/spring/integration/web/configuration/GrantedAuthorityDefaults.java +++ b/component/common/src/main/java/io/meeds/spring/web/security/GrantedAuthorityDefaults.java @@ -1,4 +1,4 @@ -package io.meeds.spring.integration.web.configuration; +package io.meeds.spring.web.security; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanPostProcessor; diff --git a/component/common/src/main/java/io/meeds/spring/integration/web/security/PortalAuthenticationManager.java b/component/common/src/main/java/io/meeds/spring/web/security/PortalAuthenticationManager.java similarity index 99% rename from component/common/src/main/java/io/meeds/spring/integration/web/security/PortalAuthenticationManager.java rename to component/common/src/main/java/io/meeds/spring/web/security/PortalAuthenticationManager.java index 31569d82aa..86278bfe63 100644 --- a/component/common/src/main/java/io/meeds/spring/integration/web/security/PortalAuthenticationManager.java +++ b/component/common/src/main/java/io/meeds/spring/web/security/PortalAuthenticationManager.java @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package io.meeds.spring.integration.web.security; +package io.meeds.spring.web.security; import java.security.Principal; import java.util.ArrayList; diff --git a/component/common/src/main/java/io/meeds/spring/integration/web/filter/PortalIdentityFilter.java b/component/common/src/main/java/io/meeds/spring/web/security/PortalIdentityFilter.java similarity index 95% rename from component/common/src/main/java/io/meeds/spring/integration/web/filter/PortalIdentityFilter.java rename to component/common/src/main/java/io/meeds/spring/web/security/PortalIdentityFilter.java index 0db66d9e87..40538f854c 100644 --- a/component/common/src/main/java/io/meeds/spring/integration/web/filter/PortalIdentityFilter.java +++ b/component/common/src/main/java/io/meeds/spring/web/security/PortalIdentityFilter.java @@ -17,7 +17,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package io.meeds.spring.integration.web.filter; +package io.meeds.spring.web.security; import org.exoplatform.services.security.web.SetCurrentIdentityFilter; diff --git a/component/common/src/main/java/io/meeds/spring/integration/web/configuration/WebSecurityConfiguration.java b/component/common/src/main/java/io/meeds/spring/web/security/WebSecurityConfiguration.java similarity index 96% rename from component/common/src/main/java/io/meeds/spring/integration/web/configuration/WebSecurityConfiguration.java rename to component/common/src/main/java/io/meeds/spring/web/security/WebSecurityConfiguration.java index f8dc994b81..38d089a099 100644 --- a/component/common/src/main/java/io/meeds/spring/integration/web/configuration/WebSecurityConfiguration.java +++ b/component/common/src/main/java/io/meeds/spring/web/security/WebSecurityConfiguration.java @@ -13,7 +13,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package io.meeds.spring.integration.web.configuration; +package io.meeds.spring.web.security; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -28,8 +28,6 @@ import org.springframework.security.config.annotation.web.configurers.JeeConfigurer; import org.springframework.security.web.SecurityFilterChain; -import io.meeds.spring.integration.web.security.PortalAuthenticationManager; - @Configuration @EnableWebSecurity @EnableMethodSecurity(prePostEnabled = true, diff --git a/component/common/src/main/java/io/meeds/spring/integration/web/configuration/WebConfiguration.java b/component/common/src/main/java/io/meeds/spring/web/transaction/FilterConfiguration.java similarity index 71% rename from component/common/src/main/java/io/meeds/spring/integration/web/configuration/WebConfiguration.java rename to component/common/src/main/java/io/meeds/spring/web/transaction/FilterConfiguration.java index 10ba2ce1c2..f6c77060dd 100644 --- a/component/common/src/main/java/io/meeds/spring/integration/web/configuration/WebConfiguration.java +++ b/component/common/src/main/java/io/meeds/spring/web/transaction/FilterConfiguration.java @@ -13,26 +13,14 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package io.meeds.spring.integration.web.configuration; +package io.meeds.spring.web.transaction; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -import io.meeds.spring.integration.web.filter.PortalIdentityFilter; -import io.meeds.spring.integration.web.filter.PortalTransactionFilter; - @Configuration -public class WebConfiguration { - - @Bean - public FilterRegistrationBean identityFilter() { - FilterRegistrationBean registrationBean = new FilterRegistrationBean<>(); - registrationBean.setFilter(new PortalIdentityFilter()); - registrationBean.addUrlPatterns("/rest/*"); - registrationBean.setOrder(1); - return registrationBean; - } +public class FilterConfiguration { @Bean public FilterRegistrationBean transactionFilter() { diff --git a/component/common/src/main/java/io/meeds/spring/integration/web/filter/PortalTransactionFilter.java b/component/common/src/main/java/io/meeds/spring/web/transaction/PortalTransactionFilter.java similarity index 98% rename from component/common/src/main/java/io/meeds/spring/integration/web/filter/PortalTransactionFilter.java rename to component/common/src/main/java/io/meeds/spring/web/transaction/PortalTransactionFilter.java index 5e00e19906..9aa329212c 100644 --- a/component/common/src/main/java/io/meeds/spring/integration/web/filter/PortalTransactionFilter.java +++ b/component/common/src/main/java/io/meeds/spring/web/transaction/PortalTransactionFilter.java @@ -17,7 +17,7 @@ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package io.meeds.spring.integration.web.filter; +package io.meeds.spring.web.transaction; import java.io.IOException; import java.util.List; diff --git a/component/common/src/test/java/io/meeds/spring/integration/test/KernelIntegrationTest.java b/component/common/src/test/java/io/meeds/spring/integration/test/KernelIntegrationTest.java new file mode 100644 index 0000000000..2fbcc1fc3f --- /dev/null +++ b/component/common/src/test/java/io/meeds/spring/integration/test/KernelIntegrationTest.java @@ -0,0 +1,65 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.integration.test; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; +import org.springframework.test.context.TestPropertySource; +import org.springframework.test.context.junit.jupiter.SpringExtension; + +import org.exoplatform.jpa.CommonsDAOJPAImplTest; + +import io.meeds.kernel.test.KernelExtension; +import io.meeds.spring.AvailableIntegration; +import io.meeds.spring.module.dao.TestDao; +import io.meeds.spring.module.service.TestExcludedService; +import io.meeds.spring.module.service.TestService; +import io.meeds.spring.module.storage.TestStorage; + +@ExtendWith({ SpringExtension.class, KernelExtension.class }) +@SpringBootApplication(scanBasePackages = { + KernelIntegrationTest.MODULE_NAME, + AvailableIntegration.KERNEL_MODULE, + AvailableIntegration.JPA_MODULE, + AvailableIntegration.LIQUIBASE_MODULE, +}) +@EnableJpaRepositories(basePackages = KernelIntegrationTest.MODULE_NAME) +@TestPropertySource(properties = { + "spring.liquibase.change-log=" + KernelIntegrationTest.CHANGELOG_PATH, +}) +public class KernelIntegrationTest extends CommonsDAOJPAImplTest { // NOSONAR + + static final String MODULE_NAME = "io.meeds.spring.module"; + + static final String CHANGELOG_PATH = "classpath:db/changelog/test-rdbms.db.changelog.xml"; + + @Test + public void beansInjected() { + assertNotNull("Spring Bean @Service layer isn't included as Kernel component", + getContainer().getComponentInstanceOfType(TestService.class)); + assertNull("Spring Bean @Service layer and @Exlude shouldn't be defined as Kernel component", + getContainer().getComponentInstanceOfType(TestExcludedService.class)); + assertNull("Spring Bean @Repository (Storage) layer shouldn't be defined as Kernel component", + getContainer().getComponentInstanceOfType(TestStorage.class)); + assertNull("Spring Bean Dao layer shouldn't be defined as Kernel component", + getContainer().getComponentInstanceOfType(TestDao.class)); + } + +} diff --git a/component/common/src/test/java/io/meeds/spring/integration/test/SpringIntegrationTest.java b/component/common/src/test/java/io/meeds/spring/integration/test/SpringIntegrationTest.java index 04a5bb4723..b4f0e46db8 100644 --- a/component/common/src/test/java/io/meeds/spring/integration/test/SpringIntegrationTest.java +++ b/component/common/src/test/java/io/meeds/spring/integration/test/SpringIntegrationTest.java @@ -21,8 +21,6 @@ import org.junit.jupiter.api.extension.ExtendWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; -import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.data.jpa.repository.config.EnableJpaRepositories; import org.springframework.test.context.TestPropertySource; import org.springframework.test.context.junit.jupiter.SpringExtension; @@ -31,24 +29,31 @@ import org.exoplatform.jpa.CommonsDAOJPAImplTest; import io.meeds.kernel.test.KernelExtension; -import io.meeds.spring.integration.model.TestModel; -import io.meeds.spring.integration.service.TestExcludedService; -import io.meeds.spring.integration.service.TestService; -import io.meeds.spring.integration.storage.TestStorage; -import io.meeds.spring.integration.test.dao.TestDao; -import io.meeds.spring.integration.test.entity.TestEntity; +import io.meeds.spring.AvailableIntegration; +import io.meeds.spring.module.dao.TestDao; +import io.meeds.spring.module.entity.TestEntity; +import io.meeds.spring.module.model.TestModel; +import io.meeds.spring.module.service.TestExcludedService; +import io.meeds.spring.module.service.TestService; +import io.meeds.spring.module.storage.TestStorage; @ExtendWith({ SpringExtension.class, KernelExtension.class }) @SpringBootApplication(scanBasePackages = { - "io.meeds.spring.integration" + SpringIntegrationTest.MODULE_NAME, + AvailableIntegration.KERNEL_MODULE, + AvailableIntegration.JPA_MODULE, + AvailableIntegration.LIQUIBASE_MODULE, }) -@EnableJpaRepositories(basePackages = "io.meeds.spring.integration") -@EnableConfigurationProperties(LiquibaseProperties.class) +@EnableJpaRepositories(basePackages = SpringIntegrationTest.MODULE_NAME) @TestPropertySource(properties = { - "spring.liquibase.change-log=classpath:db/changelog/test-rdbms.db.changelog.xml", + "spring.liquibase.change-log=" + SpringIntegrationTest.CHANGELOG_PATH, }) public class SpringIntegrationTest extends CommonsDAOJPAImplTest { // NOSONAR + static final String MODULE_NAME = "io.meeds.spring.module"; + + static final String CHANGELOG_PATH = "classpath:db/changelog/test-rdbms.db.changelog.xml"; + @Autowired private SettingService settingService; @@ -66,7 +71,7 @@ public class SpringIntegrationTest extends CommonsDAOJPAImplTest { // NOSONAR @Test public void beansInjected() { - assertNotNull("Kernel Service not included in Spring context", settingService); + assertNotNull("Kernel Component not found in Spring context", settingService); assertNotNull("Spring @Repository Bean not found", testDao); assertNotNull("Spring @Component Bean not found", testStorage); assertNotNull("Spring @Service Bean not found", testService); diff --git a/component/common/src/test/java/io/meeds/spring/integration/test/dao/TestDao.java b/component/common/src/test/java/io/meeds/spring/module/dao/TestDao.java similarity index 91% rename from component/common/src/test/java/io/meeds/spring/integration/test/dao/TestDao.java rename to component/common/src/test/java/io/meeds/spring/module/dao/TestDao.java index dc5411c6a4..10a68a0f14 100644 --- a/component/common/src/test/java/io/meeds/spring/integration/test/dao/TestDao.java +++ b/component/common/src/test/java/io/meeds/spring/module/dao/TestDao.java @@ -16,12 +16,12 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package io.meeds.spring.integration.test.dao; +package io.meeds.spring.module.dao; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; -import io.meeds.spring.integration.test.entity.TestEntity; +import io.meeds.spring.module.entity.TestEntity; @Repository public interface TestDao extends JpaRepository { diff --git a/component/common/src/test/java/io/meeds/spring/integration/test/entity/TestEntity.java b/component/common/src/test/java/io/meeds/spring/module/entity/TestEntity.java similarity index 97% rename from component/common/src/test/java/io/meeds/spring/integration/test/entity/TestEntity.java rename to component/common/src/test/java/io/meeds/spring/module/entity/TestEntity.java index 07e56fe909..8e5ca8e642 100644 --- a/component/common/src/test/java/io/meeds/spring/integration/test/entity/TestEntity.java +++ b/component/common/src/test/java/io/meeds/spring/module/entity/TestEntity.java @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package io.meeds.spring.integration.test.entity; +package io.meeds.spring.module.entity; import java.io.Serializable; diff --git a/component/common/src/test/java/io/meeds/spring/integration/mapper/EntityMapper.java b/component/common/src/test/java/io/meeds/spring/module/mapper/EntityMapper.java similarity index 89% rename from component/common/src/test/java/io/meeds/spring/integration/mapper/EntityMapper.java rename to component/common/src/test/java/io/meeds/spring/module/mapper/EntityMapper.java index a2e60b3543..ba23a737c0 100644 --- a/component/common/src/test/java/io/meeds/spring/integration/mapper/EntityMapper.java +++ b/component/common/src/test/java/io/meeds/spring/module/mapper/EntityMapper.java @@ -16,10 +16,10 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package io.meeds.spring.integration.mapper; +package io.meeds.spring.module.mapper; -import io.meeds.spring.integration.model.TestModel; -import io.meeds.spring.integration.test.entity.TestEntity; +import io.meeds.spring.module.entity.TestEntity; +import io.meeds.spring.module.model.TestModel; public class EntityMapper { diff --git a/component/common/src/test/java/io/meeds/spring/integration/model/TestModel.java b/component/common/src/test/java/io/meeds/spring/module/model/TestModel.java similarity index 96% rename from component/common/src/test/java/io/meeds/spring/integration/model/TestModel.java rename to component/common/src/test/java/io/meeds/spring/module/model/TestModel.java index 689f5f402f..61a2ea5dde 100644 --- a/component/common/src/test/java/io/meeds/spring/integration/model/TestModel.java +++ b/component/common/src/test/java/io/meeds/spring/module/model/TestModel.java @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package io.meeds.spring.integration.model; +package io.meeds.spring.module.model; import lombok.AllArgsConstructor; import lombok.Data; diff --git a/component/common/src/test/java/io/meeds/spring/integration/service/TestExcludedService.java b/component/common/src/test/java/io/meeds/spring/module/service/TestExcludedService.java similarity index 86% rename from component/common/src/test/java/io/meeds/spring/integration/service/TestExcludedService.java rename to component/common/src/test/java/io/meeds/spring/module/service/TestExcludedService.java index 95dd2e7453..152e2b435e 100644 --- a/component/common/src/test/java/io/meeds/spring/integration/service/TestExcludedService.java +++ b/component/common/src/test/java/io/meeds/spring/module/service/TestExcludedService.java @@ -16,14 +16,14 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package io.meeds.spring.integration.service; +package io.meeds.spring.module.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import io.meeds.spring.integration.Exclude; -import io.meeds.spring.integration.model.TestModel; -import io.meeds.spring.integration.storage.TestStorage; +import io.meeds.spring.kernel.annotation.Exclude; +import io.meeds.spring.module.model.TestModel; +import io.meeds.spring.module.storage.TestStorage; @Service @Exclude diff --git a/component/common/src/test/java/io/meeds/spring/integration/service/TestService.java b/component/common/src/test/java/io/meeds/spring/module/service/TestService.java similarity index 91% rename from component/common/src/test/java/io/meeds/spring/integration/service/TestService.java rename to component/common/src/test/java/io/meeds/spring/module/service/TestService.java index fefc2ff148..d809727452 100644 --- a/component/common/src/test/java/io/meeds/spring/integration/service/TestService.java +++ b/component/common/src/test/java/io/meeds/spring/module/service/TestService.java @@ -16,15 +16,15 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package io.meeds.spring.integration.service; +package io.meeds.spring.module.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.exoplatform.commons.api.settings.SettingService; -import io.meeds.spring.integration.model.TestModel; -import io.meeds.spring.integration.storage.TestStorage; +import io.meeds.spring.module.model.TestModel; +import io.meeds.spring.module.storage.TestStorage; @Service public class TestService { diff --git a/component/common/src/test/java/io/meeds/spring/integration/storage/TestStorage.java b/component/common/src/test/java/io/meeds/spring/module/storage/TestStorage.java similarity index 81% rename from component/common/src/test/java/io/meeds/spring/integration/storage/TestStorage.java rename to component/common/src/test/java/io/meeds/spring/module/storage/TestStorage.java index e7cf5f816e..a9efc77d6a 100644 --- a/component/common/src/test/java/io/meeds/spring/integration/storage/TestStorage.java +++ b/component/common/src/test/java/io/meeds/spring/module/storage/TestStorage.java @@ -16,16 +16,16 @@ * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -package io.meeds.spring.integration.storage; +package io.meeds.spring.module.storage; -import static io.meeds.spring.integration.mapper.EntityMapper.*; +import static io.meeds.spring.module.mapper.EntityMapper.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; -import io.meeds.spring.integration.model.TestModel; -import io.meeds.spring.integration.test.dao.TestDao; -import io.meeds.spring.integration.test.entity.TestEntity; +import io.meeds.spring.module.dao.TestDao; +import io.meeds.spring.module.entity.TestEntity; +import io.meeds.spring.module.model.TestModel; @Component public class TestStorage { diff --git a/component/common/src/test/java/org/exoplatform/commons/persistence/impl/LiquibaseDataInitializerTestContext.java b/component/common/src/test/java/org/exoplatform/commons/persistence/impl/LiquibaseDataInitializerTestContext.java index 178fc44460..12c5d5c883 100644 --- a/component/common/src/test/java/org/exoplatform/commons/persistence/impl/LiquibaseDataInitializerTestContext.java +++ b/component/common/src/test/java/org/exoplatform/commons/persistence/impl/LiquibaseDataInitializerTestContext.java @@ -17,7 +17,6 @@ import org.exoplatform.commons.InitialContextInitializer2; import org.exoplatform.container.xml.InitParams; -import org.exoplatform.services.naming.InitialContextInitializer; public class LiquibaseDataInitializerTestContext extends LiquibaseDataInitializer { diff --git a/component/test/core/pom.xml b/component/test/core/pom.xml index d0f3fe2859..ba67c13699 100644 --- a/component/test/core/pom.xml +++ b/component/test/core/pom.xml @@ -30,7 +30,7 @@ GateIn Portal Component Core Test - 0.33 + 0.32 diff --git a/component/test/core/src/main/java/io/meeds/kernel/test/AbstractSpringTest.java b/component/test/core/src/main/java/io/meeds/kernel/test/AbstractSpringTest.java new file mode 100644 index 0000000000..595c3be9cb --- /dev/null +++ b/component/test/core/src/main/java/io/meeds/kernel/test/AbstractSpringTest.java @@ -0,0 +1,93 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.kernel.test; + +import org.exoplatform.component.test.ConfigurationUnit; +import org.exoplatform.component.test.ConfiguredBy; +import org.exoplatform.component.test.ContainerScope; +import org.exoplatform.component.test.KernelBootstrap; +import org.exoplatform.container.ExoContainerContext; +import org.exoplatform.container.PortalContainer; +import org.exoplatform.container.component.RequestLifeCycle; + +@ConfiguredBy({ + @ConfigurationUnit(scope = ContainerScope.ROOT, + path = "conf/configuration.xml"), + @ConfigurationUnit(scope = ContainerScope.PORTAL, + path = "conf/portal/configuration.xml"), +}) +public abstract class AbstractSpringTest { + + private static KernelBootstrap bootstrap; + + public static PortalContainer bootContainer(Class clazz) { + if (isPortalContainerPresent()) { + return PortalContainer.getInstanceIfPresent(); + } + bootstrap = new KernelBootstrap(Thread.currentThread().getContextClassLoader()); + bootstrap.addConfiguration(clazz); + bootstrap.boot(); + return bootstrap.getContainer(); + } + + public PortalContainer getContainer() { + return bootstrap == null ? bootContainer() : bootstrap.getContainer(); + } + + protected PortalContainer bootContainer() { + PortalContainer container = bootContainer(getClass()); + ExoContainerContext.setCurrentContainer(container); + return container; + } + + protected void begin() { + PortalContainer container = getContainer(); + ExoContainerContext.setCurrentContainer(container); + RequestLifeCycle.begin(container); + } + + protected void end() { + RequestLifeCycle.end(); + } + + protected void restartTransaction() { + int i = 0; + // Close transactions until no encapsulated transaction + boolean success = true; + do { + try { + end(); + i++; + } catch (IllegalStateException e) { + success = false; + } + } while (success); + + // Restart transactions with the same number of encapsulations + for (int j = 0; j < i; j++) { + begin(); + } + } + + protected static boolean isPortalContainerPresent() { + return ExoContainerContext.getCurrentContainerIfPresent() != null && PortalContainer.getInstanceIfPresent() != null; + } + +} diff --git a/component/test/core/src/main/java/io/meeds/kernel/test/KernelExtension.java b/component/test/core/src/main/java/io/meeds/kernel/test/KernelExtension.java index 5bc200f423..9ec809ae35 100644 --- a/component/test/core/src/main/java/io/meeds/kernel/test/KernelExtension.java +++ b/component/test/core/src/main/java/io/meeds/kernel/test/KernelExtension.java @@ -21,14 +21,12 @@ import org.junit.jupiter.api.extension.BeforeAllCallback; import org.junit.jupiter.api.extension.ExtensionContext; -import org.exoplatform.component.test.AbstractKernelTest; - public class KernelExtension implements BeforeAllCallback { @Override public void beforeAll(ExtensionContext context) { Class testClass = context.getTestClass().orElseThrow(); - AbstractKernelTest.bootContainer(testClass); + AbstractSpringTest.bootContainer(testClass); } } diff --git a/component/test/core/src/main/java/org/exoplatform/component/test/AbstractKernelTest.java b/component/test/core/src/main/java/org/exoplatform/component/test/AbstractKernelTest.java index 9d57340fbb..5736704a79 100644 --- a/component/test/core/src/main/java/org/exoplatform/component/test/AbstractKernelTest.java +++ b/component/test/core/src/main/java/org/exoplatform/component/test/AbstractKernelTest.java @@ -74,19 +74,6 @@ protected AbstractKernelTest(String name) { super(name); } - public static PortalContainer bootContainer(Class clazz) { - // - bootstrap = new KernelBootstrap(Thread.currentThread().getContextClassLoader()); - - // Configure ourselves - bootstrap.addConfiguration(clazz); - // - bootstrap.boot(); - - // - return bootstrap.getContainer(); - } - public PortalContainer getContainer() { return bootstrap == null ? bootContainer() : bootstrap.getContainer(); } @@ -166,7 +153,7 @@ protected void beforeClass() { } } - protected void forceStop() { + private void forceStop() { RootContainer.getInstance().stop(); ExoContainerContext.setCurrentContainer(null); } @@ -185,12 +172,21 @@ protected void afterClass() { } private PortalContainer bootContainer() { - PortalContainer container = bootContainer(getClass()); - ExoContainerContext.setCurrentContainer(container); - return container; + // + bootstrap = new KernelBootstrap(Thread.currentThread().getContextClassLoader()); + + // Configure ourselves + bootstrap.addConfiguration(getClass()); + // + bootstrap.boot(); + + // + PortalContainer portalContainer = bootstrap.getContainer(); + ExoContainerContext.setCurrentContainer(portalContainer); + return portalContainer; } - private static boolean isPortalContainerPresent() { + private boolean isPortalContainerPresent() { return ExoContainerContext.getCurrentContainerIfPresent() != null && PortalContainer.getInstanceIfPresent() != null; } diff --git a/component/test/core/src/main/java/org/exoplatform/component/test/BaseGateInTest.java b/component/test/core/src/main/java/org/exoplatform/component/test/BaseGateInTest.java index 855ba7dc26..830e18378a 100644 --- a/component/test/core/src/main/java/org/exoplatform/component/test/BaseGateInTest.java +++ b/component/test/core/src/main/java/org/exoplatform/component/test/BaseGateInTest.java @@ -70,4 +70,5 @@ public static Error failure(String msg, Throwable t) { afe.initCause(t); return afe; } + } diff --git a/component/test/core/src/main/resources/logback-test.xml b/component/test/core/src/main/resources/logback-test.xml index df3b0545f4..9165395a6a 100644 --- a/component/test/core/src/main/resources/logback-test.xml +++ b/component/test/core/src/main/resources/logback-test.xml @@ -26,11 +26,11 @@ %date{ISO8601} | %highlight(%-5level) | %msg %green([%logger{40}){}%cyan(<%thread>){}%green(]){} %n%xEx - - - - - + + + + + From f474dd1c6ccbeeae77c5b3f1ecd8322e62e56030 Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Tue, 26 Dec 2023 12:30:53 +0100 Subject: [PATCH 04/14] feat: Enhance Spring Context initialization using adapters - Meeds-io/MIPs#57 --- component/common/pom.xml | 2 +- .../io/meeds/spring/AvailableIntegration.java | 31 ++- .../KernelContainerLifecyclePlugin.java | 241 ++++++++++++++++++ .../kernel/PortalApplicationContext.java | 200 +++++++++++++++ .../PortalApplicationContextInitializer.java | 53 ++++ .../kernel/SpringBeanFactoryInterceptor.java | 42 +-- .../model/SpringBeanComponentAdapter.java | 72 ++++++ .../utils/SharedSpringBeanRegistry.java | 58 ----- .../liquibase/LiquibaseIntegration.java | 30 ++- ...va => WebSecurityFilterConfiguration.java} | 4 +- ...=> WebTransactionFilterConfiguration.java} | 4 +- .../impl/LiquibaseDataInitializer.java | 25 +- .../resources/conf/portal/configuration.xml | 4 + .../test/KernelIntegrationTest.java | 2 +- .../test/SpringIntegrationTest.java | 2 +- .../kernel/test/KernelTestIntegration.java} | 103 +------- .../test/SpringBeanFactoryInterceptor.java | 67 +++++ 17 files changed, 729 insertions(+), 211 deletions(-) create mode 100644 component/common/src/main/java/io/meeds/spring/kernel/KernelContainerLifecyclePlugin.java create mode 100644 component/common/src/main/java/io/meeds/spring/kernel/PortalApplicationContext.java create mode 100644 component/common/src/main/java/io/meeds/spring/kernel/PortalApplicationContextInitializer.java create mode 100644 component/common/src/main/java/io/meeds/spring/kernel/model/SpringBeanComponentAdapter.java delete mode 100644 component/common/src/main/java/io/meeds/spring/kernel/utils/SharedSpringBeanRegistry.java rename component/common/src/main/java/io/meeds/spring/web/security/{FilterConfiguration.java => WebSecurityFilterConfiguration.java} (93%) rename component/common/src/main/java/io/meeds/spring/web/transaction/{FilterConfiguration.java => WebTransactionFilterConfiguration.java} (93%) rename component/common/src/{main/java/io/meeds/spring/kernel/utils/KernelIntegration.java => test/java/io/meeds/spring/kernel/test/KernelTestIntegration.java} (57%) create mode 100644 component/common/src/test/java/io/meeds/spring/kernel/test/SpringBeanFactoryInterceptor.java diff --git a/component/common/pom.xml b/component/common/pom.xml index ace0453376..3d8770eb2c 100644 --- a/component/common/pom.xml +++ b/component/common/pom.xml @@ -28,7 +28,7 @@ GateIn Portal Component Common - 0.41 + 0.38 diff --git a/component/common/src/main/java/io/meeds/spring/AvailableIntegration.java b/component/common/src/main/java/io/meeds/spring/AvailableIntegration.java index 8dac9a4d80..729d3b15a0 100644 --- a/component/common/src/main/java/io/meeds/spring/AvailableIntegration.java +++ b/component/common/src/main/java/io/meeds/spring/AvailableIntegration.java @@ -37,38 +37,45 @@ private AvailableIntegration() {} * add Spring {@link Service} layer Beans into Kernel and other Spring contexts * to share the service layer */ - public static final String KERNEL_MODULE = "io.meeds.spring.kernel"; + public static final String KERNEL_MODULE = "io.meeds.spring.kernel"; + + /** + * see {@link AvailableIntegration#KERNEL_MODULE} + * + * Use in Test Scope only + */ + public static final String KERNEL_TEST_MODULE = "io.meeds.spring.kernel.test"; /** * Used when JPA entities are needed to be managed by Spring Data JPA and allows * to reuse JPA PersistenceUnit defined globally in Kernel. */ - public static final String JPA_MODULE = "io.meeds.spring.jpa"; + public static final String JPA_MODULE = "io.meeds.spring.jpa"; /** * Has to be used when Liquibase configuration and annotation are needed in an addon, * otherwise, the parameter 'exclude = LiquibaseAutoConfiguration.class' has to be added in * SpringBootApplication annotation to disable auto configuration of liquibase. */ - public static final String LIQUIBASE_MODULE = "io.meeds.spring.liquibase"; + public static final String LIQUIBASE_MODULE = "io.meeds.spring.liquibase"; /** * Used to inject Meeds Portal Authentication and Authorization contexts to apply security * on Spring REST/Controller endpoints. At the same time, this will allow to inject * ConversationState.setCurrent as made in regular Portal REST endpoints. */ - public static final String WEB_SECURITY_MODULE = "io.meeds.spring.web.security"; + public static final String WEB_SECURITY_MODULE = "io.meeds.spring.web.security"; /** * Used to start and end a transaction at each Spring REST/Controller call, same as used * to be in regular Portal REST endpoints. */ - public static final String WEB_TRANSACTION_MODULE = "io.meeds.spring.web.transaction"; + public static final String WEB_TRANSACTION_MODULE = "io.meeds.spring.web.transaction"; /** * Shortcut to list all available Meeds Portal and Kernel integration modules with Spring */ - public static final String[] ALL_MODULES = { // NOSONAR + public static final String[] ALL_MODULES = { // NOSONAR KERNEL_MODULE, JPA_MODULE, LIQUIBASE_MODULE, @@ -76,4 +83,16 @@ private AvailableIntegration() {} WEB_TRANSACTION_MODULE }; + /** + * Shortcut to list all available Meeds Portal and Kernel integration modules with Spring + * in Test scope + */ + public static final String[] ALL_TEST_MODULES = { // NOSONAR + KERNEL_TEST_MODULE, + JPA_MODULE, + LIQUIBASE_MODULE, + WEB_SECURITY_MODULE, + WEB_TRANSACTION_MODULE + }; + } diff --git a/component/common/src/main/java/io/meeds/spring/kernel/KernelContainerLifecyclePlugin.java b/component/common/src/main/java/io/meeds/spring/kernel/KernelContainerLifecyclePlugin.java new file mode 100644 index 0000000000..cc58620fdf --- /dev/null +++ b/component/common/src/main/java/io/meeds/spring/kernel/KernelContainerLifecyclePlugin.java @@ -0,0 +1,241 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.kernel; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Supplier; +import java.util.stream.Stream; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Configuration; +import org.springframework.stereotype.Service; + +import org.exoplatform.container.BaseContainerLifecyclePlugin; +import org.exoplatform.container.ExoContainer; +import org.exoplatform.container.PortalContainer; + +import io.meeds.spring.kernel.annotation.Exclude; +import io.meeds.spring.kernel.model.SpringBeanComponentAdapter; + +public class KernelContainerLifecyclePlugin extends BaseContainerLifecyclePlugin { + + private static final Logger LOG = + LoggerFactory.getLogger(KernelContainerLifecyclePlugin.class); + + private static Map springBeanRegistries = new ConcurrentHashMap<>(); + + private static Map springContexts = new ConcurrentHashMap<>(); + + private static Map> springBeans = new ConcurrentHashMap<>(); + + private static boolean kernelAlreadyBooted = false; + + @Override + public void initContainer(ExoContainer container) throws Exception { + if (!(container instanceof PortalContainer portalContainer)) { + return; + } + kernelAlreadyBooted = true; // NOSONAR + if (LOG.isDebugEnabled()) { + LOG.debug("Start Spring Bean definitions of contexts [{}] injection into Kernel Container", + StringUtils.join(springContexts.keySet(), ",")); + } + + // Inject Spring contexts included for Integration with Kernel Container + springContexts.forEach((servletContextName, applicationContext) -> { + BeanDefinitionRegistry beanDefinitionRegistry = springBeanRegistries.get(servletContextName); + registerSpringBeanDefinitionAsKernelComponentAdapter(portalContainer, + applicationContext, + beanDefinitionRegistry, + servletContextName); + }); + + // Inject Early retrieved beans + springBeans.forEach((servletContextName, + beanMap) -> beanMap.forEach((beanName, + bean) -> registerSpringBeanDefinitionAsKernelComponentAdapter(portalContainer, + servletContextName, + beanName, + bean))); + + } + + public static void addSpringContext(String servletContextName, + PortalApplicationContext applicationContext, + BeanDefinitionRegistry beanDefinitionRegistry) { + if (kernelAlreadyBooted) { + LOG.warn("Adding Spring context '{}' happened too late in Server startup, spring beans of current contexts will not be injected", + servletContextName); + return; + } + LOG.info("Add Spring context '{}' to inject its Beans in Kernel as available components", servletContextName); + springContexts.put(servletContextName, applicationContext); + springBeanRegistries.put(servletContextName, beanDefinitionRegistry); + } + + public static void addSpringBean(String servletContextName, String beanName, Object bean) { + if (kernelAlreadyBooted) { + return; + } + LOG.info("Add Registered Spring Bean '{}' to be able to inject into Kernel", beanName); + springBeans.computeIfAbsent(servletContextName, key -> new ConcurrentHashMap<>()) + .put(beanName, bean); + } + + private void registerSpringBeanDefinitionAsKernelComponentAdapter(PortalContainer portalContainer, + String servletContextName, + String beanName, + Object bean) { + registerSpringBeanDefinitionAsKernelComponentAdapter(servletContextName, + portalContainer, + beanName, + bean.getClass() + .getName(), + () -> { + LOG.trace("Retrieve Bean with name '{}' from Spring to Kernel using early injected beans", + beanName); + return springContexts.get(servletContextName) + .getBean(beanName); + }); + } + + private void registerSpringBeanDefinitionAsKernelComponentAdapter(PortalContainer portalContainer, + ApplicationContext applicationContext, + BeanDefinitionRegistry beanDefinitionRegistry, + String servletContextName) { + String[] beanDefinitionNames = beanDefinitionRegistry.getBeanDefinitionNames(); + Stream.of(beanDefinitionNames) + .distinct() + .forEach(beanName -> { + BeanDefinition beanDefinition = beanDefinitionRegistry.getBeanDefinition(beanName); + registerSpringBeanDefinitionAsKernelComponentAdapter(servletContextName, + portalContainer, + beanName, + beanDefinition.getBeanClassName(), + () -> { + LOG.trace("Retrieve Bean with name '{}' from Spring to Kernel using Application context", + beanName); + return applicationContext.getBean(beanName); + }); + }); + } + + @SuppressWarnings("unchecked") + private void registerSpringBeanDefinitionAsKernelComponentAdapter(String servletContextName, + PortalContainer portalContainer, + String beanName, + String beanClassName, + Supplier getBeanFunction) { + if (portalContainer == null || beanClassName == null) { + return; + } + Class componentKey = getComponentKey(portalContainer, beanClassName, beanName); + if (componentKey != null && portalContainer.getComponentAdapter(componentKey) == null) { + Class[] componentKeyInterfaces = componentKey.getInterfaces(); + if (ArrayUtils.isNotEmpty(componentKeyInterfaces)) { + registerSpringBeanDefinitionAsKernelComponentAdapter(servletContextName, + portalContainer, + beanName, + getBeanFunction, + componentKey); + Arrays.stream(componentKeyInterfaces) + .forEach(componentKeyInterface -> registerSpringBeanDefinitionAsKernelComponentAdapter(servletContextName, + portalContainer, + beanName, + getBeanFunction, + (Class) componentKeyInterface)); + } else { + registerSpringBeanDefinitionAsKernelComponentAdapter(servletContextName, + portalContainer, + beanName, + getBeanFunction, + componentKey); + } + } + } + + private void registerSpringBeanDefinitionAsKernelComponentAdapter(String servletContextName, + PortalContainer portalContainer, + String beanName, + Supplier getBeanFunction, + Class componentKey) { + LOG.debug("Register Spring Bean '{}' issued from context '{}' as Kernel service", componentKey.getName(), servletContextName); + portalContainer.registerComponentAdapter(new SpringBeanComponentAdapter(servletContextName, + beanName, + componentKey, + getBeanFunction)); + } + + private Class getComponentKey(PortalContainer portalContainer, + String beanClassName, + String beanName) { + Stream componentKeys = Stream.of(beanClassName, beanName); + List> componentClasses = componentKeys.filter(Objects::nonNull) + .map(className -> getClass(portalContainer, className)) + .filter(Objects::nonNull) + .filter(className -> !Objects.equals(className, Object.class)) + .distinct() + .toList(); + if (componentClasses.isEmpty() + // Direct rejection of Services in some cases + || componentClasses.stream().anyMatch(this::isBeanClassRejected)) { + return null; + } else { + return componentClasses.stream() + .filter(this::isServiceBean) + .findFirst() + .orElse(null); + } + } + + private boolean isBeanClassRejected(Class beanClass) { + return beanClass.isAnnotationPresent(Exclude.class) + || beanClass.isAnnotationPresent(Configuration.class) + || beanClass.isAnnotationPresent(SpringBootApplication.class); + } + + @SuppressWarnings("unchecked") + private Class getClass(PortalContainer portalContainer, String beanClassName) { + try { + return (Class) portalContainer.getPortalClassLoader().loadClass(beanClassName); + } catch (ClassNotFoundException e) { + return null; + } + } + + private boolean isServiceBean(Class beanClass) { + return beanClass.isAnnotationPresent(Service.class) + && !StringUtils.contains(beanClass.getName(), "org.springframework") + && !StringUtils.startsWith(beanClass.getName(), "java") + && !StringUtils.startsWith(beanClass.getName(), "jdk") + && !StringUtils.contains(beanClass.getName(), "$"); + } + +} diff --git a/component/common/src/main/java/io/meeds/spring/kernel/PortalApplicationContext.java b/component/common/src/main/java/io/meeds/spring/kernel/PortalApplicationContext.java new file mode 100644 index 0000000000..6ed620344d --- /dev/null +++ b/component/common/src/main/java/io/meeds/spring/kernel/PortalApplicationContext.java @@ -0,0 +1,200 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.kernel; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.BeanDefinitionStoreException; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.AbstractBeanDefinition; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; + +import org.exoplatform.container.PortalContainer; +import org.exoplatform.container.RootContainer.PortalContainerPostInitTask; +import org.exoplatform.container.spi.ComponentAdapter; + +import io.meeds.spring.kernel.model.SpringBeanComponentAdapter; + +import jakarta.servlet.ServletContext; + +/** + * A class to let Spring context initialized once the + * {@link PortalContainer}finishes startup to bring all defined Kernel Services + * into Spring Context in order to be able to use annotations to inject Kernel + * services + */ +public class PortalApplicationContext extends AnnotationConfigServletWebServerApplicationContext { + + private static final Logger LOG = LoggerFactory.getLogger(PortalApplicationContext.class); + + private ServletContext servletContext; + + public PortalApplicationContext(ServletContext servletContext, DefaultListableBeanFactory beanFactory) { + super(beanFactory); + this.servletContext = servletContext; + } + + @Override + protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) { + BeanDefinitionRegistry beanRegistry = (BeanDefinitionRegistry) beanFactory; + // Declare spring Context for integration on Kernel when it will start + // ( Kernel container is always started after all Spring contexts are + // started, see "PortalContainersCreator" in Meeds-io/meeds project ) + KernelContainerLifecyclePlugin.addSpringContext(servletContext.getServletContextName(), this, beanRegistry); + + // Delay Spring context finishing startup until the Kernel container is + // fully started + PortalContainer.addInitTask(servletContext, new PortalContainerPostInitTask() { + @Override + public void execute(ServletContext context, PortalContainer portalContainer) { + // Register Kernel Components and other Spring contexts in current + // Spring context + LOG.info("Retrieving Kernel component adapters into Spring context '{}' as beans", servletContext.getServletContextName()); + injectKernelComponentsAsBeans(servletContext.getServletContextName(), beanRegistry, portalContainer); + finishSpringContextStartup(beanFactory); + } + }, "portal"); + } + + @Override + protected void finishRefresh() { + // Override to not be invoked in Context startup time + } + + private void finishSpringContextStartup(ConfigurableListableBeanFactory beanFactory) { + long start = System.currentTimeMillis(); + LOG.info("Continue Spring context '{}' initialization", servletContext.getServletContextName()); + PortalApplicationContext.super.finishBeanFactoryInitialization(beanFactory); + PortalApplicationContext.super.finishRefresh(); + LOG.info("Spring context '{}' initialized in {}ms", + servletContext.getServletContextName(), + System.currentTimeMillis() - start); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + private void injectKernelComponentsAsBeans(String servletContextName, + BeanDefinitionRegistry beanRegistry, + PortalContainer portalContainer) { + List beanClasses = portalContainer.getComponentAdapters() + .stream() + .map(adapter -> { + LOG.debug("Attempting to inject Kernel component '{}' with implementation class '{}' as Spring bean in content {}", + adapter.getComponentKey(), + adapter.getComponentImplementation(), + servletContextName); + return adapter; + }) + .filter(adapter -> !(adapter instanceof SpringBeanComponentAdapter springComponentAdapter) + || !springComponentAdapter.isIssuedFrom(servletContext.getServletContextName())) + .map(adapter -> computeComponentBeanName(adapter, portalContainer)) + .filter(Objects::nonNull) + .map(componentKey -> { + LOG.debug("Attempting to inject Kernel component Class '{}' as Spring bean in context {}", + componentKey, + servletContextName); + return componentKey; + }) + .toList(); + // Avoid having two instances inheriting from + // the same API interface to not get NoUniqueBeanDefinitionException + List kernelComponentClasses = new ArrayList<>(); + beanClasses.stream() + .filter(c -> { + Class ec = beanClasses.stream() + .filter(oc -> !oc.equals(c) && oc.isAssignableFrom(c)) + .findFirst() + .orElse(null); + if (ec != null || kernelComponentClasses.contains(c.getName())) { + LOG.debug("Kernel Service '{}' is already defined by other Service with Key {}. Registration will be ignored.", + c.getName(), + ec.getName()); + return false; + } else { + kernelComponentClasses.add(c.getName()); + return true; + } + }) + .forEach(beanClassName -> registerBean(portalContainer, beanRegistry, beanClassName, servletContextName)); + } + + private void registerBean(PortalContainer portalContainer, + BeanDefinitionRegistry beanRegistry, + Class beanClass, + String servletContextName) { + String beanName = beanClass.getName(); + try { + if (beanRegistry.containsBeanDefinition(beanName)) { + LOG.debug("Kernel service '{}' already injected in Spring context '{}', ignore it", beanName, servletContextName); + } else { + RootBeanDefinition beanDefinition = createBeanDefinition(beanClass, portalContainer); + beanRegistry.registerBeanDefinition(beanName, beanDefinition); + LOG.debug("Kernel component '{}' injected in Spring context '{}'", beanName, servletContextName); + } + } catch (BeanDefinitionStoreException e) { + LOG.warn("Kernel component '{}' wasn't injected in Spring context '{}'", beanName, servletContextName, e); + } + } + + private RootBeanDefinition createBeanDefinition(Class keyClass, PortalContainer portalContainer) { + RootBeanDefinition beanDefinition = new RootBeanDefinition(keyClass, + () -> { + T instance = + portalContainer.getComponentInstanceOfType(keyClass); + LOG.trace("Getting Kernel Service '{}' requested by a Spring Bean. Instance was found = {}", + keyClass, + instance != null); + return instance; + }); + beanDefinition.setLazyInit(true); + beanDefinition.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); + return beanDefinition; + } + + @SuppressWarnings("rawtypes") + private Class computeComponentBeanName(ComponentAdapter adapter, PortalContainer portalContainer) { // NOSONAR + Object key = adapter.getComponentKey(); + if (key instanceof Class keyClass) { + return keyClass; + } else { + if (key instanceof String keyString) { + try { + return portalContainer.getPortalClassLoader().loadClass(keyString); + } catch (ClassNotFoundException e) { + Class componentImplementation = adapter.getComponentImplementation(); + LOG.debug("Kernel component with key '{}' class wasn't found. Returning implementation class {}", + key, + componentImplementation == null ? null : componentImplementation.getName()); + return componentImplementation; + } + } else { + LOG.debug("Kernel component key '{}' isn't of type *Class* neither *String*. Returning key class {}", + key, + key.getClass().getName()); + return key.getClass(); + } + } + } + +} diff --git a/component/common/src/main/java/io/meeds/spring/kernel/PortalApplicationContextInitializer.java b/component/common/src/main/java/io/meeds/spring/kernel/PortalApplicationContextInitializer.java new file mode 100644 index 0000000000..dcb073b372 --- /dev/null +++ b/component/common/src/main/java/io/meeds/spring/kernel/PortalApplicationContextInitializer.java @@ -0,0 +1,53 @@ +/* + * This file is part of the Meeds project (https://meeds.io/). + * Copyright (C) 2020 - 2022 Meeds Association contact@meeds.io + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.kernel; + +import org.springframework.beans.factory.support.DefaultListableBeanFactory; +import org.springframework.boot.ApplicationContextFactory; +import org.springframework.boot.builder.SpringApplicationBuilder; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; + +import jakarta.servlet.ServletContext; +import jakarta.servlet.ServletException; + +public abstract class PortalApplicationContextInitializer extends SpringBootServletInitializer { + + private DefaultListableBeanFactory beanFactory; + + private ServletContext servletContext; + + @Override + public void onStartup(ServletContext servletContext) throws ServletException { + // Used to disable LogBack initialization in WebApp context after having + // initialized it already in Meeds Server globally + System.setProperty("org.springframework.boot.logging.LoggingSystem", "none"); + + this.servletContext = servletContext; + this.beanFactory = new DefaultListableBeanFactory(); + super.onStartup(servletContext); + } + + @Override + protected SpringApplicationBuilder createSpringApplicationBuilder() { + return new SpringApplicationBuilder() { + @Override + public SpringApplicationBuilder contextFactory(ApplicationContextFactory factory) { + return super.contextFactory(w -> new PortalApplicationContext(servletContext, beanFactory)); + } + }; + } + +} diff --git a/component/common/src/main/java/io/meeds/spring/kernel/SpringBeanFactoryInterceptor.java b/component/common/src/main/java/io/meeds/spring/kernel/SpringBeanFactoryInterceptor.java index 41f2611b9f..b02d67b07f 100644 --- a/component/common/src/main/java/io/meeds/spring/kernel/SpringBeanFactoryInterceptor.java +++ b/component/common/src/main/java/io/meeds/spring/kernel/SpringBeanFactoryInterceptor.java @@ -18,51 +18,23 @@ */ package io.meeds.spring.kernel; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.context.ApplicationContext; -import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; +import org.springframework.web.context.ServletContextAware; -import org.exoplatform.container.PortalContainer; - -import io.meeds.spring.kernel.utils.KernelIntegration; +import jakarta.servlet.ServletContext; +import lombok.Setter; @Component -public class SpringBeanFactoryInterceptor implements BeanFactoryPostProcessor, BeanPostProcessor, ApplicationContextAware { - - private static final Logger LOG = LoggerFactory.getLogger(SpringBeanFactoryInterceptor.class); - - private PortalContainer container; - - private ApplicationContext applicationContext; - - private BeanDefinitionRegistry beanRegistry; +public class SpringBeanFactoryInterceptor implements BeanPostProcessor, ServletContextAware { - @Override - public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { - this.applicationContext = applicationContext; - } - - @Override - public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - LOG.info("Integrating Spring Context with Container. Application name = {}", applicationContext.getApplicationName()); - this.container = PortalContainer.getInstance(); - this.beanRegistry = (BeanDefinitionRegistry) beanFactory; - KernelIntegration.registerKernelComponentsAsSpringBeans(container, beanRegistry); - } + @Setter + private ServletContext servletContext; @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - KernelIntegration.registerSpringBeanAsKernelComponent(container, - beanRegistry, - beanName, - bean); + KernelContainerLifecyclePlugin.addSpringBean(servletContext.getServletContextName(), beanName, bean); return bean; } diff --git a/component/common/src/main/java/io/meeds/spring/kernel/model/SpringBeanComponentAdapter.java b/component/common/src/main/java/io/meeds/spring/kernel/model/SpringBeanComponentAdapter.java new file mode 100644 index 0000000000..e7b724914f --- /dev/null +++ b/component/common/src/main/java/io/meeds/spring/kernel/model/SpringBeanComponentAdapter.java @@ -0,0 +1,72 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.kernel.model; + +import java.util.function.Supplier; + +import org.apache.commons.lang3.StringUtils; + +import org.exoplatform.container.spi.ComponentAdapter; + +import lombok.Getter; + +public class SpringBeanComponentAdapter implements ComponentAdapter { + + private final String servletContextName; + + @Getter + private final String beanName; + + private final Class componentKey; + + private final Supplier getBeanSupplier; + + public SpringBeanComponentAdapter(String servletContextName, + String beanName, + Class componentKey, + Supplier getBeanSupplier) { + this.servletContextName = servletContextName; + this.beanName = beanName; + this.componentKey = componentKey; + this.getBeanSupplier = getBeanSupplier; + } + + public Object getComponentInstance() { + return getBeanSupplier.get(); + } + + @Override + public Class getComponentImplementation() { + return componentKey; + } + + @Override + public Object getComponentKey() { // NOSONAR + return componentKey; + } + + public boolean isSingleton() { + return true; + } + + public boolean isIssuedFrom(String servletContextName) { + return StringUtils.equals(servletContextName, this.servletContextName); + } + +} diff --git a/component/common/src/main/java/io/meeds/spring/kernel/utils/SharedSpringBeanRegistry.java b/component/common/src/main/java/io/meeds/spring/kernel/utils/SharedSpringBeanRegistry.java deleted file mode 100644 index f0ca63969b..0000000000 --- a/component/common/src/main/java/io/meeds/spring/kernel/utils/SharedSpringBeanRegistry.java +++ /dev/null @@ -1,58 +0,0 @@ -/** - * This file is part of the Meeds project (https://meeds.io/). - * - * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package io.meeds.spring.kernel.utils; - -import java.util.HashMap; -import java.util.Map; - -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; - -public class SharedSpringBeanRegistry { - - private static final Map BEAN_NAMES = new HashMap<>(); - - private static final Map BEAN_DEFINITIONS = new HashMap<>(); - - private SharedSpringBeanRegistry() { - } - - public static void registerBeanDefinition(String beanName, Class beanClass, BeanDefinition beanDefinition) { - BEAN_NAMES.put(beanName, beanClass.getName()); - BEAN_DEFINITIONS.put(beanClass.getName(), beanDefinition); - } - - public static Map getBeanNames() { - return BEAN_NAMES; - } - - public static BeanDefinition getBeanDefinition(String beanName) { - return BEAN_DEFINITIONS.get(beanName); - } - - public static boolean hasBeanDefinition(String beanName) { - return BEAN_DEFINITIONS.containsKey(beanName); - } - - public static void registerSpringBeansState(BeanDefinitionRegistry beanRegistry) { - getBeanNames().forEach((beanName, beanClassName) -> beanRegistry.registerBeanDefinition(beanName, - getBeanDefinition(beanClassName))); - } - -} diff --git a/component/common/src/main/java/io/meeds/spring/liquibase/LiquibaseIntegration.java b/component/common/src/main/java/io/meeds/spring/liquibase/LiquibaseIntegration.java index 2ea735905e..96a90f7474 100644 --- a/component/common/src/main/java/io/meeds/spring/liquibase/LiquibaseIntegration.java +++ b/component/common/src/main/java/io/meeds/spring/liquibase/LiquibaseIntegration.java @@ -17,6 +17,10 @@ */ package io.meeds.spring.liquibase; +import java.sql.SQLException; + +import javax.sql.DataSource; + import org.springframework.boot.autoconfigure.liquibase.LiquibaseProperties; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; @@ -24,25 +28,45 @@ import org.exoplatform.commons.persistence.impl.LiquibaseDataInitializer; import org.exoplatform.container.PortalContainer; +import org.exoplatform.services.log.ExoLogger; +import org.exoplatform.services.log.Log; +import liquibase.database.Database; +import liquibase.database.DatabaseFactory; +import liquibase.database.jvm.JdbcConnection; +import liquibase.exception.DatabaseException; import liquibase.integration.spring.SpringLiquibase; @Configuration @EnableConfigurationProperties(LiquibaseProperties.class) public class LiquibaseIntegration { + private static final Log LOG = ExoLogger.getLogger(LiquibaseIntegration.class); + @Bean public SpringLiquibase liquibase(LiquibaseProperties liquibaseProperties) { LiquibaseDataInitializer liquibaseDataInitializer = PortalContainer.getInstance() .getComponentInstanceOfType(LiquibaseDataInitializer.class); SpringLiquibase liquibase = new SpringLiquibase(); - liquibase.setDataSource(liquibaseDataInitializer.getDatasource()); + DataSource datasource = liquibaseDataInitializer.getDatasource(); + liquibase.setDataSource(datasource); + liquibase.setContexts(liquibaseDataInitializer.getContexts()); liquibase.setChangeLog(liquibaseProperties.getChangeLog()); - liquibase.setContexts(liquibaseProperties.getContexts()); - liquibase.setDefaultSchema(liquibaseProperties.getDefaultSchema()); + liquibase.setDefaultSchema(getSchema(datasource, liquibaseProperties)); liquibase.setDropFirst(liquibaseProperties.isDropFirst()); liquibase.setShouldRun(liquibaseProperties.isEnabled()); return liquibase; } + private String getSchema(DataSource datasource, LiquibaseProperties liquibaseProperties) { + try (Database database = DatabaseFactory.getInstance() + .findCorrectDatabaseImplementation(new JdbcConnection(datasource.getConnection()))) { + return database.getDefaultSchemaName(); + } catch (DatabaseException | SQLException e) { + LOG.warn("Error while retrieving default schema name of datasource, attept to use default schema from settings 'spring.liquibase.default-schema'", + e); + return liquibaseProperties.getDefaultSchema(); + } + } + } diff --git a/component/common/src/main/java/io/meeds/spring/web/security/FilterConfiguration.java b/component/common/src/main/java/io/meeds/spring/web/security/WebSecurityFilterConfiguration.java similarity index 93% rename from component/common/src/main/java/io/meeds/spring/web/security/FilterConfiguration.java rename to component/common/src/main/java/io/meeds/spring/web/security/WebSecurityFilterConfiguration.java index 8e542e7e24..9a18129685 100644 --- a/component/common/src/main/java/io/meeds/spring/web/security/FilterConfiguration.java +++ b/component/common/src/main/java/io/meeds/spring/web/security/WebSecurityFilterConfiguration.java @@ -19,8 +19,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -@Configuration -public class FilterConfiguration { +@Configuration("WebSecurityFilterConfiguration") +public class WebSecurityFilterConfiguration { @Bean public FilterRegistrationBean identityFilter() { diff --git a/component/common/src/main/java/io/meeds/spring/web/transaction/FilterConfiguration.java b/component/common/src/main/java/io/meeds/spring/web/transaction/WebTransactionFilterConfiguration.java similarity index 93% rename from component/common/src/main/java/io/meeds/spring/web/transaction/FilterConfiguration.java rename to component/common/src/main/java/io/meeds/spring/web/transaction/WebTransactionFilterConfiguration.java index f6c77060dd..6f3158e7fc 100644 --- a/component/common/src/main/java/io/meeds/spring/web/transaction/FilterConfiguration.java +++ b/component/common/src/main/java/io/meeds/spring/web/transaction/WebTransactionFilterConfiguration.java @@ -19,8 +19,8 @@ import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; -@Configuration -public class FilterConfiguration { +@Configuration("WebTransactionFilterConfiguration") +public class WebTransactionFilterConfiguration { @Bean public FilterRegistrationBean transactionFilter() { diff --git a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/LiquibaseDataInitializer.java b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/LiquibaseDataInitializer.java index d37908ac6e..417d69d627 100644 --- a/component/common/src/main/java/org/exoplatform/commons/persistence/impl/LiquibaseDataInitializer.java +++ b/component/common/src/main/java/org/exoplatform/commons/persistence/impl/LiquibaseDataInitializer.java @@ -25,6 +25,7 @@ import liquibase.database.Database; import liquibase.database.DatabaseFactory; import liquibase.database.jvm.JdbcConnection; +import liquibase.exception.DatabaseException; import liquibase.exception.LiquibaseException; import liquibase.resource.ClassLoaderResourceAccessor; import liquibase.ui.LoggerUIService; @@ -184,17 +185,23 @@ public void initData(String datasourceName) { // NOSONAR * @param changelogsPath */ protected void applyChangeLog(DataSource datasource, String changelogsPath) { - try (Database database = DatabaseFactory.getInstance() - .findCorrectDatabaseImplementation(new JdbcConnection(datasource.getConnection()))) { - try (Liquibase liquibase = new Liquibase(changelogsPath, new ClassLoaderResourceAccessor(), database)) { - liquibase.update(liquibaseContexts); - } + Database database = null; + try { + database = DatabaseFactory.getInstance() + .findCorrectDatabaseImplementation(new JdbcConnection(datasource.getConnection())); + Liquibase liquibase = new Liquibase(changelogsPath, new ClassLoaderResourceAccessor(), database); + liquibase.update(liquibaseContexts); } catch (SQLException e) { - LOG.error("Error while getting a JDBC connection from datasource {}", datasourceName, e); + LOG.error("Error while getting a JDBC connection from datasource " + datasourceName + " - Cause : " + e.getMessage(), e); } catch (LiquibaseException e) { - // AutoCommit, database connection is auto closed - if (!(e.getCause() instanceof SQLException sqlException && StringUtils.contains(sqlException.getMessage(), "Connection is closed"))) { - LOG.error("Error while applying liquibase changelogs {}", changelogsPath, e); + LOG.error("Error while applying liquibase changelogs " + changelogsPath + " - Cause : " + e.getMessage(), e); + } finally { + if (database != null) { + try { + database.close(); + } catch (DatabaseException e) { + LOG.error("Error while closing database connection - Cause : " + e.getMessage(), e); + } } } } diff --git a/component/common/src/main/resources/conf/portal/configuration.xml b/component/common/src/main/resources/conf/portal/configuration.xml index 95626f1664..ab996b6900 100644 --- a/component/common/src/main/resources/conf/portal/configuration.xml +++ b/component/common/src/main/resources/conf/portal/configuration.xml @@ -3,6 +3,10 @@ xsi:schemaLocation="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd http://www.exoplatform.org/xml/ns/kernel_1_2.xsd" xmlns="http://www.exoplatform.org/xml/ns/kernel_1_2.xsd"> + + io.meeds.spring.kernel.KernelContainerLifecyclePlugin + + org.exoplatform.commons.persistence.impl.EntityManagerService diff --git a/component/common/src/test/java/io/meeds/spring/integration/test/KernelIntegrationTest.java b/component/common/src/test/java/io/meeds/spring/integration/test/KernelIntegrationTest.java index 2fbcc1fc3f..7e770dd428 100644 --- a/component/common/src/test/java/io/meeds/spring/integration/test/KernelIntegrationTest.java +++ b/component/common/src/test/java/io/meeds/spring/integration/test/KernelIntegrationTest.java @@ -36,7 +36,7 @@ @ExtendWith({ SpringExtension.class, KernelExtension.class }) @SpringBootApplication(scanBasePackages = { KernelIntegrationTest.MODULE_NAME, - AvailableIntegration.KERNEL_MODULE, + AvailableIntegration.KERNEL_TEST_MODULE, AvailableIntegration.JPA_MODULE, AvailableIntegration.LIQUIBASE_MODULE, }) diff --git a/component/common/src/test/java/io/meeds/spring/integration/test/SpringIntegrationTest.java b/component/common/src/test/java/io/meeds/spring/integration/test/SpringIntegrationTest.java index b4f0e46db8..7bb3d88785 100644 --- a/component/common/src/test/java/io/meeds/spring/integration/test/SpringIntegrationTest.java +++ b/component/common/src/test/java/io/meeds/spring/integration/test/SpringIntegrationTest.java @@ -40,7 +40,7 @@ @ExtendWith({ SpringExtension.class, KernelExtension.class }) @SpringBootApplication(scanBasePackages = { SpringIntegrationTest.MODULE_NAME, - AvailableIntegration.KERNEL_MODULE, + AvailableIntegration.KERNEL_TEST_MODULE, AvailableIntegration.JPA_MODULE, AvailableIntegration.LIQUIBASE_MODULE, }) diff --git a/component/common/src/main/java/io/meeds/spring/kernel/utils/KernelIntegration.java b/component/common/src/test/java/io/meeds/spring/kernel/test/KernelTestIntegration.java similarity index 57% rename from component/common/src/main/java/io/meeds/spring/kernel/utils/KernelIntegration.java rename to component/common/src/test/java/io/meeds/spring/kernel/test/KernelTestIntegration.java index 8b041f9e2b..3521184464 100644 --- a/component/common/src/main/java/io/meeds/spring/kernel/utils/KernelIntegration.java +++ b/component/common/src/test/java/io/meeds/spring/kernel/test/KernelTestIntegration.java @@ -1,38 +1,15 @@ -/** - * This file is part of the Meeds project (https://meeds.io/). - * - * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package io.meeds.spring.kernel.utils; - -import static io.meeds.spring.kernel.utils.SharedSpringBeanRegistry.hasBeanDefinition; -import static io.meeds.spring.kernel.utils.SharedSpringBeanRegistry.registerSpringBeansState; +package io.meeds.spring.kernel.test; import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.function.Function; -import java.util.function.Supplier; import java.util.stream.Stream; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.slf4j.event.Level; -import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; @@ -41,7 +18,6 @@ import org.springframework.context.annotation.Configuration; import org.springframework.stereotype.Service; -import org.exoplatform.commons.utils.PropertyManager; import org.exoplatform.container.PortalContainer; import org.exoplatform.container.spi.ComponentAdapter; @@ -53,39 +29,17 @@ * into Spring Context in order to be able to use annotations to inject Kernel * services */ -public class KernelIntegration { +public class KernelTestIntegration { - private static final Logger LOG = LoggerFactory.getLogger(KernelIntegration.class); + private static final Logger LOG = LoggerFactory.getLogger(KernelTestIntegration.class); - private static final Set KERNEL_BEAN_NAMES = new HashSet<>(); - - private KernelIntegration() { - } - - public static void integrateSpringContext(PortalContainer portalContainer, - BeanDefinitionRegistry beanRegistry, - Supplier getBeanDefinitionNamesSupplier, - Function getBeanFunction, - Runnable finishSpringContextStartup) { - registerKernelComponentsAsSpringBeans(portalContainer, beanRegistry); - if (finishSpringContextStartup != null) { - LOG.info("Continue Spring context startup with integrated Kernel Services"); - // Continue startup of Spring - finishSpringContextStartup.run(); - // Register Spring Beans in Kernel Container - LOG.info("Inject Spring Beans into Kernel after finishing startup into kernel"); - registerSpringBeansAsKernelComponents(portalContainer, - beanRegistry, - getBeanDefinitionNamesSupplier, - getBeanFunction); - } + private KernelTestIntegration() { } public static void registerKernelComponentsAsSpringBeans(PortalContainer portalContainer, BeanDefinitionRegistry beanRegistry) { LOG.info("Injecting Kernel components into Spring context as beans"); injectKernelComponentsAsBeans(beanRegistry, portalContainer); - registerSpringBeansState(beanRegistry); } public static void registerSpringBeanAsKernelComponent(PortalContainer container, @@ -95,34 +49,20 @@ public static void registerSpringBeanAsKernelComponent(PortalContainer container registerSpringBeansAsKernelComponents(container, beanRegistry, new String[] { beanName }, name -> bean); } - private static void registerSpringBeansAsKernelComponents(PortalContainer portalContainer, - BeanDefinitionRegistry beanRegistry, - Supplier getBeanDefinitionNamesSupplier, - Function getBeanFunction) { - String[] springBeanDefinitionNames = getBeanDefinitionNamesSupplier.get(); - registerSpringBeansAsKernelComponents(portalContainer, beanRegistry, springBeanDefinitionNames, getBeanFunction); - } - private static void registerSpringBeansAsKernelComponents(PortalContainer portalContainer, BeanDefinitionRegistry beanRegistry, String[] beanDefinitionNames, Function getBeanFunction) { Stream.of(beanDefinitionNames) .filter(beanName -> portalContainer.getComponentAdapter(beanName) == null) - .filter(beanName -> !isBeanRegisteredInKernel(beanName)) .forEach(beanName -> { BeanDefinition beanDefinition = getBeanDefinition(beanRegistry, beanName); - if (beanDefinition == null) { - LOG.debug("Bean {} seems to have an empty definition name", beanName); - } else { + if (beanDefinition != null) { String beanClassName = beanDefinition.getBeanClassName(); Object bean = getBeanFunction.apply(beanName); Class beanClass = getBeanClass(portalContainer, beanClassName, bean); if (beanClass != null && portalContainer.getComponentInstanceOfType(beanClass) == null) { - LOG.atLevel(PropertyManager.isDevelopping() ? Level.INFO : Level.DEBUG) - .log("Register Spring Bean '{}' as Kernel service", beanClass.getName()); portalContainer.registerComponentInstance(beanClass, bean); - SharedSpringBeanRegistry.registerBeanDefinition(beanName, beanClass, beanDefinition); } } }); @@ -132,12 +72,12 @@ private static void registerSpringBeansAsKernelComponents(PortalContainer portal private static void injectKernelComponentsAsBeans(BeanDefinitionRegistry beanRegistry, PortalContainer portalContainer) { List beanClasses = portalContainer.getComponentAdapters() .stream() - .filter(adapter -> !hasBeanDefinition(adapter.getComponentKey().toString())) .map(adapter -> beanNameToClass(adapter, portalContainer)) .filter(Objects::nonNull) .distinct() .toList(); + Set kernelBeanNames = new HashSet<>(); beanClasses.stream() // Avoid having two instances inheriting from the same API // interface to not get NoUniqueBeanDefinitionException @@ -146,13 +86,10 @@ private static void injectKernelComponentsAsBeans(BeanDefinitionRegistry beanReg .filter(oc -> !oc.equals(c) && oc.isAssignableFrom(c)) .findFirst() .orElse(null); - if (ec != null || KERNEL_BEAN_NAMES.contains(c.getName())) { - LOG.debug("Kernel Service '{}' is already defined by other Service with Key {}. Registration will be ignored.", - c.getName(), - ec.getName()); + if (ec != null || kernelBeanNames.contains(c.getName())) { return false; } else { - KERNEL_BEAN_NAMES.add(c.getName()); + kernelBeanNames.add(c.getName()); return true; } }) @@ -162,20 +99,12 @@ private static void injectKernelComponentsAsBeans(BeanDefinitionRegistry beanReg private static Class registerBean(PortalContainer portalContainer, BeanDefinitionRegistry beanRegistry, Class beanClass) { RootBeanDefinition beanDefinition = createBeanDefinition(beanClass, portalContainer); beanRegistry.registerBeanDefinition(beanClass.getName(), beanDefinition); - LOG.debug("Kernel service '{}' injected in Spring context", beanClass.getName()); return beanClass; } private static RootBeanDefinition createBeanDefinition(Class keyClass, PortalContainer portalContainer) { RootBeanDefinition beanDefinition = new RootBeanDefinition(keyClass, - () -> { - T instance = - portalContainer.getComponentInstanceOfType(keyClass); - LOG.debug("Getting Kernel Service '{}' to inject in Spring context. Found = {}", - keyClass, - instance != null); - return instance; - }); + () -> portalContainer.getComponentInstanceOfType(keyClass)); beanDefinition.setLazyInit(true); beanDefinition.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); return beanDefinition; @@ -201,9 +130,6 @@ private static Class getBeanClass(PortalContainer portalContainer, return beanClass; } } else { - LOG.debug("Ignore registering Spring Bean '{}/{}' as Kernel service", - beanClassName, - bean.getClass().getName()); return null; } } catch (ClassNotFoundException e) { @@ -214,12 +140,7 @@ private static Class getBeanClass(PortalContainer portalContainer, } private static BeanDefinition getBeanDefinition(BeanDefinitionRegistry beanRegistry, String beanName) { - try { - return beanRegistry.getBeanDefinition(beanName); - } catch (NoSuchBeanDefinitionException e) { - LOG.debug("Can't find Bean with name {}. Ignore adding it into Kernel Container", beanName); - return null; - } + return beanRegistry.containsBeanDefinition(beanName) ? beanRegistry.getBeanDefinition(beanName) : null; } private static boolean isConfigurationBean(Class beanClass) { @@ -259,8 +180,4 @@ private static Class beanNameToClass(ComponentAdapter adapter, PortalContainer p } } - private static boolean isBeanRegisteredInKernel(String beanName) { - return KERNEL_BEAN_NAMES.contains(beanName); - } - } diff --git a/component/common/src/test/java/io/meeds/spring/kernel/test/SpringBeanFactoryInterceptor.java b/component/common/src/test/java/io/meeds/spring/kernel/test/SpringBeanFactoryInterceptor.java new file mode 100644 index 0000000000..53bab934e1 --- /dev/null +++ b/component/common/src/test/java/io/meeds/spring/kernel/test/SpringBeanFactoryInterceptor.java @@ -0,0 +1,67 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.kernel.test; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.config.BeanFactoryPostProcessor; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationContextAware; +import org.springframework.stereotype.Component; + +import org.exoplatform.container.PortalContainer; + +@Component +public class SpringBeanFactoryInterceptor implements BeanFactoryPostProcessor, BeanPostProcessor, ApplicationContextAware { + + private static final Logger LOG = LoggerFactory.getLogger(SpringBeanFactoryInterceptor.class); + + private PortalContainer container; + + private ApplicationContext applicationContext; + + private BeanDefinitionRegistry beanRegistry; + + @Override + public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { + this.applicationContext = applicationContext; + } + + @Override + public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { + LOG.info("Integrating Spring Context with Container. Application name = {}", applicationContext.getApplicationName()); + this.container = PortalContainer.getInstance(); + this.beanRegistry = (BeanDefinitionRegistry) beanFactory; + KernelTestIntegration.registerKernelComponentsAsSpringBeans(container, beanRegistry); + } + + @Override + public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + KernelTestIntegration.registerSpringBeanAsKernelComponent(container, + beanRegistry, + beanName, + bean); + return bean; + } + +} From bc370d072f00716f9408916b5ecc05fcf79cc248 Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Tue, 26 Dec 2023 19:41:09 +0100 Subject: [PATCH 05/14] feat Simplify Spring Context Integration Algorithm - Meeds-io/MIPs#57 --- component/common/pom.xml | 2 +- .../KernelContainerLifecyclePlugin.java | 391 ++++++++++++------ .../kernel/PortalApplicationContext.java | 118 ------ .../kernel/SpringBeanFactoryInterceptor.java | 41 -- .../liquibase/LiquibaseIntegration.java | 5 +- .../src/main/resources/application.properties | 3 +- .../WEB-INF/conf/jdbc/configuration.xml | 4 +- 7 files changed, 263 insertions(+), 301 deletions(-) delete mode 100644 component/common/src/main/java/io/meeds/spring/kernel/SpringBeanFactoryInterceptor.java rename component/{api => common}/src/main/resources/application.properties (96%) diff --git a/component/common/pom.xml b/component/common/pom.xml index 3d8770eb2c..263a22e28d 100644 --- a/component/common/pom.xml +++ b/component/common/pom.xml @@ -28,7 +28,7 @@ GateIn Portal Component Common - 0.38 + 0.37 diff --git a/component/common/src/main/java/io/meeds/spring/kernel/KernelContainerLifecyclePlugin.java b/component/common/src/main/java/io/meeds/spring/kernel/KernelContainerLifecyclePlugin.java index cc58620fdf..6805c15e56 100644 --- a/component/common/src/main/java/io/meeds/spring/kernel/KernelContainerLifecyclePlugin.java +++ b/component/common/src/main/java/io/meeds/spring/kernel/KernelContainerLifecyclePlugin.java @@ -18,20 +18,26 @@ */ package io.meeds.spring.kernel; +import java.util.AbstractMap.SimpleEntry; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Objects; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Supplier; +import java.util.stream.Collectors; import java.util.stream.Stream; -import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; +import org.picocontainer.Startable; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.Configuration; @@ -40,6 +46,10 @@ import org.exoplatform.container.BaseContainerLifecyclePlugin; import org.exoplatform.container.ExoContainer; import org.exoplatform.container.PortalContainer; +import org.exoplatform.container.PropertyConfigurator; +import org.exoplatform.container.spi.ComponentAdapter; +import org.exoplatform.services.rest.impl.StartableApplication; +import org.exoplatform.services.rest.resource.ResourceContainer; import io.meeds.spring.kernel.annotation.Exclude; import io.meeds.spring.kernel.model.SpringBeanComponentAdapter; @@ -53,45 +63,13 @@ public class KernelContainerLifecyclePlugin extends BaseContainerLifecyclePlugin private static Map springContexts = new ConcurrentHashMap<>(); - private static Map> springBeans = new ConcurrentHashMap<>(); - private static boolean kernelAlreadyBooted = false; - @Override - public void initContainer(ExoContainer container) throws Exception { - if (!(container instanceof PortalContainer portalContainer)) { - return; - } - kernelAlreadyBooted = true; // NOSONAR - if (LOG.isDebugEnabled()) { - LOG.debug("Start Spring Bean definitions of contexts [{}] injection into Kernel Container", - StringUtils.join(springContexts.keySet(), ",")); - } - - // Inject Spring contexts included for Integration with Kernel Container - springContexts.forEach((servletContextName, applicationContext) -> { - BeanDefinitionRegistry beanDefinitionRegistry = springBeanRegistries.get(servletContextName); - registerSpringBeanDefinitionAsKernelComponentAdapter(portalContainer, - applicationContext, - beanDefinitionRegistry, - servletContextName); - }); - - // Inject Early retrieved beans - springBeans.forEach((servletContextName, - beanMap) -> beanMap.forEach((beanName, - bean) -> registerSpringBeanDefinitionAsKernelComponentAdapter(portalContainer, - servletContextName, - beanName, - bean))); - - } - public static void addSpringContext(String servletContextName, PortalApplicationContext applicationContext, BeanDefinitionRegistry beanDefinitionRegistry) { if (kernelAlreadyBooted) { - LOG.warn("Adding Spring context '{}' happened too late in Server startup, spring beans of current contexts will not be injected", + LOG.warn("Adding Spring context '{}' happened too late in Server startup, spring beans will not be injected for this context", servletContextName); return; } @@ -100,136 +78,219 @@ public static void addSpringContext(String servletContextName, springBeanRegistries.put(servletContextName, beanDefinitionRegistry); } - public static void addSpringBean(String servletContextName, String beanName, Object bean) { - if (kernelAlreadyBooted) { + @Override + public void initContainer(ExoContainer container) throws Exception { + if (!(container instanceof PortalContainer portalContainer) || springContexts.isEmpty()) { return; } - LOG.info("Add Registered Spring Bean '{}' to be able to inject into Kernel", beanName); - springBeans.computeIfAbsent(servletContextName, key -> new ConcurrentHashMap<>()) - .put(beanName, bean); - } - - private void registerSpringBeanDefinitionAsKernelComponentAdapter(PortalContainer portalContainer, - String servletContextName, - String beanName, - Object bean) { - registerSpringBeanDefinitionAsKernelComponentAdapter(servletContextName, - portalContainer, - beanName, - bean.getClass() - .getName(), - () -> { - LOG.trace("Retrieve Bean with name '{}' from Spring to Kernel using early injected beans", - beanName); - return springContexts.get(servletContextName) - .getBean(beanName); - }); - } - - private void registerSpringBeanDefinitionAsKernelComponentAdapter(PortalContainer portalContainer, - ApplicationContext applicationContext, - BeanDefinitionRegistry beanDefinitionRegistry, - String servletContextName) { - String[] beanDefinitionNames = beanDefinitionRegistry.getBeanDefinitionNames(); - Stream.of(beanDefinitionNames) - .distinct() - .forEach(beanName -> { - BeanDefinition beanDefinition = beanDefinitionRegistry.getBeanDefinition(beanName); - registerSpringBeanDefinitionAsKernelComponentAdapter(servletContextName, - portalContainer, - beanName, - beanDefinition.getBeanClassName(), - () -> { - LOG.trace("Retrieve Bean with name '{}' from Spring to Kernel using Application context", - beanName); - return applicationContext.getBean(beanName); - }); - }); + kernelAlreadyBooted = true; // NOSONAR + if (LOG.isInfoEnabled()) { + LOG.info("Start Spring Bean definitions of contexts [{}] integration with Kernel Container", + StringUtils.join(springContexts.keySet(), ",")); + } + long start = System.currentTimeMillis(); + Collection> kernelComponentAdapters = portalContainer.getComponentAdapters(); + Map> springBeansByContext = getBeansByServletContext(); + LOG.info("1. Add Kernel Services in all Spring contexts"); + addKernelToSpring(portalContainer, kernelComponentAdapters); + LOG.info("2. Add Spring Beans of all contexts into Kernel"); + addSpringToKernel(portalContainer, springBeansByContext); + LOG.info("3. Add Spring Beans into each other context"); + addSpringToEachOther(springBeansByContext); + LOG.info("End spring integration with Kernel Container within {}ms", System.currentTimeMillis() - start); } - @SuppressWarnings("unchecked") - private void registerSpringBeanDefinitionAsKernelComponentAdapter(String servletContextName, - PortalContainer portalContainer, - String beanName, - String beanClassName, - Supplier getBeanFunction) { - if (portalContainer == null || beanClassName == null) { - return; - } - Class componentKey = getComponentKey(portalContainer, beanClassName, beanName); + private void addKernelToSpring(PortalContainer portalContainer, Collection> kernelComponentAdapters) { + kernelComponentAdapters.forEach(adapter -> { + Class componentKey = componentToBeanName(adapter); + springContexts.forEach((servletContextName, applicationContext) -> { + BeanDefinitionRegistry beanRegistry = springBeanRegistries.get(servletContextName); + String componentKeyName = componentKey.getName(); + if (beanRegistry.containsBeanDefinition(componentKeyName)) { + LOG.debug("Ignore Kernel Component '{}' as it's already injected in Spring context '{}'", + componentKeyName, + servletContextName); + } else { + RootBeanDefinition beanDefinition = createBeanDefinition(componentKey, portalContainer); + LOG.debug("Add Kernel Component '{}' in Spring context '{}'", componentKeyName, servletContextName); + beanRegistry.registerBeanDefinition(componentKeyName, beanDefinition); + } + }); + }); + } + + private void addSpringToKernel(PortalContainer portalContainer, Map> springBeansByContext) { + springContexts.forEach((servletContextName, applicationContext) -> { + Map beansMap = springBeansByContext.get(servletContextName); + beansMap.forEach((beanName, beanDefinition) -> { + ComponentAdapter componentAdapter = createComponentAdapter(servletContextName, + portalContainer, + beanName, + beanDefinition.getBeanClassName(), + () -> getBeanInstance(applicationContext, + beanName)); + if (componentAdapter != null) { + LOG.debug("Add Spring Bean '{}' from context '{}' as Kernel component '{}'", + beanName, + servletContextName, + componentAdapter.getComponentKey()); + portalContainer.registerComponentAdapter(componentAdapter); + } else { + LOG.debug("Ignore Spring bean '{}' injector to kernel, with class '{}'", + beanName, + beanDefinition.getBeanClassName()); + } + }); + }); + } + + private void addSpringToEachOther(Map> springBeansByContext) { + springContexts.forEach((senderServletContextName, senderApplicationContext) -> { + Map beansMap = springBeansByContext.get(senderServletContextName); + springBeanRegistries.entrySet() + .stream() + .filter(e -> !StringUtils.equals(senderServletContextName, e.getKey())) + .forEach(entry -> { + String receiverServletContextName = entry.getKey(); + BeanDefinitionRegistry receiverBeanRegistry = entry.getValue(); + beansMap.forEach((beanName, senderBeanDefinition) -> { + Class beanClassName = getClass(senderBeanDefinition.getBeanClassName()); + if (beanClassName != null + && !receiverBeanRegistry.containsBeanDefinition(beanName) + && !receiverBeanRegistry.containsBeanDefinition(senderBeanDefinition.getBeanClassName())) { + RootBeanDefinition receiverBeanDefinition = createBeanDefinition(beanName, + beanClassName, + senderApplicationContext); + receiverBeanRegistry.registerBeanDefinition(beanName, receiverBeanDefinition); + LOG.debug("Add Spring Bean '{}' from Spring Context '{}' to Spring context '{}'", + beanName, + senderServletContextName, + receiverServletContextName); + } + }); + }); + }); + } + + @SuppressWarnings({ "rawtypes", "unchecked" }) + private ComponentAdapter createComponentAdapter(String servletContextName, + PortalContainer portalContainer, + String beanName, + String beanClassName, + Supplier getBeanFunction) { + Class componentKey = beanNameToComponentKey(beanClassName, beanName); if (componentKey != null && portalContainer.getComponentAdapter(componentKey) == null) { - Class[] componentKeyInterfaces = componentKey.getInterfaces(); - if (ArrayUtils.isNotEmpty(componentKeyInterfaces)) { - registerSpringBeanDefinitionAsKernelComponentAdapter(servletContextName, - portalContainer, - beanName, - getBeanFunction, - componentKey); - Arrays.stream(componentKeyInterfaces) - .forEach(componentKeyInterface -> registerSpringBeanDefinitionAsKernelComponentAdapter(servletContextName, - portalContainer, - beanName, - getBeanFunction, - (Class) componentKeyInterface)); - } else { - registerSpringBeanDefinitionAsKernelComponentAdapter(servletContextName, - portalContainer, - beanName, - getBeanFunction, - componentKey); - } + return new SpringBeanComponentAdapter(servletContextName, + beanName, + componentKey, + getBeanFunction); + } else { + return null; } } - private void registerSpringBeanDefinitionAsKernelComponentAdapter(String servletContextName, - PortalContainer portalContainer, - String beanName, - Supplier getBeanFunction, - Class componentKey) { - LOG.debug("Register Spring Bean '{}' issued from context '{}' as Kernel service", componentKey.getName(), servletContextName); - portalContainer.registerComponentAdapter(new SpringBeanComponentAdapter(servletContextName, - beanName, - componentKey, - getBeanFunction)); + private RootBeanDefinition createBeanDefinition(Class keyClass, PortalContainer portalContainer) { + RootBeanDefinition beanDefinition = new RootBeanDefinition(keyClass, + () -> getComponentInstance(portalContainer, keyClass)); + beanDefinition.setLazyInit(true); + beanDefinition.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); + return beanDefinition; + } + + private RootBeanDefinition createBeanDefinition(String beanName, + Class beanClassName, + ApplicationContext senderApplicationContext) { + RootBeanDefinition receiverBeanDefinition = new RootBeanDefinition(beanClassName, + () -> getBeanInstance(senderApplicationContext, + beanName)); + receiverBeanDefinition.setLazyInit(true); + receiverBeanDefinition.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); + return receiverBeanDefinition; + } + + private Map> getBeansByServletContext() { + return springBeanRegistries.entrySet() + .stream() + .collect(Collectors.toMap(Entry::getKey, + e -> getEligibleBeans(e.getValue()))); + } + + private Map getEligibleBeans(BeanDefinitionRegistry beanRegistry) { + String[] beanNames = beanRegistry.getBeanDefinitionNames(); + return Stream.of(beanNames) + .map(beanName -> { + BeanDefinition beanDefinition = + beanRegistry.getBeanDefinition(beanName); + if (isBeanEligible(beanName, beanDefinition.getBeanClassName())) { + return new SimpleEntry<>(beanName, beanDefinition); + } + return null; + }) + .filter(Objects::nonNull) + .collect(Collectors.toMap(Entry::getKey, + Entry::getValue)); + } + + @SuppressWarnings("rawtypes") + private Class componentToBeanName(ComponentAdapter adapter) { // NOSONAR + Object key = adapter.getComponentKey(); + Class componentKeyClass = getComponentKeyClass(key); + if (componentKeyClass == null || !isComponentClassValid(componentKeyClass)) { + Class componentImplementation = adapter.getComponentImplementation(); + LOG.debug("Kernel component with key '{}' class {}. Returning implementation class '{}'", + key, + componentKeyClass == null ? "wasn't found" : "isn't valid", + componentImplementation == null ? null : componentImplementation.getName()); + return componentImplementation; + } else { + return componentKeyClass; + } } - private Class getComponentKey(PortalContainer portalContainer, - String beanClassName, - String beanName) { - Stream componentKeys = Stream.of(beanClassName, beanName); + private Class beanNameToComponentKey(String... beanClassNames) { + Stream componentKeys = Stream.of(beanClassNames); List> componentClasses = componentKeys.filter(Objects::nonNull) - .map(className -> getClass(portalContainer, className)) + .map(this::getClass) .filter(Objects::nonNull) .filter(className -> !Objects.equals(className, Object.class)) .distinct() .toList(); if (componentClasses.isEmpty() // Direct rejection of Services in some cases - || componentClasses.stream().anyMatch(this::isBeanClassRejected)) { + || componentClasses.stream().anyMatch(this::isServiceBeanRejected)) { return null; } else { - return componentClasses.stream() - .filter(this::isServiceBean) - .findFirst() - .orElse(null); + Class beanClassName = componentClasses.stream() + .filter(this::isServiceBean) + .findFirst() + .orElse(null); + Class[] componentKeyInterfaces = beanClassName == null ? null : beanClassName.getInterfaces(); + return componentKeyInterfaces == null ? beanClassName : + Arrays.stream(componentKeyInterfaces) + .filter(this::isServiceBean) + .findFirst() + .orElse(beanClassName); } } - private boolean isBeanClassRejected(Class beanClass) { + private boolean isBeanEligible(String... beanClassNames) { + Stream componentKeys = Stream.of(beanClassNames); + List> componentClasses = componentKeys.filter(Objects::nonNull) + .map(this::getClass) + .filter(Objects::nonNull) + .distinct() + .toList(); + return !componentClasses.isEmpty() + && componentClasses.stream().noneMatch(this::isServiceBeanRejected) + && componentClasses.stream().anyMatch(this::isServiceBean); + } + + private boolean isServiceBeanRejected(Class beanClass) { return beanClass.isAnnotationPresent(Exclude.class) || beanClass.isAnnotationPresent(Configuration.class) || beanClass.isAnnotationPresent(SpringBootApplication.class); } - @SuppressWarnings("unchecked") - private Class getClass(PortalContainer portalContainer, String beanClassName) { - try { - return (Class) portalContainer.getPortalClassLoader().loadClass(beanClassName); - } catch (ClassNotFoundException e) { - return null; - } - } - private boolean isServiceBean(Class beanClass) { return beanClass.isAnnotationPresent(Service.class) && !StringUtils.contains(beanClass.getName(), "org.springframework") @@ -238,4 +299,60 @@ private boolean isServiceBean(Class beanClass) { && !StringUtils.contains(beanClass.getName(), "$"); } + private boolean isComponentClassValid(Class componentClass) { + return !componentClass.equals(Object.class) + && !componentClass.isAssignableFrom(PropertyConfigurator.class) + && !componentClass.equals(Startable.class) + && !componentClass.equals(StartableApplication.class) + && !componentClass.equals(ResourceContainer.class) + && !StringUtils.startsWith(componentClass.getName(), "jakarta") + && !StringUtils.equals(componentClass.getName(), "org.exoplatform.commons.cluster.StartableClusterAware"); + } + + private Object getBeanInstance(ApplicationContext applicationContext, String beanName) { + LOG.trace("Retrieve Bean with name '{}' from Spring to Kernel using Application context", + beanName); + return applicationContext.getBean(beanName); + } + + private T getComponentInstance(PortalContainer portalContainer, Class keyClass) { + LOG.trace("Getting Kernel Service '{}' requested by a Spring Bean.", + keyClass); + try { + T instance = + portalContainer.getComponentInstanceOfType(keyClass); + if (instance == null) { + LOG.trace("Kernel Service '{}' requested by a Spring Bean wasn't", + keyClass); + } + return instance; + } catch (Exception e) { + LOG.warn("Kernel Service '{}' requested by a Spring Bean wasn't found", + keyClass, + e); + return null; + } + } + + private Class getComponentKeyClass(Object key) { + if (key instanceof Class keyClass) { + return keyClass; + } else if (key instanceof String keyString) { + return getClass(keyString); + } else { + LOG.warn("Ignoring Kernel component with key '{}' which isn't of type *Class* neither *String*.", key); + return null; + } + } + + @SuppressWarnings("unchecked") + private Class getClass(String beanClassName) { + try { + // Must be found in shared library + return (Class) this.getClass().getClassLoader().loadClass(beanClassName); + } catch (ClassNotFoundException e) { + return null; + } + } + } diff --git a/component/common/src/main/java/io/meeds/spring/kernel/PortalApplicationContext.java b/component/common/src/main/java/io/meeds/spring/kernel/PortalApplicationContext.java index 6ed620344d..3c61cb6e95 100644 --- a/component/common/src/main/java/io/meeds/spring/kernel/PortalApplicationContext.java +++ b/component/common/src/main/java/io/meeds/spring/kernel/PortalApplicationContext.java @@ -17,25 +17,15 @@ */ package io.meeds.spring.kernel; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; - import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.BeanDefinitionStoreException; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; -import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.DefaultListableBeanFactory; -import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext; import org.exoplatform.container.PortalContainer; import org.exoplatform.container.RootContainer.PortalContainerPostInitTask; -import org.exoplatform.container.spi.ComponentAdapter; - -import io.meeds.spring.kernel.model.SpringBeanComponentAdapter; import jakarta.servlet.ServletContext; @@ -69,10 +59,6 @@ protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory b PortalContainer.addInitTask(servletContext, new PortalContainerPostInitTask() { @Override public void execute(ServletContext context, PortalContainer portalContainer) { - // Register Kernel Components and other Spring contexts in current - // Spring context - LOG.info("Retrieving Kernel component adapters into Spring context '{}' as beans", servletContext.getServletContextName()); - injectKernelComponentsAsBeans(servletContext.getServletContextName(), beanRegistry, portalContainer); finishSpringContextStartup(beanFactory); } }, "portal"); @@ -93,108 +79,4 @@ private void finishSpringContextStartup(ConfigurableListableBeanFactory beanFact System.currentTimeMillis() - start); } - @SuppressWarnings({ "unchecked", "rawtypes" }) - private void injectKernelComponentsAsBeans(String servletContextName, - BeanDefinitionRegistry beanRegistry, - PortalContainer portalContainer) { - List beanClasses = portalContainer.getComponentAdapters() - .stream() - .map(adapter -> { - LOG.debug("Attempting to inject Kernel component '{}' with implementation class '{}' as Spring bean in content {}", - adapter.getComponentKey(), - adapter.getComponentImplementation(), - servletContextName); - return adapter; - }) - .filter(adapter -> !(adapter instanceof SpringBeanComponentAdapter springComponentAdapter) - || !springComponentAdapter.isIssuedFrom(servletContext.getServletContextName())) - .map(adapter -> computeComponentBeanName(adapter, portalContainer)) - .filter(Objects::nonNull) - .map(componentKey -> { - LOG.debug("Attempting to inject Kernel component Class '{}' as Spring bean in context {}", - componentKey, - servletContextName); - return componentKey; - }) - .toList(); - // Avoid having two instances inheriting from - // the same API interface to not get NoUniqueBeanDefinitionException - List kernelComponentClasses = new ArrayList<>(); - beanClasses.stream() - .filter(c -> { - Class ec = beanClasses.stream() - .filter(oc -> !oc.equals(c) && oc.isAssignableFrom(c)) - .findFirst() - .orElse(null); - if (ec != null || kernelComponentClasses.contains(c.getName())) { - LOG.debug("Kernel Service '{}' is already defined by other Service with Key {}. Registration will be ignored.", - c.getName(), - ec.getName()); - return false; - } else { - kernelComponentClasses.add(c.getName()); - return true; - } - }) - .forEach(beanClassName -> registerBean(portalContainer, beanRegistry, beanClassName, servletContextName)); - } - - private void registerBean(PortalContainer portalContainer, - BeanDefinitionRegistry beanRegistry, - Class beanClass, - String servletContextName) { - String beanName = beanClass.getName(); - try { - if (beanRegistry.containsBeanDefinition(beanName)) { - LOG.debug("Kernel service '{}' already injected in Spring context '{}', ignore it", beanName, servletContextName); - } else { - RootBeanDefinition beanDefinition = createBeanDefinition(beanClass, portalContainer); - beanRegistry.registerBeanDefinition(beanName, beanDefinition); - LOG.debug("Kernel component '{}' injected in Spring context '{}'", beanName, servletContextName); - } - } catch (BeanDefinitionStoreException e) { - LOG.warn("Kernel component '{}' wasn't injected in Spring context '{}'", beanName, servletContextName, e); - } - } - - private RootBeanDefinition createBeanDefinition(Class keyClass, PortalContainer portalContainer) { - RootBeanDefinition beanDefinition = new RootBeanDefinition(keyClass, - () -> { - T instance = - portalContainer.getComponentInstanceOfType(keyClass); - LOG.trace("Getting Kernel Service '{}' requested by a Spring Bean. Instance was found = {}", - keyClass, - instance != null); - return instance; - }); - beanDefinition.setLazyInit(true); - beanDefinition.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); - return beanDefinition; - } - - @SuppressWarnings("rawtypes") - private Class computeComponentBeanName(ComponentAdapter adapter, PortalContainer portalContainer) { // NOSONAR - Object key = adapter.getComponentKey(); - if (key instanceof Class keyClass) { - return keyClass; - } else { - if (key instanceof String keyString) { - try { - return portalContainer.getPortalClassLoader().loadClass(keyString); - } catch (ClassNotFoundException e) { - Class componentImplementation = adapter.getComponentImplementation(); - LOG.debug("Kernel component with key '{}' class wasn't found. Returning implementation class {}", - key, - componentImplementation == null ? null : componentImplementation.getName()); - return componentImplementation; - } - } else { - LOG.debug("Kernel component key '{}' isn't of type *Class* neither *String*. Returning key class {}", - key, - key.getClass().getName()); - return key.getClass(); - } - } - } - } diff --git a/component/common/src/main/java/io/meeds/spring/kernel/SpringBeanFactoryInterceptor.java b/component/common/src/main/java/io/meeds/spring/kernel/SpringBeanFactoryInterceptor.java deleted file mode 100644 index b02d67b07f..0000000000 --- a/component/common/src/main/java/io/meeds/spring/kernel/SpringBeanFactoryInterceptor.java +++ /dev/null @@ -1,41 +0,0 @@ -/** - * This file is part of the Meeds project (https://meeds.io/). - * - * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 3 of the License, or (at your option) any later version. - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package io.meeds.spring.kernel; - -import org.springframework.beans.BeansException; -import org.springframework.beans.factory.config.BeanPostProcessor; -import org.springframework.stereotype.Component; -import org.springframework.web.context.ServletContextAware; - -import jakarta.servlet.ServletContext; -import lombok.Setter; - -@Component -public class SpringBeanFactoryInterceptor implements BeanPostProcessor, ServletContextAware { - - @Setter - private ServletContext servletContext; - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - KernelContainerLifecyclePlugin.addSpringBean(servletContext.getServletContextName(), beanName, bean); - return bean; - } - -} diff --git a/component/common/src/main/java/io/meeds/spring/liquibase/LiquibaseIntegration.java b/component/common/src/main/java/io/meeds/spring/liquibase/LiquibaseIntegration.java index 96a90f7474..a95fce1a13 100644 --- a/component/common/src/main/java/io/meeds/spring/liquibase/LiquibaseIntegration.java +++ b/component/common/src/main/java/io/meeds/spring/liquibase/LiquibaseIntegration.java @@ -51,8 +51,11 @@ public SpringLiquibase liquibase(LiquibaseProperties liquibaseProperties) { DataSource datasource = liquibaseDataInitializer.getDatasource(); liquibase.setDataSource(datasource); liquibase.setContexts(liquibaseDataInitializer.getContexts()); + String schema = getSchema(datasource, liquibaseProperties); + liquibase.setDefaultSchema(schema); + liquibase.setLiquibaseSchema(schema); + liquibase.setChangeLog(liquibaseProperties.getChangeLog()); - liquibase.setDefaultSchema(getSchema(datasource, liquibaseProperties)); liquibase.setDropFirst(liquibaseProperties.isDropFirst()); liquibase.setShouldRun(liquibaseProperties.isEnabled()); return liquibase; diff --git a/component/api/src/main/resources/application.properties b/component/common/src/main/resources/application.properties similarity index 96% rename from component/api/src/main/resources/application.properties rename to component/common/src/main/resources/application.properties index 89cad93a91..2253ff5b12 100644 --- a/component/api/src/main/resources/application.properties +++ b/component/common/src/main/resources/application.properties @@ -20,6 +20,7 @@ spring.main.banner-mode=off logging.level.org.springframework=info application.buildNumber=${buildNumber} +spring.jpa.open-in-view=false # Essential to not have collision between Spring Controller DispatcherServlet and Portlet Container Dispatcher spring.mvc.servlet.path=/rest/ -spring.jpa.open-in-view=false +spring.security.ignored=/tomcatgateinservlet diff --git a/web/portal/src/main/webapp/WEB-INF/conf/jdbc/configuration.xml b/web/portal/src/main/webapp/WEB-INF/conf/jdbc/configuration.xml index 8b35625fc8..9341b112b3 100644 --- a/web/portal/src/main/webapp/WEB-INF/conf/jdbc/configuration.xml +++ b/web/portal/src/main/webapp/WEB-INF/conf/jdbc/configuration.xml @@ -67,11 +67,11 @@ org.exoplatform.settings.jpa.dao.SettingsDAO - org.exoplatform.portal.mop.jdbc.dao.ApplicationDAO + org.exoplatform.application.registry.dao.ApplicationDAO org.exoplatform.application.registry.dao.ApplicationDAOImpl - org.exoplatform.portal.mop.jdbc.dao.CategoryDAO + org.exoplatform.application.registry.dao.CategoryDAO org.exoplatform.application.registry.dao.CategoryDAOImpl From f587163109ae0f8bb7eb000ae63f610cf9952063 Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Wed, 27 Dec 2023 20:08:19 +0100 Subject: [PATCH 06/14] fix: Enhance Unit Tests to use same Algorithm of Beans and components injection - Meeds-io/MIPs#57 --- component/common/pom.xml | 2 +- .../KernelContainerLifecyclePlugin.java | 85 ++++---- .../liquibase/LiquibaseIntegration.java | 1 - .../kernel/test/KernelTestIntegration.java | 183 ------------------ .../test/SpringBeanFactoryInterceptor.java | 35 ++-- .../src/test/resources/application.properties | 26 +++ .../meeds/kernel/test/AbstractSpringTest.java | 7 + .../io/meeds/kernel/test/KernelExtension.java | 2 +- 8 files changed, 99 insertions(+), 242 deletions(-) delete mode 100644 component/common/src/test/java/io/meeds/spring/kernel/test/KernelTestIntegration.java create mode 100644 component/common/src/test/resources/application.properties diff --git a/component/common/pom.xml b/component/common/pom.xml index 263a22e28d..ace0453376 100644 --- a/component/common/pom.xml +++ b/component/common/pom.xml @@ -28,7 +28,7 @@ GateIn Portal Component Common - 0.37 + 0.41 diff --git a/component/common/src/main/java/io/meeds/spring/kernel/KernelContainerLifecyclePlugin.java b/component/common/src/main/java/io/meeds/spring/kernel/KernelContainerLifecyclePlugin.java index 6805c15e56..dbc5e652b4 100644 --- a/component/common/src/main/java/io/meeds/spring/kernel/KernelContainerLifecyclePlugin.java +++ b/component/common/src/main/java/io/meeds/spring/kernel/KernelContainerLifecyclePlugin.java @@ -30,6 +30,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.picocontainer.Startable; import org.slf4j.Logger; @@ -66,7 +67,7 @@ public class KernelContainerLifecyclePlugin extends BaseContainerLifecyclePlugin private static boolean kernelAlreadyBooted = false; public static void addSpringContext(String servletContextName, - PortalApplicationContext applicationContext, + ApplicationContext applicationContext, BeanDefinitionRegistry beanDefinitionRegistry) { if (kernelAlreadyBooted) { LOG.warn("Adding Spring context '{}' happened too late in Server startup, spring beans will not be injected for this context", @@ -100,9 +101,12 @@ public void initContainer(ExoContainer container) throws Exception { LOG.info("End spring integration with Kernel Container within {}ms", System.currentTimeMillis() - start); } - private void addKernelToSpring(PortalContainer portalContainer, Collection> kernelComponentAdapters) { + public static void addKernelToSpring(PortalContainer portalContainer, Collection> kernelComponentAdapters) { kernelComponentAdapters.forEach(adapter -> { Class componentKey = componentToBeanName(adapter); + if (!componentKey.isInterface() && isComponentDuplicated(portalContainer, componentKey)) { + return; + } springContexts.forEach((servletContextName, applicationContext) -> { BeanDefinitionRegistry beanRegistry = springBeanRegistries.get(servletContextName); String componentKeyName = componentKey.getName(); @@ -119,7 +123,7 @@ private void addKernelToSpring(PortalContainer portalContainer, Collection> springBeansByContext) { + public static void addSpringToKernel(PortalContainer portalContainer, Map> springBeansByContext) { springContexts.forEach((servletContextName, applicationContext) -> { Map beansMap = springBeansByContext.get(servletContextName); beansMap.forEach((beanName, beanDefinition) -> { @@ -144,7 +148,7 @@ private void addSpringToKernel(PortalContainer portalContainer, Map> springBeansByContext) { + public static void addSpringToEachOther(Map> springBeansByContext) { springContexts.forEach((senderServletContextName, senderApplicationContext) -> { Map beansMap = springBeansByContext.get(senderServletContextName); springBeanRegistries.entrySet() @@ -172,8 +176,15 @@ private void addSpringToEachOther(Map> sprin }); } + public static Map> getBeansByServletContext() { + return springBeanRegistries.entrySet() + .stream() + .collect(Collectors.toMap(Entry::getKey, + e -> getEligibleBeans(e.getValue()))); + } + @SuppressWarnings({ "rawtypes", "unchecked" }) - private ComponentAdapter createComponentAdapter(String servletContextName, + private static ComponentAdapter createComponentAdapter(String servletContextName, PortalContainer portalContainer, String beanName, String beanClassName, @@ -189,7 +200,7 @@ private ComponentAdapter createComponentAdapter(String servletContextNam } } - private RootBeanDefinition createBeanDefinition(Class keyClass, PortalContainer portalContainer) { + private static RootBeanDefinition createBeanDefinition(Class keyClass, PortalContainer portalContainer) { RootBeanDefinition beanDefinition = new RootBeanDefinition(keyClass, () -> getComponentInstance(portalContainer, keyClass)); beanDefinition.setLazyInit(true); @@ -197,7 +208,7 @@ private RootBeanDefinition createBeanDefinition(Class keyClass, PortalCon return beanDefinition; } - private RootBeanDefinition createBeanDefinition(String beanName, + private static RootBeanDefinition createBeanDefinition(String beanName, Class beanClassName, ApplicationContext senderApplicationContext) { RootBeanDefinition receiverBeanDefinition = new RootBeanDefinition(beanClassName, @@ -208,14 +219,7 @@ private RootBeanDefinition createBeanDefinition(String beanName, return receiverBeanDefinition; } - private Map> getBeansByServletContext() { - return springBeanRegistries.entrySet() - .stream() - .collect(Collectors.toMap(Entry::getKey, - e -> getEligibleBeans(e.getValue()))); - } - - private Map getEligibleBeans(BeanDefinitionRegistry beanRegistry) { + private static Map getEligibleBeans(BeanDefinitionRegistry beanRegistry) { String[] beanNames = beanRegistry.getBeanDefinitionNames(); return Stream.of(beanNames) .map(beanName -> { @@ -232,7 +236,7 @@ private Map getEligibleBeans(BeanDefinitionRegistry bean } @SuppressWarnings("rawtypes") - private Class componentToBeanName(ComponentAdapter adapter) { // NOSONAR + private static Class componentToBeanName(ComponentAdapter adapter) { // NOSONAR Object key = adapter.getComponentKey(); Class componentKeyClass = getComponentKeyClass(key); if (componentKeyClass == null || !isComponentClassValid(componentKeyClass)) { @@ -247,51 +251,51 @@ private Class componentToBeanName(ComponentAdapter adapter) { // NOSONAR } } - private Class beanNameToComponentKey(String... beanClassNames) { + private static Class beanNameToComponentKey(String... beanClassNames) { Stream componentKeys = Stream.of(beanClassNames); List> componentClasses = componentKeys.filter(Objects::nonNull) - .map(this::getClass) + .map(KernelContainerLifecyclePlugin::getClass) .filter(Objects::nonNull) .filter(className -> !Objects.equals(className, Object.class)) .distinct() .toList(); if (componentClasses.isEmpty() // Direct rejection of Services in some cases - || componentClasses.stream().anyMatch(this::isServiceBeanRejected)) { + || componentClasses.stream().anyMatch(KernelContainerLifecyclePlugin::isServiceBeanRejected)) { return null; } else { Class beanClassName = componentClasses.stream() - .filter(this::isServiceBean) + .filter(KernelContainerLifecyclePlugin::isServiceBean) .findFirst() .orElse(null); Class[] componentKeyInterfaces = beanClassName == null ? null : beanClassName.getInterfaces(); return componentKeyInterfaces == null ? beanClassName : Arrays.stream(componentKeyInterfaces) - .filter(this::isServiceBean) + .filter(KernelContainerLifecyclePlugin::isServiceBean) .findFirst() .orElse(beanClassName); } } - private boolean isBeanEligible(String... beanClassNames) { + private static boolean isBeanEligible(String... beanClassNames) { Stream componentKeys = Stream.of(beanClassNames); List> componentClasses = componentKeys.filter(Objects::nonNull) - .map(this::getClass) + .map(KernelContainerLifecyclePlugin::getClass) .filter(Objects::nonNull) .distinct() .toList(); return !componentClasses.isEmpty() - && componentClasses.stream().noneMatch(this::isServiceBeanRejected) - && componentClasses.stream().anyMatch(this::isServiceBean); + && componentClasses.stream().noneMatch(KernelContainerLifecyclePlugin::isServiceBeanRejected) + && componentClasses.stream().anyMatch(KernelContainerLifecyclePlugin::isServiceBean); } - private boolean isServiceBeanRejected(Class beanClass) { + private static boolean isServiceBeanRejected(Class beanClass) { return beanClass.isAnnotationPresent(Exclude.class) || beanClass.isAnnotationPresent(Configuration.class) || beanClass.isAnnotationPresent(SpringBootApplication.class); } - private boolean isServiceBean(Class beanClass) { + private static boolean isServiceBean(Class beanClass) { return beanClass.isAnnotationPresent(Service.class) && !StringUtils.contains(beanClass.getName(), "org.springframework") && !StringUtils.startsWith(beanClass.getName(), "java") @@ -299,7 +303,7 @@ private boolean isServiceBean(Class beanClass) { && !StringUtils.contains(beanClass.getName(), "$"); } - private boolean isComponentClassValid(Class componentClass) { + private static boolean isComponentClassValid(Class componentClass) { return !componentClass.equals(Object.class) && !componentClass.isAssignableFrom(PropertyConfigurator.class) && !componentClass.equals(Startable.class) @@ -309,13 +313,13 @@ private boolean isComponentClassValid(Class componentClass) { && !StringUtils.equals(componentClass.getName(), "org.exoplatform.commons.cluster.StartableClusterAware"); } - private Object getBeanInstance(ApplicationContext applicationContext, String beanName) { + private static Object getBeanInstance(ApplicationContext applicationContext, String beanName) { LOG.trace("Retrieve Bean with name '{}' from Spring to Kernel using Application context", beanName); return applicationContext.getBean(beanName); } - private T getComponentInstance(PortalContainer portalContainer, Class keyClass) { + private static T getComponentInstance(PortalContainer portalContainer, Class keyClass) { LOG.trace("Getting Kernel Service '{}' requested by a Spring Bean.", keyClass); try { @@ -334,7 +338,7 @@ private T getComponentInstance(PortalContainer portalContainer, Class key } } - private Class getComponentKeyClass(Object key) { + private static Class getComponentKeyClass(Object key) { if (key instanceof Class keyClass) { return keyClass; } else if (key instanceof String keyString) { @@ -345,11 +349,26 @@ private Class getComponentKeyClass(Object key) { } } + private static boolean isComponentDuplicated(PortalContainer portalContainer, Class componentKey) { + Class[] interfaces = componentKey.getInterfaces(); + return ArrayUtils.isNotEmpty(interfaces) + && Arrays.stream(interfaces) + .anyMatch(interfaceClass -> { + boolean componentIsDuplicated = portalContainer.getComponentAdapter(interfaceClass) != null; + if (componentIsDuplicated) { + LOG.debug("Ignore Kernel Component '{}' as it's already injected in Spring context with class '{}'", + componentKey, + interfaceClass); + } + return componentIsDuplicated; + }); + } + @SuppressWarnings("unchecked") - private Class getClass(String beanClassName) { + private static Class getClass(String beanClassName) { try { // Must be found in shared library - return (Class) this.getClass().getClassLoader().loadClass(beanClassName); + return (Class) KernelContainerLifecyclePlugin.class.getClassLoader().loadClass(beanClassName); } catch (ClassNotFoundException e) { return null; } diff --git a/component/common/src/main/java/io/meeds/spring/liquibase/LiquibaseIntegration.java b/component/common/src/main/java/io/meeds/spring/liquibase/LiquibaseIntegration.java index a95fce1a13..16229ceeb9 100644 --- a/component/common/src/main/java/io/meeds/spring/liquibase/LiquibaseIntegration.java +++ b/component/common/src/main/java/io/meeds/spring/liquibase/LiquibaseIntegration.java @@ -54,7 +54,6 @@ public SpringLiquibase liquibase(LiquibaseProperties liquibaseProperties) { String schema = getSchema(datasource, liquibaseProperties); liquibase.setDefaultSchema(schema); liquibase.setLiquibaseSchema(schema); - liquibase.setChangeLog(liquibaseProperties.getChangeLog()); liquibase.setDropFirst(liquibaseProperties.isDropFirst()); liquibase.setShouldRun(liquibaseProperties.isEnabled()); diff --git a/component/common/src/test/java/io/meeds/spring/kernel/test/KernelTestIntegration.java b/component/common/src/test/java/io/meeds/spring/kernel/test/KernelTestIntegration.java deleted file mode 100644 index 3521184464..0000000000 --- a/component/common/src/test/java/io/meeds/spring/kernel/test/KernelTestIntegration.java +++ /dev/null @@ -1,183 +0,0 @@ -package io.meeds.spring.kernel.test; - -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Set; -import java.util.function.Function; -import java.util.stream.Stream; - -import org.apache.commons.lang3.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.beans.factory.support.AbstractBeanDefinition; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.context.annotation.Configuration; -import org.springframework.stereotype.Service; - -import org.exoplatform.container.PortalContainer; -import org.exoplatform.container.spi.ComponentAdapter; - -import io.meeds.spring.kernel.annotation.Exclude; - -/** - * A class to let Spring context initialized once the - * {@link PortalContainer}finishes startup to bring all defined Kernel Services - * into Spring Context in order to be able to use annotations to inject Kernel - * services - */ -public class KernelTestIntegration { - - private static final Logger LOG = LoggerFactory.getLogger(KernelTestIntegration.class); - - private KernelTestIntegration() { - } - - public static void registerKernelComponentsAsSpringBeans(PortalContainer portalContainer, - BeanDefinitionRegistry beanRegistry) { - LOG.info("Injecting Kernel components into Spring context as beans"); - injectKernelComponentsAsBeans(beanRegistry, portalContainer); - } - - public static void registerSpringBeanAsKernelComponent(PortalContainer container, - BeanDefinitionRegistry beanRegistry, - String beanName, - Object bean) { - registerSpringBeansAsKernelComponents(container, beanRegistry, new String[] { beanName }, name -> bean); - } - - private static void registerSpringBeansAsKernelComponents(PortalContainer portalContainer, - BeanDefinitionRegistry beanRegistry, - String[] beanDefinitionNames, - Function getBeanFunction) { - Stream.of(beanDefinitionNames) - .filter(beanName -> portalContainer.getComponentAdapter(beanName) == null) - .forEach(beanName -> { - BeanDefinition beanDefinition = getBeanDefinition(beanRegistry, beanName); - if (beanDefinition != null) { - String beanClassName = beanDefinition.getBeanClassName(); - Object bean = getBeanFunction.apply(beanName); - Class beanClass = getBeanClass(portalContainer, beanClassName, bean); - if (beanClass != null && portalContainer.getComponentInstanceOfType(beanClass) == null) { - portalContainer.registerComponentInstance(beanClass, bean); - } - } - }); - } - - @SuppressWarnings({ "unchecked", "rawtypes" }) - private static void injectKernelComponentsAsBeans(BeanDefinitionRegistry beanRegistry, PortalContainer portalContainer) { - List beanClasses = portalContainer.getComponentAdapters() - .stream() - .map(adapter -> beanNameToClass(adapter, portalContainer)) - .filter(Objects::nonNull) - .distinct() - .toList(); - - Set kernelBeanNames = new HashSet<>(); - beanClasses.stream() - // Avoid having two instances inheriting from the same API - // interface to not get NoUniqueBeanDefinitionException - .filter(c -> { - Class ec = beanClasses.stream() - .filter(oc -> !oc.equals(c) && oc.isAssignableFrom(c)) - .findFirst() - .orElse(null); - if (ec != null || kernelBeanNames.contains(c.getName())) { - return false; - } else { - kernelBeanNames.add(c.getName()); - return true; - } - }) - .forEach(beanClassName -> registerBean(portalContainer, beanRegistry, beanClassName)); - } - - private static Class registerBean(PortalContainer portalContainer, BeanDefinitionRegistry beanRegistry, Class beanClass) { - RootBeanDefinition beanDefinition = createBeanDefinition(beanClass, portalContainer); - beanRegistry.registerBeanDefinition(beanClass.getName(), beanDefinition); - return beanClass; - } - - private static RootBeanDefinition createBeanDefinition(Class keyClass, PortalContainer portalContainer) { - RootBeanDefinition beanDefinition = new RootBeanDefinition(keyClass, - () -> portalContainer.getComponentInstanceOfType(keyClass)); - beanDefinition.setLazyInit(true); - beanDefinition.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); - return beanDefinition; - } - - private static Class getBeanClass(PortalContainer portalContainer, - String beanClassName, - Object bean) { - try { - if (StringUtils.isBlank(beanClassName) || bean == null) { - return null; - } - Class beanClass = portalContainer.getPortalClassLoader().loadClass(beanClassName); - if (bean.getClass().isAssignableFrom(beanClass) - && (isBeanClassValid(beanClass) || isBeanClassValid(bean.getClass())) - && !isConfigurationBean(beanClass) - && !isConfigurationBean(bean.getClass()) - && (isServiceBean(beanClass) || isServiceBean(bean.getClass())) - && (!isServiceBeanExcluded(beanClass) || !isServiceBeanExcluded(bean.getClass()))) { - if (isServiceBean(bean.getClass())) { - return bean.getClass(); - } else { - return beanClass; - } - } else { - return null; - } - } catch (ClassNotFoundException e) { - LOG.warn("Ignore registering Spring Bean '{}' as Kernel service since it's class is not found in shared ClassLoader", - beanClassName); - return null; - } - } - - private static BeanDefinition getBeanDefinition(BeanDefinitionRegistry beanRegistry, String beanName) { - return beanRegistry.containsBeanDefinition(beanName) ? beanRegistry.getBeanDefinition(beanName) : null; - } - - private static boolean isConfigurationBean(Class beanClass) { - return beanClass.isAnnotationPresent(Configuration.class) || beanClass.isAnnotationPresent(SpringBootApplication.class); - } - - private static boolean isBeanClassValid(Class beanClass) { - return !StringUtils.contains(beanClass.getName(), "org.springframework") - && !StringUtils.startsWith(beanClass.getName(), "java") - && !StringUtils.startsWith(beanClass.getName(), "jdk") - && !StringUtils.contains(beanClass.getName(), "$"); - } - - private static boolean isServiceBean(Class beanClass) { - return beanClass.isAnnotationPresent(Service.class); - } - - private static boolean isServiceBeanExcluded(Class beanClass) { - return beanClass.isAnnotationPresent(Exclude.class); - } - - @SuppressWarnings("rawtypes") - private static Class beanNameToClass(ComponentAdapter adapter, PortalContainer portalContainer) { - Object key = adapter.getComponentKey(); - if (key instanceof Class keyClass) { - return keyClass; - } else { - if (key instanceof String keyString) { - try { - return portalContainer.getPortalClassLoader().loadClass(keyString); - } catch (ClassNotFoundException e) { - return adapter.getComponentImplementation(); - } - } else { - return key.getClass(); - } - } - } - -} diff --git a/component/common/src/test/java/io/meeds/spring/kernel/test/SpringBeanFactoryInterceptor.java b/component/common/src/test/java/io/meeds/spring/kernel/test/SpringBeanFactoryInterceptor.java index 53bab934e1..7928f00604 100644 --- a/component/common/src/test/java/io/meeds/spring/kernel/test/SpringBeanFactoryInterceptor.java +++ b/component/common/src/test/java/io/meeds/spring/kernel/test/SpringBeanFactoryInterceptor.java @@ -18,29 +18,26 @@ */ package io.meeds.spring.kernel.test; +import static io.meeds.kernel.test.AbstractSpringTest.bootContainer; +import static io.meeds.kernel.test.AbstractSpringTest.getTestClass; +import static io.meeds.spring.kernel.KernelContainerLifecyclePlugin.addSpringContext; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeansException; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; -import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; -import org.exoplatform.container.PortalContainer; - @Component -public class SpringBeanFactoryInterceptor implements BeanFactoryPostProcessor, BeanPostProcessor, ApplicationContextAware { - - private static final Logger LOG = LoggerFactory.getLogger(SpringBeanFactoryInterceptor.class); - - private PortalContainer container; +public class SpringBeanFactoryInterceptor implements BeanFactoryPostProcessor, ApplicationContextAware { - private ApplicationContext applicationContext; + private static final Logger LOG = LoggerFactory.getLogger(SpringBeanFactoryInterceptor.class); - private BeanDefinitionRegistry beanRegistry; + private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { @@ -49,19 +46,11 @@ public void setApplicationContext(ApplicationContext applicationContext) throws @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException { - LOG.info("Integrating Spring Context with Container. Application name = {}", applicationContext.getApplicationName()); - this.container = PortalContainer.getInstance(); - this.beanRegistry = (BeanDefinitionRegistry) beanFactory; - KernelTestIntegration.registerKernelComponentsAsSpringBeans(container, beanRegistry); - } - - @Override - public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { - KernelTestIntegration.registerSpringBeanAsKernelComponent(container, - beanRegistry, - beanName, - bean); - return bean; + LOG.info("Integrating Spring Context with Container. Application name = '{}' using Kernel configuration class '{}'", + applicationContext.getApplicationName(), + getTestClass()); + addSpringContext("test", applicationContext, (BeanDefinitionRegistry) beanFactory); + bootContainer(getTestClass()); } } diff --git a/component/common/src/test/resources/application.properties b/component/common/src/test/resources/application.properties new file mode 100644 index 0000000000..6f57bb138c --- /dev/null +++ b/component/common/src/test/resources/application.properties @@ -0,0 +1,26 @@ +# +# This file is part of the Meeds project (https://meeds.io/). +# +# Copyright (C) 2023 Meeds Association contact@meeds.io +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 3 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +spring.main.banner-mode=off +logging.level.org.springframework=off +application.buildNumber=${buildNumber} +spring.jpa.open-in-view=false +# Essential to not have collision between Spring Controller DispatcherServlet and Portlet Container Dispatcher +spring.mvc.servlet.path=/rest/ +spring.security.ignored=/tomcatgateinservlet diff --git a/component/test/core/src/main/java/io/meeds/kernel/test/AbstractSpringTest.java b/component/test/core/src/main/java/io/meeds/kernel/test/AbstractSpringTest.java index 595c3be9cb..c7afe3708d 100644 --- a/component/test/core/src/main/java/io/meeds/kernel/test/AbstractSpringTest.java +++ b/component/test/core/src/main/java/io/meeds/kernel/test/AbstractSpringTest.java @@ -27,6 +27,9 @@ import org.exoplatform.container.PortalContainer; import org.exoplatform.container.component.RequestLifeCycle; +import lombok.Getter; +import lombok.Setter; + @ConfiguredBy({ @ConfigurationUnit(scope = ContainerScope.ROOT, path = "conf/configuration.xml"), @@ -35,6 +38,10 @@ }) public abstract class AbstractSpringTest { + @Setter + @Getter + private static Class testClass; + private static KernelBootstrap bootstrap; public static PortalContainer bootContainer(Class clazz) { diff --git a/component/test/core/src/main/java/io/meeds/kernel/test/KernelExtension.java b/component/test/core/src/main/java/io/meeds/kernel/test/KernelExtension.java index 9ec809ae35..5d0a4247d7 100644 --- a/component/test/core/src/main/java/io/meeds/kernel/test/KernelExtension.java +++ b/component/test/core/src/main/java/io/meeds/kernel/test/KernelExtension.java @@ -26,7 +26,7 @@ public class KernelExtension implements BeforeAllCallback { @Override public void beforeAll(ExtensionContext context) { Class testClass = context.getTestClass().orElseThrow(); - AbstractSpringTest.bootContainer(testClass); + AbstractSpringTest.setTestClass(testClass); } } From 5f82ff9d50bd89f15d20a039e4bffbed9124c09f Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Thu, 28 Dec 2023 06:41:50 +0100 Subject: [PATCH 07/14] fix: Fix Postgres Lisquibase SQL - Meeds-io/MIPs#57 --- .../db/changelog/idm.db.changelog-1.0.0.xml | 22 +++++++++---------- .../portal-rdbms.db.changelog-1.0.0.xml | 3 +-- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/component/identity/src/main/resources/db/changelog/idm.db.changelog-1.0.0.xml b/component/identity/src/main/resources/db/changelog/idm.db.changelog-1.0.0.xml index c3263be506..f93ef7ee98 100644 --- a/component/identity/src/main/resources/db/changelog/idm.db.changelog-1.0.0.xml +++ b/component/identity/src/main/resources/db/changelog/idm.db.changelog-1.0.0.xml @@ -413,17 +413,17 @@ - SELECT setval(‘JBID_IO_ID_SEQ’, (SELECT MAX(ID) FROM JBID_IO)) - SELECT setval(‘JBID_IO_ATTR_ID_SEQ’, (SELECT MAX(ID) FROM JBID_IO_ATTR)) - SELECT setval(‘JBID_ATTR_BIN_VALUE_ID_SEQ’, (SELECT MAX(ID) FROM JBID_ATTR_BIN_VALUE)) - SELECT setval(‘JBID_IO_CREDEN_ID_SEQ’, (SELECT MAX(ID) FROM JBID_IO_CREDEN)) - SELECT setval(‘JBID_CREDEN_BIN_VALUE_ID_SEQ’, (SELECT MAX(ID) FROM JBID_CREDEN_BIN_VALUE)) - SELECT setval(‘JBID_IO_CREDEN_TYPE_ID_SEQ’, (SELECT MAX(ID) FROM JBID_IO_CREDEN_TYPE)) - SELECT setval(‘JBID_IO_REL_ID_SEQ’, (SELECT MAX(ID) FROM JBID_IO_REL)) - SELECT setval(‘JBID_IO_REL_NAME_ID_SEQ’, (SELECT MAX(ID) FROM JBID_IO_REL_NAME)) - SELECT setval(‘JBID_IO_REL_TYPE_ID_SEQ’, (SELECT MAX(ID) FROM JBID_IO_REL_TYPE)) - SELECT setval(‘JBID_IO_TYPE_ID_SEQ’, (SELECT MAX(ID) FROM JBID_IO_TYPE)) - SELECT setval(‘JBID_REALM_ID_SEQ’, (SELECT MAX(ID) FROM JBID_REALM)) + SELECT setval('JBID_IO_ID_SEQ', (SELECT MAX(ID) FROM JBID_IO)) + SELECT setval('JBID_IO_ATTR_ID_SEQ', (SELECT MAX(ATTRIBUTE_ID) FROM JBID_IO_ATTR)) + SELECT setval('JBID_ATTR_BIN_VALUE_ID_SEQ', (SELECT MAX(BIN_VALUE_ID) FROM JBID_ATTR_BIN_VALUE)) + SELECT setval('JBID_IO_CREDEN_ID_SEQ', (SELECT MAX(ID) FROM JBID_IO_CREDEN)) + SELECT setval('JBID_CREDEN_BIN_VALUE_ID_SEQ', (SELECT MAX(BIN_VALUE_ID) FROM JBID_CREDEN_BIN_VALUE)) + SELECT setval('JBID_IO_CREDEN_TYPE_ID_SEQ', (SELECT MAX(ID) FROM JBID_IO_CREDEN_TYPE)) + SELECT setval('JBID_IO_REL_ID_SEQ', (SELECT MAX(ID) FROM JBID_IO_REL)) + SELECT setval('JBID_IO_REL_NAME_ID_SEQ', (SELECT MAX(ID) FROM JBID_IO_REL_NAME)) + SELECT setval('JBID_IO_REL_TYPE_ID_SEQ', (SELECT MAX(ID) FROM JBID_IO_REL_TYPE)) + SELECT setval('JBID_IO_TYPE_ID_SEQ', (SELECT MAX(ID) FROM JBID_IO_TYPE)) + SELECT setval('JBID_REALM_ID_SEQ', (SELECT MAX(ID) FROM JBID_REALM)) diff --git a/component/portal/src/main/resources/db/changelog/portal-rdbms.db.changelog-1.0.0.xml b/component/portal/src/main/resources/db/changelog/portal-rdbms.db.changelog-1.0.0.xml index 84e3281f5f..0478e53a0e 100644 --- a/component/portal/src/main/resources/db/changelog/portal-rdbms.db.changelog-1.0.0.xml +++ b/component/portal/src/main/resources/db/changelog/portal-rdbms.db.changelog-1.0.0.xml @@ -437,8 +437,7 @@ - 8:3192bea7bfda7a7ec61ae9a0badab022 - 8:2a666543dcf9d902f88283141bef8620 + ANY UPDATE PORTAL_SITES set DISPLAYED=1 where NAME LIKE '/spaces/%'; From 747046e9756abe8692091981b8af32316114ae21 Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Fri, 29 Dec 2023 18:49:43 +0100 Subject: [PATCH 08/14] fix: Change application.properties file to avoid generic configuration - Meeds-io/MIPs#57 Prior to this change, the file application.properties was included into a common JAR put into shared library of Tomcat without abuluty to exclude it from a Spring application. This change will rename the file to let include it into a spring context only when needed. --- .../{application.properties => application-common.properties} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename component/common/src/main/resources/{application.properties => application-common.properties} (100%) diff --git a/component/common/src/main/resources/application.properties b/component/common/src/main/resources/application-common.properties similarity index 100% rename from component/common/src/main/resources/application.properties rename to component/common/src/main/resources/application-common.properties From 3d134381958fa02c8857b7714a4928c60f01b599 Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Fri, 29 Dec 2023 18:56:18 +0100 Subject: [PATCH 09/14] feat: Centralize Spring common properties - Meeds-io/MIPs#57 This change will add common properties to disable displaying Spring Banner and let the default logging level on context startup to info. --- .../resources/application-common.properties | 3 --- .../src/main/resources/application.properties | 22 +++++++++++++++++++ 2 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 component/common/src/main/resources/application.properties diff --git a/component/common/src/main/resources/application-common.properties b/component/common/src/main/resources/application-common.properties index 2253ff5b12..d77859f5c7 100644 --- a/component/common/src/main/resources/application-common.properties +++ b/component/common/src/main/resources/application-common.properties @@ -17,9 +17,6 @@ # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # -spring.main.banner-mode=off -logging.level.org.springframework=info -application.buildNumber=${buildNumber} spring.jpa.open-in-view=false # Essential to not have collision between Spring Controller DispatcherServlet and Portlet Container Dispatcher spring.mvc.servlet.path=/rest/ diff --git a/component/common/src/main/resources/application.properties b/component/common/src/main/resources/application.properties new file mode 100644 index 0000000000..ed7df9f671 --- /dev/null +++ b/component/common/src/main/resources/application.properties @@ -0,0 +1,22 @@ +# +# This file is part of the Meeds project (https://meeds.io/). +# +# Copyright (C) 2023 Meeds Association contact@meeds.io +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 3 of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# + +spring.main.banner-mode=off +logging.level.org.springframework=info +application.buildNumber=${buildNumber} From 1e5f10398c4a4afb3a2a632f9d007f0de943a452 Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Fri, 29 Dec 2023 19:06:29 +0100 Subject: [PATCH 10/14] feat: Generate Build Number in Spring Properties - Meeds-io/MIPs#57 --- component/common/pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/component/common/pom.xml b/component/common/pom.xml index ace0453376..5bb386fa92 100644 --- a/component/common/pom.xml +++ b/component/common/pom.xml @@ -60,6 +60,13 @@ + + + + src/main/resources/ + true + + com.jcabi From a00b649ac69e13a525a5afcccc0135ab68a7a86a Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Fri, 29 Dec 2023 20:00:01 +0100 Subject: [PATCH 11/14] fix: Fix Coverage Ratio after Rebase - Meeds-io/MIPs#57 --- component/application-registry/pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/component/application-registry/pom.xml b/component/application-registry/pom.xml index 9308d6970e..3bbcc1bd49 100644 --- a/component/application-registry/pom.xml +++ b/component/application-registry/pom.xml @@ -29,7 +29,7 @@ jar - 0.49 + 0.48 From 1cfc6dc78bd644750845563da1c9aa1e390259ea Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Sat, 30 Dec 2023 14:46:02 +0100 Subject: [PATCH 12/14] feat: Include RootContainer Components in spring Contexts - Meeds-io/MIPs#57 --- .../spring/kernel/KernelContainerLifecyclePlugin.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/component/common/src/main/java/io/meeds/spring/kernel/KernelContainerLifecyclePlugin.java b/component/common/src/main/java/io/meeds/spring/kernel/KernelContainerLifecyclePlugin.java index dbc5e652b4..4f4bf182b3 100644 --- a/component/common/src/main/java/io/meeds/spring/kernel/KernelContainerLifecyclePlugin.java +++ b/component/common/src/main/java/io/meeds/spring/kernel/KernelContainerLifecyclePlugin.java @@ -21,6 +21,7 @@ import java.util.AbstractMap.SimpleEntry; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -30,6 +31,7 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.commons.collections4.CollectionUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.StringUtils; import org.picocontainer.Startable; @@ -90,7 +92,11 @@ public void initContainer(ExoContainer container) throws Exception { StringUtils.join(springContexts.keySet(), ",")); } long start = System.currentTimeMillis(); - Collection> kernelComponentAdapters = portalContainer.getComponentAdapters(); + Collection> containerComponentAdapters = portalContainer.getComponentAdapters(); + Collection> parentComponentAdapters = portalContainer.getParent() + != null ? portalContainer.getParent().getComponentAdapters() : Collections.emptyList(); + Collection> kernelComponentAdapters = CollectionUtils.union(containerComponentAdapters, + parentComponentAdapters); Map> springBeansByContext = getBeansByServletContext(); LOG.info("1. Add Kernel Services in all Spring contexts"); addKernelToSpring(portalContainer, kernelComponentAdapters); From e7e9205c9c9ec89daef9e329294dcb20f1d9e535 Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Sat, 30 Dec 2023 16:52:19 +0100 Subject: [PATCH 13/14] feat: Allow to deleguate Endpoint Security Access to a custom Bean - Meeds-io/MIPs#57 --- .../security/WebSecurityConfiguration.java | 95 ++++++++++++++++--- 1 file changed, 80 insertions(+), 15 deletions(-) diff --git a/component/common/src/main/java/io/meeds/spring/web/security/WebSecurityConfiguration.java b/component/common/src/main/java/io/meeds/spring/web/security/WebSecurityConfiguration.java index 38d089a099..4a0d433e5d 100644 --- a/component/common/src/main/java/io/meeds/spring/web/security/WebSecurityConfiguration.java +++ b/component/common/src/main/java/io/meeds/spring/web/security/WebSecurityConfiguration.java @@ -15,8 +15,16 @@ */ package io.meeds.spring.web.security; +import java.util.function.Supplier; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.security.authorization.AuthorizationDecision; +import org.springframework.security.authorization.AuthorizationManager; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -25,15 +33,29 @@ import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.ContentTypeOptionsConfig; import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.FrameOptionsConfig; import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.XXssConfig; +import org.springframework.security.core.Authentication; import org.springframework.security.config.annotation.web.configurers.JeeConfigurer; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.access.AccessDeniedHandler; +import org.springframework.security.web.access.intercept.RequestAuthorizationContext; +import org.springframework.security.web.util.matcher.RequestMatcher; +import org.springframework.web.context.ServletContextAware; + +import jakarta.servlet.DispatcherType; +import jakarta.servlet.ServletContext; +import lombok.Setter; @Configuration @EnableWebSecurity @EnableMethodSecurity(prePostEnabled = true, securedEnabled = true, jsr250Enabled = true) -public class WebSecurityConfiguration { +public class WebSecurityConfiguration implements ServletContextAware { + + private static final Logger LOG = LoggerFactory.getLogger(WebSecurityConfiguration.class); + + @Setter + private ServletContext servletContext; @Bean public static GrantedAuthorityDefaults grantedAuthorityDefaults() { @@ -41,28 +63,71 @@ public static GrantedAuthorityDefaults grantedAuthorityDefaults() { return new GrantedAuthorityDefaults(); } - @SuppressWarnings("removal") @Bean - public SecurityFilterChain filterChain(HttpSecurity http, PortalAuthenticationManager authenticationProvider) throws Exception { + @SuppressWarnings("removal") + public SecurityFilterChain filterChain(HttpSecurity http, + PortalAuthenticationManager authenticationProvider, + @Qualifier("restRequestMatcher") + RequestMatcher restRequestMatcher, + @Qualifier("staticResourcesRequestMatcher") + RequestMatcher staticResourcesRequestMatcher, + @Qualifier("accessDeniedHandler") + AccessDeniedHandler accessDeniedHandler, + @Qualifier("requestAuthorizationManager") + AuthorizationManager requestAuthorizationManager) throws Exception { return http.authenticationProvider(authenticationProvider) + .jee(JeeConfigurer::and) // NOSONAR no method replacement + .csrf(CsrfConfigurer::disable) + .headers(headers -> { + headers.cacheControl(CacheControlConfig::disable); + headers.frameOptions(FrameOptionsConfig::disable); + headers.xssProtection(XXssConfig::disable); + headers.contentTypeOptions(ContentTypeOptionsConfig::disable); + }) .authorizeHttpRequests(customizer -> { try { - customizer.anyRequest() - .authenticated() - .and() - .jee(JeeConfigurer::and) - .csrf(CsrfConfigurer::disable) - .headers(headers -> { - headers.cacheControl(CacheControlConfig::disable); - headers.frameOptions(FrameOptionsConfig::disable); - headers.xssProtection(XXssConfig::disable); - headers.contentTypeOptions(ContentTypeOptionsConfig::disable); - }); + customizer.requestMatchers(restRequestMatcher) + .access(requestAuthorizationManager); } catch (Exception e) { - throw new IllegalStateException("Unable to configure Security Filter Chain", e); + LOG.error("Error configuring REST endpoints security manager", e); } + customizer.requestMatchers(staticResourcesRequestMatcher) + .permitAll(); + customizer.dispatcherTypeMatchers(DispatcherType.INCLUDE, + DispatcherType.FORWARD) + .permitAll(); }) + .exceptionHandling(exceptionCustomizer -> exceptionCustomizer.accessDeniedHandler(accessDeniedHandler)) .build(); } + @Bean("restRequestMatcher") + public RequestMatcher restRequestMatcher() { + return request -> StringUtils.startsWith(request.getRequestURI(), servletContext.getContextPath() + "/rest/"); + } + + @Bean("staticResourcesRequestMatcher") + public RequestMatcher staticResourcesRequestMatcher() { + return request -> !StringUtils.startsWith(request.getRequestURI(), servletContext.getContextPath() + "/rest/"); + } + + @Bean("accessDeniedHandler") + public AccessDeniedHandler accessDeniedHandler() { + return (request, response, accessDeniedException) -> LOG.warn("Access denied for path {} and method {}", + request.getRequestURI(), + request.getMethod(), + accessDeniedException); + } + + @Bean("requestAuthorizationManager") + public AuthorizationManager requestAuthorizationManager() { + return (Supplier authentication, RequestAuthorizationContext object) -> { + Authentication userAuthentication = authentication.get(); + // Permit anonymous and authentication users to access + // the REST endpoints and rely on jee & secured permission + // management + return userAuthentication.isAuthenticated() ? new AuthorizationDecision(true) : new AuthorizationDecision(false); + }; + } + } From 973adffeeed9e22b056cec5fe401023db1b24277 Mon Sep 17 00:00:00 2001 From: Boubaker Khanfir Date: Sat, 30 Dec 2023 17:11:18 +0100 Subject: [PATCH 14/14] feat: Reuse Quartz configuration from Portal Container - Meeds-io/MIPs#57 --- .../spring/kernel/QuartzConfiguration.java | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 component/common/src/main/java/io/meeds/spring/kernel/QuartzConfiguration.java diff --git a/component/common/src/main/java/io/meeds/spring/kernel/QuartzConfiguration.java b/component/common/src/main/java/io/meeds/spring/kernel/QuartzConfiguration.java new file mode 100644 index 0000000000..961304a62f --- /dev/null +++ b/component/common/src/main/java/io/meeds/spring/kernel/QuartzConfiguration.java @@ -0,0 +1,39 @@ +/** + * This file is part of the Meeds project (https://meeds.io/). + * + * Copyright (C) 2020 - 2023 Meeds Association contact@meeds.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package io.meeds.spring.kernel; + +import org.quartz.Scheduler; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import org.exoplatform.services.scheduler.impl.QuartzSheduler; + +/** + * A configuration setting for Spring in order to reuse the PortalContainer + * Quartz Scheduler instance + */ +@Configuration +public class QuartzConfiguration { + + @Bean + public Scheduler scheduler(QuartzSheduler quartzSheduler) { + return quartzSheduler.getQuartzSheduler(); + } + +}