branch develop-2.x created (now b9faa88)
This is an automated email from the git hooks/post-receive script. New change to branch develop-2.x in repository topia. See http://git.nuiton.org/topia.git at b9faa88 [jgitflow-maven-plugin]updating poms for 2.9.4-SNAPSHOT development This branch includes the following new commits: new 5cae68f Initial Commit new b9faa88 [jgitflow-maven-plugin]updating poms for 2.9.4-SNAPSHOT development The 2 revisions listed above as "new" are entirely new to this repository and will be described in separate emails. The revisions listed as "adds" were already present in the repository and have only been added to this reference. Detailed log of new commits: commit b9faa888ccd8fdf0d1cf7db049a9cf2f6d22b737 Author: Maven Release <maven-release@codelutin.com> Date: Mon Nov 24 15:12:02 2014 +0000 [jgitflow-maven-plugin]updating poms for 2.9.4-SNAPSHOT development commit 5cae68f682e5ecb35f12c388a9124f93ca9a3cb7 Author: Maven Release <maven-release@codelutin.com> Date: Mon Nov 24 15:11:53 2014 +0000 Initial Commit -- To stop receiving notification emails like this one, please contact nuiton.org SCM administrator <admin+scm@nuiton.org>.
This is an automated email from the git hooks/post-receive script. New commit to branch develop-2.x in repository topia. See http://git.nuiton.org/topia.git commit 5cae68f682e5ecb35f12c388a9124f93ca9a3cb7 Author: Maven Release <maven-release@codelutin.com> Date: Mon Nov 24 15:11:53 2014 +0000 Initial Commit --- .gitignore | 12 + LICENSE.txt | 165 ++ README.txt | 2 + changelog.txt | 193 ++ doc/presentation/lutin.jpg | Bin 0 -> 62266 bytes doc/presentation/topia.pdf | Bin 0 -> 132605 bytes doc/presentation/topia.tex | 173 ++ pom.xml | 482 +++++ src/site/rst/index.rst | 70 + src/site/site.xml | 69 + topia-persistence/LICENSE.txt | 165 ++ topia-persistence/README.txt | 2 + topia-persistence/changelog.txt | 67 + topia-persistence/pom.xml | 377 ++++ topia-persistence/src/it/ANOMALIE-1640/LICENSE.txt | 0 topia-persistence/src/it/ANOMALIE-1640/README.txt | 1 + .../src/it/ANOMALIE-1640/changelog.txt | 0 .../src/it/ANOMALIE-1640/invoker.properties | 45 + topia-persistence/src/it/ANOMALIE-1640/pom.xml | 139 ++ .../src/it/ANOMALIE-1640/postbuild.groovy | 25 + .../src/main/xmi/topiatest-absract.properties | 26 + .../src/main/xmi/topiatest-absract.zargo | Bin 0 -> 3855 bytes .../topia/testabstract/TopiaAbstractTest.java | 48 + .../src/test/resources/TopiaContextImpl.properties | 35 + .../src/test/resources/log4j.properties | 29 + topia-persistence/src/it/settings.xml | 88 + .../src/license/THIRD-PARTY.properties | 53 + .../main/java/org/nuiton/topia/TopiaContext.java | 378 ++++ .../java/org/nuiton/topia/TopiaContextFactory.java | 194 ++ .../main/java/org/nuiton/topia/TopiaException.java | 75 + .../org/nuiton/topia/TopiaNotFoundException.java | 74 + .../org/nuiton/topia/TopiaRuntimeException.java | 73 + .../java/org/nuiton/topia/TopiaVetoException.java | 75 + .../nuiton/topia/event/TopiaContextAdapter.java | 59 + .../org/nuiton/topia/event/TopiaContextEvent.java | 57 + .../nuiton/topia/event/TopiaContextListener.java | 85 + .../org/nuiton/topia/event/TopiaEntitiesEvent.java | 58 + .../nuiton/topia/event/TopiaEntitiesVetoable.java | 42 + .../org/nuiton/topia/event/TopiaEntityEvent.java | 65 + .../nuiton/topia/event/TopiaEntityListener.java | 46 + .../nuiton/topia/event/TopiaEntityVetoable.java | 46 + .../nuiton/topia/event/TopiaTransactionEvent.java | 116 ++ .../topia/event/TopiaTransactionListener.java | 71 + .../topia/event/TopiaTransactionVetoable.java | 40 + .../java/org/nuiton/topia/event/package-info.java | 28 + .../org/nuiton/topia/framework/EntityFilter.java | 169 ++ .../org/nuiton/topia/framework/EntityState.java | 155 ++ .../topia/framework/TopiaConnectionProvider.java | 353 ++++ .../nuiton/topia/framework/TopiaContextImpl.java | 1478 +++++++++++++++ .../topia/framework/TopiaContextImplementor.java | 147 ++ .../org/nuiton/topia/framework/TopiaFilter.java | 229 +++ .../nuiton/topia/framework/TopiaFiresSupport.java | 1068 +++++++++++ .../org/nuiton/topia/framework/TopiaQuery.java | 1801 ++++++++++++++++++ .../org/nuiton/topia/framework/TopiaSQLQuery.java | 248 +++ .../org/nuiton/topia/framework/TopiaService.java | 85 + .../topia/framework/TopiaTransactionAware.java | 57 + .../java/org/nuiton/topia/framework/TopiaUtil.java | 523 ++++++ .../org/nuiton/topia/framework/package-info.java | 41 + .../topia/generator/BinderHelperTransformer.java | 273 +++ .../topia/generator/DAOAbstractTransformer.java | 1027 +++++++++++ .../topia/generator/DAOHelperTransformer.java | 560 ++++++ .../nuiton/topia/generator/DAOImplTransformer.java | 126 ++ .../org/nuiton/topia/generator/DAOTransformer.java | 62 + .../org/nuiton/topia/generator/DTOTransformer.java | 461 +++++ .../topia/generator/EntityDAOTransformer.java | 1233 +++++++++++++ .../topia/generator/EntityDTOTransformer.java | 363 ++++ .../generator/EntityHibernateMappingGenerator.java | 764 ++++++++ .../nuiton/topia/generator/EntityTransformer.java | 1634 +++++++++++++++++ .../topia/generator/QueryHelperTransformer.java | 601 ++++++ .../nuiton/topia/generator/ServiceTransformer.java | 773 ++++++++ .../nuiton/topia/generator/TopiaGeneratorUtil.java | 1928 ++++++++++++++++++++ .../nuiton/topia/generator/TopiaJavaValidator.java | 156 ++ .../topia/generator/TopiaMetaTransformer.java | 127 ++ .../topia/generator/TopiaRelationValidator.java | 120 ++ .../nuiton/topia/generator/TopiaStereoTypes.java | 117 ++ .../org/nuiton/topia/generator/TopiaTagValues.java | 501 +++++ .../org/nuiton/topia/generator/package-info.java | 117 ++ .../main/java/org/nuiton/topia/package-info.java | 136 ++ .../topia/persistence/DepthEntityVisitor.java | 148 ++ .../nuiton/topia/persistence/EntityVisitor.java | 103 ++ .../topia/persistence/HorizontalEntityVisitor.java | 143 ++ .../org/nuiton/topia/persistence/SearchFields.java | 76 + .../org/nuiton/topia/persistence/TopiaDAO.java | 688 +++++++ .../topia/persistence/TopiaDAODeprecated.java | 169 ++ .../org/nuiton/topia/persistence/TopiaDAOImpl.java | 925 ++++++++++ .../nuiton/topia/persistence/TopiaDAOLegacy.java | 625 +++++++ .../org/nuiton/topia/persistence/TopiaEntity.java | 215 +++ .../topia/persistence/TopiaEntityAbstract.java | 355 ++++ .../topia/persistence/TopiaEntityContextable.java | 72 + .../nuiton/topia/persistence/TopiaEntityEnum.java | 142 ++ .../java/org/nuiton/topia/persistence/TopiaId.java | 186 ++ .../topia/persistence/TopiaPersistenceHelper.java | 43 + .../topia/persistence/csv/CsvProgressModel.java | 46 + .../topia/persistence/csv/EntityCsvModel.java | 255 +++ .../topia/persistence/csv/TopiaCsvCommons.java | 275 +++ .../persistence/csv/in/AbstractImportModel.java | 56 + .../persistence/csv/in/CsvFileImportResult.java | 111 ++ .../topia/persistence/csv/in/CsvImportResult.java | 118 ++ .../csv/in/EntityAssociationImportModel.java | 75 + .../persistence/csv/in/ImportModelFactory.java | 47 + .../topia/persistence/csv/in/ImportStrategy.java | 89 + .../topia/persistence/csv/in/TopiaCsvImports.java | 590 ++++++ .../topia/persistence/csv/in/package-info.java | 31 + .../csv/out/EntityAssociationExportModel.java | 64 + .../persistence/csv/out/ExportEntityVisitor.java | 212 +++ .../persistence/csv/out/ExportModelFactory.java | 43 + .../persistence/csv/out/PrepareDataForExport.java | 42 + .../topia/persistence/csv/out/TopiaCsvExports.java | 406 +++++ .../topia/persistence/csv/out/package-info.java | 31 + .../nuiton/topia/persistence/csv/package-info.java | 31 + .../persistence/metadata/AssociationMeta.java | 146 ++ .../topia/persistence/metadata/ColumnMeta.java | 94 + .../nuiton/topia/persistence/metadata/DbMeta.java | 130 ++ .../persistence/metadata/MetaFilenameAware.java | 51 + .../topia/persistence/metadata/Metadatas.java | 64 + .../topia/persistence/metadata/TableMeta.java | 257 +++ .../topia/persistence/metadata/package-info.java | 31 + .../org/nuiton/topia/persistence/package-info.java | 37 + .../nuiton/topia/persistence/pager/FilterRule.java | 62 + .../persistence/pager/FilterRuleGroupOperator.java | 35 + .../persistence/pager/FilterRuleOperator.java | 176 ++ .../topia/persistence/pager/TopiaPagerBean.java | 88 + .../persistence/pager/TopiaPagerBeanBuilder.java | 102 ++ .../nuiton/topia/persistence/util/Collector.java | 273 +++ .../org/nuiton/topia/persistence/util/Creator.java | 56 + .../nuiton/topia/persistence/util/DBMapping.java | 451 +++++ .../org/nuiton/topia/persistence/util/Deletor.java | 47 + .../nuiton/topia/persistence/util/DiffState.java | 136 ++ .../topia/persistence/util/EntityListUpdator.java | 157 ++ .../topia/persistence/util/EntityOperator.java | 797 ++++++++ .../persistence/util/EntityOperatorStore.java | 90 + .../nuiton/topia/persistence/util/ListUpdator.java | 116 ++ .../org/nuiton/topia/persistence/util/Loador.java | 73 + .../topia/persistence/util/TopiaEntityBinder.java | 83 + .../topia/persistence/util/TopiaEntityHelper.java | 1210 ++++++++++++ .../topia/persistence/util/TopiaEntityIdsMap.java | 53 + .../topia/persistence/util/TopiaEntityMap.java | 112 ++ .../topia/persistence/util/TopiaEntityRef.java | 101 + .../argouml.org/profiles/uml14/default-java.xmi | 78 + .../argouml.org/profiles/uml14/default-uml14.xmi | 196 ++ ....models.stereotype.StereotypeDefinitionProvider | 1 + ...gene.models.tagvalue.TagValueDefinitionProvider | 1 + .../i18n/topia-persistence_en_GB.properties | 33 + .../i18n/topia-persistence_es_ES.properties | 33 + .../i18n/topia-persistence_fr_FR.properties | 33 + .../src/site/resources/ClassDiagram_BookAuthor.png | Bin 0 -> 3487 bytes .../ClassDiagram_BookAuthorWithOperation.png | Bin 0 -> 3856 bytes .../src/site/resources/ContactUseCases.png | Bin 0 -> 11196 bytes .../src/site/resources/ServiceCall.png | Bin 0 -> 36877 bytes .../src/site/resources/modelForTopiaQuery.png | Bin 0 -> 2836 bytes .../src/site/resources/modelForTopiaQuery.zargo | Bin 0 -> 4742 bytes topia-persistence/src/site/resources/topia.zargo | Bin 0 -> 11789 bytes .../src/site/rst/TopiaDocumentation.rst | 68 + topia-persistence/src/site/rst/devel/Devel.rst | 171 ++ .../src/site/rst/devel/HibernateMapping.rst | 120 ++ topia-persistence/src/site/rst/devel/Isolation.rst | 92 + .../src/site/rst/devel/SchemaMigration.rst | 177 ++ topia-persistence/src/site/rst/devel/Todo.rst | 255 +++ topia-persistence/src/site/rst/devel/event.rst | 92 + topia-persistence/src/site/rst/devel/project.rst | 35 + topia-persistence/src/site/rst/devel/security.rst | 52 + topia-persistence/src/site/rst/index.rst | 113 ++ topia-persistence/src/site/rst/user/FAQ.rst | 60 + .../src/site/rst/user/ModelGeneration.rst.vm | 102 ++ topia-persistence/src/site/rst/user/TopiaQuery.rst | 543 ++++++ .../src/site/rst/user/continue_devel.rst | 56 + .../src/site/rst/user/extend_model.rst | 141 ++ topia-persistence/src/site/rst/user/howto.rst.vm | 156 ++ topia-persistence/src/site/rst/user/start.rst.vm | 247 +++ .../src/site/rst/user/start_using_api.rst | 196 ++ topia-persistence/src/site/rst/user/tagvalues.rst | 81 + topia-persistence/src/site/site.xml | 92 + .../src/test/java/org/nuiton/topia/TestHelper.java | 159 ++ .../org/nuiton/topia/TopiaContextFactoryTest.java | 188 ++ .../test/java/org/nuiton/topia/TopiaDatabase.java | 191 ++ .../nuiton/topia/framework/EntityStateTest.java | 95 + .../TopiaConnectionProviderHardCoded.java | 266 +++ .../framework/TopiaConnectionProviderTest.java | 139 ++ .../topia/framework/TopiaContextImplTest.java | 551 ++++++ .../topia/framework/TopiaContextReplicateTest.java | 156 ++ .../org/nuiton/topia/framework/TopiaQueryTest.java | 188 ++ .../org/nuiton/topia/framework/TopiaUtilTest.java | 115 ++ .../generator/QueryHelperTransformerTest.java | 67 + .../topia/generator/TopiaGeneratorUtilTest.java | 63 + .../org/nuiton/topia/generator/TopiaTestCase.java | 169 ++ .../persistence/EntityVisitorExportXmlTest.java | 131 ++ .../nuiton/topia/persistence/ExportXMLVisitor.java | 106 ++ .../nuiton/topia/persistence/NaturalIdTest.java | 177 ++ .../topia/persistence/TopiaContextFindTest.java | 162 ++ .../org/nuiton/topia/persistence/TopiaDAOTest.java | 201 ++ .../topia/persistence/util/CollectorTest.java | 123 ++ .../topia/persistence/util/EntityOperatorTest.java | 237 +++ .../persistence/util/TopiaEntityBinderTest.java | 104 ++ .../persistence/util/TopiaEntityHelperTest.java | 278 +++ .../persistence/util/TopiaEntityRefTester.java | 330 ++++ .../persistence/util/TopiaEntityRefTesterTest.java | 154 ++ .../test/ano1882/DAOAbstractTransformerTest.java | 56 + .../nuiton/topia/test/ano1991/TopiaQueryTest.java | 130 ++ .../test/evo1912/EntityDTOTransformerTest.java | 45 + .../test/java/org/nuiton/topiatest/EnumTest.java | 74 + .../nuiton/topiatest/ExtraDAOEntityDAOImpl.java | 39 + .../src/test/java/org/nuiton/topiatest/Gender.java | 28 + .../src/test/java/org/nuiton/topiatest/Title.java | 36 + .../org/nuiton/topiatest/beangen/RoueImpl.java | 52 + .../topiatest/deletetest/Contact2DAOImpl.java | 49 + .../topiatest/deletetest/DeleteEntityTest.java | 178 ++ .../org/nuiton/topiatest/persistence/Entity1.java | 48 + .../topiatest/persistence/Entity1Abstract.java | 82 + .../topiatest/persistence/Entity1Impl.hbm.xml | 36 + .../nuiton/topiatest/persistence/Entity1Impl.java | 36 + .../nuiton/topiatest/persistence/package-info.java | 27 + .../org/nuiton/topiatest/service/FakeService.java | 63 + .../org/nuiton/topiatest/service/TestService.java | 66 + .../org/nuiton/topiatest/service/package-info.java | 28 + .../TopiaConnectionProviderHardcoded.properties | 34 + .../src/test/resources/TopiaContextImpl.properties | 33 + .../src/test/resources/log4j.properties | 35 + .../src/test/xmi/topiatest.properties | 43 + topia-persistence/src/test/xmi/topiatest.zargo | Bin 0 -> 25448 bytes topia-service-migration/LICENSE.txt | 165 ++ topia-service-migration/README.txt | 2 + topia-service-migration/changelog.txt | 63 + topia-service-migration/pom.xml | 131 ++ .../src/license/THIRD-PARTY.properties | 49 + .../migration/AbstractTopiaMigrationCallback.java | 225 +++ .../topia/migration/MigrationServiceException.java | 75 + .../topia/migration/TopiaMigrationCallback.java | 58 + .../migration/TopiaMigrationCallbackByClass.java | 162 ++ .../migration/TopiaMigrationCallbackByClassNG.java | 191 ++ .../migration/TopiaMigrationCallbackByMethod.java | 76 + .../topia/migration/TopiaMigrationEngine.java | 728 ++++++++ .../topia/migration/TopiaMigrationService.java | 60 + .../topia/migration/mappings/TMSVersion.java | 81 + .../topia/migration/mappings/TMSVersionDAO.java | 193 ++ .../i18n/topia-service-migration_en_GB.properties | 14 + .../i18n/topia-service-migration_es_ES.properties | 14 + .../i18n/topia-service-migration_fr_FR.properties | 14 + .../topia/migration/mappings/TMSVersion.hbm.xml | 31 + .../src/main/xmi/MigrationService.zargo | Bin 0 -> 3813 bytes topia-service-migration/src/site/rst/index.rst | 235 +++ .../src/site/rst/manualMigration.rst | 215 +++ topia-service-migration/src/site/site.xml | 61 + .../ConfigurationAdapterTest-hibernate.cfg.xml | 42 + topia-service-replication/LICENSE.txt | 165 ++ topia-service-replication/README.txt | 2 + topia-service-replication/changelog.txt | 2 + topia-service-replication/pom.xml | 199 ++ .../src/license/THIRD-PARTY.properties | 49 + .../topia/replication/TopiaReplicationContext.java | 276 +++ .../replication/TopiaReplicationModelBuilder.java | 303 +++ .../replication/TopiaReplicationOperation.java | 101 + .../TopiaReplicationOperationProvider.java | 127 ++ .../TopiaReplicationOperationUndoable.java | 55 + .../topia/replication/TopiaReplicationService.java | 125 ++ .../replication/TopiaReplicationServiceImpl.java | 329 ++++ .../topia/replication/model/ReplicationLink.java | 116 ++ .../topia/replication/model/ReplicationModel.java | 669 +++++++ .../topia/replication/model/ReplicationNode.java | 267 +++ .../replication/model/ReplicationOperationDef.java | 131 ++ .../model/ReplicationOperationPhase.java | 49 + .../replication/operation/AttachAssociation.java | 267 +++ .../topia/replication/operation/AttachLink.java | 244 +++ .../replication/operation/DettachAssociation.java | 95 + .../topia/replication/operation/Duplicate.java | 128 ++ .../topia/replication/operation/LoadLink.java | 108 ++ ...ton.topia.replication.TopiaReplicationOperation | 8 + .../topia-service-replication_en_GB.properties | 5 + .../topia-service-replication_es_ES.properties | 5 + .../topia-service-replication_fr_FR.properties | 5 + .../src/site/resources/dependencies.html | 1434 +++++++++++++++ .../src/site/resources/dependency-management.html | 511 ++++++ topia-service-replication/src/site/rst/index.rst | 31 + topia-service-replication/src/site/site.xml | 58 + .../AbstractTopiaReplicationServiceTest.java | 694 +++++++ .../replication/TopiaReplicationOperationTest.java | 367 ++++ .../TopiaReplicationServiceImplAllTest.java | 306 ++++ .../TopiaReplicationServiceImplTest.java | 458 +++++ .../topia/replication/operation/FakeOperation.java | 76 + .../operation/UncreatableOperation.java | 67 + .../operation/UnregistredOperation.java | 64 + ...ton.topia.replication.TopiaReplicationOperation | 6 + .../src/test/resources/log4j.properties | 35 + topia-service-security/LICENSE.txt | 165 ++ topia-service-security/README.txt | 2 + topia-service-security/changelog.txt | 48 + topia-service-security/pom.xml | 165 ++ .../src/license/THIRD-PARTY.properties | 49 + .../topia/security/TopiaSecurityService.java | 54 + .../topia/security/TopiaSecurityServiceImpl.java | 504 +++++ .../TopiaAssociationAuthorizationImpl.java | 88 + .../authorization/TopiaAuthorizationImpl.java | 116 ++ .../TopiaEntityAuthorizationImpl.java | 62 + .../authorization/TopiaExpressionLinkImpl.java | 52 + .../security/entities/user/TopiaGroupImpl.java | 74 + .../security/entities/user/TopiaUserImpl.java | 86 + .../topia/security/jaas/TopiaCallbackHandler.java | 85 + .../topia/security/jaas/TopiaConfiguration.java | 159 ++ .../topia/security/jaas/TopiaLoginModule.java | 218 +++ .../topia/security/jaas/TopiaPermission.java | 112 ++ .../nuiton/topia/security/jaas/TopiaPolicy.java | 172 ++ .../nuiton/topia/security/jaas/TopiaPrincipal.java | 83 + .../topia/security/listener/EntityVetoable.java | 125 ++ .../topia/security/listener/NoSecurityLoad.java | 33 + .../security/listener/PropertyReadListener.java | 79 + .../topia/security/listener/PropertyVetoable.java | 105 ++ .../security/listener/PropertyWriteListener.java | 68 + .../topia/security/util/TopiaSecurityCaching.java | 132 ++ .../security/util/TopiaSecurityFactoryFilter.java | 114 ++ .../topia/security/util/TopiaSecurityUtil.java | 258 +++ .../java/org/nuiton/topia/taas/TaasService.java | 468 +++++ .../main/java/org/nuiton/topia/taas/TaasUtil.java | 241 +++ .../nuiton/topia/taas/event/TaasAccessEntity.java | 32 + .../topia/taas/event/TaasEntityVetoable.java | 136 ++ .../event/TaasEntityVetoableRequestPermission.java | 123 ++ .../topia/taas/jaas/TaasCallbackHandler.java | 85 + .../nuiton/topia/taas/jaas/TaasConfiguration.java | 161 ++ .../nuiton/topia/taas/jaas/TaasLoginModule.java | 244 +++ .../org/nuiton/topia/taas/jaas/TaasPermission.java | 202 ++ .../org/nuiton/topia/taas/jaas/TaasPolicy.java | 174 ++ .../topia/taas/jaas/TaasPrincipalWrapper.java | 111 ++ .../nuiton/topia/taas/jaas/TaasSubjectFinder.java | 51 + .../topia/taas/jaas/TaasSubjectFinderImpl.java | 58 + .../TopiaAssociationAuthorizationImpl.hbm.xml | 35 + .../authorization/TopiaAuthorizationImpl.hbm.xml | 33 + .../TopiaEntityAuthorizationImpl.hbm.xml | 34 + .../authorization/TopiaExpressionLinkImpl.hbm.xml | 35 + .../security/entities/user/TopiaGroupImpl.hbm.xml | 44 + .../security/entities/user/TopiaUserImpl.hbm.xml | 42 + .../taas/entities/TaasAuthorizationImpl.hbm.xml | 35 + .../topia/taas/entities/TaasPrincipalImpl.hbm.xml | 38 + .../topia/taas/entities/TaasUserImpl.hbm.xml | 44 + .../TopiaAssociationAuthorizationImpl.hbm.xml | 35 + .../authorization/TopiaAuthorizationImpl.hbm.xml | 33 + .../TopiaEntityAuthorizationImpl.hbm.xml | 34 + .../authorization/TopiaExpressionLinkImpl.hbm.xml | 35 + .../security/entities/user/TopiaGroupImpl.hbm.xml | 44 + .../security/entities/user/TopiaUserImpl.hbm.xml | 42 + .../taas/entities/TaasAuthorizationImpl.hbm.xml | 35 + .../topia/taas/entities/TaasPrincipalImpl.hbm.xml | 38 + .../topia/taas/entities/TaasUserImpl.hbm.xml | 44 + .../TopiaAssociationAuthorizationImpl.hbm.xml | 35 + .../authorization/TopiaAuthorizationImpl.hbm.xml | 33 + .../TopiaEntityAuthorizationImpl.hbm.xml | 34 + .../authorization/TopiaExpressionLinkImpl.hbm.xml | 35 + .../security/entities/user/TopiaGroupImpl.hbm.xml | 44 + .../security/entities/user/TopiaUserImpl.hbm.xml | 42 + .../taas/entities/TaasAuthorizationImpl.hbm.xml | 35 + .../topia/taas/entities/TaasPrincipalImpl.hbm.xml | 38 + .../topia/taas/entities/TaasUserImpl.hbm.xml | 44 + .../src/main/xmi/TopiaSecurity.properties | 28 + .../src/main/xmi/TopiaSecurity.zargo | Bin 0 -> 14782 bytes topia-service-security/src/site/rst/index.rst | 30 + topia-service-security/src/site/site.xml | 65 + .../src/test/java/org/nuiton/topia/TestUtils.java | 92 + .../nuiton/topia/security/TopiaSecurityTest.java | 456 +++++ 355 files changed, 59320 insertions(+) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d14ee6f --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +/target +/*/target +/*.ipr +/*/*.ipr +/.idea +/*.iws +/*/*.iws +/*.iml +/*/*.iml +/*~ +/*/*~ + diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..cca7fc2 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..d2e50d3 --- /dev/null +++ b/README.txt @@ -0,0 +1,2 @@ +To deploy new version of pom: mvn deploy +To install localy: mvn install diff --git a/changelog.txt b/changelog.txt new file mode 100644 index 0000000..f261c48 --- /dev/null +++ b/changelog.txt @@ -0,0 +1,193 @@ + +Since 2.2.2 changelog is no more maintained + +see nuiton.org forge for changes : http://nuiton.org/projects/show/topia + +-------------------------------------------------------------------------------- + +2.2.1 chemit 20090903 + + * [FIX #38] manual migration service does not work on windows OS + * [FEATURE #40] clean manual migration service + * [FEATURE #42] add i18n in migration service + +-- chemit -- Thu, 03 Sep 2009 22:50:14 +0200 + +2.2.0 + * migrate to nuiton + +2.1.6 chemit 20090511 + * bump versions (lutinproject, lutinutil, lutinprocessor, maven-license-switcher-plugin) + * use doxia-modules-jrst instead of maven-jrst-plugin + * improve download section on site + +2.1.5 chemit 20090420 + * use lutinproject 3.5.3, doxia-module-jrst 1.0.0, maven-license-switcher 0.7, xom 1.1 + * fix site (reports has disappeared) + +2.1.3 chemit 20090220 + * 20090126 [chemit] - refactor poms (all dependencies in parent-pom dependencyManagment) + * 20090129 [chemit] - use lutinproject 3.4 + +2.1.2 chemit 20090115 + * 20090114 [chemit] - using lutinproject 3.3 + +2.1.1 chemit 20081215 + * 20081215 [chemit] - new release for isis-fish :) + +2.1.0 chemit 20081210 + * 20081205 [chemit] - improve poms, use lutinproject 3.2 + - use JUnit4 for test, fix some tests (but not all). + - add some usefull method to build regex on topiaIds + * 20081118 Switch to multi module project + +ver-2-0-29 chatellier 20081117 + * 20081117 [chemit] improve EntityEnum + * 20081117 [chemit] add method findAllWithOrder in DAO to make have sorted lists + +ver-2-0-28 chatellier 20081114 + * 20081113 [chorlet] add support for lazy loading on attribute with 0..* multiplicity + * 20081113 [chorlet] fix bug on bidirectional association by adding inverse attribute in the one side of two hibernate mapping files + * 20081107 [chatellier] fix "result" named attribute generation in model + * 20081107 [chatellier] fix ant:ant, org.apache.ant:ant conflict + * 20081107 [chatellier] set lutingenerator provided scope + * 20081101 [chemit] bump lutingenerator to 0.61 + * 20081101 [chemit] add a EntityEnum to have a generic way to access Entities. + * 20081101 [chemit] improve generators : + - can exclude some generators on TopiaMetaGenerator + - refactor TopiaMetaGenerator to use a simple List of Generator to launch + - refactor Generators to have a default public constructor + + * 20081029 [chemit] fix infinitive recursion on method findByPrimaryKey and findByProperty in TopiaDAODeleagtor class + * 20081026 [chemit] add dbName of table if explicit in javadoc + * 20081024 [chemit] fix bug when no entity defined in model + +ver-2-0-27 chemit 20081021 + * 20081021 [chemit] clean pom, use lutingenerator 0.60 + * 20080922 [thimel] Switched to lutinproject 3.0 + * 20080922 [thimel] Sources are correctly maven2 structuted (src/main/xxx) + * 20080922 [thimel] Fixed maven-processor-plugin usage + * 20080922 [thimel] License switched to LGPL + +ver-2-0-26 thimel 20080922 + * 20080922 [thimel] Last version with lutinproject 2.2 + * 20082608 [chemit] permettre de recuperer uniquement une fenetre de resultat en hql (TopiaContext#find(String hql,int startIndex,int endIndex, Object ... args) + * 20082907 [chemit] Suppression des dependances en dur sur les implantations d'entites + * 20072012 [thimel] Support des index sur les attibuts + * 20072012 [ruchaud] Récupération des classes persistées + * 20072012 [ruchaud] Création d'un vetoable sur les finds + +ver-2-0-25 poussin 20071214 + + * 20071120 [chatellier] modify service init methods, return boolean to stay + activated + * 20071116 [chatellier] add support for topia context listeners + * 20071114 [chatellier] add support for service without persitent classes + * 20071109 [chatellier] add type="string" on topiaId in templates + - update hibernate version to 3.2.5.ga (event patch) + - jetty version 5.1.10 (better pom.xml) + * 20071001 [chatellier] move tapestry version to 5.0.5 + * 20070528 [chatellier] add stateModel generation support + - tapestry generation templates + - tapestry 5.0.4 dependency + + * 20071106 [poussin] add removeContext on TopiaContextFactory and call it + in TopiaContextImpl.closeContext(). + + * 20071107 [thimel] add db schema support + * 20071107 [thimel] several .hbm.xml refactoring + * 20071108 [thimel] add DTO generator + * 20071108 [thimel] add copyright support in generators + * 20071108 [thimel] add serialVersionUID support in EntityAbstractGenerator + +ver-2-0-24 poussin 20070425 + + * 20070420 [chatellier] add services interfaces generation (stereotype=service) + * 20070420 [chatellier] add topia services support + * 20070330 [poussin] TopiaContextFactory return new TopiaContext if context is closed + * 20070331 [poussin] change many string argument to class argument + * 20070331 [poussin] add getService(Class) method on TopiaContext + * 20070331 [poussin] remove helper in service that only return service instant + * 20070331 [poussin] add Devel.rst documentation + * 20070402 [poussin] replace init to preInit and postInit in TopiaService + +ver-2-0-22 ruchaud 20061023 + + * 20061023 [ruchaud][improve] manage events + * 20061023 [ruchaud][bug fix] in generator of DAO Abstract on delete + +ver-2-0-21 poussin 20061019 + + * add support for auto-import entities for hql + * add getComposite and getAggregate on TopiaEntity + * bug fix for association hibernate mapping wit have attributeh + cascade="delete" for not navigable link to prevent constraint exception + * bug fix in delete, remove link before do delete + +ver-2-0-20 poussin 20061017 + + * add support for TopiaService mechanisme + +ver-2-0-16 poussin 20060907 + + * Implantation des méthodes update et delete sur les TopiaUserImpl et TopiaEntityPermissionImpl + * add backup/restore method on TopiaContext (backup only works for h2) + * TopiaContext is set in Entity during preload + * add updateSchema in TopiaContext interface + * add methods update et delete on TopiaUserImpl and TopiaEntityPermissionImpl + +ver-2-0-13 thimel 20060822 + + * [Secu] ajout d'un champ notes sur les TopiaUser + * [Secu] ajout d'un champ linkApplication sur les TopiaUser (permettant de faire le lien avec une application externe) + * [Secu] ajout de commits et rollbacks sur les Manager + * [Secu] ajout d'un setPassword (sans verification ancien mdp) sur les TopiaUser + +ver-2-0-12 thimel 20060721 + + * ajout des projections sur les DAO + * positionnement unique des userManager et permissionManager au niveau du contexte racine + * ajout de la possibilite de tester des permissions sur le permissionManager + +ver-2-0-11 thimel 20060703 + + * support de la fermeture d'un contexte + * support de la recherche sur tous les attributs d'une entite / annotation sur les interfaces + +ver-2-0-8 thimel 20060606 + + * amelioration detection des classes abstraites + * correction du polymorphisme avec les proxies d'Hibernate + * Hibernate 3.2 + +ver-2-0-7 thimel 20060523 + + * import/export XML "experimental" + * one-to-one transformes en many-to-one + unique="true" + * bug au niveau de la detection des parents abstract + +ver-2-0-6 thimel 20060504 + + * support des annotations sur les attributs des XXXAbstract + * re-correction du support des relations unidirectionnelles 1-n avec classes d'heritage du cote n + * ajout de la validation pour les attributs sans inverses (pour les objectmodel faits a la main) + +ver-2-0-5 thimel 20060427 + + * isIndexed remplace isOrdered dans le mapping hibernate + * add postCreate and postLoad on AbstractTopiaEntity + * findContains sur les DAO (implante hors Hibernate pour l'instant) + * support plus coherent des classes abstraites + * projet compatible maven2 + * correction du support des relations unidirectionnelles 1-n avec classes d'heritage du cote n + +ver-2-0-4 thimel 20060303 + + * Correction du support des interfaces et classes abstraites (conjointement a LutinGenerator 0.30) + * Ajout des exceptions sur le operations (conjointement a LutinGenerator 0.30) + +ver-2-0-3 thimel 20060228 + + * Correction du support des classes d'associations (mauvais mapping Hibernate) + * Support de super classes pour les classes d'association (LutinGenerator 0.29) + diff --git a/doc/presentation/lutin.jpg b/doc/presentation/lutin.jpg new file mode 100644 index 0000000..0feb020 Binary files /dev/null and b/doc/presentation/lutin.jpg differ diff --git a/doc/presentation/topia.pdf b/doc/presentation/topia.pdf new file mode 100644 index 0000000..eb9c161 Binary files /dev/null and b/doc/presentation/topia.pdf differ diff --git a/doc/presentation/topia.tex b/doc/presentation/topia.tex new file mode 100644 index 0000000..1a0c540 --- /dev/null +++ b/doc/presentation/topia.tex @@ -0,0 +1,173 @@ +\documentclass[pdf,13pt]{beamer} +\usepackage[utf8]{inputenc} +\usepackage[T1]{fontenc} +\usepackage[francais]{babel} % francais +\usepackage{graphicx} % images +\usepackage{times} +\usepackage{listings} +\usepackage{color} + +% Theme beamer and color +% JuanLesPins Malmoe PaloAlto Berlin Boadilla Copenhagen Hannover Goettingen +% Montpellier Rochester Madrid Antibes Singapore Szeged Warsaw +% Ilmenau Luebeck Dresden Frankfurt* Darmstadt* +\usetheme{Frankfurt} +% albatross,beetle,crane,default,dove,fly,lily,orchid,rose,seagull +% seahorse,whale,dolphin +%\usecolortheme{sidebartab} +%\setbeamertemplate{navigation symbols}{} % supprime la navigation +\usefonttheme[onlylarge]{structurebold} +\setbeamerfont*{frametitle}{size=\normalsize,series=\bfseries} +\setbeamertemplate{navigation symbols}{} +%\setbeamercovered{transparent} +\beamertemplatetransparentcovered + +% Types d'images utilisees +\DeclareGraphicsExtensions{.png, .eps, .jpg} + +% Ajout du logo Code Lutin en fond +\setbeamertemplate{background}{% + \parbox[c][\paperheight]{\paperwidth}{% + \vfill + \vfill + \hfill + \includegraphics[width=0.23\paperwidth,height=0.7\paperheight]{lutin} + \hfill + \vfill +}} + +% ######################### Document Infos ##################################### +% Infos de la page de presentation +\title{ToPIA} +\subtitle{Framework d'application} +\author{Code Lutin} +%\institute{ +% Soci\'et\'e Code Lutin +%} +\date{29 Octobre 2009} + +% ############################################################################## +\begin{document} + +\begin{frame} +\titlepage +\end{frame} + +%\begin{frame} +%\frametitle{Plan} +%\tableofcontents +%\end{frame} + +% ############################################################################## +\section{ToPIA} + +\subsection{Pr\'esentation} +\begin{frame} + \frametitle{Pr\'esentation} + + \begin{itemize} + \item ToPIA + \begin{itemize} + \item \textbf{To}ols for \textbf{P}ortable and \textbf{I}ndependant \textbf{A}rchitecture + \item Framework d'application + \item Abstraction de plateforme technique + \end{itemize} + \end{itemize} + + \onslide<2-> + \begin{itemize} + \item Modules + \begin{itemize} + \item topia-persistence + \item topia-soa + \item topia-ui + \item ... + \end{itemize} + \end{itemize} + +\end{frame} + +% ------------------------------------------------------------------------------ +\begin{frame} + \frametitle{Persistence} + + \begin{itemize} + \item ToPIA-persitence + \begin{itemize} + \item Application => ToPIA-persitence => \alert<2->{Hibernate} => SGDB + \item Application => ToPIA-persitence => \alert<2->{JDO} => SGDB + \end{itemize} + \end{itemize} + + \onslide<3-> + \begin{itemize} + \item \'Evolution + \begin{itemize} + \item Application => ToPIA-persitence => \alert<3->{JPA} => Hibernate => SGDB + \end{itemize} + \end{itemize} +\end{frame} + +% ------------------------------------------------------------------------------ +\begin{frame} + \frametitle{G\'en\'eration} + + \begin{itemize} + \item Fournit : + \begin{itemize} + \item Template de g\'en\'eration de code (Eugene) + \begin{itemize} + \item UML + \item => Entit\'es m\'etier + \item => DAO (C-R-U-D) + \item => Helper + \end{itemize} + \item API + \begin{itemize} + \item TopiaContext + \end{itemize} + \end{itemize} + \end{itemize} + +\end{frame} + + +% ############################################################################## +\subsection{Utilisation} +\begin{frame} + \frametitle{Exemples d'utilisation} + + \begin{exampleblock}{Cr\'eation d'une entit\'e} + \begin{tiny} + \texttt{Properties config = ...\\ + \textcolor{red!50!black}{TopiaContext} rootContext = \textcolor{red!50!black}{TopiaContextFactory}.getContext(config);\\ + \[...\]\\ + \textcolor{red!50!black}{TopiaContext} context = rootContext.beginTransaction();\\ + \textcolor{blue!50!black}{PersonDAO} dao = \textcolor{blue!50!black}{MyApplicationHelper}.getPersonDAO(context);\\ + \textcolor{blue!50!black}{Person} myPerson = new \textcolor{blue!50!black}{Person}();\\ + dao.create(myPerson);\\ + context.commit();\\ + context.closeContext(); + } + \end{tiny} + \end{exampleblock} + +\end{frame} + +% ------------------------------------------------------------------------------ +\begin{frame} + \frametitle{Exemples d'utilisation} + + \begin{exampleblock}{Recherche d'un entit\'e} + \begin{tiny} + \texttt{\textcolor{red!50!black}{TopiaContext} context = rootContext.beginTransaction();\\ + \textcolor{blue!50!black}{CompanyDAO} dao = \textcolor{blue!50!black}{MyApplicationHelper}.getCompanyDAO(context);\\ + \textcolor{blue!50!black}{Company} myCompany = dao.findByName("Code Lutin");\\ + context.closeContext(); + } + \end{tiny} + \end{exampleblock} + +\end{frame} + +\end{document} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..f29d100 --- /dev/null +++ b/pom.xml @@ -0,0 +1,482 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA + %% + Copyright (C) 2004 - 2010 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.nuiton</groupId> + <artifactId>nuitonpom</artifactId> + <version>1.6</version> + </parent> + + <artifactId>topia</artifactId> + <version>2.9.3-SNAPSHOT</version> + <packaging>pom</packaging> + + <name>ToPIA</name> + <description> + Tools for Portable and Independent Architecture : + Framework de persistance et de distribution d'application. + </description> + <url>http://topia.nuiton.org</url> + <inceptionYear>2004</inceptionYear> + + <developers> + + <developer> + <name>Benjamin Poussin</name> + <id>bpoussin</id> + <email>poussin@codelutin.com</email> + <organization>CodeLutin</organization> + <organizationUrl>http://codelutin.com</organizationUrl> + <timezone>Europe/Paris</timezone> + <roles> + <role>Développeur</role> + </roles> + </developer> + + <developer> + <name>Arnaud Thimel</name> + <id>athimel</id> + <email>thimel@codelutin.com</email> + <organization>CodeLutin</organization> + <organizationUrl>http://codelutin.com</organizationUrl> + <timezone>Europe/Paris</timezone> + <roles> + <role>Développeur</role> + </roles> + </developer> + + <developer> + <name>Julien Ruchaud</name> + <id>jruchaud</id> + <email>ruchaud@codelutin.com</email> + <organization>CodeLutin</organization> + <organizationUrl>http://codelutin.com</organizationUrl> + <timezone>Europe/Paris</timezone> + <roles> + <role>Développeur</role> + </roles> + </developer> + + <developer> + <name>Eric Chatellier</name> + <id>echatellier</id> + <email>chatellier@codelutin.com</email> + <organization>CodeLutin</organization> + <organizationUrl>http://codelutin.com</organizationUrl> + <timezone>Europe/Paris</timezone> + <roles> + <role>Développeur</role> + </roles> + </developer> + + <developer> + <name>Tony Chemit</name> + <id>tchemit</id> + <email>chemit@codelutin.com</email> + <organization>CodeLutin</organization> + <organizationUrl>http://codelutin.com</organizationUrl> + <timezone>Europe/Paris</timezone> + <roles> + <role>Développeur</role> + </roles> + </developer> + <developer> + <name>Brendan Le Ny</name> + <id>bleny</id> + <email>bleny@codelutin.com</email> + <organization>CodeLutin</organization> + <organizationUrl>http://codelutin.com</organizationUrl> + <timezone>Europe/Paris</timezone> + <roles> + <role>Développeur</role> + </roles> + </developer> + + <developer> + <name>Jean Couteau</name> + <id>jcouteau</id> + <email>couteau@codelutin.com</email> + <organization>CodeLutin</organization> + <organizationUrl>http://codelutin.com</organizationUrl> + <timezone>Europe/Paris</timezone> + <roles> + <role>Documentation writer</role> + </roles> + </developer> + </developers> + <contributors> + <contributor> + <name>Florian Desbois</name> + <roles> + <role>Développeur</role> + </roles> + </contributor> + <contributor> + <name>Sylvain Letellier</name> + <timezone>Europe/Paris</timezone> + <roles> + <role>Développeur</role> + </roles> + </contributor> + <contributor> + <name>Nicolas Dupont</name> + <email>ndupont@codelutin.com</email> + <organization>CodeLutin</organization> + <organizationUrl>http://codelutin.com</organizationUrl> + <timezone>Europe/Paris</timezone> + </contributor> + <contributor> + <name>Eduardo Ore</name> + <email>eore@codelutin.com</email> + <organization>CodeLutin</organization> + <organizationUrl>http://codelutin.com</organizationUrl> + <timezone>Europe/Paris</timezone> + </contributor> + <contributor> + <name>Gabriel Landais</name> + <email>glandais@codelutin.com</email> + <organization>CodeLutin</organization> + <organizationUrl>http://codelutin.com</organizationUrl> + <timezone>Europe/Paris</timezone> + </contributor> + <contributor> + <name>Stéphane Chorlet</name> + <email>schorlet@codelutin.com</email> + <organization>CodeLutin</organization> + <organizationUrl>http://codelutin.com</organizationUrl> + <timezone>Europe/Paris</timezone> + </contributor> + <contributor> + <name>Jonathan pepin</name> + <email>jpepin@codelutin.com</email> + <organization>CodeLutin</organization> + <organizationUrl>http://codelutin.com</organizationUrl> + <timezone>Europe/Paris</timezone> + </contributor> + <contributor> + <name>Nolwenn Ranou</name> + <email>nrannou@codelutin.com</email> + <organization>CodeLutin</organization> + <organizationUrl>http://codelutin.com</organizationUrl> + <timezone>Europe/Paris</timezone> + </contributor> + </contributors> + + <modules> + <module>topia-persistence</module> + <module>topia-service-replication</module> + <module>topia-service-migration</module> + <module>topia-service-security</module> + </modules> + + <scm> + <connection>scm:git:https://git.nuiton.org/topia_2.x.git</connection> + <developerConnection>scm:git:https://git.nuiton.org/topia_2.x.git</developerConnection> + <url>https://gitweb.nuiton.org/topia_2.x.git</url> + </scm> + <distributionManagement> + <site> + <id>${site.server}</id> + <url>${site.url}</url> + </site> + </distributionManagement> + + <properties> + <!-- pour un muli module on doit fixer le projectId --> + <projectId>topia</projectId> + + <!-- Allow SVN keywords in license headers in ToPIA --> + <license.addSvnKeyWords>true</license.addSvnKeyWords> + + <!-- libs version --> + <eugeneVersion>2.13</eugeneVersion> + <nuitonCsvVersion>3.0-alpha-3</nuitonCsvVersion> + <nuitonDecoratorVersion>3.0-alpha-3</nuitonDecoratorVersion> + <nuitonUtilsVersion>3.0-rc-8</nuitonUtilsVersion> + <processorPluginVersion>1.3</processorPluginVersion> + <nuitonI18nVersion>3.3</nuitonI18nVersion> + <xmlrpcVersion>3.1.2</xmlrpcVersion> + <hibernateVersion>4.3.5.Final</hibernateVersion> + <sl4jVersion>1.7.6</sl4jVersion> + <h2Version>1.3.175</h2Version> + <hamcrestVersion>1.3</hamcrestVersion> + + <!-- i18n configuration --> + <i18n.bundles>fr_FR,en_GB,es_ES</i18n.bundles> + + </properties> + + <dependencyManagement> + <dependencies> + + <dependency> + <groupId>org.nuiton.eugene</groupId> + <artifactId>eugene</artifactId> + <version>${eugeneVersion}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.nuiton.eugene</groupId> + <artifactId>eugene-java-templates</artifactId> + <version>${eugeneVersion}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.nuiton</groupId> + <artifactId>nuiton-utils</artifactId> + <version>${nuitonUtilsVersion}</version> + </dependency> + + <dependency> + <groupId>org.nuiton</groupId> + <artifactId>nuiton-csv</artifactId> + <version>${nuitonCsvVersion}</version> + </dependency> + + <dependency> + <groupId>org.nuiton</groupId> + <artifactId>nuiton-decorator</artifactId> + <version>${nuitonDecoratorVersion}</version> + </dependency> + + <dependency> + <groupId>org.nuiton.i18n</groupId> + <artifactId>nuiton-i18n</artifactId> + <version>${nuitonI18nVersion}</version> + </dependency> + + <dependency> + <groupId>org.hibernate</groupId> + <artifactId>hibernate-core</artifactId> + <version>${hibernateVersion}</version> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </exclusion> + </exclusions> + <scope>compile</scope> + </dependency> + + <dependency> + <groupId>org.hibernate</groupId> + <artifactId>hibernate-ehcache</artifactId> + <version>${hibernateVersion}</version> + <exclusions> + <exclusion> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </exclusion> + </exclusions> + <scope>runtime</scope> + </dependency> + + <dependency> + <groupId>org.javassist</groupId> + <artifactId>javassist</artifactId> + <version>3.18.1-GA</version> + <scope>runtime</scope> + </dependency> + + <!-- hibernate-core only include api, need implementation, + binding with log4j will be used + --> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <version>${sl4jVersion}</version> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + <version>${sl4jVersion}</version> + <scope>test</scope> + </dependency> + + <!-- Guava --> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + <version>17.0</version> + </dependency> + + <!-- Commons libs --> + + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-collections4</artifactId> + <version>4.0</version> + </dependency> + + <dependency> + <groupId>commons-beanutils</groupId> + <artifactId>commons-beanutils</artifactId> + <version>1.9.2</version> + </dependency> + + <dependency> + <groupId>commons-collections</groupId> + <artifactId>commons-collections</artifactId> + <version>3.2.1</version> + </dependency> + + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + <version>1.1.3</version> + </dependency> + + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>2.4</version> + </dependency> + + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <version>3.3.2</version> + </dependency> + + <!-- BD H2 for testing --> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + <version>${h2Version}</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-core</artifactId> + <version>${hamcrestVersion}</version> + <scope>test</scope> + </dependency> + + <!-- Junit --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <version>4.11</version> + <scope>test</scope> + </dependency> + + <!-- Log4J --> + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + <version>1.2.17</version> + </dependency> + + </dependencies> + </dependencyManagement> + + <build> + <pluginManagement> + <plugins> + + <plugin> + <groupId>org.nuiton.processor</groupId> + <artifactId>processor-maven-plugin</artifactId> + <version>${processorPluginVersion}</version> + <executions> + <execution> + <phase>generate-sources</phase> + <goals> + <goal>process</goal> + </goals> + </execution> + </executions> + <configuration> + <includes>**/*.java</includes> + <filters> + org.nuiton.processor.filters.GeneratorTemplatesFilter, + org.nuiton.processor.filters.ActiveLogsCodeFilter + </filters> + </configuration> + </plugin> + + <plugin> + <groupId>org.nuiton.eugene</groupId> + <artifactId>eugene-maven-plugin</artifactId> + <version>${eugeneVersion}</version> + <configuration> + <inputs>zargo</inputs> + <resolver>org.nuiton.util.FasterCachedResourceResolver</resolver> + </configuration> + </plugin> + + <plugin> + <groupId>org.nuiton.i18n</groupId> + <artifactId>i18n-maven-plugin</artifactId> + <version>${nuitonI18nVersion}</version> + </plugin> + + <plugin> + <artifactId>maven-site-plugin</artifactId> + <dependencies> + <dependency> + <groupId>org.nuiton.jrst</groupId> + <artifactId>doxia-module-jrst</artifactId> + <version>${jrstPluginVersion}</version> + </dependency> + </dependencies> + </plugin> + + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>license-maven-plugin</artifactId> + <configuration> + <extraExtensions> + <objectmodel>xml</objectmodel> + <xsl>xml</xsl> + <xsd>xml</xsd> + </extraExtensions> + </configuration> + </plugin> + + <plugin> + <groupId>external.atlassian.jgitflow</groupId> + <artifactId>jgitflow-maven-plugin</artifactId> + <configuration> + <flowInitContext> + <masterBranchName>master-2.x</masterBranchName> + <developBranchName>develop-2.x</developBranchName> + </flowInitContext> + </configuration> + </plugin> + + </plugins> + </pluginManagement> + </build> + + <reporting> + <!--TC-20100413 : by default do nothing except documentation --> + <excludeDefaults>true</excludeDefaults> + </reporting> + +</project> diff --git a/src/site/rst/index.rst b/src/site/rst/index.rst new file mode 100755 index 0000000..b20b868 --- /dev/null +++ b/src/site/rst/index.rst @@ -0,0 +1,70 @@ +.. - +.. * #%L +.. * ToPIA +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +======= +Accueil +======= + +Presentation +------------ + +ToPIA stands for Tools for Portable and Independent Architecture. It is a +technical platform abstraction framework. This documentation is not up-to-date +and in French. We are in the process to update this documentation, we will +translate it in English at the same time. + +Présentation +------------ + +ToPIA, pour Tools for Portable and Independant Architecture, est un framework +d'abstraction des plateformes techniques. + +Constitution +------------ + +Il est actuellement composé d'un module principal : + + * `ToPIA-persistence`_ : pour la gestion de la persistance sur hibernate + +Depuis la version 2.3.3, le module **ToPIA-soa** n'est plus maintenu. + +Services +-------- + +Depuis la version 2.2.0, ToPIA-service a été intégré au projet ToPIA, chaque service devient un module : + + * `ToPIA-service-migration`_ : service de migration de bases sans perte de données. + * `ToPIA-service-replication`_ : service de replication de données entre 2 bases. + * `ToPIA-service-security`_ : service de sécurité. + +Depuis la version 2.3.3, certains services ne sont plus maintenus (car ils sont à +l'heure actuelle des prototypes et ne sont pas utilisables ad hoc). + + * **ToPIA-service-history** : service d'historisation de données en base. + * **ToPIA-service-index** : service d'indexation de données. + +.. _ToPIA-persistence: ./topia-persistence +.. _ToPIA-service-migration: ./topia-service-migration +.. _ToPIA-service-replication: ./topia-service-replication +.. _ToPIA-service-security: ./topia-service-security diff --git a/src/site/site.xml b/src/site/site.xml new file mode 100644 index 0000000..a5f2511 --- /dev/null +++ b/src/site/site.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<project name="ToPIA"> + + <bannerLeft> + <src alt="ToPIA">${siteCommonResourcesUrl}/images/logos/topia-logo.png</src> + <name>${project.name}</name> + <href>index.html</href> + </bannerLeft> + + <bannerRight> + <src>http://www.codelutin.com/images/lutinorange-codelutin.png</src> + <href>http://www.codelutin.com</href> + </bannerRight> + + <poweredBy> + + <logo href="http://docutils.sourceforge.net/rst.html" + name="ReStructuredText" + img="${siteCommonResourcesUrl}/images/logos/restructuredtext-logo.png"/> + + <logo href="http://maven-site.nuiton.org/jrst" name="JRst" + img="${siteCommonResourcesUrl}/images/logos/jrst-logo.png"/> + + <logo href="http://argouml.tigris.org/" name="ArgoUML" + img="${siteCommonResourcesUrl}/images/logos/argouml-logo.png"/> + + </poweredBy> + + <body> + + <breadcrumbs> + <item name="${project.name}" href="${project.url}/index.html"/> + <item name="${project.version}" href="${project.url}/${siteDeployClassifier}/index.html"/> + </breadcrumbs> + + <menu name="Utilisateur"> + <item href="/index.html" name="Accueil"/> + </menu> + + <menu ref="modules"/> + + <menu ref="reports"/> + + </body> +</project> diff --git a/topia-persistence/LICENSE.txt b/topia-persistence/LICENSE.txt new file mode 100644 index 0000000..cca7fc2 --- /dev/null +++ b/topia-persistence/LICENSE.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/topia-persistence/README.txt b/topia-persistence/README.txt new file mode 100644 index 0000000..d2e50d3 --- /dev/null +++ b/topia-persistence/README.txt @@ -0,0 +1,2 @@ +To deploy new version of pom: mvn deploy +To install localy: mvn install diff --git a/topia-persistence/changelog.txt b/topia-persistence/changelog.txt new file mode 100644 index 0000000..2506b97 --- /dev/null +++ b/topia-persistence/changelog.txt @@ -0,0 +1,67 @@ +2.2.0 +* add a EntityOperatorStore to share operators +* migrate to nuiton (relocate coordinates) +* introduce TopiaEntityOperator api +* refactor EntityEnumContract to TopiaEntityEnum +* introduce Collector api (and gives some implementations in TopiaEntityHelper) +* add generateOperatorForDAOHelper tagValue on model to enable operator feature in dao helper +* add algorithm to build diff between two list of entities (DiffState) +* improve TopiaDAO and TopiaDAOImpl code, and add findAllIds method + +* 20090605 [desbois] - add DAO developper implementation support (<<dao>> stereotype on entity operation) +* 20090604 [desbois] - suppress Flatfile support + - suppress TopiaDAODelegator, packages persitence.flatfile and persistence.hibernate + - merge TopiaDAOAbstract and TopiaDAOHibernate + - modify generated DAO which inherits of TopiaDAOAbstract by default (instead of TopiaDAODelegator) + - modify in TopiaContextImpl (no configuration can be used to define DAO implementation) +* 20090604 [desbois] - resolve issue of NMultiplicity relation between two entities with inheritance (DAOAbsractGenerator delete method generation) +* 20090528 [desbois] - resolve issue of NMultiplicity on attribute with primitive type (like String) +* 20090520 [desbois] - add DTO generation with BeanGenerator + +2.1.5 + +* 20090504 [chemit] - suppress deprecated class TopiaEntityEnumEntry + - improve TopiaEntityAbstract (fix javadoc, add @Override) + +2.1.4 xxxx 200903xx +* 20090418 [chemit] - suppress deprecated generators + - add a BeanGenerator + - add methods isXXXEmpty on association in generated entities +* 20090317 [chatellier] Change tagValue from "orderBy" to "order-by" + +2.1.3 chemit 20090220 +* 20090210 [chatellier] - add first letter capitalize on model name use +* 20090129 [chemit] - add methods in TopiaGeneratorUtil class to optimize imports +* 20090129 [thimel] - Refactor generators (part1 : remove any methods calls in <%=...%>) +* 20090129 [thimel] - Renamed GeneratorUtil to TopiaGeneratorUtil +* 20090128 [bpoussin] - introduce Visitor design pattern on generated TopiaEntityAbstract +* 20090126 [chemit] - refactor poms (all dependencies in parent-pom dependencyManagment) +* 20090126 [chemit] - add somes junit tests to check none-regression when modify ToPIA's templates. + - fix generation bug (notNull could ne null (line 226 EntityHibernateMappingGenerator) +* 20090116 [chemit] - fix a NPE of TopiaDAOFlatFile when do a putAll on a map with some null values on entries. + - ajout des methodes replicate et replicateEntities sur TopiaContext car les methodes existantes + importXML et exportXML ne peuvent pas prendre en compte tous les cas possibles (et on a peut-être + pas envie de passer par du xml...) +2.1.2 chemit 20090115 +* 20090115 [chemit] - pour le moment pas d'embed-xml sur les association multiples +* 20090114 [chemit] - improve exportXML (prepare queries then executes then when parameters are known to be fine) +* 20090106 [chemit] - amélioration du générateur de mapping hibernate : + - génération des clefs metier dans le mapping hibernate via la tag value naturalId + - mise en constantes des tagValues utiliser dans le génératuer du mapping hibernate + - ajout du tagValue notNull pour le mapping hibernate + - changement du tagValue order-by en orderBy car sinon on récupère order au lieu de order-by + - ajout de la méthode obtainProgperties sur la classe Loador pour recuperer la map des propriétés à binder + (car pour les clef métiers on doit les ajouter à la création sinon hibernate pleure...) +* 20090104 [chemit] - utilisation foreach dans les générateurs + - ajout de méthode getXXXByTopiaId pour les attributs à multiplicité dans les entités (interface et abstract) + - fix generic dans les méthodes générées + - ajout d'un tagValue i18n pour générer dans les entités abstraites les chaines i18n + - ajout d'un paquetage org.codelutin.topia.persistence.util avec du code utile :) (javadoc a faire...) + +2.1.1 chemit 20081215 +* 20081215 [chemit] - new release for isis-fish :) +* 20081212 [chemit] - add a InterfaceGenerator to generate simple with no stereotype interfaces. + +2.1.0 chemit 20081210 +* 20081205 [chemit] - improve poms, use lutinproject 3.2 + - add some usefull methods in TopiaUtil to help with regex on topiaId expression diff --git a/topia-persistence/pom.xml b/topia-persistence/pom.xml new file mode 100644 index 0000000..88b19b2 --- /dev/null +++ b/topia-persistence/pom.xml @@ -0,0 +1,377 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Persistence + + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2010 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.nuiton</groupId> + <artifactId>topia</artifactId> + <version>2.9.3-SNAPSHOT</version> + </parent> + + <groupId>org.nuiton.topia</groupId> + <artifactId>topia-persistence</artifactId> + + <name>ToPIA :: Persistence</name> + <description>Hibernate based persistence module</description> + + <dependencies> + + <dependency> + <groupId>org.nuiton</groupId> + <artifactId>nuiton-utils</artifactId> + </dependency> + + <dependency> + <groupId>org.nuiton</groupId> + <artifactId>nuiton-csv</artifactId> + </dependency> + + <dependency> + <groupId>org.nuiton</groupId> + <artifactId>nuiton-decorator</artifactId> + </dependency> + + <dependency> + <groupId>org.nuiton.i18n</groupId> + <artifactId>nuiton-i18n</artifactId> + </dependency> + + <dependency> + <groupId>org.nuiton.eugene</groupId> + <artifactId>eugene</artifactId> + </dependency> + + <dependency> + <groupId>org.nuiton.eugene</groupId> + <artifactId>eugene-java-templates</artifactId> + </dependency> + + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava</artifactId> + </dependency> + + <dependency> + <groupId>commons-collections</groupId> + <artifactId>commons-collections</artifactId> + </dependency> + + <dependency> + <groupId>commons-beanutils</groupId> + <artifactId>commons-beanutils</artifactId> + </dependency> + + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + </dependency> + + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + </dependency> + + <dependency> + <groupId>org.hibernate</groupId> + <artifactId>hibernate-core</artifactId> + </dependency> + <dependency> + <groupId>org.hibernate</groupId> + <artifactId>hibernate-ehcache</artifactId> + </dependency> + + <dependency> + <groupId>org.javassist</groupId> + <artifactId>javassist</artifactId> + </dependency> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + </dependency> + + <!-- BD H2 for testing --> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + + <dependency> + <groupId>org.hamcrest</groupId> + <artifactId>hamcrest-core</artifactId> + </dependency> + + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + <scope>test</scope> + </dependency> + + </dependencies> + + <build> + <testResources> + + <testResource> + <directory>${project.build.directory}/generated-test-sources/java</directory> + <includes> + <include>**/*.hbm.xml</include> + </includes> + </testResource> + + <testResource> + <directory>${project.basedir}/src/test/resources</directory> + <includes> + <include>**/*.properties</include> + </includes> + </testResource> + + <testResource> + <directory>${project.basedir}/src/test/java</directory> + <includes> + <include>**/*.hbm.xml</include> + </includes> + </testResource> + + </testResources> + <plugins> + + <plugin> + <groupId>org.nuiton.processor</groupId> + <artifactId>processor-maven-plugin</artifactId> + </plugin> + + <plugin> + <groupId>org.nuiton.i18n</groupId> + <artifactId>i18n-maven-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>parserJava</goal> + <goal>gen</goal> + </goals> + <!-- restrict parsing beacause this is expensive --> + <!-- if you want to parse more, add your package in includes --> + <configuration> + <treateDefaultEntry>false</treateDefaultEntry> + <entries> + <entry> + <basedir>${project.basedir}/src/main/java</basedir> + <includes> + <include>org/nuiton/topia/framework/*.java</include> + </includes> + </entry> + </entries> + </configuration> + </execution> + </executions> + </plugin> + + <!-- expose new plexus components --> + <plugin> + <groupId>org.codehaus.plexus</groupId> + <artifactId>plexus-component-metadata</artifactId> + <executions> + <execution> + <goals> + <goal>generate-metadata</goal> + </goals> + </execution> + </executions> + </plugin> + + <plugin> + <groupId>org.nuiton.eugene</groupId> + <artifactId>eugene-maven-plugin</artifactId> + <configuration> + + </configuration> + <executions> + <execution> + <id>Test Generator</id> + <phase>generate-test-sources</phase> + <configuration> + <testPhase>true</testPhase> + <templates> + org.nuiton.topia.generator.TopiaMetaTransformer, + org.nuiton.eugene.java.JavaInterfaceTransformer, + org.nuiton.eugene.java.JavaBeanTransformer, + org.nuiton.topia.generator.EntityDTOTransformer, + org.nuiton.topia.generator.BinderHelperTransformer, + org.nuiton.topia.generator.QueryHelperTransformer + </templates> + <fullPackagePath>org.nuiton.topia</fullPackagePath> + <defaultPackage>org.nuiton.topia</defaultPackage> + <outputDirectory>${project.build.directory}/generated-test-sources</outputDirectory> + </configuration> + <goals> + <goal>generate</goal> + </goals> + </execution> + </executions> + </plugin> + + <!-- expose tests --> + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <id>attach-test</id> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + + </plugin> + </plugins> + </build> + + <profiles> + + <!-- perform only on a release stage when using the maven-release-plugin --> + <profile> + <id>release-profile</id> + <activation> + <property> + <name>performRelease</name> + <value>true</value> + </property> + </activation> + + <build> + <plugins> + + <!-- always expose tests source jar --> + <plugin> + <artifactId>maven-source-plugin</artifactId> + <executions> + <execution> + <id>attach-test-sources</id> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> + + <!-- always expose tests javadoc jar --> + <plugin> + <artifactId>maven-javadoc-plugin</artifactId> + <executions> + <execution> + <id>attach-test-javadoc</id> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> + + </plugins> + </build> + </profile> + + <profile> + <id>run-its</id> + <activation> + <property> + <name>performRelease</name> + <value>true</value> + <!-- Reuse this when MINVOKER-107 will be done (syas invoker 1.6 --> + <!--name>maven.test.skip</name> + <value>!false</value--> + </property> + </activation> + <build> + <defaultGoal>integration-test</defaultGoal> + <plugins> + <plugin> + <artifactId>maven-invoker-plugin</artifactId> + <configuration> + <localRepositoryPath> + ${basedir}/target/local-repo + </localRepositoryPath> + <settingsFile>${project.basedir}/src/it/settings.xml</settingsFile> + <cloneProjectsTo>${project.build.directory}/its</cloneProjectsTo> + <debug>${maven.verbose}</debug> + </configuration> + <executions> + <execution> + <id>integration-test</id> + <goals> + <goal>install</goal> + <goal>run</goal> + </goals> + <phase>integration-test</phase> + </execution> + </executions> + </plugin> + </plugins> + </build> + </profile> + + <!-- reporting at release time --> + <profile> + <id>reporting</id> + <activation> + <property> + <name>performRelease</name> + <value>true</value> + </property> + </activation> + + <reporting> + <plugins> + <plugin> + <groupId>org.codehaus.plexus</groupId> + <artifactId>plexus-maven-plugin</artifactId> + <version>${plexusPluginVersion}</version> + </plugin> + + <plugin> + <artifactId>maven-invoker-plugin</artifactId> + <version>${invokerPluginVersion}</version> + </plugin> + </plugins> + </reporting> + + </profile> + + </profiles> +</project> diff --git a/topia-persistence/src/it/ANOMALIE-1640/LICENSE.txt b/topia-persistence/src/it/ANOMALIE-1640/LICENSE.txt new file mode 100644 index 0000000..e69de29 diff --git a/topia-persistence/src/it/ANOMALIE-1640/README.txt b/topia-persistence/src/it/ANOMALIE-1640/README.txt new file mode 100644 index 0000000..b972614 --- /dev/null +++ b/topia-persistence/src/it/ANOMALIE-1640/README.txt @@ -0,0 +1 @@ +To test the http://nuiton.org/issues/1640 bug. \ No newline at end of file diff --git a/topia-persistence/src/it/ANOMALIE-1640/changelog.txt b/topia-persistence/src/it/ANOMALIE-1640/changelog.txt new file mode 100644 index 0000000..e69de29 diff --git a/topia-persistence/src/it/ANOMALIE-1640/invoker.properties b/topia-persistence/src/it/ANOMALIE-1640/invoker.properties new file mode 100644 index 0000000..4d958e4 --- /dev/null +++ b/topia-persistence/src/it/ANOMALIE-1640/invoker.properties @@ -0,0 +1,45 @@ +### +# #%L +# ToPIA :: Persistence +# $Id$ +# $HeadURL$ +# %% +# Copyright (C) 2004 - 2014 CodeLutin +# %% +# 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 General Lesser Public License for more details. +# +# You should have received a copy of the GNU General Lesser Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/lgpl-3.0.html>. +# #L% +### + +# A comma or space separated list of goals/phases to execute, may +# specify an empty list to execute the default goal of the IT project +invoker.goals=clean test + +# Optionally, a list of goals to run during further invocations of Maven +#invoker.goals.2=${project.groupId}:${project.artifactId}:${project.version}:run + +# A comma or space separated list of profiles to activate +#invoker.profiles=run-all run-once + +# The value for the environment variable MAVEN_OPTS +#invoker.mavenOpts=-Dfile.encoding=UTF-16 -Xms32m -Xmx256m + +# Possible values are "fail-fast" (default), "fail-at-end" and "fail-never" +invoker.failureBehavior=fail-fast + +# The expected result of the build, possible values are "success" (default) and "failure" +#invoker.buildResult=success + +# A boolean value controlling the -N flag, defaults to "false" +#invoker.nonRecursive=false diff --git a/topia-persistence/src/it/ANOMALIE-1640/pom.xml b/topia-persistence/src/it/ANOMALIE-1640/pom.xml new file mode 100644 index 0000000..370034e --- /dev/null +++ b/topia-persistence/src/it/ANOMALIE-1640/pom.xml @@ -0,0 +1,139 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Persistence + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + + <modelVersion>4.0.0</modelVersion> + + <!-- ************************************************************* --> + <!-- *** POM Relationships *************************************** --> + <!-- ************************************************************* --> + + <parent> + <groupId>org.nuiton</groupId> + <artifactId>mavenpom4redmine</artifactId> + <version>3.0.3</version> + </parent> + + <groupId>org.nuiton.topia</groupId> + <artifactId>topia-test-ANOMALIE-1640</artifactId> + <version>@pom.version@</version> + + <!-- ************************************************************* --> + <!-- *** Project Information ************************************* --> + <!-- ************************************************************* --> + + <name>Topia :: ANOMALIE-1640</name> + <description>Module de test pour Topia avec entités abstraites</description> + <url>http://nuiton.org/issues/1640</url> + + <!-- ************************************************************* --> + <!-- *** Build Environment ************************************** --> + <!-- ************************************************************* --> + <properties> + <eugeneVersion>@eugeneVersion@</eugeneVersion> + <hibernateVersion>@hibernateVersion@</hibernateVersion> + <slf4jVersion>@sl4jVersion@</slf4jVersion> + <h2Version>@h2Version@</h2Version> + </properties> + + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>topia-persistence</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>org.hibernate</groupId> + <artifactId>hibernate-core</artifactId> + <version>${hibernateVersion}</version> + </dependency> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + <version>${h2Version}</version> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-jcl</artifactId> + <scope>test</scope> + <version>${slf4jVersion}</version> + </dependency> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + <scope>test</scope> + <version>${slf4jVersion}</version> + </dependency> + + <dependency> + <groupId>log4j</groupId> + <artifactId>log4j</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.nuiton.eugene</groupId> + <artifactId>eugene-maven-plugin</artifactId> + <version>${eugeneVersion}</version> + <configuration> + <inputs>zargo</inputs> + <resolver>org.nuiton.util.FasterCachedResourceResolver</resolver> + </configuration> + <executions> + <execution> + <id>generate-entities</id> + <phase>generate-sources</phase> + <configuration> + <!-- Corresponding to extracted package from zargo file --> + <fullPackagePath>org.nuiton.topia.testabstract</fullPackagePath> + <!-- DefaultPackage used for DAOHelper generation --> + <defaultPackage>org.nuiton.topia.testabstract</defaultPackage> + <templates> + org.nuiton.topia.generator.TopiaMetaTransformer + </templates> + </configuration> + <goals> + <goal>smart-generate</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + +</project> diff --git a/topia-persistence/src/it/ANOMALIE-1640/postbuild.groovy b/topia-persistence/src/it/ANOMALIE-1640/postbuild.groovy new file mode 100644 index 0000000..09d9cd9 --- /dev/null +++ b/topia-persistence/src/it/ANOMALIE-1640/postbuild.groovy @@ -0,0 +1,25 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +// Tests are made in the test phase +return true; diff --git a/topia-persistence/src/it/ANOMALIE-1640/src/main/xmi/topiatest-absract.properties b/topia-persistence/src/it/ANOMALIE-1640/src/main/xmi/topiatest-absract.properties new file mode 100644 index 0000000..94a2037 --- /dev/null +++ b/topia-persistence/src/it/ANOMALIE-1640/src/main/xmi/topiatest-absract.properties @@ -0,0 +1,26 @@ +### +# #%L +# ToPIA :: Persistence +# $Id$ +# $HeadURL$ +# %% +# Copyright (C) 2004 - 2014 CodeLutin +# %% +# 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 General Lesser Public License for more details. +# +# You should have received a copy of the GNU General Lesser Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/lgpl-3.0.html>. +# #L% +### +model.tagvalue.copyright=/* *##%\n Copyright (C) 2011 Code Lutin\n *##%*/ +model.tagvalue.notGenerateToString=true +model.tagvalue.constantPrefix=PROPERTY_ diff --git a/topia-persistence/src/it/ANOMALIE-1640/src/main/xmi/topiatest-absract.zargo b/topia-persistence/src/it/ANOMALIE-1640/src/main/xmi/topiatest-absract.zargo new file mode 100644 index 0000000..49a05da Binary files /dev/null and b/topia-persistence/src/it/ANOMALIE-1640/src/main/xmi/topiatest-absract.zargo differ diff --git a/topia-persistence/src/it/ANOMALIE-1640/src/test/java/org/nuiton/topia/testabstract/TopiaAbstractTest.java b/topia-persistence/src/it/ANOMALIE-1640/src/test/java/org/nuiton/topia/testabstract/TopiaAbstractTest.java new file mode 100644 index 0000000..56070ae --- /dev/null +++ b/topia-persistence/src/it/ANOMALIE-1640/src/test/java/org/nuiton/topia/testabstract/TopiaAbstractTest.java @@ -0,0 +1,48 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.testabstract; + +import org.junit.Test; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaContextFactory; +import org.nuiton.topia.TopiaException; + +public class TopiaAbstractTest { + + @Test + public void testCreateCar() throws TopiaException { + + TopiaContext context = TopiaContextFactory.getContext(); + try { + TopiaContext transaction = context.beginTransaction(); + + CarDAO carDAO = TopiaTestAbstractDAOHelper.getCarDAO(transaction); + carDAO.create(); + transaction.commitTransaction(); + } finally { + context.closeContext(); + } + } + +} diff --git a/topia-persistence/src/it/ANOMALIE-1640/src/test/resources/TopiaContextImpl.properties b/topia-persistence/src/it/ANOMALIE-1640/src/test/resources/TopiaContextImpl.properties new file mode 100644 index 0000000..4a11cdc --- /dev/null +++ b/topia-persistence/src/it/ANOMALIE-1640/src/test/resources/TopiaContextImpl.properties @@ -0,0 +1,35 @@ +### +# #%L +# ToPIA :: Persistence +# $Id$ +# $HeadURL$ +# %% +# Copyright (C) 2004 - 2014 CodeLutin +# %% +# 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 General Lesser Public License for more details. +# +# You should have received a copy of the GNU General Lesser Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/lgpl-3.0.html>. +# #L% +### +topia.persistence.classes=org.nuiton.topia.testabstract.CarImpl,org.nuiton.topia.testabstract.VehicleImpl + +hibernate.hbm2ddl.auto=update +hibernate.show_sql=false +hibernate.format_sql=false + +hibernate.dialect=org.hibernate.dialect.H2Dialect +hibernate.connection.username=sa +hibernate.connection.password= +hibernate.connection.driver_class=org.h2.Driver +hibernate.connection.url=jdbc:h2:file:target/surefire-workdir/db/mydb + diff --git a/topia-persistence/src/it/ANOMALIE-1640/src/test/resources/log4j.properties b/topia-persistence/src/it/ANOMALIE-1640/src/test/resources/log4j.properties new file mode 100644 index 0000000..4a1ee01 --- /dev/null +++ b/topia-persistence/src/it/ANOMALIE-1640/src/test/resources/log4j.properties @@ -0,0 +1,29 @@ +### +# #%L +# ToPIA :: Persistence +# $Id$ +# $HeadURL$ +# %% +# Copyright (C) 2004 - 2014 CodeLutin +# %% +# 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 General Lesser Public License for more details. +# +# You should have received a copy of the GNU General Lesser Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/lgpl-3.0.html>. +# #L% +### +# Global logging configuration +log4j.rootLogger=INFO, stdout +# Console output... +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%d{dd MMM yyyy HH:mm:ss} %5p [%t] (%F:%L) %M - %m%n diff --git a/topia-persistence/src/it/settings.xml b/topia-persistence/src/it/settings.xml new file mode 100644 index 0000000..6ec904d --- /dev/null +++ b/topia-persistence/src/it/settings.xml @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Persistence + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<settings> + + <profiles> + <profile> + <id>it-repo</id> + <activation> + <activeByDefault>true</activeByDefault> + </activation> + <repositories> + <repository> + <id>local.central</id> + <url>file:///@localRepository@</url> + <releases> + <enabled>true</enabled> + </releases> + <snapshots> + <enabled>true</enabled> + </snapshots> + </repository> + + <!-- depot des releases nuiton --> + <repository> + <id>nuiton.release</id> + <name>NuitonReleaseRepository</name> + <url>http://nexus.nuiton.org/nexus/content/groups/public/</url> + <snapshots> + <enabled>true</enabled> + </snapshots> + <releases> + <enabled>true</enabled> + <checksumPolicy>warn</checksumPolicy> + </releases> + </repository> + + </repositories> + + <pluginRepositories> + <pluginRepository> + <id>local.central</id> + <url>file:///@localRepository@</url> + <releases> + <enabled>true</enabled> + </releases> + <snapshots> + <enabled>true</enabled> + </snapshots> + </pluginRepository> + + <pluginRepository> + <id>nuiton.release</id> + <name>NuitonReleaseRepository</name> + <url>http://nexus.nuiton.org/nexus/content/groups/public</url> + <snapshots> + <enabled>false</enabled> + </snapshots> + <releases> + <enabled>true</enabled> + </releases> + </pluginRepository> + </pluginRepositories> + </profile> + </profiles> +</settings> diff --git a/topia-persistence/src/license/THIRD-PARTY.properties b/topia-persistence/src/license/THIRD-PARTY.properties new file mode 100644 index 0000000..f51af0f --- /dev/null +++ b/topia-persistence/src/license/THIRD-PARTY.properties @@ -0,0 +1,53 @@ +### +# #%L +# ToPIA :: Persistence +# $Id$ +# $HeadURL$ +# %% +# Copyright (C) 2004 - 2014 CodeLutin +# %% +# 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 General Lesser Public License for more details. +# +# You should have received a copy of the GNU General Lesser Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/lgpl-3.0.html>. +# #L% +### +# Generated by org.codehaus.mojo.license.AddThirdPartyMojo +#------------------------------------------------------------------------------- +# Already used licenses in project : +# - Apache License 2.0 +# - BSD License +# - COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 +# - Common Public License Version 1.0 +# - GNU Lesser General Public License, version 2.1 +# - GNU Library or Lesser General Public License +# - Indiana University Extreme! Lab Software License, vesion 1.1.1 +# - Lesser General Public License (LGPL) v 3.0 +# - Lesser General Public License (LPGL) +# - Lesser General Public License (LPGL) v 2.1 +# - MIT License +# - MPL 1.1 +# - Mozilla Public License Version 1.0 +# - New BSD License +# - The Apache Software License, Version 2.0 +# - The H2 License, Version 1.0 +# - The SAX License +# - The W3C License +# - http://jaxen.codehaus.org/license.html +# - license.txt +#------------------------------------------------------------------------------- +# Please fill the missing licenses for dependencies : +# +# +#Fri Mar 15 12:36:23 CET 2013 +commons-primitives--commons-primitives--1.0=The Apache Software License, Version 2.0 +dom4j--dom4j--1.6.1=BSD License diff --git a/topia-persistence/src/main/java/org/nuiton/topia/TopiaContext.java b/topia-persistence/src/main/java/org/nuiton/topia/TopiaContext.java new file mode 100644 index 0000000..e4ef10b --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/TopiaContext.java @@ -0,0 +1,378 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia; + +import org.nuiton.topia.event.TopiaContextListener; +import org.nuiton.topia.event.TopiaEntitiesVetoable; +import org.nuiton.topia.event.TopiaEntityListener; +import org.nuiton.topia.event.TopiaEntityVetoable; +import org.nuiton.topia.event.TopiaTransactionListener; +import org.nuiton.topia.event.TopiaTransactionVetoable; +import org.nuiton.topia.framework.TopiaQuery; +import org.nuiton.topia.framework.TopiaService; +import org.nuiton.topia.persistence.TopiaEntity; + +import java.beans.PropertyChangeListener; +import java.io.File; +import java.io.Reader; +import java.io.Writer; +import java.util.List; + +/** + * TODO-FD20100507 : Need javadoc + translate the one on methods. + * <p/> + * Created: 3 janv. 2006 21:18:34 + * + * @author poussin <poussin@codelutin.com> + * @author tchemit <tchemit@codelutin.com> + * @version $Id$ + */ +public interface TopiaContext { + + /* Adders */ + + void addTopiaEntityListener(TopiaEntityListener listener); + + void addTopiaEntityListener( + Class<? extends TopiaEntity> entityClass, + TopiaEntityListener listener); + + void addTopiaEntityVetoable(TopiaEntityVetoable vetoable); + + void addTopiaEntityVetoable( + Class<? extends TopiaEntity> entityClass, + TopiaEntityVetoable vetoable); + + void addTopiaTransactionListener(TopiaTransactionListener listener); + + void addTopiaTransactionVetoable(TopiaTransactionVetoable vetoable); + + void addPropertyChangeListener(PropertyChangeListener listener); + + void addTopiaContextListener(TopiaContextListener listener); + + void addTopiaEntitiesVetoable(TopiaEntitiesVetoable vetoable); + + /* Removers */ + + void removeTopiaEntityListener(TopiaEntityListener listener); + + void removeTopiaEntityListener( + Class<? extends TopiaEntity> entityClass, + TopiaEntityListener listener); + + void removeTopiaEntityVetoable(TopiaEntityVetoable vetoable); + + void removeTopiaEntityVetoable( + Class<? extends TopiaEntity> entityClass, + TopiaEntityVetoable vetoable); + + void removeTopiaTransactionListener(TopiaTransactionListener listener); + + void removeTopiaTransactionVetoable(TopiaTransactionVetoable vetoable); + + void removePropertyChangeListener(PropertyChangeListener listener); + + void removeTopiaContextListener(TopiaContextListener listener); + + void removeTopiaEntitiesVetoable(TopiaEntitiesVetoable vetoable); + + /** + * Return true if specific service is available. + * + * @param <E> type of service + * @param interfaceService fqn of the service + * @return the service + */ + <E extends TopiaService> boolean serviceEnabled( + Class<E> interfaceService); + + /** + * Return the service. This service must be valid with public static final + * SERVICE_NAME property. + * + * @param <E> type of service + * @param interfaceService class of the service + * @return the service + * @throws TopiaNotFoundException if service can't be retrieved + */ + <E extends TopiaService> E getService(Class<E> interfaceService) + throws TopiaNotFoundException; + + /** + * Permet de créer le schema de la base de données. + * + * @throws TopiaException if any exception + */ + void createSchema() throws TopiaException; + + /** + * Permet d'afficher les requetes SQL de creation de base. + * + * @throws TopiaException if any exception + */ + void showCreateSchema() throws TopiaException; + + /** + * Permet de mettre à jour le schema de la base de données. + * + * @throws TopiaException if any exception + */ + void updateSchema() throws TopiaException; + + /** + * Return a new context containing his own transaction. + * + * @return new context with transaction + * @throws TopiaException if any exception + */ + TopiaContext beginTransaction() throws TopiaException; + + /** + * applique les modifications apporté a ce context sur la base de données. + * + * @throws TopiaException if any exception + */ + void commitTransaction() throws TopiaException; + + /** + * annule les modifications apporté a ce context. + * + * @throws TopiaException if any exception + */ + void rollbackTransaction() throws TopiaException; + + /** + * Retrieve {@link TopiaEntity} using its unique {@code id}. + * + * @param topiaId unique identifier of the entity in all the application. + * @return the entity found or null if not + * @throws TopiaException for errors on retrieving the entity + */ + TopiaEntity findByTopiaId(String topiaId) throws TopiaException; + + /** + * Retrieve results executing a simple {@code query}. Generally this method + * is used for complex query where output type is specific (more than one + * element in the SELECT). + * + * @param query TopiaQuery to execute + * @return a List of results as hibernate give us + * @throws TopiaException + * @deprecated since 2.6.12, {@link TopiaQuery} will be removed in version 3.0 + */ + @Deprecated + List findByQuery(TopiaQuery query) throws TopiaException; + + /** + * Instantiate a new TopiaQuery. + * + * @param entityClass main entity class for the Query + * @param alias alias of the entity in the Query + * @return a new TopiaQuery + * @see TopiaQuery + * @deprecated since 2.6.12, {@link TopiaQuery} will be removed in version 3.0 + */ + @Deprecated + TopiaQuery createQuery(Class<?> entityClass, String alias); + + /** + * Permet de faire une requete HQL hibernate directement sur la base. + * + * @param hql la requete a faire + * @param args les arguments de la requete + * @return La liste des resultats + * @throws TopiaException si une erreur survient durant la requete + */ + List findAll(String hql, Object... args) throws TopiaException; + + /** + * Permet de faire une requete HQL hibernate directement sur la base en + * precisant la fenetre des elements a remonter avec les parametres {@code + * startIndex} et {@code endIndex}. + * + * @param hql la requete a faire + * @param startIndex la position du premier element a remonter + * @param endIndex la position du dernier element a remonter + * @param args les arguments de la requete + * @return La liste des resultats + * @throws TopiaException si une erreur survient durant la requete + */ + List find(String hql, int startIndex, int endIndex, Object... args) + throws TopiaException; + + /** + * Allow to do some HQL query and return an unique result. If nothing if + * found by the query, will return null. If more than one result is found, + * will throw an exception. + * <p/> + * WARNING : Depending on the registered service, this method may not + * support something else than queries on TopiaEntity + * + * @param jpaql the JPA-QL query to execute + * @param paramNamesAndValues an array of query parameters based on + * [key,value,key,value,...] + * @return The result instance or null + * @throws TopiaException for any error during querying or if the the query + * returns more than one result. + */ + Object findUnique(String jpaql, Object... paramNamesAndValues) + throws TopiaException; + + /** + * Execute HQL operation on data (Update, Delete). + * + * @param hql la requete a faire + * @param args les arguments de la requete + * @return The number of entities updated or deleted. + * @throws TopiaException if any exception + */ + int execute(String hql, Object... args) throws TopiaException; + + /** + * Permet d'ajouter dans le TopiaContext une TopiaEntity créé par un autre + * context. + * + * @param e l'entity a ajouter + * @throws TopiaException if any exception + */ + void add(TopiaEntity e) throws TopiaException; + + /** + * Permet de dupliquer de ce context vers un context d'une autre base des + * données sans modification des entites. + * <p/> + * <b>Note:</b> Si le parametre <code>entityAndCondition</code> est vide, + * alors on duplique toutes les entités de la base. + * <p/> + * <b>Note 2:</b> Il se peut que la replication simple ne soit pas + * suffisante (par example si l'on veut repliquer q'une partie d'une + * entité), on utilisera donc la seconde méthode {@link + * #replicateEntities(TopiaContext, List)}. + * + * @param dstCtxt le context de la base destination + * @param entityAndCondition paramètre qui vont par deux, qui represente la + * classe de l'entity a exporter et la condition + * where que doit respecter l'objet pour etre + * exporter (entityClass, condition) + * @throws TopiaException si une erreur pendant la duplication + * @throws IllegalArgumentException si l'un des context n'est pas ouvert, ou + * si on essaye de dupliquer dans la même + * base. + */ + void replicate(TopiaContext dstCtxt, Object... entityAndCondition) + throws TopiaException, IllegalArgumentException; + + /** + * Permet de dupliquer une entité du type donné vers un autre context. + * + * @param dstCtxt le context de la base destination + * @param entity l'entité à répliquer + * @param <T> le type des entités à répliquer + * @throws TopiaException si une erreur pendant la duplication + * @throws IllegalArgumentException si l'un des context n'est pas ouvert, ou + * si on essaye de dupliquer dans la même + * base. + */ + <T extends TopiaEntity> void replicateEntity(TopiaContext dstCtxt, T entity) + throws TopiaException, IllegalArgumentException; + + /** + * Permet de dupliquer les entités du type donné vers un autre context. + * + * @param dstCtxt le context de la base destination + * @param entities les entités à répliquer + * @param <T> le type des entités à répliquer + * @throws TopiaException si une erreur pendant la duplication + * @throws IllegalArgumentException si l'un des context n'est pas ouvert, ou + * si on essaye de dupliquer dans la même + * base. + */ + <T extends TopiaEntity> void replicateEntities(TopiaContext dstCtxt, + List<T> entities) + throws TopiaException, IllegalArgumentException; + + /** + * Sauve la base de données dans un format natif a la base, la + * representation n'est pas portable d'une base a l'autre. Cette methode ne + * doit être utilisé que pour un stockage temporaire utile à une + * application. + * + * @param file le nom du fichier ou stocker les informations + * @param compress si vrai compress le fichier avec gzip + * @throws TopiaException if any exception + */ + void backup(File file, boolean compress) throws TopiaException; + + /** + * Supprime toutes les tables et autres elements de la database. + * + * @param dropDatabase si vrai alors supprime aussi la base de données si la + * base utilise des fichiers les fichiers seront + * supprimé (ex: h2) ou sera fait sur la base + * (postgresql) + * @throws TopiaException if any exception + */ + void clear(boolean dropDatabase) throws TopiaException; + + /** + * Clear persistence implementation cache. + * + * @since 2.6.13 + */ + void clearCache() throws TopiaException; + + /** + * l'inverse de la methode {@link #backup(File,boolean)}. + * + * @param file le fichier ou prendre les informations, il peut-etre + * compressé avec gzip ou non. + * @throws TopiaException if any exception + */ + void restore(File file) throws TopiaException; + + /** + * Ferme le contexte. + * + * @throws TopiaException if any exception + */ + void closeContext() throws TopiaException; + + /** + * Indique si le contexte a ete ferme. + * + * @return {@code true} si le context est ferme, {@code false} autrement + */ + boolean isClosed(); + + /** + * Execute a given sql code inside this transaction. + * + * @param sqlScript the sql script to execute + * @throws TopiaException if any problem occurs while executing the sql script. + */ + void executeSQL(String sqlScript) throws TopiaException; + +} //TopiaContext diff --git a/topia-persistence/src/main/java/org/nuiton/topia/TopiaContextFactory.java b/topia-persistence/src/main/java/org/nuiton/topia/TopiaContextFactory.java new file mode 100644 index 0000000..b871dd3 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/TopiaContextFactory.java @@ -0,0 +1,194 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia; + +import org.apache.commons.collections.map.AbstractReferenceMap; +import org.apache.commons.collections.map.ReferenceMap; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.cfg.Environment; +import org.nuiton.topia.framework.TopiaContextImpl; +import org.nuiton.topia.framework.TopiaUtil; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +/** + * TODO-FD20100507 : Need javadoc + translate the one on methods. + * <p/> + * Created: 3 janv. 2006 21:19:37 + * + * @author poussin <poussin@codelutin.com> + * @author tchemit <tchemit@codelutin.com> + * @version $Id$ + */ +public class TopiaContextFactory { + + private static final Log log = LogFactory.getLog(TopiaContextFactory.class); + + private static final String DEFAULT_CONFIG_PROPERTIES = + "TopiaContextImpl.properties"; + + /** Cache contenant tous les contexts deja créé. */ + protected static Map<Properties, TopiaContext> contextCache = + new ReferenceMap(AbstractReferenceMap.HARD, + AbstractReferenceMap.SOFT); + + public final static String CONFIG_DEFAULT_SCHEMA = Environment.DEFAULT_SCHEMA; + + public final static String CONFIG_USER = Environment.USER; + + public final static String CONFIG_PASS = Environment.PASS; + + public final static String CONFIG_DRIVER = Environment.DRIVER; + + public final static String CONFIG_DIALECT = Environment.DIALECT; + + public final static String CONFIG_CONNECTION_PROVIDER = Environment.CONNECTION_PROVIDER; + + public final static String CONFIG_BYTECODE_PROVIDER = Environment.BYTECODE_PROVIDER; + + public final static String CONFIG_CURRENT_SESSION_CONTEXT_CLASS = Environment.CURRENT_SESSION_CONTEXT_CLASS; + + public final static String CONFIG_GENERATE_STATISTICS = Environment.GENERATE_STATISTICS; + + public final static String CONFIG_FORMAT_SQL = Environment.FORMAT_SQL; + + public final static String CONFIG_HBM2DDL_AUTO = Environment.HBM2DDL_AUTO; + + public final static String CONFIG_POOL_SIZE = Environment.POOL_SIZE; + + public final static String CONFIG_SHOW_SQL = Environment.SHOW_SQL; + + public final static String CONFIG_URL = Environment.URL; + + public final static String CONFIG_PERSISTENCE_DIRECTORIES = + "topia.persistence.directories"; + + public final static String CONFIG_PERSISTENCE_CLASSES = + "topia.persistence.classes"; + + public final static String CONFIG_PERSISTENCE_PROPERTIES_FILE = + "topia.persistence.properties.file"; + + /** + * Permet de connaitre la liste des contexts encore en memoire, utile pour + * du debuggage. + * + * @return la liste des urls de connexion + */ + public static List<String> getContextOpened() { + List<String> result = new ArrayList<String>(); + for (Properties e : contextCache.keySet()) { + // Useless test : will never happened that e.getValue() is null, + // not allowed for {@link AbstractReferenceMap#SOFT}. +// if (e.getValue() != null) { + result.add(e.getProperty(CONFIG_URL)); +// } + } + return result; + } + + /** + * Used when TopiaContext root is closed + * + * @param context closed + */ + public static void removeContext(TopiaContext context) { +// Properties key = null; +// for (Entry<Properties, TopiaContextImpl> e : contextCache.entrySet()) { +// if (e.getValue() == context) { +// key = e.getKey(); +// break; +// } +// } +// if (key != null) { +// contextCache.remove(key); +// } + + // Replaced by more powerful algorithm using iterator to remove context + + Iterator<TopiaContext> it = contextCache.values().iterator(); + + while (it.hasNext()) { + TopiaContext curr = it.next(); + if (curr == context) { + it.remove(); + break; + } + } + } + + /** + * Utilise par defaut le fichier de propriete TopiaContextImpl.properties + * + * @return the context using the default configuration file + * @throws TopiaNotFoundException Si le fichier de configuration par defaut + * n'est pas retrouvé. + */ + public static TopiaContext getContext() throws TopiaNotFoundException { + Properties config = TopiaUtil.getProperties(DEFAULT_CONFIG_PROPERTIES); + TopiaContext result = getContext(config); + return result; + } + + /** + * Methode static permettant de recuperer un context. Si on donne plusieurs + * fois le meme objet config, on obtient la meme instance de + * TopiaContextImpl. Si le context qui devrait etre retourné est ferme, + * alors un nouveau est creer et retourné. + * + * @param config the configuration of the context + * @return Un TopiaContext ouvert + * @throws TopiaNotFoundException if any pb + */ + public static TopiaContext getContext(Properties config) + throws TopiaNotFoundException { + // Put all properties from a hierarchy in the current properties object. + // Resolve problem with hibernate which used iterator to get properties + // and so only values from the current properties object and not all + // hierarchy + Properties cloned = new Properties(); + for (String key : config.stringPropertyNames()) { + cloned.setProperty(key, config.getProperty(key)); + } + TopiaContext result = contextCache.get(cloned); + // useless test, context is automatically removed from Factory when closed + if (result == null/* || result.isClosed()*/) { + result = new TopiaContextImpl(cloned); + if (log.isDebugEnabled()) { + log.debug("instantiate new topiaContext : " + result); + } + contextCache.put(cloned, result); + } else if (log.isDebugEnabled()) { + log.debug("topiaContext found : " + result); + } + return result; + } + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/TopiaException.java b/topia-persistence/src/main/java/org/nuiton/topia/TopiaException.java new file mode 100644 index 0000000..bc2bc9e --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/TopiaException.java @@ -0,0 +1,75 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia; + +/** + * Main exception for Topia errors on hibernate manipulations. + * + * Created: 23 déc. 2005 23:03:36 + * + * @author poussin <poussin@codelutin.com> + * @version $Id$ + */ +public class TopiaException extends Exception { + + /** Version UID */ + private static final long serialVersionUID = -1251439453383121393L; + + /** + * Default constructor. + */ + public TopiaException() { + } + + /** + * Constructor with {@code message}. + * + * @param message exception message + */ + public TopiaException(String message) { + super(message); + } + + /** + * Constructor for a wrapped TopiaException over a {@code cause} + * with a {@code message}. + * + * @param message exception message + * @param cause exception cause + */ + public TopiaException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor for a wrapped TopiaException over a {@code cause}. + * + * @param cause exception cause + */ + public TopiaException(Throwable cause) { + super(cause); + } + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/TopiaNotFoundException.java b/topia-persistence/src/main/java/org/nuiton/topia/TopiaNotFoundException.java new file mode 100644 index 0000000..709a761 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/TopiaNotFoundException.java @@ -0,0 +1,74 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia; + +/** + * TODO-FD20100507 : Need javadoc. + * + * Created: 23 déc. 2005 23:04:28 + * + * @author poussin <poussin@codelutin.com> + * @version $Id$ + */ +public class TopiaNotFoundException extends TopiaException { + + /** Version UID */ + private static final long serialVersionUID = -8206486077608923797L; + + /** + * Default constructor. + */ + public TopiaNotFoundException() { + } + + /** + * Constructor with {@code message}. + * + * @param message exception message + */ + public TopiaNotFoundException(String message) { + super(message); + } + + /** + * Constructor for a wrapped TopiaNotFoundException over a {@code cause} + * with a {@code message}. + * + * @param message exception message + * @param cause exception cause + */ + public TopiaNotFoundException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor for a wrapped TopiaNotFoundException over a {@code cause}. + * + * @param cause exception cause + */ + public TopiaNotFoundException(Throwable cause) { + super(cause); + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/TopiaRuntimeException.java b/topia-persistence/src/main/java/org/nuiton/topia/TopiaRuntimeException.java new file mode 100644 index 0000000..581a248 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/TopiaRuntimeException.java @@ -0,0 +1,73 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia; + +/** + * TODO-FD20100507 : Need javadoc. + * + * @author chatellier <chatellier@codelutin.com> + * @version $Id$ + */ +public class TopiaRuntimeException extends RuntimeException { + + /** Version UID */ + private static final long serialVersionUID = 4706337137948838375L; + + /** + * Default constructor. + */ + public TopiaRuntimeException() { + } + + /** + * Constructor with {@code message}. + * + * @param message exception message + */ + public TopiaRuntimeException(String message) { + super(message); + } + + /** + * Constructor for a wrapped TopiaRuntimeException over a {@code cause} + * with a {@code message}. + * + * @param message exception message + * @param cause exception cause + */ + public TopiaRuntimeException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor for a wrapped TopiaRuntimeException over a {@code cause}. + * + * @param cause exception cause + */ + public TopiaRuntimeException(Throwable cause) { + super(cause); + } + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/TopiaVetoException.java b/topia-persistence/src/main/java/org/nuiton/topia/TopiaVetoException.java new file mode 100644 index 0000000..37b1c86 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/TopiaVetoException.java @@ -0,0 +1,75 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia; + +/** + * TODO-FD20100507 : Need javadoc. + * + * Created: 5 janv. 2006 00:47:51 + * + * @author poussin <poussin@codelutin.com> + * @version $Id$ + */ +public class TopiaVetoException extends RuntimeException { + + /** Version UID */ + private static final long serialVersionUID = 6809613247516488399L; + + /** + * Default constructor. + */ + public TopiaVetoException() { + } + + /** + * Constructor with {@code message}. + * + * @param message exception message + */ + public TopiaVetoException(String message) { + super(message); + } + + /** + * Constructor for a wrapped TopiaVetoException over a {@code cause} + * with a {@code message}. + * + * @param message exception message + * @param cause exception cause + */ + public TopiaVetoException(String message, Throwable cause) { + super(message, cause); + } + + /** + * Constructor for a wrapped TopiaVetoException over a {@code cause}. + * + * @param cause exception cause + */ + public TopiaVetoException(Throwable cause) { + super(cause); + } + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaContextAdapter.java b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaContextAdapter.java new file mode 100644 index 0000000..0f10d44 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaContextAdapter.java @@ -0,0 +1,59 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.event; + +/** + * Adapter pattern of {@link TopiaContextListener}. + * <p/> + * This implementation does nothing but permits developpers to use this adapater + * without to have to implements all methods. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.3.4 + */ +public class TopiaContextAdapter implements TopiaContextListener { + @Override + public void preCreateSchema(TopiaContextEvent event) { + } + + @Override + public void postCreateSchema(TopiaContextEvent event) { + } + + @Override + public void preUpdateSchema(TopiaContextEvent event) { + } + + @Override + public void postUpdateSchema(TopiaContextEvent event) { + } + + @Override + public void preRestoreSchema(TopiaContextEvent event) { + } + + @Override + public void postRestoreSchema(TopiaContextEvent event) { + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaContextEvent.java b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaContextEvent.java new file mode 100644 index 0000000..42486ec --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaContextEvent.java @@ -0,0 +1,57 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.event; + +import org.nuiton.topia.TopiaContext; + +import java.util.EventObject; + +/** + * TODO-fdesbois-20100507 : Need javadoc. + * Used for Migration service. + * + * @author chatellier <chatellier@codelutin.com> + * @version $Id$ + */ +public class TopiaContextEvent extends EventObject { + + /** Version UID */ + private static final long serialVersionUID = 560256125962144181L; + + /** + * Constructor + * + * @param source + */ + public TopiaContextEvent(Object source) { + super(source); + } + + @Override + public TopiaContext getSource() { + return (TopiaContext) source; + } + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaContextListener.java b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaContextListener.java new file mode 100644 index 0000000..bf68976 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaContextListener.java @@ -0,0 +1,85 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.event; + +import java.util.EventListener; + +/** + * Listener for TopiaContext actions. + * <p/> + * Listener are notified for action such as : + * <ul> + * <li>createSchema</li> + * <li>updateSchema</li> + * <li>...</li> + * </ul> + * + * @author chatellier <chatellier@codelutin.com> + * @version $Id$ + */ +public interface TopiaContextListener extends EventListener { + + /** + * Called before createSchema call + * + * @param event evet + */ + void preCreateSchema(TopiaContextEvent event); + + /** + * Called after createSchema call + * + * @param event event + */ + void postCreateSchema(TopiaContextEvent event); + + /** + * Called before updateSchema call + * + * @param event event + */ + void preUpdateSchema(TopiaContextEvent event); + + /** + * Called after updateSchema call + * + * @param event event + */ + void postUpdateSchema(TopiaContextEvent event); + + /** + * Called after updateSchema call + * + * @param event event + */ + void preRestoreSchema(TopiaContextEvent event); + + /** + * Called after updateSchema call + * + * @param event event + */ + void postRestoreSchema(TopiaContextEvent event); +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaEntitiesEvent.java b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaEntitiesEvent.java new file mode 100644 index 0000000..3e1aca6 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaEntitiesEvent.java @@ -0,0 +1,58 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.event; + +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.persistence.TopiaEntity; + +import java.util.EventObject; +import java.util.List; + +/** + * TODO-fdesbois-20100507 : Need javadoc. + * + * @author jruchaud <jruchaud@codelutin.com> + * @version $Id$ + */ +public class TopiaEntitiesEvent<E extends TopiaEntity> extends EventObject { + + private static final long serialVersionUID = 1L; + + private List<E> entities; + + public TopiaEntitiesEvent(Object source, List<E> entities) { + super(source); + this.entities = entities; + } + + public List<E> getEntities() { + return entities; + } + + @Override + public TopiaContext getSource() { + return (TopiaContext) super.getSource(); + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaEntitiesVetoable.java b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaEntitiesVetoable.java new file mode 100644 index 0000000..4e7cb5b --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaEntitiesVetoable.java @@ -0,0 +1,42 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.event; + +import org.nuiton.topia.persistence.TopiaEntity; + +import java.util.EventListener; +import java.util.List; + +/** + * Permet de lancer des événements liés au récupération des données + * + * @author jruchaud <jruchaud@codelutin.com> + * @version $Id$ + */ +public interface TopiaEntitiesVetoable extends EventListener { + + <E extends TopiaEntity> List<E> load(TopiaEntitiesEvent<E> event); + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaEntityEvent.java b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaEntityEvent.java new file mode 100644 index 0000000..a38e3c5 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaEntityEvent.java @@ -0,0 +1,65 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.event; + +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.persistence.TopiaEntity; + +import java.util.EventObject; + +/** + * TODO-fdesbois-20100507 : Need javadoc. + * + * @author jruchaud <jruchaud@codelutin.com> + * @version $Id$ + */ +public class TopiaEntityEvent extends EventObject { + + private static final long serialVersionUID = 1L; + + private TopiaEntity entity; + + private Object[] state; + + public TopiaEntityEvent(Object source, TopiaEntity entity, Object[] state) { + super(source); + this.entity = entity; + this.state = state; + } + + public TopiaEntity getEntity() { + return entity; + } + + @Override + public TopiaContext getSource() { + return (TopiaContext) super.getSource(); + } + + public Object[] getState() { + return state; + } + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaEntityListener.java b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaEntityListener.java new file mode 100644 index 0000000..72ea2b5 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaEntityListener.java @@ -0,0 +1,46 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.event; + +import java.util.EventListener; + +/** + * TODO-fdesbois-20100507 : Need javadoc. + * + * @author poussin <poussin@codelutin.com> + * @author tchemit <tchemit@codelutin.com> + * @version $Id$ + */ +public interface TopiaEntityListener extends EventListener { + + void create(TopiaEntityEvent event); + + void load(TopiaEntityEvent event); + + void update(TopiaEntityEvent event); + + void delete(TopiaEntityEvent event); + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaEntityVetoable.java b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaEntityVetoable.java new file mode 100644 index 0000000..31f973f --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaEntityVetoable.java @@ -0,0 +1,46 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.event; + +import java.util.EventListener; + +/** + * TODO-fdesbois-20100507 : Need javadoc. + * + * @author jruchaud <jruchaud@codelutin.com> + * @author tchemit <tchemit@codelutin.com> + * @version $Id$ + */ +public interface TopiaEntityVetoable extends EventListener { + + void create(TopiaEntityEvent event); + + void load(TopiaEntityEvent event); + + void update(TopiaEntityEvent event); + + void delete(TopiaEntityEvent event); + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaTransactionEvent.java b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaTransactionEvent.java new file mode 100644 index 0000000..8878620 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaTransactionEvent.java @@ -0,0 +1,116 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.event; + +import org.apache.commons.collections.map.IdentityMap; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.framework.EntityState; +import org.nuiton.topia.persistence.TopiaEntity; + +import java.util.EventObject; +import java.util.Map; +import java.util.Set; + +/** + * Event fires for {@link TopiaTransactionListener}. + * + * @author poussin <poussin@codelutin.com> + * @version $Id$ + * @see TopiaTransactionListener + */ +public class TopiaTransactionEvent extends EventObject { + + private static final long serialVersionUID = 1L; + + private Map<TopiaEntity, EntityState> entities = new IdentityMap(); + + public TopiaTransactionEvent(TopiaContext source) { + super(source); + } + + public TopiaTransactionEvent(TopiaContext source, + Map<TopiaEntity, EntityState> entities) { + this(source); + this.entities.putAll(entities); + } + + public Set<TopiaEntity> getEntities() { + return entities.keySet(); + } + + public boolean isLoad(TopiaEntity entity) { + EntityState state = entities.get(entity); + return state != null && state.isLoad(); + } + + public boolean isRead(TopiaEntity entity) { + EntityState state = entities.get(entity); + return state != null && state.isRead(); + } + + public boolean isCreate(TopiaEntity entity) { + EntityState state = entities.get(entity); + return state != null && state.isCreate(); + } + + public boolean isUpdate(TopiaEntity entity) { + EntityState state = entities.get(entity); + return state != null && state.isUpdate(); + } + + public boolean isDelete(TopiaEntity entity) { + EntityState state = entities.get(entity); + return state != null && state.isDelete(); + } + + public boolean isModification(TopiaEntity entity) { + EntityState state = entities.get(entity); + return state != null + && (state.isCreate() || state.isUpdate() || state.isDelete()); + } + + @Override + public TopiaContext getSource() { + return (TopiaContext) super.getSource(); + } + + /** + * @return the source context that fires the event + * @deprecated since 2.3.4, prefer the overriden {@link #getSource()}. + */ + @Deprecated + public TopiaContext getTopiaContext() { + return getSource(); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + if (entities != null) { + entities.clear(); + entities = null; + } + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaTransactionListener.java b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaTransactionListener.java new file mode 100644 index 0000000..de4ddf4 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaTransactionListener.java @@ -0,0 +1,71 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.event; + +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.framework.TopiaFiresSupport; + +import java.util.EventListener; + +/** + * To listen transaction operations as commit and rollback. + * <p/> + * <b>Warning:</b> Must be attached to the current transaction. + * <p/> + * {@link TopiaContext} listens such listeners via {@code javaBeans} methods : + * <ul> + * <li>{@link TopiaContext#addTopiaTransactionListener(TopiaTransactionListener)}</li> + * <li>{@link TopiaContext#removeTopiaTransactionListener(TopiaTransactionListener)}</li> + * </ul> + * + * @author poussin <poussin@codelutin.com> + * @version $Id$ + * @see TopiaContext + * @see TopiaTransactionEvent + * @see TopiaFiresSupport#fireOnPostCommit(TopiaContextImplementor) + * @see TopiaFiresSupport#fireOnPostRollback(TopiaContextImplementor) + */ +public interface TopiaTransactionListener extends EventListener { + + /** + * Fired by {@link TopiaFiresSupport#fireOnPostCommit(TopiaContextImplementor)}. + * <p/> + * Says after a commit was performed on listened transaction. + * + * @param event the transaction event + */ + void commit(TopiaTransactionEvent event); + + /** + * Fired by {@link TopiaFiresSupport#fireOnPostRollback(TopiaContextImplementor)}. + * <p/> + * Says after a rollback was performed on listened transaction. + * + * @param event the transaction event + */ + void rollback(TopiaTransactionEvent event); + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaTransactionVetoable.java b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaTransactionVetoable.java new file mode 100644 index 0000000..3e76409 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/event/TopiaTransactionVetoable.java @@ -0,0 +1,40 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.event; + +import java.util.EventListener; + +/** + * TODO-fdesbois-20100507 : Need javadoc. + * + * @author jruchaud <jruchaud@codelutin.com> + * @author tchemit <tchemit@codelutin.com> + * @version $Id$ + */ +public interface TopiaTransactionVetoable extends EventListener { + + void beginTransaction(TopiaTransactionEvent event); + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/event/package-info.java b/topia-persistence/src/main/java/org/nuiton/topia/event/package-info.java new file mode 100644 index 0000000..fd05f08 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/event/package-info.java @@ -0,0 +1,28 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +/** + * Events used for topia services. + * TODO-fdesbois-20100507 : Need more javadoc. + */ +package org.nuiton.topia.event; diff --git a/topia-persistence/src/main/java/org/nuiton/topia/framework/EntityFilter.java b/topia-persistence/src/main/java/org/nuiton/topia/framework/EntityFilter.java new file mode 100644 index 0000000..1f05da2 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/framework/EntityFilter.java @@ -0,0 +1,169 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.framework; + +import java.beans.PropertyChangeListener; + +/** + * Created: 3 juin 2010 + * + * @author fdesbois <fdesbois@codelutin.com> + * @version $Id$ + */ +public interface EntityFilter { + + String PROPERTY_START_INDEX = "startIndex"; + + String PROPERTY_END_INDEX = "endIndex"; + + String PROPERTY_ORDER_BY = "orderBy"; + + String PROPERTY_REFERENCE_ID = "referenceId"; + + String PROPERTY_REFERENCE_PROPERTY = "referenceProperty"; + + /** + * Get the value of startIndex + * + * @return the value of startIndex + */ + Integer getStartIndex(); + + /** + * Set the value of startIndex + * + * @param startIndex new value of startIndex + */ + void setStartIndex(Integer startIndex); + + /** + * Get the value of orderBy + * + * @return the value of orderBy + */ + String getOrderBy(); + + /** + * Set the value of orderBy + * + * @param orderBy new value of orderBy + */ + void setOrderBy(String orderBy); + + /** + * Get the value of endIndex + * + * @return the value of endIndex + */ + Integer getEndIndex(); + + /** + * Set the value of endIndex + * + * @param endIndex new value of endIndex + */ + void setEndIndex(Integer endIndex); + + /** + * Get the value of referenceId + * + * @return the value of referenceId + */ + String getReferenceId(); + + /** + * Set the value of referenceId + * + * @param referenceId + */ + void setReferenceId(String referenceId); + + /** + * Set the value of referenceId from {@code entity} + * + * @param entity + */ + void setReference(Object entity) throws IllegalArgumentException; + + /** + * Used to check if the filter contains a reference. + * + * @return true if the filter contains a reference + */ + boolean hasReference(); + + /** + * Use to check if {@code reference} class is supported by the current + * filter reference. The reference can be not {@code mandatory}. Exceptions + * are thrown if the check failed. If you prefer to have a boolean instead + * of exceptions, you can use {@link #isClassReference(Class)}. + * + * @param reference Class reference to check + * @param mandatory If the existence of the reference is mandatory + * @throws IllegalArgumentException for errors on check + * @see #hasReference() + * @see #isClassReference(Class) + */ + void checkReference(Class<?> reference, boolean mandatory) + throws IllegalArgumentException; + + /** + * Test if the {@code entityClass} is corresponding to the current reference + * in the filter. Will return false if no reference is set in the filter. + * + * @param entityClass Class reference to test + * @return true if the classReference is corresponding, false otherwise + * @see #checkReference(Class, boolean) + */ + boolean isClassReference(Class<?> entityClass); + + /** + * Get the value of referenceProperty + * + * @return the value of referenceProperty + */ + String getReferenceProperty(); + + /** + * Set the value of referenceProperty + * + * @param referenceProperty + */ + void setReferenceProperty(String referenceProperty); + + /** + * Add PropertyChangeListener. + * + * @param listener + */ + void addPropertyChangeListener(PropertyChangeListener listener); + + /** + * Remove PropertyChangeListener. + * + * @param listener + */ + void removePropertyChangeListener(PropertyChangeListener listener); + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/framework/EntityState.java b/topia-persistence/src/main/java/org/nuiton/topia/framework/EntityState.java new file mode 100644 index 0000000..4946a50 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/framework/EntityState.java @@ -0,0 +1,155 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * + * EntityState.java + * + * Created: 22 nov. 06 12:10:57 + * + * @author poussin <poussin@codelutin.com> + * @version $Revision$ + * + * Last update: $Date$ + * by : $Author$ + */ + +package org.nuiton.topia.framework; + +/** + * Used to know the state of entity during transaction. + * + * @author poussin <poussin@codelutin.com> + */ +public class EntityState implements Comparable<EntityState>{ + + final static int NULL = 0; + + final static int LOAD = 1; + + final static int READ = 2; + + final static int CREATE = 4; + + final static int UPDATE = 8; + + final static int DELETE = 16; + + /** internal representation of all states of entity (use bitwise operation). */ + protected int state = NULL; + + /** + * Add load state. + * <p/> + * After the invocation, method {@link #isLoad()} will always return {@code true}. + */ + public void addLoad() { + state |= LOAD; + } + + /** + * Add read state + * <p/> + * After the invocation, method {@link #isRead()} will always return {@code true}. + */ + public void addRead() { + state |= READ; + } + + /** + * Add create state. + * <p/> + * After the invocation, method {@link #isCreate()} will always return {@code true}. + */ + public void addCreate() { + state |= CREATE; + } + + /** + * Add update state. + * <p/> + * After the invocation, method {@link #isUpdate()} will always return {@code true}. + */ + public void addUpdate() { + state |= UPDATE; + } + + /** + * Add delete state. + * <p/> + * After the invocation, method {@link #isDelete()} will always return {@code true}. + */ + public void addDelete() { + state |= DELETE; + } + + /** + * Tells if the {@link #LOAD} state is on. + * + * @return {@code true} if {@link #LOAD} state is on, {@code false} otherwise. + */ + public boolean isLoad() { + return (state & LOAD) == LOAD; + } + + /** + * Tells if the {@link #READ} state is on. + * + * @return {@code true} if {@link #READ} state is on, {@code false} otherwise. + */ + public boolean isRead() { + return (state & READ) == READ; + } + + /** + * Tells if the {@link #CREATE} state is on. + * + * @return {@code true} if {@link #CREATE} state is on, {@code false} otherwise. + */ + public boolean isCreate() { + return (state & CREATE) == CREATE; + } + + /** + * Tells if the {@link #UPDATE} state is on. + * + * @return {@code true} if {@link #UPDATE} state is on, {@code false} otherwise. + */ + public boolean isUpdate() { + return (state & UPDATE) == UPDATE; + } + + /** + * Tells if the {@link #DELETE} state is on. + * + * @return {@code true} if {@link #DELETE} state is on, {@code false} otherwise. + */ + public boolean isDelete() { + return (state & DELETE) == DELETE; + } + + @Override + public int compareTo(EntityState o) { + return state - o.state; + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaConnectionProvider.java b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaConnectionProvider.java new file mode 100644 index 0000000..af4e0ec --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaConnectionProvider.java @@ -0,0 +1,353 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.framework; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.HibernateException; +import org.hibernate.cfg.Environment; +import org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.internal.util.ReflectHelper; +import org.hibernate.internal.util.config.ConfigurationHelper; +import org.hibernate.service.UnknownUnwrapTypeException; +import org.hibernate.service.spi.Configurable; +import org.hibernate.service.spi.Stoppable; + +/** + * Customized connection provider. + * <p/> + * This provider fix the following bug : + * http://nuiton.org/issues/show/561 + * <p/> + * To use this connection provider, add this property to topia configuration + * <p/> + * <pre> + * config.setProperty(Environment.CONNECTION_PROVIDER, TopiaConnectionProvider.class.getName()); + * </pre> + * <p/> + * or in a properties file : + * <p/> + * <pre> + * hibernate.connection.provider_class=org.nuiton.topia.framework.TopiaConnectionProvider + * </pre> + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.3 + */ +public class TopiaConnectionProvider implements ConnectionProvider, Configurable, Stoppable { + + /** Logger. */ + private static final Log log = + LogFactory.getLog(TopiaConnectionProvider.class); + + private static final long serialVersionUID = -8190835231054317644L; + + /** + * JDBC url of connection. + * <p/> + * This is a mandatory hibernate configuration vi the property + * {@link Environment#URL}. + */ + protected String url; + + /** All grabbed connection properties */ + protected Properties connectionProps; + + /** + * Sql isolation level to use in connection. + * <p/> + * Can be configured by hibernate property {@link Environment#ISOLATION_LEVELS}. + * + * @see Connection#getTransactionIsolation() + */ + protected Integer isolation; + + /** + * auto commit connection state. + * <p/> + * Can be configured by hibernate property {@link Environment#AUTOCOMMIT}. + * + * @see Connection#getAutoCommit() + */ + protected boolean autocommit; + + /** + * Size of connection pool. + * <p/> + * By default use {@code 20}, can be specify by using the hibernate + * configuration property {@link Environment#POOL_SIZE}. + */ + protected int poolSize; + + private boolean stopped; + + /** Our pool of connections which are not closed and availables. */ + protected final List<Connection> pool; + + public TopiaConnectionProvider() { + pool = new ArrayList<Connection>(); + } + + @Override + public void configure(Map configurationValues) throws HibernateException { + + poolSize = ConfigurationHelper.getInt(Environment.POOL_SIZE, configurationValues, 20); //default pool size 20 + if (log.isDebugEnabled()) { + log.debug("Connection pool size: " + poolSize); + } + + autocommit = ConfigurationHelper.getBoolean(Environment.AUTOCOMMIT, configurationValues); + if (log.isDebugEnabled()) { + log.debug("autocommit mode: " + autocommit); + } + + isolation = ConfigurationHelper.getInteger(Environment.ISOLATION, configurationValues); + if (isolation != null) { + if (log.isDebugEnabled()) { + log.debug("JDBC isolation level: " + + Environment.isolationLevelToString(isolation)); + } + } + + String driverClass = ConfigurationHelper.getString(Environment.DRIVER, configurationValues); + if (driverClass == null) { + + if (log.isWarnEnabled()) { + log.warn("no JDBC Driver class was specified by property " + + Environment.DRIVER); + } + } else { + try { + // trying via forName() first to be as close to DriverManager's semantics + Class.forName(driverClass); + } catch (ClassNotFoundException cnfe) { + try { + ReflectHelper.classForName(driverClass); + } catch (ClassNotFoundException e) { + String msg = "JDBC Driver class not found: " + driverClass; + log.error(msg, e); + throw new HibernateException(msg, e); + } + } + } + + url = (String) configurationValues.get(Environment.URL); + if (url == null) { + String msg = "JDBC URL was not specified by property " + Environment.URL; + if (log.isErrorEnabled()) { + log.error(msg); + } + throw new HibernateException(msg); + } + + connectionProps = + ConnectionProviderInitiator.getConnectionProperties(configurationValues); + + if (log.isDebugEnabled()) { + log.debug("using driver: " + driverClass + " at URL: " + url); + } + + // if debug level is enabled, then log the password, otherwise mask it + if (log.isTraceEnabled()) { + log.debug("connection properties: " + connectionProps); + } else if (log.isDebugEnabled()) { + log.debug("connection properties: " + + ConfigurationHelper.maskOut(connectionProps, "password")); + } + } + + @Override + public Connection getConnection() throws SQLException { + + Connection connection = null; + + synchronized (pool) { + + // try to use a connection from the pool (if any) + + while (!pool.isEmpty() && connection == null) { + int last = pool.size() - 1; + if (log.isTraceEnabled()) { + log.trace("using pooled JDBC connection, pool size: " + + last); + } + + connection = pool.remove(last); + if (connection.isClosed()) { + + // this connection is closed!, don't use it + connection = null; + + if (log.isDebugEnabled()) { + log.debug("Remove already closed connection from pool " + + connection); + } + } + } + } + + if (connection == null) { + + // the pool was empty, creates a new connection + + if (log.isDebugEnabled()) { + log.debug("opening new JDBC connection to " + url); + } + connection = DriverManager.getConnection(url, connectionProps); + } + + // configure connection + + if (isolation != null) { + connection.setTransactionIsolation(isolation); + } + if (connection.getAutoCommit() != autocommit) { + connection.setAutoCommit(autocommit); + } + + return connection; + } + + @Override + public void closeConnection(Connection conn) throws SQLException { + + // if connection is already closed, nothing has to be done + // we can't keep this connection (and can not be push in pool) + + if (conn.isClosed()) { + + if (log.isDebugEnabled()) { + log.debug("Connection [" + conn + + "] alreay closed!, will not use it any longer "); + } + return; + } + + // connection was not closed, can push it in the pool (if pool is not + // full) + + synchronized (pool) { + int currentSize = pool.size(); + if (currentSize < getPoolSize()) { + if (log.isTraceEnabled()) { + log.trace("returning connection to pool, pool size: " + + (currentSize + 1)); + } + pool.add(conn); + return; + } + } + + // pool was full, must release the connection which will be loose + + if (log.isDebugEnabled()) { + log.debug("closing JDBC connection"); + } + + conn.close(); + } + + @Override + protected void finalize() throws Throwable { + if (!stopped) { + stop(); + } + super.finalize(); + } + + @Override + public void stop() { + + if (log.isDebugEnabled()) { + log.debug("cleaning up connection pool: " + url); + } + + for (Connection connection : pool) { + try { + connection.close(); + } catch (SQLException sqle) { + if (log.isWarnEnabled()) { + log.warn("problem closing pooled connection", sqle); + } + } + } + pool.clear(); + stopped = true; + } + + @Override + public boolean supportsAggressiveRelease() { + return false; + } + + public String getUrl() { + return url; + } + + public Properties getConnectionProps() { + return connectionProps; + } + + public Integer getIsolation() { + return isolation; + } + + public List<Connection> getPool() { + return pool; + } + + public int getPoolSize() { + return poolSize; + } + + public boolean isAutocommit() { + return autocommit; + } + + @Override + public boolean isUnwrappableAs(Class unwrapType) { + return ConnectionProvider.class.equals(unwrapType) || + getClass().isAssignableFrom(unwrapType); + } + + @Override + @SuppressWarnings({"unchecked"}) + public <T> T unwrap(Class<T> unwrapType) { + if (ConnectionProvider.class.equals(unwrapType) || + getClass().isAssignableFrom(unwrapType)) { + return (T) this; + } else { + throw new UnknownUnwrapTypeException(unwrapType); + } + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaContextImpl.java b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaContextImpl.java new file mode 100644 index 0000000..2b452b3 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaContextImpl.java @@ -0,0 +1,1478 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin, Chatellier Eric + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.framework; + +import static org.nuiton.i18n.I18n.t; + +import java.beans.PropertyChangeListener; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.lang.reflect.Field; +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.WeakHashMap; +import java.util.zip.GZIPInputStream; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.FlushMode; +import org.hibernate.HibernateException; +import org.hibernate.Query; +import org.hibernate.ReplicationMode; +import org.hibernate.SQLQuery; +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.Transaction; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.Configuration; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.event.service.spi.EventListenerRegistry; +import org.hibernate.event.spi.EventType; +import org.hibernate.jdbc.Work; +import org.hibernate.service.spi.Stoppable; +import org.hibernate.tool.hbm2ddl.SchemaExport; +import org.hibernate.tool.hbm2ddl.SchemaUpdate; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaContextFactory; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaNotFoundException; +import org.nuiton.topia.event.TopiaContextListener; +import org.nuiton.topia.event.TopiaEntitiesVetoable; +import org.nuiton.topia.event.TopiaEntityListener; +import org.nuiton.topia.event.TopiaEntityVetoable; +import org.nuiton.topia.event.TopiaTransactionListener; +import org.nuiton.topia.event.TopiaTransactionVetoable; +import org.nuiton.topia.persistence.TopiaDAO; +import org.nuiton.topia.persistence.TopiaDAOImpl; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaId; + +/** + * Le TopiaContextImpl est le point d'entre pour acceder aux donnees. Il est + * configurer par un fichier de propriete + * <p/> + * List des proprietes disponible <dl> <dt> topia.persistence.properties.file + * <dd> le fichier de propriété a utiliser pour configurer hibernate + * <p/> + * <dt> topia.persistence.directories <dd> la liste des repertoires contenant + * les mappings hibernates (.hbm.xml) la liste de repertoire est separer par des + * virgules ',' + * <p/> + * <dt> topia.persistence.classes <dd> la liste des classes que doit géré + * hibernate. On peut tres bien utiliser topia.persistence.directories pour un + * ensemble d'entié du meme repertoire et topia.persistence.classes pour + * d'autres classes </dl> + * <p/> + * TopiaContextImpl.java + * <p/> + * Created: 23 déc. 2005 16:58:50 + * + * @author poussin <poussin@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @author fdesbois <desbois@codelutin.com> + * @version $Id$ + */ +//TODO-fdesbois-20100507 : Need translation of javadoc. +public class TopiaContextImpl implements TopiaContextImplementor { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + private static final Log log = LogFactory.getLog(TopiaContextImpl.class); + + /** @deprecated since 2.5.4 use directly {@link TopiaContextFactory#CONFIG_PERSISTENCE_DIRECTORIES}*/ + @Deprecated + public static final String TOPIA_PERSISTENCE_DIRECTORIES = + TopiaContextFactory.CONFIG_PERSISTENCE_DIRECTORIES; + + /** @deprecated since 2.5.4 use directly {@link TopiaContextFactory#CONFIG_PERSISTENCE_CLASSES}*/ + @Deprecated + public static final String TOPIA_PERSISTENCE_CLASSES = + TopiaContextFactory.CONFIG_PERSISTENCE_CLASSES; + + /** @deprecated since 2.5.4 use directly {@link TopiaContextFactory#CONFIG_PERSISTENCE_PROPERTIES_FILE}*/ + @Deprecated + public static final String TOPIA_PERSISTENCE_PROPERTIES_FILE = + TopiaContextFactory.CONFIG_PERSISTENCE_PROPERTIES_FILE; + + /** Le pere de ce context, les contexts initaux n'ont pas de context pere */ + protected TopiaContextImplementor parentContext; + + /** L'objet configuration utilisé pour la creation de la factory hibernate */ + protected Configuration hibernateConfiguration; + + /** + * la factory permettant de recuperer la session hibernate. Seul les + * TopiaContextImpl initiaux contiennent un hibernateFactory + */ + protected SessionFactory hibernateFactory; + + /** La session utilisé par le TopiaContextImpl */ + protected Session hibernate; + + /** Indique si le contexte a ete ferme */ + protected boolean closed; + + /** + * This flag permits to use (or not) the flush mode when doing queries. + * + * The normal usage is to says yes (that's why the default value is + * {@code true}), in that case whebn doing queries (says in method + * {@link #findAll(String, Object...)} or {@link #find(String, int, int, Object...)}) + * it will use the flush mode {@link FlushMode#AUTO}). + * + * But sometimes, when doing a lot of queries (for some imports for example), + * we do NOT want the session to be flushed each time we do a find, then you + * can set this flag to {@code false} using the method {@link #setUseFlushMode(boolean)} + * @since 2.5 + */ + protected boolean useFlushMode = true; + + /** Propriete de configuration */ + protected Properties config; + + /** cache des DAO deja chargé pour ce context */ + protected Map<Class<? extends TopiaEntity>, + TopiaDAO<? extends TopiaEntity>> daoCache = + new HashMap<Class<? extends TopiaEntity>, + TopiaDAO<? extends TopiaEntity>>(); + + /** + * Set of child context created with {@link #beginTransaction()}. We are + * listener on these context. A WeakHashMap is used to remove old context + * automically when it's not used anymore. The {@link #finalize} method will + * be executed when Garbage collector is called when reference is removed. + * The set is synchronized in case of using multi-threading. + * + * @see Collections#synchronizedSet(Set) + * @see Collections#newSetFromMap(Map) + */ + protected final Set<TopiaContextImplementor> childContext = + Collections.synchronizedSet( + Collections.newSetFromMap( + new WeakHashMap<TopiaContextImplementor, Boolean>())); + + /** key: service name; value: service instance */ + protected Map<String, TopiaService> services; + + protected TopiaFiresSupport firesSupport = new TopiaFiresSupport(); + + /** Liste des classes perssitance */ + protected List<Class<?>> persistenceClasses = new ArrayList<Class<?>>(); + + /** Default constructor, useful for tests. */ + protected TopiaContextImpl() { + } + + /** + * Constructor used by {@link TopiaContextFactory} to initialize rootContext + * using {@code config}. + * + * @param config for the new root context + * @throws TopiaNotFoundException if one of persistent class from + * configuration is not found + */ + public TopiaContextImpl(Properties config) throws TopiaNotFoundException { + this.config = config; + services = loadServices(config); + preInitServices(services); + getHibernateConfiguration(); // force mapping loading + postInitServices(services); + } + + protected String getProperExceptionMessage(Throwable eee) { + return eee.getClass().getSimpleName() + " : " + + eee.getMessage(); + } + + /* -------------------- SERVICES MANAGMENT -------------------------------*/ + + protected Map<String, TopiaService> loadServices(Properties config) { + Map<String, TopiaService> result = new HashMap<String, TopiaService>(); + // recherche des services present dans la config + for (Enumeration<?> e = config.propertyNames(); e.hasMoreElements();) { + String key = (String) e.nextElement(); + if (key.matches("^topia\\.service\\.\\w+$")) { + String classService = config.getProperty(key); + try { + Class<?> forName = Class.forName(classService); + Object newInstance = forName.getConstructor().newInstance(); + TopiaService service = (TopiaService) newInstance; + if (key.equals("topia.service." + service.getServiceName())) { + result.put(service.getServiceName(), service); + log.info(t("topia.persistence.service.loaded", + key, classService)); + } else { + log.warn(t("topia.persistence.warn.service.not.loaded", + key, service.getServiceName())); + } + } catch (Throwable eee) { + String message = + t("topia.persistence.error.service.unknown", + key, classService); + if (log.isDebugEnabled()) { + log.debug(message, eee); + } else if (log.isErrorEnabled()) { + log.error(message); + } + } + } + } + return result; + } + + protected void preInitServices(Map<String, TopiaService> services) { + for (TopiaService service : services.values()) { + if (!service.preInit(this)) { + log.warn(t("topia.persistence.warn.service.not.preInit", + service.getServiceName())); + } + } + } + + protected void postInitServices(Map<String, TopiaService> services) { + for (TopiaService service : services.values()) { + if (!service.postInit(this)) { + log.warn(t("topia.persistence.warn.service.not.postInit", + service.getServiceName())); + } + } + } + + protected TopiaService getService(String name) { + TopiaService result = getServices().get(name); + return result; + } + + protected boolean serviceEnabled(String name) { + boolean result = getServices().containsKey(name); + return result; + } + + /** + * Retrieve service name using SERVICE_NAME static field on service + * interface. + * + * @param interfaceService class of the service + * @param <E> type of the service that extends {@link + * TopiaService} + * @return the service name + * @throws IllegalAccessException if field SERVICE_NAME can't be accessed + * @throws NoSuchFieldException if no field SERVICE_NAME is defined + */ + protected <E extends TopiaService> String getServiceName( + Class<E> interfaceService) + throws IllegalAccessException, NoSuchFieldException { + Field f = interfaceService.getField("SERVICE_NAME"); + String name = (String) f.get(null); + return name; + } + + @Override + public Map<String, TopiaService> getServices() { + TopiaContextImplementor parent = getParentContext(); + + Map<String, TopiaService> result; + if (parent != null) { + result = parent.getServices(); + } else { + result = services; + } + return result; + } + + /** + * Take one service, this service must be valid service interface with + * public static final SERVICE_NAME declaration. + * + * @param <E> type of the service that extends {@link TopiaService} + * @throws TopiaNotFoundException if an error appears or service not found. + * @see #getServiceName(Class) + */ + @Override + public <E extends TopiaService> E getService(Class<E> interfaceService) + throws TopiaNotFoundException { + E result; + try { + String name = getServiceName(interfaceService); + result = (E) getService(name); + } catch (Exception eee) { + throw new TopiaNotFoundException( + t("topia.persistence.error.service.not.retreaved", + interfaceService, getProperExceptionMessage(eee)), + eee); + } + if (result == null) { + throw new TopiaNotFoundException( + t("topia.persistence.error.service.not.found", + interfaceService)); + } + return result; + } + + @Override + public <E extends TopiaService> boolean serviceEnabled( + Class<E> interfaceService) { + boolean result = false; + try { + String name = getServiceName(interfaceService); + result = serviceEnabled(name); + } catch (Exception eee) { + String message = t("topia.persistence.warn.service.not.found", + interfaceService, getProperExceptionMessage(eee)); + if (log.isDebugEnabled()) { + log.debug(message, eee); + } else if (log.isWarnEnabled()) { + log.warn(message); + } + } + return result; + } + + @Override + public Collection<TopiaService> getAllServices() { + Collection<TopiaService> result = getServices().values(); + return result; + } + + /* -------------------- CONTEXT HIERARCHY MANAGMENT ----------------------*/ + + /** + * Constructor used by {@link #beginTransaction()} to instantiate child from + * {@code parentContext}. + * + * @param parentContext context parent of the new TopiaContext child + */ + protected TopiaContextImpl(TopiaContextImplementor parentContext) { + this.parentContext = parentContext; + } + + @Override + public Set<TopiaContextImplementor> getChildContext() { + // fdesbois-20100421 : Ano #546 + // Copy the childContext into a new set + Set<TopiaContextImplementor> values; + // Synchronize copy to be thread-safe during iteration + synchronized (childContext) { + values = new HashSet<TopiaContextImplementor>(childContext); + } + return values; + } + + protected void addChildContext(TopiaContextImplementor child) { + childContext.add(child); + } + + @Override + public void removeChildContext(TopiaContextImplementor child) { + // Remove child only if this context is not already closed. + if (!closed) { + childContext.remove(child); + } + } + + @Override + public TopiaContextImplementor getParentContext() { + return parentContext; + } + + @Override + public TopiaContextImplementor getRootContext() { + TopiaContextImplementor result = this; + if (getParentContext() != null) { + result = getParentContext().getRootContext(); + } + return result; + } + + @Override + public Properties getConfig() { + if (config == null && getParentContext() != null) { + config = getParentContext().getConfig(); + } + return config; + } + + /** + * Change the value of flag {@link #useFlushMode}. + * + * @param useFlushMode the new value to set + * @see #useFlushMode + * @since 2.5 + */ + public void setUseFlushMode(boolean useFlushMode) { + this.useFlushMode = useFlushMode; + } + + /* -------------------- HIBERNATE MANAGMENT -----------------------------*/ + + @Override + public void createSchema() throws TopiaException { + try { + boolean showSchema = false; + if (log.isDebugEnabled()) { + showSchema = true; + } + getFiresSupport().firePreCreateSchema(this); + new SchemaExport(getHibernateConfiguration()).create(showSchema, + true); + getFiresSupport().firePostCreateSchema(this); + } catch (HibernateException eee) { + throw new TopiaException( + t("topia.persistence.error.create.schema", + eee.getMessage()), eee); + } + } + + @Override + public void showCreateSchema() throws TopiaException { + try { + new SchemaExport(getHibernateConfiguration()). + execute(true, false, false, true); + } catch (HibernateException eee) { + throw new TopiaException( + t("topia.persistence.error.create.schema", + eee.getMessage()), eee); + } + } + + @Override + public void updateSchema() throws TopiaException { + try { + boolean showSchema = false; + if (log.isDebugEnabled()) { + showSchema = true; + } + getFiresSupport().firePreUpdateSchema(this); + new SchemaUpdate(getHibernateConfiguration()).execute(showSchema, + true); + getFiresSupport().firePostUpdateSchema(this); + } catch (HibernateException eee) { + throw new TopiaException( + t("topia.persistence.error.update.schema", + eee.getMessage()), eee); + } + } + + @Override + public Session getHibernate() throws TopiaException { + if (hibernate == null) { + throw new TopiaException( + t("topia.persistence.error.no.hibernate.session")); + } + return hibernate; + } + + @Override + public SessionFactory getHibernateFactory() throws TopiaNotFoundException { + if (hibernateFactory == null) { + if (getParentContext() != null) { + hibernateFactory = getParentContext().getHibernateFactory(); + } else { + + // init service registry + Properties properties = getHibernateConfiguration().getProperties(); + StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder(); + StandardServiceRegistry standardServiceRegistry = builder.applySettings(properties).build(); + hibernateFactory = getHibernateConfiguration().buildSessionFactory(standardServiceRegistry); + + EventListenerRegistry eventListenerRegistry = TopiaUtil.getHibernateService(hibernateFactory, EventListenerRegistry.class); + + TopiaFiresSupport.TopiaHibernateEvent listener = + new TopiaFiresSupport.TopiaHibernateEvent(this); + eventListenerRegistry.appendListeners(EventType.PRE_INSERT, listener); + eventListenerRegistry.appendListeners(EventType.PRE_LOAD, listener); + eventListenerRegistry.appendListeners(EventType.PRE_UPDATE, listener); + eventListenerRegistry.appendListeners(EventType.PRE_DELETE, listener); + eventListenerRegistry.appendListeners(EventType.POST_INSERT, listener); + eventListenerRegistry.appendListeners(EventType.POST_LOAD, listener); + eventListenerRegistry.appendListeners(EventType.POST_UPDATE, listener); + eventListenerRegistry.appendListeners(EventType.POST_DELETE, listener); + } + } + return hibernateFactory; + } + + @Override + public Configuration getHibernateConfiguration() + throws TopiaNotFoundException { + if (hibernateConfiguration == null) { + if (getParentContext() != null) { + hibernateConfiguration = getParentContext().getHibernateConfiguration(); + } else { + hibernateConfiguration = new Configuration(); + + // ajout des repertoires contenant les mappings hibernate + String[] dirs = getConfig().getProperty( + TopiaContextFactory.CONFIG_PERSISTENCE_DIRECTORIES, "").split(","); + for (String dir : dirs) { + dir = dir.trim(); + if (StringUtils.isNotEmpty(dir)) { + if (log.isDebugEnabled()) { + log.debug("Load persistence from dir : " + dir); + } + hibernateConfiguration.addDirectory(new File(dir)); + } + } + + // ajout des classes dites persistentes + Set<Class<?>> hibernatePersistanceClasses = new HashSet<Class<?>>(); + for (TopiaService service : getServices().values()) { + Class<?>[] classes = service.getPersistenceClasses(); + + // certains service n'ont pas de classe persistantes + if (classes != null) { + // sletellier 20110411 : http://www.nuiton.org/issues/show/1454 + hibernatePersistanceClasses.addAll(Arrays.asList(classes)); +// for (Class<?> clazz : classes) { +// hibernateConfiguration.addClass(clazz); +// } + } + } + + String listPersistenceClasses = getConfig().getProperty( + TopiaContextFactory.CONFIG_PERSISTENCE_CLASSES, ""); + + String[] classes = listPersistenceClasses.split(","); + for (String classname : classes) { + classname = classname.trim(); + if (StringUtils.isNotEmpty(classname)) { + if (log.isDebugEnabled()) { + log.debug("Load persistent class : " + classname); + } + + // XXX echatellier 20111007 ce cqui est dommage ici, c'est + // la definition de cette classe ne sert a rien (apart security) + // car pour hibernate hibernateConfiguration.addClass(persistanceClass) + // il ne se sert pas de la classe en fait et fait seulement + // un classname.replace( '.', '/' ) + ".hbm.xml"; + // pour obtenir le mapping et la reinstancier ensuite + + Class<?> clazz; + try { + clazz = Class.forName(classname); + } catch (ClassNotFoundException eee) { + if (log.isDebugEnabled()) { + log.debug("Class " + classname + " not found"); + } + throw new TopiaNotFoundException( + t("topia.persistence.error.class.not.found", + classname)); + } + persistenceClasses.add(clazz); + + // sletellier 20110411 : http://www.nuiton.org/issues/show/1454 +// hibernateConfiguration.addClass(clazz); + hibernatePersistanceClasses.add(clazz); + } + } + + // sletellier 20110411 : http://www.nuiton.org/issues/show/1454 + // Add persistance classes in hibernate config + for (Class<?> persistanceClass : hibernatePersistanceClasses) { + hibernateConfiguration.addClass(persistanceClass); + } + + Properties prop = new Properties(); + prop.putAll(hibernateConfiguration.getProperties()); + prop.putAll(getConfig()); + + // Strange behavior, all properties are already loaded from + // constructor. Difficult to use this behavior, need to have + // TOPIA_PERSISTENCE_PROPERTIES_FILE in config. + Properties propertiesFromClasspath = + TopiaUtil.getProperties(getConfig(). + getProperty(TopiaContextFactory.CONFIG_PERSISTENCE_PROPERTIES_FILE)); + + if (!propertiesFromClasspath.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("Load properties from file : " + + propertiesFromClasspath); + } + prop.putAll(propertiesFromClasspath); + } + + hibernateConfiguration.setProperties(prop); + + // tchemit 2011-05-26 When using hibernate > 3.3, need to make sure all mappings are loaded (the one from directory files are not still done). + hibernateConfiguration.buildMappings(); + } + } + return hibernateConfiguration; + } + + /* -------------------- CHILD CONTEXT AND DAOS --------------------------*/ + + @SuppressWarnings({"unchecked"}) + @Override + public <E extends TopiaEntity> TopiaDAO<E> getDAO(Class<E> entityClass) + throws TopiaException { + if (entityClass == null) { + throw new IllegalArgumentException( + t("topia.persistence.error.null.param", + "entityClass", "getDAO")); + } + if (equals(getRootContext())) { + throw new TopiaException( + t("topia.persistence.error.rootContext.access")); + } + if (getHibernateFactory().getClassMetadata(entityClass) == null && + getHibernateFactory().getClassMetadata( + entityClass.getName() + "Impl") == null && + getHibernateFactory().getClassMetadata( + entityClass.getName() + "Abstract") == null) { + + log.info(t("topia.persistence.supported.classes.for.context", + getHibernateFactory().getAllClassMetadata().keySet())); + throw new TopiaException( + t("topia.persistence.error.unsupported.class", + entityClass.getName())); + } + + TopiaDAO<E> result = (TopiaDAO<E>) daoCache.get(entityClass); + if (result == null) { + + // looking for specialized DAO + // normalement il en existe un car il est généré automatiquement + // si on utilise la génération + String daoClassname = entityClass.getName() + "DAO"; + try { + Class<TopiaDAO<E>> daoClass = + (Class<TopiaDAO<E>>) Class.forName(daoClassname); + TopiaDAO<E> spe = daoClass.getConstructor().newInstance(); + result = spe; + } catch (Exception eee) { + log.warn("specialized DAO " + daoClassname + + " not found, use default TopiaDAOHibernate"); + result = new TopiaDAOImpl<E>(); + } + + result.init(this, entityClass); + daoCache.put(entityClass, result); + } + return result; + } + + @SuppressWarnings({"unchecked"}) + @Override + public <E extends TopiaEntity, D extends TopiaDAO<E>> D getDAO(Class<E> entityClass, + Class<D> daoClass) throws TopiaException { + return (D) getDAO(entityClass); + } + + @Override + public TopiaContext beginTransaction() throws TopiaException { + checkClosed(t("topia.persistence.error.context.is.closed")); + TopiaContextImpl result = new TopiaContextImpl(this); + + SessionFactory factory = getHibernateFactory(); + result.hibernate = factory.openSession(); + + // new TopiaInterceptor(result)); + // on ne synchronise jamais les données avec la base tant que + // l'utilisateur n'a pas fait de commit du context + result.hibernate.setFlushMode(FlushMode.MANUAL); + + // tchemit 2010-12-06 propagates the value of the flag + result.useFlushMode = useFlushMode; + + // 20060926 poussin ajouter pour voir si ca regle les problemes de + // deadlock h2. Conclusion, il faut bien ouvrir une transaction + // maintenant, sinon lorsque l'on fait des acces a la base, une + // transaction par defaut est utilisé mais elle n'est jamais vraiment + // fermé ce qui pose des problemes de lock sur les tables. + try { + result.hibernate.beginTransaction(); + } catch (Exception eee) { + + // on a pas pu ouvrir la transaction, on faut donc tout fermer + // et declancher une exception + try { + result.hibernate.close(); + } catch (HibernateException e1) { + if (log.isErrorEnabled()) { + log.error("Could not close hibernate session", e1); + } + } + + throw new TopiaException( + t("topia.persistence.error.open.transaction.failed", + eee.getMessage()), + eee); + } + + // 20081217 : add child AFTER hibernate session is opened + addChildContext(result); + + // fire event + getFiresSupport().fireOnBeginTransaction(result); + return result; + } + + @Override + public void commitTransaction() throws TopiaException { + if (equals(getRootContext())) { + throw new TopiaException(t("topia.persistence.error.unsupported.operation.on.root.context", + "commit")); + } + checkClosed(t("topia.persistence.error.unsupported.operation.on.closed.context", + "commit")); + + try { +// for (TopiaDAO<? extends TopiaEntity> dao : daoCache.values()) { +// // TODO-fdesbois-20100507 : need to be removed for 2.5 version +// dao.commitTransaction(); +// } + Transaction tx = hibernate.getTransaction(); + // Transaction tx = hibernate.beginTransaction(); + hibernate.flush(); + tx.commit(); + + getFiresSupport().fireOnPostCommit(this); + TopiaContextImplementor parent = getParentContext(); + if (parent != null) { + parent.getFiresSupport().fireOnPostCommit(this); + } + + hibernate.beginTransaction(); + + // it's seem necessary to change session after commit + // NON, NON, NON, il ne faut surtout pas le faire, ca pose plein de + // probleme + // hibernate = getHibernateFactory().openSession(); + // hibernate.setFlushMode(FlushMode.NEVER); + } catch (Exception eee) { + throw new TopiaException(t("topia.persistence.error.on.commit", + eee.getMessage()), eee); + } + } + + @Override + public void rollbackTransaction() throws TopiaException { + if (equals(getRootContext())) { + throw new TopiaException(t("topia.persistence.error.unsupported.operation.on.root.context", + "rollback")); + } + checkClosed(t("topia.persistence.error.unsupported.operation.on.closed.context", + "rollback")); + try { +// for (TopiaDAO<? extends TopiaEntity> dao : daoCache.values()) { +// // TODO-fdesbois-20100507 : need to be removed for 2.5 version +// dao.rollbackTransaction(); +// } + Transaction tx = hibernate.getTransaction(); + // Transaction tx = hibernate.beginTransaction(); + hibernate.clear(); + tx.rollback(); + hibernate.close(); + // it's very important to change the session after rollback + // otherwize there are many error during next Entity's modification + hibernate = getHibernateFactory().openSession(); + hibernate.setFlushMode(FlushMode.MANUAL); + + hibernate.beginTransaction(); + + getFiresSupport().fireOnPostRollback(this); + TopiaContextImplementor parent = getParentContext(); + if (parent != null) { + parent.getFiresSupport().fireOnPostRollback(this); + } + + } catch (HibernateException eee) { + throw new TopiaException( + t("topia.persistence.error.on.rollback", + eee.getMessage()), eee); + } + } + + @Override + public void closeContext() throws TopiaException { + // Throw exception if context is already closed + checkClosed(t("topia.persistence.error.context.already.closed")); + + // FD-20100421 : Ano #546 : no need to copy childContext, the + // {@link #getChildContext()} provides a thread-safe copy to iterate + // on it. +// TopiaContextImplementor[] children = childContext.toArray( +// new TopiaContextImplementor[childContext.size()]); + + // Remove all children context + for (TopiaContextImplementor child : getChildContext()) { + // Avoid to have exception from checkClosed method on child + if (!child.isClosed()) { + child.closeContext(); + } + } + + // on se desenregistre du context pere et on ferme les connexions si + // on est pas le root context + if (!equals(getRootContext())) { + closed = true; + hibernate.close(); + getParentContext().removeChildContext(this); + } else { + if (hibernateFactory != null) { + + // XXX AThimel 03/04/14 This is just a security, I'm not sure it is useful anymore + // close connection provider if possible (http://nuiton.org/issues/2757) + ConnectionProvider service = TopiaUtil.getHibernateService(hibernateFactory, ConnectionProvider.class); + if (service != null && service instanceof Stoppable) { + Stoppable stoppable = (Stoppable) service; + stoppable.stop(); + } + + hibernateFactory.close(); + closed = true; + TopiaContextFactory.removeContext(this); + log.debug("TopiaContext removed"); + } + } + } + + /** + * Pour le context root on ferme tous les fils, et la factory hibernate. + */ + @Override + protected void finalize() throws Throwable { +// if (hibernateFactory != null) { +// closeContext(); +// hibernateFactory.close(); +// closed = true; +// log.debug("TopiaContext finalized"); +// } + if (!closed && log.isErrorEnabled()) { + log.error("TopiaContext "+this+" was not closed!"); + } + super.finalize(); + } + + @Override + public boolean isClosed() { + return closed; + } + + @Override + public void executeSQL(String sqlScript) throws TopiaException { + SQLWork sqlWork = new SQLWork(sqlScript); + try { + getHibernate().doWork(sqlWork); + } catch (HibernateException e) { + throw new TopiaException("Could not execute sql code", e); + } + } + + protected void checkClosed(String message) throws TopiaException { + if (closed) { + throw new TopiaException(message); + } + } + + /* -------------------- GLOBAL OPERATIONS ON SCHEMA ----------------------*/ + + + @SuppressWarnings({"unchecked"}) + @Override + public TopiaEntity findByTopiaId(String id) throws TopiaException { + checkClosed(t("topia.persistence.error.unsupported.operation.on.closed.context", + "findById")); + + Class<TopiaEntity> entityClass = TopiaId.getClassName(id); + TopiaDAO<TopiaEntity> dao = getDAO(entityClass); + TopiaEntity result = dao.findByTopiaId(id); + return result; + } + + @Override + public List<?> findByQuery(TopiaQuery query) throws TopiaException { + return query.execute(this); + } + + @SuppressWarnings({"unchecked"}) + @Override + public TopiaQuery createQuery(Class<?> entityClass, String alias) { + return new TopiaQuery((Class<? extends TopiaEntity>)entityClass, alias); + } + + @Override + public List<?> findAll(String hql, Object... args) throws TopiaException { + checkClosed(t("topia.persistence.error.unsupported.operation.on.closed.context", + "findAll")); + + try { + Query query = getHibernate().createQuery(hql); + for (int j = 0; j < args.length; j += 2) { + String name = (String) args[j]; + Object value = args[j + 1]; + if (value.getClass().isArray()) { + query.setParameterList(name, (Object[]) value); + } else if (value instanceof Collection<?>) { + query.setParameterList(name, (Collection<?>) value); + } else { + query.setParameter(name, value); + } + } + // tchemit 2010-11-30 reproduce the same behaviour than before with the dao legacy + if (useFlushMode) { + query.setFlushMode(FlushMode.AUTO); + } + List result = query.list(); + result = firesSupport.fireEntitiesLoad(this, result); + return result; + } catch (HibernateException eee) { + throw new TopiaException(t("topia.persistence.error.on.query", + hql, eee.getMessage()), eee); + } + } + + @Override + public List<?> find(String hql, int startIndex, int endIndex, Object... args) + throws TopiaException { + checkClosed(t("topia.persistence.error.unsupported.operation.on.closed.context", + "find")); + + try { + Query query = getHibernate().createQuery(hql); + for (int j = 0; j < args.length; j += 2) { + String name = (String) args[j]; + Object value = args[j + 1]; + if (value.getClass().isArray()) { + query.setParameterList(name, (Object[]) value); + } else if (value instanceof Collection<?>) { + query.setParameterList(name, (Collection<?>) value); + } else { + query.setParameter(name, value); + } + } + query.setFirstResult(startIndex); + query.setMaxResults(endIndex - startIndex + 1); + // tchemit 2010-11-30 reproduce the same behaviour than before with the dao legacy + if (useFlushMode) { + query.setFlushMode(FlushMode.AUTO); + } + List result = query.list(); + result = firesSupport.fireEntitiesLoad(this, result); + return result; + } catch (HibernateException eee) { + throw new TopiaException(t("topia.persistence.error.on.query", + hql, eee.getMessage()), eee); + } + } + + @Override + public Object findUnique(String hql, Object... paramNamesAndValues) + throws TopiaException { + checkClosed(t("topia.persistence.error.unsupported.operation.on.closed.context", + "findUnique")); + + List<?> results = find(hql, 0, 1, paramNamesAndValues); + + // If there is more than 1 result, throw an exception + if (results.size() > 1) { + String message = String.format( + "Query '%s' returns more than 1 unique result", hql); + throw new TopiaException(message); + } + + // otherwise return the first one, or null + Object result = null; + if (!results.isEmpty()) { + result = results.get(0); + } + return result; + } + + /** + * Execute HQL operation on data (Update, Delete) + * + * @param hql HQL query + * @param args arguments for query + * @return The number of entities updated or deleted. + * @throws TopiaException + */ + @Override + public int execute(String hql, Object... args) throws TopiaException { + checkClosed(t("topia.persistence.error.unsupported.operation.on.closed.context", + "find")); + + try { + Query query = getHibernate().createQuery(hql); + for (int j = 0; j < args.length; j += 2) { + query.setParameter((String) args[j], args[j + 1]); + } + int result = query.executeUpdate(); + return result; + } catch (HibernateException eee) { + throw new TopiaException(t("topia.persistence.error.on.query", + hql, eee.getMessage()), eee); + } + } + + @Override + public void add(TopiaEntity e) throws TopiaException { + checkClosed(t("topia.persistence.error.unsupported.operation.on.closed.context", + "add")); + + String id = e.getTopiaId(); + Class<TopiaEntity> entityClass = TopiaId.getClassName(id); + TopiaDAO<TopiaEntity> dao = getDAO(entityClass); + dao.update(e); + } + + @Override + public void replicate(TopiaContext dstCtxt, Object... entityAndCondition) + throws TopiaException, IllegalArgumentException { + checkClosed(t("topia.persistence.error.unsupported.operation.on.closed.context", + "replicate")); + + TopiaContextImpl dstContextImpl = (TopiaContextImpl) dstCtxt; + dstContextImpl.checkClosed( + t("topia.persistence.error.unsupported.operation.on.closed.context", + "replicate")); + + if (getRootContext().equals(dstContextImpl.getRootContext())) { + throw new IllegalArgumentException( + t("topia.persistence.error.replicate.on.same.context")); + } + + String[] queries = buildQueries(entityAndCondition); + try { + for (String query : queries) { + if (log.isDebugEnabled()) { + log.debug("acquire entities " + query); + } + // acquire data to replicate + List<?> entities = findAll(query); + replicate0(dstContextImpl, entities.toArray()); + if (log.isDebugEnabled()) { + log.debug("replication of entities " + query + + " was sucessfully done."); + } + } + } catch (HibernateException eee) { + throw new TopiaException(t("topia.persistence.error.on.replicate", + eee.getMessage()), eee); + } + } + + @Override + public <T extends TopiaEntity> void replicateEntity(TopiaContext dstCtxt, + T entity) + throws TopiaException, IllegalArgumentException { + checkClosed(t("topia.persistence.error.unsupported.operation.on.closed.context", + "replicateEntity")); + + TopiaContextImpl dstContextImpl = (TopiaContextImpl) dstCtxt; + dstContextImpl.checkClosed(t("topia.persistence.error.unsupported.operation.on.closed.context", + "replicateEntity")); + + if (getRootContext().equals(dstContextImpl.getRootContext())) { + throw new IllegalArgumentException(t( + "topia.persistence.error.replicate.on.same.context")); + } + replicate0(dstContextImpl, entity); + } + + @Override + public <T extends TopiaEntity> void replicateEntities(TopiaContext dstCtxt, + List<T> entities) + throws TopiaException, IllegalArgumentException { + checkClosed(t("topia.persistence.error.unsupported.operation.on.closed.context", + "replicateEntities")); + + TopiaContextImpl dstContextImpl = (TopiaContextImpl) dstCtxt; + dstContextImpl.checkClosed(t("topia.persistence.error.unsupported.operation.on.closed.context", + "replicateEntities")); + + if (getRootContext().equals(dstContextImpl.getRootContext())) { + throw new IllegalArgumentException(t("topia.persistence.error.replicate.on.same.context")); + } + replicate0(dstContextImpl, entities.toArray()); + } + + @Override + public TopiaFiresSupport getFiresSupport() { + return firesSupport; + } + + /** + * Backup database in gzip compressed file. + * + * <b>Note: </b> Only works for h2 database. + * + * @param file file to write backup + * @param compress if true then use gzip to compress file + * @see TopiaContext#backup(File,boolean) + */ + @Override + public void backup(File file, boolean compress) throws TopiaException { + checkClosed(t("topia.persistence.error.unsupported.operation.on.closed.context", + "backup")); + try { + String options = ""; + if (compress) { + options += " COMPRESSION GZIP"; + } + + SQLQuery query = getHibernate().createSQLQuery( + "SCRIPT TO '" + file.getAbsolutePath() + "'" + options); + query.list(); + + } catch (Exception eee) { + throw new TopiaException(t( + "topia.persistence.error.on.backup", + eee.getMessage()), eee); + } + } + + /** + * Read database from gzip compressed file + * <p/> + * Only work for h2 database + * + * @see TopiaContext#restore(File) + */ + @Override + public void restore(File file) throws TopiaException { + // send event + getFiresSupport().firePreRestoreSchema(this); + checkClosed(t("topia.persistence.error.unsupported.operation.on.closed.context", + "restore")); + + String sql = null; + String options = ""; + try { + // decompresse file in temporary file + InputStream in = new BufferedInputStream(new FileInputStream(file)); + try { + in.mark(2); + + // read header to see if is compressed file + int b = in.read(); + // redundant cast : int magic = ((int) in.read() << 8) | b; + int magic = in.read() << 8 | b; + in.reset(); + + if (magic == GZIPInputStream.GZIP_MAGIC) { + options += " COMPRESSION GZIP"; + } + } finally { + + in.close(); + } + + SQLQuery query = getHibernate().createSQLQuery( + "RUNSCRIPT FROM '" + file.getAbsolutePath() + "'" + options); + + query.executeUpdate(); + + // send event AFTER restore + getFiresSupport().firePostRestoreSchema(this); + } catch (Exception eee) { + throw new TopiaException(t( + "topia.persistence.error.on.restore", + sql, eee.getMessage()), eee); + } + } + + /** + * Only h2 supported for now + * + * @see TopiaContext#clear(boolean) + */ + @Override + public void clear(boolean dropDatabase) throws TopiaException { + try { + TopiaContextImpl root = (TopiaContextImpl) getRootContext(); + TopiaContextImpl tx = (TopiaContextImpl) root.beginTransaction(); + + String sql = "DROP ALL OBJECTS"; + if (dropDatabase) { + sql += " DELETE FILES"; + } + Query query = tx.getHibernate().createSQLQuery(sql); + query.executeUpdate(); + tx.closeContext(); + + // Do not invoke finalize here, we are not garbage collector... + // duplicate then the previous code of the finalize method + root.closeContext(); + root.hibernateFactory.close(); + root.closed = true; +// root.finalize(); + } catch (Throwable eee) { + throw new TopiaException( + t("topia.persistence.error.on.clear", eee.getMessage()), eee); + } + } + + /** + * Clear hibernate cache to free memory. + * + * see http://docs.jboss.org/hibernate/orm/3.5/reference/en-US/html/transactions.ht... + */ + @Override + public void clearCache() throws TopiaException { + getHibernate().clear(); + } + + @Override + public List<Class<?>> getPersistenceClasses() { + return persistenceClasses; + } + + @Override + public boolean isSchemaExist(Class<?> clazz) + throws TopiaException { + checkClosed(t("topia.persistence.error.unsupported.operation.on.closed.context", + "replicateEntity")); + boolean result = TopiaUtil.isSchemaExist(this, clazz.getName()); + return result; + } + + /* Listeners adders */ + + @Override + public void addTopiaEntityListener(TopiaEntityListener listener) { + getFiresSupport().addTopiaEntityListener(listener); + } + + @Override + public void addTopiaEntityListener( + Class<? extends TopiaEntity> entityClass, + TopiaEntityListener listener) { + getFiresSupport().addTopiaEntityListener(entityClass, listener); + } + + @Override + public void addTopiaEntityVetoable(TopiaEntityVetoable vetoable) { + getFiresSupport().addTopiaEntityVetoable(TopiaEntity.class, vetoable); + } + + @Override + public void addTopiaEntityVetoable( + Class<? extends TopiaEntity> entityClass, + TopiaEntityVetoable vetoable) { + getFiresSupport().addTopiaEntityVetoable(entityClass, vetoable); + } + + @Override + public void addTopiaTransactionListener(TopiaTransactionListener listener) { + getFiresSupport().addTopiaTransactionListener(listener); + } + + @Override + public void addTopiaTransactionVetoable(TopiaTransactionVetoable vetoable) { + getFiresSupport().addTopiaTransactionVetoable(vetoable); + } + + @Override + public void addPropertyChangeListener(PropertyChangeListener listener) { + getFiresSupport().addPropertyChangeListener(listener); + } + + @Override + public void addTopiaContextListener(TopiaContextListener listener) { + getFiresSupport().addTopiaContextListener(listener); + } + + /* Listeners removers */ + + @Override + public void removeTopiaEntityListener(TopiaEntityListener listener) { + getFiresSupport().removeTopiaEntityListener(TopiaEntity.class, + listener); + } + + @Override + public void removeTopiaEntityListener( + Class<? extends TopiaEntity> entityClass, + TopiaEntityListener listener) { + getFiresSupport().removeTopiaEntityListener(entityClass, listener); + } + + @Override + public void removeTopiaEntityVetoable(TopiaEntityVetoable vetoable) { + getFiresSupport().removeTopiaEntityVetoable(TopiaEntity.class, + vetoable); + } + + @Override + public void removeTopiaEntityVetoable( + Class<? extends TopiaEntity> entityClass, + TopiaEntityVetoable vetoable) { + getFiresSupport().removeTopiaEntityVetoable(entityClass, vetoable); + } + + @Override + public void removeTopiaTransactionListener( + TopiaTransactionListener listener) { + getFiresSupport().removeTopiaTransactionListener(listener); + } + + @Override + public void removeTopiaTransactionVetoable( + TopiaTransactionVetoable vetoable) { + getFiresSupport().removeTopiaTransactionVetoable(vetoable); + } + + @Override + public void removePropertyChangeListener(PropertyChangeListener listener) { + getFiresSupport().removePropertyChangeListener(listener); + } + + @Override + public void removeTopiaContextListener(TopiaContextListener listener) { + getFiresSupport().removeTopiaContextListener(listener); + } + + @Override + public void addTopiaEntitiesVetoable(TopiaEntitiesVetoable vetoable) { + getFiresSupport().addTopiaEntitiesVetoable(vetoable); + } + + @Override + public void removeTopiaEntitiesVetoable(TopiaEntitiesVetoable vetoable) { + getFiresSupport().removeTopiaEntitiesVetoable(vetoable); + } + + /** + * Build the list of queries from the given parameter + * <code>entityAndCondition</code>. + * <p/> + * If no parameter is given, then build the queries for all entities is db, + * with no condition. + * + * @param entityAndCondition the list of tuples (Class,String) + * @return the list of queries. + * @throws TopiaException if any pb of db while getting entities + * classes. + * @throws IllegalArgumentException if any pb with the given parameter + * (mainly ClassCastException). + */ + protected String[] buildQueries(Object... entityAndCondition) + throws TopiaException, IllegalArgumentException { + Class<?> entityClass; + String condition; + + // si entityAndcondition est vide alors il faut le remplir + // avec toutes les entités du mapping (class, null) + if (entityAndCondition.length == 0) { + Map<?,?> classMetadata = getHibernateFactory().getAllClassMetadata(); + entityAndCondition = new Object[classMetadata.size() * 2]; + int i = 0; + for (Object className : classMetadata.keySet()) { + try { + entityAndCondition[i++] = Class.forName((String) className); + } catch (ClassNotFoundException e) { + // should never happen! + throw new TopiaException( + "class cast exception for entity " + className); + } + entityAndCondition[i++] = null; + + } + } + + // prepare queries to perform beofre opening any transaction + if (entityAndCondition.length % 2 != 0) { + throw new IllegalArgumentException( + "entityAndCondition must be a couple of (Class, String)"); + } + String queries[] = new String[entityAndCondition.length / 2]; + for (int i = 0; i < entityAndCondition.length;) { + try { + entityClass = (Class<?>) entityAndCondition[i++]; + condition = (String) entityAndCondition[i++]; + String query = "from " + entityClass.getName(); + if (condition != null && !condition.isEmpty()) { + query += " where " + condition; + } + queries[(i - 1) / 2] = query; + } catch (ClassCastException e) { + if (i % 2 == 0) { + throw new IllegalArgumentException( + "Others arguement must be String not " + + entityAndCondition[i - 1], e); + } else { + throw new IllegalArgumentException( + "Others arguement must be Class not " + + entityAndCondition[i - 1], e); + } + } + } + return queries; + } + + protected void replicate0(TopiaContextImpl dstContextImpl, + Object... entities) throws TopiaException { + try { + for (Object entity : entities) { + // dettach entity to source session, to make possible copy of + // collection without a hibernate exception (list opened in + // two session...) + getHibernate().evict(entity); + dstContextImpl.getHibernate().replicate(entity, + ReplicationMode.EXCEPTION); + } + + } catch (HibernateException eee) { + throw new TopiaException(t("topia.persistence.error.on.replicate", + eee.getMessage()), eee); + } + } + + public static class SQLWork implements Work { + private final String script; + + public SQLWork(String script) { + this.script = script; + } + + @Override + public void execute(Connection connection) throws SQLException { + PreparedStatement sta = connection.prepareStatement(script); + try { + sta.execute(); + } finally { + sta.close(); + } + } + } +} //TopiaContextImpl + diff --git a/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaContextImplementor.java b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaContextImplementor.java new file mode 100644 index 0000000..bb6c969 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaContextImplementor.java @@ -0,0 +1,147 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.framework; + +import org.hibernate.Session; +import org.hibernate.SessionFactory; +import org.hibernate.cfg.Configuration; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaNotFoundException; +import org.nuiton.topia.persistence.TopiaDAO; +import org.nuiton.topia.persistence.TopiaEntity; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +/** + * Technical contract of a {@link TopiaContext}. + * + * Any implementation of the {@link TopiaContext} should also implements this + * contract. + * + * @author poussin <poussin@codelutin.com> + * @version $Id$ + */ +//TODO-fdesbois-20100507 : Need more javadoc. +public interface TopiaContextImplementor extends TopiaContext { + + /** + * Retrieve a thread-safe copy of children context set. + * + * @return Returns the childContext. + */ + Set<TopiaContextImplementor> getChildContext(); + + /** @return Returns the parentContext. */ + TopiaContextImplementor getParentContext(); + + TopiaContextImplementor getRootContext(); + + /** @return Returns the config. */ + Properties getConfig(); + + /** + * @return Returns the hibernate. + * @throws TopiaException si aucune transaction n'est ouverte + */ + Session getHibernate() throws TopiaException; + + /** + * @return Returns the hibernateFactory. + * @throws TopiaNotFoundException + */ + SessionFactory getHibernateFactory() throws TopiaNotFoundException; + + /** + * @return Returns the hibernate configuration + * @throws TopiaNotFoundException + */ + Configuration getHibernateConfiguration() + throws TopiaNotFoundException; + + /** + * Tells to the context if it has to use a flush mode before each query. + * + * By default, we use a flush mode, but in some case it costs to much doing + * this, that's why you can desactivate it setting the value to {@code false}. + * + * @param useFlushMode the new value to set + * @since 2.5 + */ + void setUseFlushMode(boolean useFlushMode); + /** + * Detect if the table is created on storage for a given persistant class. + * + * @param clazz the researched class + * @return Returns the hibernate. + * @throws TopiaException si aucune transaction n'est ouverte + */ + boolean isSchemaExist(Class<?> clazz) throws TopiaException; + + /** + * Get DAO for specified class. If Specialized DAO exists then it returned + * otherwize TopiaDAO<entityClass> is returned + * + * @param <E> type of entity + * @param entityClass type of entity + * @return the required dao + * @throws TopiaException if any error + */ + <E extends TopiaEntity> TopiaDAO<E> getDAO(Class<E> entityClass) + throws TopiaException; + + /** + * Get DAO for specified class. If Specialized DAO exists then it returned + * otherwize TopiaDAO<entityClass> is returned + * + * @param <E> type of entity + * @param entityClass type of entity + * @param daoClass the concrete dao class to use + * @return the required dao + * @throws TopiaException if any error + */ + <E extends TopiaEntity, D extends TopiaDAO<E>> D getDAO(Class<E> entityClass,Class<D> daoClass) + throws TopiaException; + + TopiaFiresSupport getFiresSupport(); + + void removeChildContext(TopiaContextImplementor child); + + Map<String, TopiaService> getServices(); + + /** + * @return a collection of {@link TopiaService} + * @deprecated since 2.3.4 : useless method, use {@link #getServices()} instead + */ + @Deprecated + Collection<TopiaService> getAllServices(); + + List<Class<?>> getPersistenceClasses(); +} //TopiaContextImplementor + diff --git a/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaFilter.java b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaFilter.java new file mode 100644 index 0000000..af467c1 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaFilter.java @@ -0,0 +1,229 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.framework; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaNotFoundException; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaId; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; + +/** + * Filter + * <p/> + * Created: 23 avr. 2010 + * + * @author fdesbois + * @since 2.0 + */ +public class TopiaFilter implements EntityFilter { + + private static final Log log = LogFactory.getLog(TopiaFilter.class); + + protected Integer startIndex; + + protected Integer endIndex; + + protected String orderBy; + + protected String referenceId; + + protected String referenceProperty; + + private PropertyChangeSupport propertyChangeSupport = + new PropertyChangeSupport(this); + + @Override + public Integer getStartIndex() { + return startIndex; + } + + @Override + public void setStartIndex(Integer startIndex) { + Integer oldStartIndex = this.startIndex; + this.startIndex = startIndex; + propertyChangeSupport.firePropertyChange(PROPERTY_START_INDEX, + oldStartIndex, startIndex); + } + + @Override + public String getOrderBy() { + return orderBy; + } + + @Override + public void setOrderBy(String orderBy) { + String oldOrderBy = this.orderBy; + this.orderBy = orderBy; + propertyChangeSupport.firePropertyChange(PROPERTY_ORDER_BY, + oldOrderBy, orderBy); + } + + @Override + public Integer getEndIndex() { + return endIndex; + } + + @Override + public void setEndIndex(Integer endIndex) { + Integer oldEndIndex = this.endIndex; + this.endIndex = endIndex; + propertyChangeSupport.firePropertyChange(PROPERTY_END_INDEX, + oldEndIndex, endIndex); + } + + @Override + public String getReferenceId() { + return referenceId; + } + + @Override + public void setReferenceId(String referenceId) { + String oldReferenceId = this.referenceId; + this.referenceId = referenceId; + propertyChangeSupport.firePropertyChange(PROPERTY_REFERENCE_ID, + oldReferenceId, referenceId); + } + + @Override + public void setReference(Object entity) throws IllegalArgumentException { + + if (! (entity instanceof TopiaEntity)) { + throw new IllegalArgumentException("Can't set reference of type '" + + entity.getClass().getName() + "' need a TopiaEntity"); + } + + setReferenceId(((TopiaEntity)entity).getTopiaId()); + } + + @Override + public boolean hasReference() { + return StringUtils.isNotEmpty(referenceId); + } + + @Override + public String getReferenceProperty() { + return referenceProperty; + } + + @Override + public void setReferenceProperty(String referenceProperty) { + String oldReferenceProperty = this.referenceProperty; + this.referenceProperty = referenceProperty; + propertyChangeSupport.firePropertyChange(PROPERTY_REFERENCE_PROPERTY, + oldReferenceProperty, referenceProperty); + } + + @Override + public void addPropertyChangeListener(PropertyChangeListener listener) { + propertyChangeSupport.addPropertyChangeListener(listener); + } + + @Override + public void removePropertyChangeListener(PropertyChangeListener listener) { + propertyChangeSupport.removePropertyChangeListener(listener); + } + + /** + * Test if the {@code entityClass} is corresponding to the current reference + * in the filter. Will return false if no reference is set in the filter. + * + * @param entityClass Class reference to test + * @return true if the classReference is corresponding, false otherwise + * @see #checkReference(Class, boolean) + */ + @Override + public boolean isClassReference(Class<?> entityClass) { + + boolean result = false; + + if (hasReference()) { + try { + Class<?> referenceClass = TopiaId.getClassName(referenceId); + if (referenceClass.isAssignableFrom(entityClass)) { + result = true; + } + } catch (TopiaNotFoundException eee) { + if (log.isWarnEnabled()) { + log.warn("ReferenceId '" + referenceId + "' is not a" + + " compatible topiaId : " + eee.getMessage()); + } + } + } + return result; + } + + /** + * Use to check if {@code reference} class is supported by the current + * filter reference. The reference can be not {@code mandatory}. Exceptions + * are thrown if the check failed. If you prefer to have a boolean instead + * of exceptions, you can use {@link #isClassReference(Class)}. + * + * @param reference Class reference to check + * @param mandatory If the existence of the reference is mandatory + * @throws IllegalArgumentException for errors on check + * @see #hasReference() + * @see #isClassReference(Class) + */ + @Override + public void checkReference(Class<?> reference, boolean mandatory) + throws IllegalArgumentException { + + if (log.isTraceEnabled()) { + log.trace("referenceClass to check : " + reference.getName()); + log.trace("mandatory : " + mandatory); + log.trace("filter hasReference : " + hasReference()); + log.trace("filter isClassReference : " + isClassReference(reference)); + } + + if (mandatory && !hasReference()) { + throw new IllegalArgumentException("The filter reference" + + " of type '" + reference.getSimpleName() + "' is mandatory !"); + } + + if (hasReference() && + !isClassReference(reference)) { + throw new IllegalArgumentException("Reference filtered need to be" + + " a '" + reference.getSimpleName() + "' (referenceId = " + + referenceId + ")"); + } + + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("TopiaFilter{"). + append("referenceProperty = '").append(referenceProperty). + append("', startIndex = ").append(startIndex). + append(", endIndex = ").append(endIndex). + append(", orderBy = '").append(orderBy). + append("' , referenceId = '").append(referenceId). + append("'}"); + return builder.toString(); + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaFiresSupport.java b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaFiresSupport.java new file mode 100644 index 0000000..6e50c43 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaFiresSupport.java @@ -0,0 +1,1068 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.framework; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.beans.VetoableChangeSupport; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.collections.map.IdentityMap; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.Session; +import org.hibernate.event.spi.PostDeleteEvent; +import org.hibernate.event.spi.PostDeleteEventListener; +import org.hibernate.event.spi.PostInsertEvent; +import org.hibernate.event.spi.PostInsertEventListener; +import org.hibernate.event.spi.PostLoadEvent; +import org.hibernate.event.spi.PostLoadEventListener; +import org.hibernate.event.spi.PostUpdateEvent; +import org.hibernate.event.spi.PostUpdateEventListener; +import org.hibernate.event.spi.PreDeleteEvent; +import org.hibernate.event.spi.PreDeleteEventListener; +import org.hibernate.event.spi.PreInsertEvent; +import org.hibernate.event.spi.PreInsertEventListener; +import org.hibernate.event.spi.PreLoadEvent; +import org.hibernate.event.spi.PreLoadEventListener; +import org.hibernate.event.spi.PreUpdateEvent; +import org.hibernate.event.spi.PreUpdateEventListener; +import org.hibernate.persister.entity.EntityPersister; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaVetoException; +import org.nuiton.topia.event.TopiaContextEvent; +import org.nuiton.topia.event.TopiaContextListener; +import org.nuiton.topia.event.TopiaEntitiesEvent; +import org.nuiton.topia.event.TopiaEntitiesVetoable; +import org.nuiton.topia.event.TopiaEntityEvent; +import org.nuiton.topia.event.TopiaEntityListener; +import org.nuiton.topia.event.TopiaEntityVetoable; +import org.nuiton.topia.event.TopiaTransactionEvent; +import org.nuiton.topia.event.TopiaTransactionListener; +import org.nuiton.topia.event.TopiaTransactionVetoable; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityAbstract; +import org.nuiton.util.CategorisedListenerSet; +import org.nuiton.util.ListenerSet; + +/** + * TODO-fdesbois-20100507 : Need translation of javadoc. + * <p/> + * Contient l'ensemble de la partie listener et vetoable c'est à dire la + * gestion, les fires, ... + * + * @author jruchaud <jruchaud@codelutin.com> + * @version $Id$ + */ +public class TopiaFiresSupport { + + static private Log log = LogFactory.getLog(TopiaFiresSupport.class); + + /** used to fire read event */ + final static Object NO_CHANGE = new Object(); + + /** used to collect entity modification during transaction */ + protected Map<TopiaEntity, EntityState> transactionEntities = + new IdentityMap(); + + protected Set<PropertyChangeListener> propertyChangeListeners = + new HashSet<PropertyChangeListener>(); + + /* Pour la transaction */ + + protected ListenerSet<TopiaTransactionListener> transactionListeners = + new ListenerSet<TopiaTransactionListener>(); + + protected ListenerSet<TopiaTransactionVetoable> transactionVetoables = + new ListenerSet<TopiaTransactionVetoable>(); + + /* Pour les entités */ + + protected CategorisedListenerSet<TopiaEntityListener> entityListeners = + new CategorisedListenerSet<TopiaEntityListener>(); + + protected CategorisedListenerSet<TopiaEntityVetoable> entityVetoables = + new CategorisedListenerSet<TopiaEntityVetoable>(); + + /* Pour les listes d'entités */ + + protected ListenerSet<TopiaEntitiesVetoable> entitiesVetoables = + new ListenerSet<TopiaEntitiesVetoable>(); + + /* Pour les actions du topia context */ + + protected ListenerSet<TopiaContextListener> topiaContextListeners = + new ListenerSet<TopiaContextListener>(); + + /** + * used to register objects loaded during transaction. + * + * @param entity the loaded entity + */ + public void warnOnLoadEntity(TopiaEntity entity) { + if (log.isDebugEnabled()) { + log.debug("warnOnReadEntity"); + } + EntityState state = transactionEntities.get(entity); + if (state == null) { + state = new EntityState(); + transactionEntities.put(entity, state); + } + state.addLoad(); + } + + /** + * used to register objects created during transaction. + * + * @param entity the created entity + */ + public void warnOnCreateEntity(TopiaEntity entity) { + if (log.isDebugEnabled()) { + log.debug("warnOnCreateEntity"); + } + EntityState state = transactionEntities.get(entity); + if (state == null) { + state = new EntityState(); + transactionEntities.put(entity, state); + } + state.addCreate(); + } + + /** + * used to register objects loaded during transaction. + * + * @param entity the read entity + */ + public void warnOnReadEntity(TopiaEntity entity) { + if (log.isDebugEnabled()) { + log.debug("warnOnReadEntity"); + } + EntityState state = transactionEntities.get(entity); + if (state == null) { + state = new EntityState(); + transactionEntities.put(entity, state); + } + state.addRead(); + } + + /** + * used to register objects modified during transaction. + * + * @param entity the updated entity + */ + public void warnOnUpdateEntity(TopiaEntity entity) { + if (log.isDebugEnabled()) { + log.debug("warnOnUpdateEntity"); + } + + EntityState state = transactionEntities.get(entity); + if (state == null) { + state = new EntityState(); + transactionEntities.put(entity, state); + } + state.addUpdate(); + } + + /** + * used to register objects deleted during transaction. + * + * @param entity the deleted entity + */ + public void warnOnDeleteEntity(TopiaEntity entity) { + if (log.isDebugEnabled()) { + log.debug("warnOnDeleteEntity"); + } + EntityState state = transactionEntities.get(entity); + if (state == null) { + state = new EntityState(); + transactionEntities.put(entity, state); + } + state.addDelete(); + } + + protected boolean isNotEmpty(ListenerSet<?> set) { + return set.size() > 0; + } + + protected boolean isNotEmpty(CategorisedListenerSet<?> set, Class<?> category) { + return set.iterator(category).hasNext(); + } + + /* Fires sur les transactions */ + + public void fireOnBeginTransaction(TopiaContextImplementor context) { + if (log.isDebugEnabled()) { + log.debug("fireOnBeginTransaction"); + } + if (isNotEmpty(transactionVetoables)) { + TopiaTransactionEvent e = new TopiaTransactionEvent(context); + for (TopiaTransactionVetoable listener : transactionVetoables) { + try { + listener.beginTransaction(e); + } catch (Exception eee) { + throw new TopiaVetoException(eee); + } + } + } + } + + public void fireOnPostCommit(TopiaContextImplementor context) { + if (log.isDebugEnabled()) { + log.debug("fireOnPostCommit"); + } + if (isNotEmpty(transactionListeners)) { + TopiaTransactionEvent e = new TopiaTransactionEvent(context, + transactionEntities); + for (TopiaTransactionListener listener : transactionListeners) { + try { + listener.commit(e); + } catch (Exception eee) { + if (log.isErrorEnabled()) { + log.error("Can't fireOnPostCommit", eee); + } + } + } + } + transactionEntities.clear(); + } + + public void fireOnPostRollback(TopiaContextImplementor context) { + if (log.isDebugEnabled()) { + log.debug("fireOnPostRollback"); + } + if (isNotEmpty(transactionListeners)) { + TopiaTransactionEvent e = new TopiaTransactionEvent(context, + transactionEntities); + for (TopiaTransactionListener listener : transactionListeners) { + try { + listener.rollback(e); + } catch (Exception eee) { + if (log.isErrorEnabled()) { + log.error("Can't fireOnPostRollback", eee); + } + } + } + } + transactionEntities.clear(); + } + + /* Fires sur les entités */ + + public void fireOnPreCreate(TopiaContextImplementor context, + TopiaEntity entity, Object[] state) { + if (log.isDebugEnabled()) { + log.debug("fireOnPreCreate"); + } + if (isNotEmpty(entityVetoables, entity.getClass())) { + TopiaEntityEvent event = new TopiaEntityEvent(context, entity, state); + for (Iterator<TopiaEntityVetoable> l = entityVetoables.iterator(entity + .getClass()); l.hasNext(); ) { + try { + l.next().create(event); + } catch (Exception eee) { + throw new TopiaVetoException(eee); + } + } + } + } + + public void fireOnPostCreate(TopiaContextImplementor context, + TopiaEntity entity, Object[] state) { + if (log.isDebugEnabled()) { + log.debug("fireOnPostCreate"); + } + warnOnCreateEntity(entity); + if (isNotEmpty(entityListeners, entity.getClass())) { + TopiaEntityEvent event = new TopiaEntityEvent(context, entity, state); + for (Iterator<TopiaEntityListener> l = entityListeners.iterator(entity + .getClass()); l.hasNext(); ) { + try { + l.next().create(event); + } catch (Exception eee) { + if (log.isErrorEnabled()) { + log.error("Can't fireOnPostCreate for entity: " + entity, + eee); + } + } + } + } + } + + public void fireOnPreLoad(TopiaContextImplementor context, + TopiaEntity entity, Object[] state) { + if (log.isDebugEnabled()) { + log.debug("fireOnPreLoad"); + } + if (isNotEmpty(entityVetoables, entity.getClass())) { + TopiaEntityEvent event = new TopiaEntityEvent(context, entity, state); + for (Iterator<TopiaEntityVetoable> l = entityVetoables.iterator(entity + .getClass()); l.hasNext(); ) { + try { + l.next().load(event); + } catch (Exception eee) { + throw new TopiaVetoException(eee); + } + } + } + } + + public void fireOnPostLoad(TopiaContextImplementor context, + TopiaEntity entity, Object[] state) { + if (log.isDebugEnabled()) { + log.debug("fireOnPostLoad"); + } + warnOnLoadEntity(entity); + if (isNotEmpty(entityListeners, entity.getClass())) { + TopiaEntityEvent event = new TopiaEntityEvent(context, entity, state); + for (Iterator<TopiaEntityListener> l = entityListeners.iterator(entity + .getClass()); l.hasNext(); ) { + try { + l.next().load(event); + } catch (Exception eee) { + if (log.isErrorEnabled()) { + log + .error( + "Can't fireOnPostLoad for entity: " + + entity, eee); + } + } + } + } + } + + public void fireOnPreUpdate(TopiaContextImplementor context, + TopiaEntity entity, Object[] state) { + if (log.isDebugEnabled()) { + log.debug("fireOnPreUpdate"); + } + if (isNotEmpty(entityVetoables, entity.getClass())) { + TopiaEntityEvent event = new TopiaEntityEvent(context, entity, state); + for (Iterator<TopiaEntityVetoable> l = entityVetoables.iterator(entity + .getClass()); l.hasNext(); ) { + try { + l.next().update(event); + } catch (Exception eee) { + throw new TopiaVetoException(eee); + } + } + } + } + + public void fireOnPostUpdate(TopiaContextImplementor context, + TopiaEntity entity, Object[] state) { + if (log.isDebugEnabled()) { + log.debug("fireOnPostUpdate"); + } + warnOnUpdateEntity(entity); + if (isNotEmpty(entityListeners, entity.getClass())) { + TopiaEntityEvent event = new TopiaEntityEvent(context, entity, state); + for (Iterator<TopiaEntityListener> l = entityListeners.iterator(entity + .getClass()); l.hasNext(); ) { + try { + l.next().update(event); + } catch (Exception eee) { + if (log.isErrorEnabled()) { + log.error("Can't fireOnPostUpdate for entity: " + entity, + eee); + } + } + } + } + } + + public void fireOnPreDelete(TopiaContextImplementor context, + TopiaEntity entity, Object[] state) { + if (log.isDebugEnabled()) { + log.debug("fireOnPreDelete"); + } + if (isNotEmpty(entityVetoables, entity.getClass())) { + TopiaEntityEvent event = new TopiaEntityEvent(context, entity, state); + for (Iterator<TopiaEntityVetoable> l = entityVetoables.iterator(entity + .getClass()); l.hasNext(); ) { + try { + l.next().delete(event); + } catch (Exception eee) { + throw new TopiaVetoException(eee); + } + } + } + } + + public void fireOnPostDelete(TopiaContextImplementor context, + TopiaEntity entity, Object[] state) { + if (log.isDebugEnabled()) { + log.debug("fireOnPostDelete"); + } + warnOnDeleteEntity(entity); + if (isNotEmpty(entityListeners, entity.getClass())) { + TopiaEntityEvent event = new TopiaEntityEvent(context, entity, state); + for (Iterator<TopiaEntityListener> l = entityListeners.iterator(entity + .getClass()); l.hasNext(); ) { + try { + l.next().delete(event); + } catch (Exception eee) { + if (log.isErrorEnabled()) { + log.error("Can't fireOnPostDelete for entity: " + entity, + eee); + } + } + } + } + } + + /* Fires sur les propriétés */ + + public void fireOnPreRead(VetoableChangeSupport vetoables, + TopiaEntity entity, + String propertyName, + Object value) { + + if (log.isDebugEnabled()) { + log.debug("fireOnPreRead"); + } + try { + vetoables.fireVetoableChange(propertyName, value, NO_CHANGE); + } catch (Exception eee) { + throw new TopiaVetoException(eee); + } + } + + public void fireOnPostRead(PropertyChangeSupport listeners, + TopiaEntity entity, String propertyName, + Object value) { + + if (log.isDebugEnabled()) { + log.debug("fireOnPostRead"); + } + warnOnReadEntity(entity); + try { + listeners.firePropertyChange(propertyName, value, NO_CHANGE); + } catch (Exception eee) { + if (log.isErrorEnabled()) { + log.error("Can't fireOnPostRead", eee); + } + } + + } + + public void fireOnPostRead(PropertyChangeSupport listeners, + TopiaEntity entity, + String propertyName, + int index, + Object value) { + + if (log.isDebugEnabled()) { + log.debug("fireOnPostRead"); + } + warnOnReadEntity(entity); + try { + listeners.fireIndexedPropertyChange(propertyName, index, value, + NO_CHANGE); + } catch (Exception eee) { + if (log.isErrorEnabled()) { + log.error("Can't fireOnPostRead", eee); + } + } + + } + + public void fireOnPreWrite(VetoableChangeSupport vetoables, + TopiaEntity entity, + String propertyName, + Object oldValue, + Object newValue) { + + if (log.isDebugEnabled()) { + log.debug("fireOnPreWrite"); + } + try { + vetoables.fireVetoableChange(propertyName, oldValue, newValue); + } catch (Exception eee) { + throw new TopiaVetoException(eee); + } + } + + public void fireOnPostWrite(PropertyChangeSupport listeners, + TopiaEntity entity, + String propertyName, + Object oldValue, + Object newValue) { + + if (log.isDebugEnabled()) { + log.debug("fireOnPostWrite"); + } + warnOnUpdateEntity(entity); + if (propertyChangeListeners.size() > 0) { + PropertyChangeEvent e = new PropertyChangeEvent(entity, + propertyName, oldValue, newValue); + for (PropertyChangeListener l : propertyChangeListeners) { + try { + l.propertyChange(e); + } catch (Exception eee) { + if (log.isErrorEnabled()) { + log.error("Can't fire property change for: " + + propertyName, eee); + } + } + } + } + try { + listeners.firePropertyChange(propertyName, oldValue, newValue); + } catch (Exception eee) { + if (log.isErrorEnabled()) { + log.error("Can't fireOnPostWrite: " + propertyName, eee); + } + } + } + + public void fireOnPostWrite(PropertyChangeSupport listeners, + TopiaEntity entity, + String propertyName, + int index, + Object oldValue, + Object newValue) { + + if (log.isDebugEnabled()) { + log.debug("fireOnPostWrite"); + } + warnOnUpdateEntity(entity); + try { + listeners.fireIndexedPropertyChange(propertyName, index, oldValue, + newValue); + } catch (Exception eee) { + if (log.isErrorEnabled()) { + log.error("Can't fireOnPostWrite", eee); + } + } + } + + /** + * Object permettant de faire le lien entre les events hibernate et topia + * + * @author poussin <poussin@codelutin.com> + */ + static public class TopiaHibernateEvent implements PreInsertEventListener, + PostInsertEventListener, PreLoadEventListener, + PostLoadEventListener, PreUpdateEventListener, + PostUpdateEventListener, PreDeleteEventListener, + PostDeleteEventListener { + + private static final long serialVersionUID = 7303593133642169218L; + + protected TopiaContextImplementor rootContext; + + public TopiaHibernateEvent(TopiaContextImplementor rootContext) { + this.rootContext = rootContext; + } + + // Hibernate 4.3.x + @Override + public boolean requiresPostCommitHanding(EntityPersister persister) { + // TODO AThimel 17/12/13 I don't know what to return + return false; + } + + /** + * Recherche le context utilisant la session hibernate passe en + * parametre + * + * @param parent le context parent + * @param hibernate la session hibernate que doit utiliser le + * TopiaContext pour etre retourne + * @return le TopiaContext utilisant cette session hibernate ou null si + * aucun TopiaContext n'utilise cette session. + */ + protected TopiaContextImplementor getContext( + TopiaContextImplementor parent, Session hibernate) { + TopiaContextImplementor result = null; + + // FD-20100421 : Ano #546 : no need to copy childContext, the + // {@link #getChildContext()} provides a thread-safe copy to iterate + // on it. +// Set<TopiaContextImplementor> contextChilds = new HashSet<TopiaContextImplementor>(parent.getChildContext()); + for (TopiaContextImplementor context : parent.getChildContext()) { + +// by sletellier 24/09/09 : Fix concurent acces error +// ArrayList<TopiaContextImplementor> children = new ArrayList(parent.getChildContext()); +// for (TopiaContextImplementor context : children) { + try { + if (context.getHibernate() == hibernate) { + result = context; + } else { + // TODO: poussin 20090706 on pourrait ameliorer en ne faisant pas un parcours recursif, en utilisant la liste children (sans doute a transformer en stack) + result = getContext(context, hibernate); + } + if (result != null) { + break; + } + } catch (TopiaException eee) { + if (log.isWarnEnabled()) { + log.warn("Error durant la recherche d'un context pour" + + " lancer un event", eee); + } + } + } + return result; + } + + private void attachContext(Object entity, + TopiaContextImplementor context) { + if (entity instanceof TopiaEntityAbstract) { + TopiaEntityAbstract entityAbstract = (TopiaEntityAbstract) entity; + if (entityAbstract.getTopiaContext() == null) { + try { + entityAbstract.setTopiaContext(context); + } catch (TopiaException eee) { + if (log.isWarnEnabled()) { + log.warn("Impossible d'initialiser le TopiaContext" + + " sur cette entité : " + entityAbstract, + eee); + } + } + } + } + } + + /* Création */ + + @Override + public boolean onPreInsert(PreInsertEvent event) { + TopiaContextImplementor context = getContext(rootContext, event + .getSession()); + if (context != null && event.getEntity() instanceof TopiaEntity) { + context.getFiresSupport().fireOnPreCreate(context, + (TopiaEntity) event.getEntity(), event.getState()); + } + return false; + } + + @Override + public void onPostInsert(PostInsertEvent event) { + TopiaContextImplementor context = getContext(rootContext, event + .getSession()); + if (context != null && event.getEntity() instanceof TopiaEntity) { + context.getFiresSupport().fireOnPostCreate(context, + (TopiaEntity) event.getEntity(), event.getState()); + } + } + + /* Chargement */ + + @Override + public void onPreLoad(PreLoadEvent event) { + TopiaContextImplementor context = getContext(rootContext, event + .getSession()); + if (context != null && event.getEntity() instanceof TopiaEntity) { + // try { + context.getFiresSupport().fireOnPreLoad(context, + (TopiaEntity) event.getEntity(), event.getState()); + //TODO (thimel 20071213) On commente pour le moment @see(TopiaDAOHibernate#filterElements) + // } catch (TopiaVetoException tve) { + // //On ne fait pas de remontee d'exception + // vers Hibernate pour le preLoad, on va agir au niveau du DAO + // } + } + } + + @Override + public void onPostLoad(PostLoadEvent event) { + TopiaContextImplementor context = getContext(rootContext, event + .getSession()); + if (context != null && event.getEntity() instanceof TopiaEntity) { + attachContext(event.getEntity(), context); + context.getFiresSupport().fireOnPostLoad(context, + (TopiaEntity) event.getEntity(), new Object[]{}); + } + } + + /* Modification */ + + @Override + public boolean onPreUpdate(PreUpdateEvent event) { + TopiaContextImplementor context = getContext(rootContext, event + .getSession()); + if (context != null && event.getEntity() instanceof TopiaEntity) { + context.getFiresSupport().fireOnPreUpdate(context, + (TopiaEntity) event.getEntity(), event.getOldState()); + } + return false; + } + + @Override + public void onPostUpdate(PostUpdateEvent event) { + TopiaContextImplementor context = getContext(rootContext, event + .getSession()); + if (context != null && event.getEntity() instanceof TopiaEntity) { + context.getFiresSupport().fireOnPostUpdate(context, + (TopiaEntity) event.getEntity(), event.getState()); + } + // FIXME indexation + // if (!(entity instanceof NotIndexable) && context.isIndexEnabled()) { + // context.getIndexEnginImplementor().recordForIndexation(id, event.getState()); + // } + } + + /* Suppression */ + + @Override + public boolean onPreDelete(PreDeleteEvent event) { + TopiaContextImplementor context = getContext(rootContext, event + .getSession()); + if (context != null && event.getEntity() instanceof TopiaEntity) { + context.getFiresSupport().fireOnPreDelete(context, + (TopiaEntity) event.getEntity(), + event.getDeletedState()); + } + return false; + } + + @Override + public void onPostDelete(PostDeleteEvent event) { + TopiaContextImplementor context = getContext(rootContext, event + .getSession()); + if (context != null && event.getEntity() instanceof TopiaEntity) { + context.getFiresSupport().fireOnPostDelete(context, + (TopiaEntity) event.getEntity(), + event.getDeletedState()); + } +// FIXME indexation +// if (!(entity instanceof NotIndexable) && context.isIndexEnabled()) { +// context.getIndexEnginImplementor().recordForIndexation(id, null); +// } + } + } + + /** + * Notify topia context listeners for create schema pre operation + * + * @param context topia context + */ + public void firePreCreateSchema(TopiaContext context) { + if (log.isDebugEnabled()) { + log.debug("firePreCreateSchema"); + } + if (isNotEmpty(topiaContextListeners)) { + TopiaContextEvent event = new TopiaContextEvent(context); + for (TopiaContextListener topiaContextListener : topiaContextListeners) { + try { + topiaContextListener.preCreateSchema(event); + } catch (Exception eee) { + throw new TopiaVetoException(eee); + } + } + } + } + + /** + * Notify topia context listeners for create schema post operation + * + * @param context topia context + */ + public void firePostCreateSchema(TopiaContext context) { + if (log.isDebugEnabled()) { + log.debug("firePostCreateSchema"); + } + if (isNotEmpty(topiaContextListeners)) { + TopiaContextEvent event = new TopiaContextEvent(context); + for (TopiaContextListener topiaContextListener : topiaContextListeners) { + try { + topiaContextListener.postCreateSchema(event); + } catch (Exception eee) { + throw new TopiaVetoException(eee); + } + } + } + } + + /** + * Notify topia context listeners for create schema pre operation + * + * @param context topia context + */ + public void firePreUpdateSchema(TopiaContext context) { + if (log.isDebugEnabled()) { + log.debug("firePostCreateSchema"); + } + if (isNotEmpty(topiaContextListeners)) { + TopiaContextEvent event = new TopiaContextEvent(context); + for (TopiaContextListener topiaContextListener : topiaContextListeners) { + try { + topiaContextListener.preUpdateSchema(event); + } catch (Exception eee) { + throw new TopiaVetoException(eee); + } + } + } + } + + /** + * Notify topia context listeners for create schema post operation + * + * @param context topia context + */ + public void firePostUpdateSchema(TopiaContext context) { + if (log.isDebugEnabled()) { + log.debug("firePostCreateSchema"); + } + if (isNotEmpty(topiaContextListeners)) { + TopiaContextEvent event = new TopiaContextEvent(context); + for (TopiaContextListener topiaContextListener : topiaContextListeners) { + try { + topiaContextListener.postUpdateSchema(event); + } catch (Exception eee) { + throw new TopiaVetoException(eee); + } + } + } + } + + /** + * Notify topia context listeners for schema restore pre operation + * + * @param context topia context + */ + public void firePreRestoreSchema(TopiaContext context) { + if (log.isDebugEnabled()) { + log.debug("firePreRestoreSchema"); + } + if (isNotEmpty(topiaContextListeners)) { + TopiaContextEvent event = new TopiaContextEvent(context); + for (TopiaContextListener topiaContextListener : topiaContextListeners) { + try { + topiaContextListener.preRestoreSchema(event); + } catch (Exception eee) { + throw new TopiaVetoException(eee); + } + } + } + } + + /** + * Notify topia context listeners for schema restore post operation + * + * @param context topia context + */ + public void firePostRestoreSchema(TopiaContext context) { + if (log.isDebugEnabled()) { + log.debug("firePostRestoreSchema"); + } + if (isNotEmpty(topiaContextListeners)) { + TopiaContextEvent event = new TopiaContextEvent(context); + for (TopiaContextListener topiaContextListener : topiaContextListeners) { + try { + topiaContextListener.postRestoreSchema(event); + } catch (Exception eee) { + throw new TopiaVetoException(eee); + } + } + } + } + + /** + * Notify entities listeners for load operation + * + * @param <E> type of entities + * @param context context used + * @param entities entities loaded + * @return the list of entities loaded + */ + public <E extends TopiaEntity> List<E> fireEntitiesLoad( + TopiaContextImplementor context, List<E> entities) { + if (log.isDebugEnabled()) { + log.debug("fireEntitiesLoad"); + } + + List<E> result = entities; + for (TopiaEntitiesVetoable entitiesVetoable : entitiesVetoables) { + try { + //FIXME tchemit 20100513 Why instanciate n events? if necessary MUST add a comment + TopiaEntitiesEvent<E> event = new TopiaEntitiesEvent<E>(context, result); + result = entitiesVetoable.load(event); + } catch (Exception eee) { + throw new TopiaVetoException(eee); + } + } + return result; + } + + /* Getters */ + + public CategorisedListenerSet<TopiaEntityListener> getEntityListeners() { + return entityListeners; + } + + public CategorisedListenerSet<TopiaEntityVetoable> getEntityVetoables() { + return entityVetoables; + } + + public ListenerSet<TopiaTransactionListener> getTransactionListeners() { + return transactionListeners; + } + + public ListenerSet<TopiaTransactionVetoable> getTransactionVetoable() { + return transactionVetoables; + } + + public ListenerSet<TopiaContextListener> getTopiaContextListeners() { + return topiaContextListeners; + } + + public ListenerSet<TopiaEntitiesVetoable> getTopiaEntitiesVetoable() { + return entitiesVetoables; + } + + /* Adders */ + + public void addTopiaEntityListener(TopiaEntityListener listener) { + addTopiaEntityListener(TopiaEntity.class, listener); + } + + public void addTopiaEntityListener( + Class<? extends TopiaEntity> entityClass, + TopiaEntityListener listener) { + if (listener == null) { + throw new NullPointerException("listener can not be null."); + } + entityListeners.add(entityClass, listener); + } + + public void addTopiaEntityVetoable(TopiaEntityVetoable vetoable) { + addTopiaEntityVetoable(TopiaEntity.class, vetoable); + } + + public void addTopiaEntityVetoable( + Class<? extends TopiaEntity> entityClass, + TopiaEntityVetoable vetoable) { + if (vetoable == null) { + throw new NullPointerException("listener can not be null."); + } + entityVetoables.add(entityClass, vetoable); + } + + public void addTopiaTransactionListener(TopiaTransactionListener listener) { + if (listener == null) { + throw new NullPointerException("listener can not be null."); + } + transactionListeners.add(listener); + } + + public void addTopiaTransactionVetoable(TopiaTransactionVetoable vetoable) { + if (vetoable== null) { + throw new NullPointerException("listener can not be null."); + } + transactionVetoables.add(vetoable); + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + if (listener == null) { + throw new NullPointerException("listener can not be null."); + } + propertyChangeListeners.add(listener); + } + + public void addTopiaContextListener(TopiaContextListener listener) { + if (listener == null) { + throw new NullPointerException("listener can not be null."); + } + topiaContextListeners.add(listener); + } + + public void addTopiaEntitiesVetoable(TopiaEntitiesVetoable vetoable) { + if (vetoable == null) { + throw new NullPointerException("listener can not be null."); + } + entitiesVetoables.add(vetoable); + } + + /* Removers */ + + public void removeTopiaEntityListener(TopiaEntityListener listener) { + removeTopiaEntityListener(TopiaEntity.class, listener); + } + + public void removeTopiaEntityListener( + Class<? extends TopiaEntity> entityClass, + TopiaEntityListener listener) { + if (listener == null) { + throw new NullPointerException("listener can not be null."); + } + entityListeners.remove(entityClass, listener); + } + + public void removeTopiaEntityVetoable(TopiaEntityVetoable vetoable) { + removeTopiaEntityVetoable(TopiaEntity.class, vetoable); + } + + public void removeTopiaEntityVetoable( + Class<? extends TopiaEntity> entityClass, + TopiaEntityVetoable vetoable) { + if (vetoable == null) { + throw new NullPointerException("listener can not be null."); + } + entityVetoables.remove(entityClass, vetoable); + } + + public void removeTopiaTransactionListener(TopiaTransactionListener listener) { + if (listener == null) { + throw new NullPointerException("listener can not be null."); + } + transactionListeners.remove(listener); + } + + public void removeTopiaTransactionVetoable(TopiaTransactionVetoable vetoable) { + if (vetoable == null) { + throw new NullPointerException("listener can not be null."); + } + transactionVetoables.remove(vetoable); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + if (listener == null) { + throw new NullPointerException("listener can not be null."); + } + propertyChangeListeners.remove(listener); + } + + public void removeTopiaContextListener(TopiaContextListener listener) { + if (listener == null) { + throw new NullPointerException("listener can not be null."); + } + topiaContextListeners.remove(listener); + } + + public void removeTopiaEntitiesVetoable(TopiaEntitiesVetoable vetoable) { + if (vetoable == null) { + throw new NullPointerException("listener can not be null."); + } + entitiesVetoables.remove(vetoable); + } + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaQuery.java b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaQuery.java new file mode 100644 index 0000000..5c7a4a6 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaQuery.java @@ -0,0 +1,1801 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.framework; + +import org.apache.commons.beanutils.PropertyUtils; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.util.StringUtil; + +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * Query HQL managment to simplify usage of {@link TopiaContext#findAll(String, + * Object...) }. + * <p/> + * TODO-FD20091224 JUnit Tests + * <p/> + * <pre> + * This class is used to construct a HQL query and then execute it from a + * TopiaContext. The TopiaQuery is linked to a TopiaEntity which + * is the main element manipulated in the query. There is two parts in using + * this class : + * - construction of the query, using add, addFrom, addOrder, addSelect, + * addGroup, ... + * - execution of the query, using executeToEntityList, executeToEntity, + * executeToInteger, ... + * + * Construction + * ============ + * + * This class make easier the way to construct a HQL query. + * + * Example 1 : + * ----------- + * + * SQL : + * "SELECT * FROM PersonImpl WHERE firstName LIKE 'M%' AND year > 1980" + * + * HQL using {@link TopiaContext#findAll(String, Object...) } : + * TopiaContext context = rootContext.beginTransaction(); + * context.find("FROM " + Person.class.getName() + " WHERE firstName LIKE + * :firstName AND year > :year", + * "firstName", "M%", year, 1980); + * + * TopiaQuery : + * TopiaQuery query = TopiaQuery.createQuery(Person.class).add( + * Person.FIRST_NAME, Op.LIKE, "M%").add(Person.YEAR, Op.GT, 1980); + * + * But the real advantage is when you have some parameters to test before + * adding + * them to the query. With the older method, it was tidious to construct + * and add parameters to finally use the find method from TopiaContext. + * + * Example 2 : + * ----------- + * + * HQL using {@link TopiaContext#findAll(String, Object...) } : + * TopiaContext context = rootContext.beginTransaction(); + * + * String query = "FROM " + Person.class.getName(); + * List<Object> params = new ArrayList<Object>(); + * String separator = " WHERE "; + * // company parameter can be null + * if (company != null) { + * query += separator + "company = :company"; + * params.add("company"); + * params.add(company); + * separator = " AND "; + * } + * + * // contact paramater can be null + * if (contact != null) { + * query += separator + "contact = :contact"; + * params.add("contact"); + * params.add(contact); + * separator = " AND "; + * } + * + * context.findAll(query, params.toArray()); + * + * Here we have only two non obligatory params, but imagine if we must have + * almost 6 or 7 parameters like this ! + * + * TopiaQuery : + * TopiaQuery query = TopiaQuery.createQuery(Person.class); + * + * if (company != null) { + * query.add(Person.COMPANY, company); + * } + * + * if (contact != null) { + * query.add(Person.CONTACT, contact); + * } + * + * Many ways to create the same query : + * ------------------------------------ + * + * You can use multiple different manners to create a query, it depends on the + * complexicity. More complex is the query, more easier is to construct it. + * + * HQL : "FROM PersonImpl AS P WHERE (P.company IS NULL OR P.company = + * :company) + * AND P.firstName LIKE :firstName" + * + * Using TopiaQuery and an Alias (these different queries are equivalent) : + * query = TopiaQuery.createQuery(Person.class, "P"); + * 1- query.add("(P.company IS NULL OR P.company = :company") AND P.firstName + * LIKE :firstName") + * .addParam("company", company).addParam("firstName",firstName + "%"); + * 2- query.add("P.company IS NULL OR P.company = :company") + * .add("P.firstName LIKE :firstName").addParam("company", company) + * .addParam("firstName",firstName + "%"); + * 3- query.add("P.company IS NULL OR P.company = :company") + * .add("P.firstName", Op.LIKE, firstName + "%") + * .addParam("company", company); + * 4- query.addNullOr("P.company", Op.EQ, company). + * add("P.firstName", Op.LIKE, firstName + "%"); + * + * You can use TopiaQuery to create a subquery in an other TopiaQuery, you have + * to use the method {@link #fullQuery() } to get the full query in HQL and + * give + * it as a string in the other TopiaQuery. + * + * Execution + * ========= + * + * After construction, you can execute the query in different ways. + * + * Default method : + * ---------------- + * + * - execute : as the same result as + * {@link TopiaContext#findAll(String, Object...) } + * + * Depends on entity type ; + * ------------------------ + * + * - executeToEntity : only one result, the first one + * - executeToEntityList : all results returned in a List + * - executeToEntityMap : all results returned in a Map with key defined by + * user + * or topiaId by default + * + * For aggregate : + * --------------- + * + * These methods have in argument the SELECT to execute the query. The previous + * SELECT (if defined) will not be deleted, but temporarly not used. + * + * - executeToInteger : for example for "SUM", "COUNT" + * - executeToString : for example for "MAX" + * - executeCount : directly a "count(*)" + * - executeToObject : for other type of possible result (Long, Boolean, + * Double, + * ...) + * + * Property loading + * ================ + * + * When using Hibernate, some times, Entities linked to the main one will be + * lazy initialized, but you want them directly when the query will be executed + * to avoid problems when closing context. You can use the method + * {@link #addLoad(String...) } to tell the TopiaQuery to load some + * properties when executing the query. After that, you don't need to call them + * for loading them in Hibernate. + * + * The syntax is the same as a property in HQL query using delegation : + * "person.company" where person and company are entities. + * + * Note : loading only available on collection or entities but not property + * on a collection of entities which must be made manually. + * + * For a Contact which is linked to a person (entity) and the person linked to + * company (entity) you can add to a TopiaQuery<Contact> : + * query.addLoad("person.company") + * + * For a list of addresses (entity) in the contact you can do : + * query.addLoad("addresses") + * + * But it's not possible to do for example with meeting (entity) linked to the + * contact and responsible (entity) linked to a meeting : + * query.addLoad("meetings.responsible") + * + * </pre> + * <p/> + * Created: 21 déc. 2009 + * + * @author fdesbois + * @version $Revision$ + * @since 2.3.0 + * @deprecated since 2.6.12, {@link TopiaQuery} will be removed in version 3.0 + */ +@Deprecated +public class TopiaQuery { + + private static final Log log = LogFactory.getLog(TopiaQuery.class); + + public static final String FROM_SEPARATOR_DEFAULT = ","; + + public static final String FROM_SEPARATOR_JOIN = "JOIN"; + + public static final String FROM_SEPARATOR_LEFT_JOIN = "LEFT JOIN"; + + /** Params for HQL query. */ + protected List<Object> params; + +// /** Select part of the query * */ +// protected StringBuilder select; + + /** + * To keep SELECT part of the query filled by user. + * + * @since 2.6.7 + */ + protected List<String> userSelects; + + /** + * To keep GROUP BY part of the query filled by user. + * + * @since 2.6.7 + */ + protected List<String> groupBys; + + /** + * To keep ORDER BY part of the query filled by user. + * + * @since 2.6.7 + */ + protected List<String> orderBys; + + /** + * To keep WHERE part of the query filled by user. + * + * @since 2.6.7 + */ + protected List<String> wheres; + + protected boolean distinct; + + //TODO tchemit-2012-02-01 Remove this (see Evol #1931) + + /** From part of the query. */ + protected StringBuilder from; + + //TODO tchemit-2012-02-01 Remove this (see Evol #1931) +// /** Where part of the query. */ +// protected StringBuilder where; + +// /** Order By part of the query. */ +// protected StringBuilder orderBy; + +// /** Group By part of the query. */ +// protected StringBuilder groupBy; + + protected Integer startIndex; + + protected Integer endIndex; + +// /** Used to determine if parentheses are needed for Where statement. */ +// protected boolean parentheses; + + protected List<String> propertiesToLoad; + + protected String mainAlias; + + /** Enum to simmplify using operation in query. */ + public enum Op { + + /** EQUALS. */ + EQ("="), + /** GREATER THAN. */ + GT(">"), + /** GREATER OR EQUALS. */ + GE(">="), + /** LIKE for String manipulation. */ + LIKE("LIKE"), + /** LESS THAN. */ + LT("<"), + /** LESS OR EQUALS. */ + LE("<="), + /** IS NOT NULL. */ + NOT_NULL("IS NOT NULL"), + /** IS NULL. */ + NULL("IS NULL"), + /** NOT EQUAL. */ + NEQ("!="), + /** IN. */ + IN("IN"), + /** NOT IN. */ + NOT_IN("NOT IN"); + + protected String value; + + /** + * Constructor of the Op Enum. + * + * @param value corresponding to the String for the query + */ + Op(String value) { + this.value = value; + } + + @Override + public String toString() { + return value; + } + } + + public TopiaQuery() { +// parentheses = true; + } + + /** + * Create a TopiaQuery based on the {@code entityClass}. The from statement + * is automatically set. + * + * @param mainEntityClass used as from part of the query + */ + public TopiaQuery(Class<? extends TopiaEntity> mainEntityClass) { + this(); + setFrom(mainEntityClass); + } + + /** + * Create a TopiaQuery based on the {@code entityClass}. The from statement + * is automatically set, the select statement must be necessary in some + * case, the query will manage this case using the mainAlias by default. + * + * @param mainEntityClass used as from part of the query + * @param alias for the mainEntityClass + */ + public TopiaQuery(Class<? extends TopiaEntity> mainEntityClass, + String alias) { + this(); + setFrom(mainEntityClass, alias); + } + + /** + * Set the mainEntity in the from part of the query. + * + * @param mainEntityClass type of the mainEntity + * @return the TopiaQuery + */ + public TopiaQuery setFrom(Class<? extends TopiaEntity> mainEntityClass) { + setFrom(mainEntityClass, null); + return this; + } + + /** + * Set the mainEntity in the from part of the query and use an alias for + * this mainEntity. + * + * @param mainEntityClass type of the mainEntity + * @param alias for the entity in the query + * @return the TopiaQuery + */ + public TopiaQuery setFrom(Class<? extends TopiaEntity> mainEntityClass, + String alias) { + from = new StringBuilder(" FROM ").append(mainEntityClass.getName()); + mainAlias = alias; + if (StringUtils.isNotEmpty(mainAlias)) { + from.append(' ').append(alias); + } + return this; + } + + /** + * Add an element to the from in the query. Used to add some other data in + * the query. The default separator used is the ", ". + * + * @param str the element to add + * @return the TopiaQuery + * @see #addFrom(Class, String) + * @see #addJoin(String, String, boolean) + * @see #addLeftJoin(String, String, boolean) + * @deprecated since 2.3.4 use correct addFrom or addJoin or addLeftJoin + */ + @Deprecated + public TopiaQuery addFrom(String str) { + return addFrom(FROM_SEPARATOR_DEFAULT, str, null); + } + + /** + * Add an element to the from in the query. Used to add some other data in + * the query or for join as specific {@code separator}. + * + * @param property the property to add + * @param alias alias of the property to add in form part of the query + * @param separator The separator to use before adding the element (if null + * the {@link #FROM_SEPARATOR_DEFAULT} will be used). + * @return the TopiaQuery + * @since 2.3.4 + */ + protected TopiaQuery addFrom(String separator, String property, String alias) { + if (!separator.equals(FROM_SEPARATOR_DEFAULT)) { + from.append(' '); + } + from.append(separator).append(' ').append(property); + + if (alias != null) { + from.append(' ').append(alias); + } + return this; + } + + /** + * Add a inner join {@code property} to the query with {@code alias}. + * The join is done in From statement as : FROM Contact C JOIN C.boat B. The + * added part is 'JOIN C.boat B' using addJoin("C.boat", "B", false). The order + * of calling {@link #addFrom(Class, String)} or this method is very important. + * The first element in the FROM is always the main entity of the query. + * + * @param property Property name to use as a Join + * @param alias Alias of the property in the query + * @param fetch Add FETCH keyword to load the property in result (avoid + * lazy initialization) + * @return the TopiaQuery + * @since 2.3.4 + */ + public TopiaQuery addJoin(String property, String alias, boolean fetch) { + return addFromJoin(FROM_SEPARATOR_JOIN, property, alias, fetch); + } + + + /** + * Add a left join {@code property} to the query with {@code alias}. + * The join is done in From statement as : FROM Contact C LEFT JOIN C.boat B. + * The added part is 'LEFT JOIN C.boat B' using addJoin("C.boat", "B", false). + * The order of calling {@link #addFrom(Class, String)} or this method is + * very important. The first element in the FROM is always the main entity + * of the query. + * + * @param property Property name to use as a Join + * @param alias Alias of the property in the query + * @param fetch Add FETCH keyword to load the property in result (avoid + * lazy initialization) + * @return the TopiaQuery + * @since 2.3.4 + */ + public TopiaQuery addLeftJoin(String property, String alias, boolean fetch) { + return addFromJoin(FROM_SEPARATOR_LEFT_JOIN, property, alias, fetch); + } + + protected TopiaQuery addFromJoin(String separator, String property, + String alias, boolean fetch) { + String sep = separator; + if (fetch) { + sep = new StringBuilder(separator).append(" FETCH").toString(); + } + return addFrom(sep, property, alias); + } + + /** + * Add an other entity type to the from in the query. + * + * @param entityClass different from the mainEntity + * @return the TopiaQuery + */ + public TopiaQuery addFrom(Class<? extends TopiaEntity> entityClass) { + return addFrom(entityClass, null); + } + + /** + * Add an other entity type to the from in the query with an alias. + * + * @param entityClass different from the mainEntity + * @param alias of the entity in the query + * @return the TopiaQuery + */ + public TopiaQuery addFrom(Class<? extends TopiaEntity> entityClass, + String alias) { + return addFrom(FROM_SEPARATOR_DEFAULT, entityClass.getName(), alias); + } + + /** + * Get the full query. + * + * @return a String corresponding to the full query. + */ + public String fullQuery() { + StringBuilder result = new StringBuilder(); + StringBuilder selectStatement = new StringBuilder("SELECT "); + if (distinct) { + selectStatement.append("DISTINCT "); + } + if (CollectionUtils.isNotEmpty(userSelects)) { + + result.append(selectStatement); + result.append(StringUtils.join(userSelects, ',')); + } else if (StringUtils.contains(from.toString(), ',') && + StringUtils.isNotEmpty(mainAlias)) { + // Set default select if there is more than one table in from + // part and main alias is defined + // Note : maybe a problem with using new {@link #addFrom(String, String) with other than ',' separator + result.append(selectStatement).append(mainAlias); + } + +// if (select != null) { +// result.append(selectStatement).append(select); +// // Set default select if there is more than one table in from +// // part and main alias is defined +// // Note : maybe a problem with using new {@link #addFrom(String, String) with other than ',' separator +// } else if (StringUtils.contains(from.toString(), ',') && +// StringUtils.isNotEmpty(mainAlias)) { +// result.append(selectStatement).append(mainAlias); +// } + result.append(from); + + if (CollectionUtils.isNotEmpty(wheres)) { + result.append(" WHERE "); + boolean first = true; + boolean moreThanOne = wheres.size() > 1; + for (String where : wheres) { + if (!first) { + result.append(" AND "); + } + if (moreThanOne) { + result.append('('); + } + result.append(where); + if (moreThanOne) { + result.append(')'); + } + first = false; + } + } + if (CollectionUtils.isNotEmpty(groupBys)) { + result.append(" GROUP BY ").append(StringUtils.join(groupBys, ',')); + } + if (CollectionUtils.isNotEmpty(orderBys)) { + result.append(" ORDER BY ").append(StringUtils.join(orderBys, ',')); + } +// if (where != null) { +// result.append(where); +// } +// if (groupBy != null) { +// result.append(groupBy); +// } +// if (orderBy != null) { +// result.append(orderBy); +// } + return StringUtils.trim(result.toString()); + } + + /** + * Add a HQL parameter to the Query. + * + * @param id identification of the param in the query + * @param paramValue value of the param + * @return the TopiaQuery + */ + public TopiaQuery addParam(String id, Object paramValue) { + getParams().add(id); + getParams().add(paramValue); + return this; + } + + /** + * Add muliple paramaters to the Query. The key of each param will be tested + * if not already exist in the existing params list and will be renamed in + * this case. + * + * @param params a list of HQL params with key and value in order. + * @return the TopiaQuery + * @see TopiaQuery#getValueName(String) + */ + public TopiaQuery addParams(List<Object> params) { + for (int i = 0; i < params.size(); i += 2) { + String paramName = (String) params.get(i); + addParam(getValueName(paramName), params.get(i + 1)); + } + return this; + } + + public List<Object> getParams() { + if (params == null) { + params = new ArrayList<Object>(); + } + return params; + } + + /** + * Return the mainAlias set from constructor. + * + * @return a String or null if no alias is set + */ + public String getMainAlias() { + return mainAlias; + } + + /** + * Add a property to load when query is executed. Used to avoid + * LazyInitializationException for property needed after closing context. + * The property is a string like those in HQL query. + * <pre> + * Exemples : + * - "person.company" (Property TopiaEntity person linked to the result + * entity in query and company linked to person) + * --> calling myEntity.getPerson().getCompany(); + * - "partyRoles" (Property Collection partyRoles linked to the result + * entity in query) + * --> calling myEntity.getPartyRoles().size(); + * </pre> + * + * @param properties List of properties to load + * @return the TopiaQuery + */ + public TopiaQuery addLoad(String... properties) { + getPropertiesToLoad().addAll(Arrays.asList(properties)); + return this; + } + + /** + * Used to load properties during query execution using FETCH keyword. This + * keyword is used in a JOIN, so the alias is needed to identify properties + * to load. + * </p> + * Also an empty SELECT statement will be defined to retrieve the correct + * entity depends on the mainEntity type in the query. Carefull using + * addFetch, hibernate doesn't support more than 3 or 4 join. In this case, + * you can use {@link #addLoad(String...)} or load manually the entities + * wanted. + * + * @param properties Properties to load during query execution + * @return the TopiaQuery + */ + public TopiaQuery addFetch(String... properties) { + + // Note : creating alias is not very efficient if other parameters is needed + // Maybe the solution is to throw an exception if no mainAlias is defined + + // Check mainAlias, necessary to use join fetch + boolean needAlias = false; + if (StringUtils.isEmpty(mainAlias)) { + mainAlias = RandomStringUtils.randomAlphabetic(4); + from.append(' ').append(mainAlias); + needAlias = true; + } + + // Init select for single result +// if (select == null) { +// setSelect(mainAlias); +// } + if (userSelects == null) { + setSelect(mainAlias); + } + + for (String current : properties) { + // Add missing alias if needed + String property = needAlias ? + getProperty(mainAlias, current) : current; + + // Split property on . + String[] parts = property.split("\\."); + + // First alias need to be a property in query + String alias = parts[0]; + + for (int i = 1; i < parts.length; i++) { + // Construct property with current alias + String propertyToJoin = getProperty(alias, parts[i]); + + // If next occurence exists create a new alias + if ((i + 1) < parts.length) { + alias = RandomStringUtils.randomAlphabetic(4); + } else { + // loop will stop, last alias is null + alias = null; + } + + // Add the property in left join with fetch to true + addLeftJoin(propertyToJoin, alias, true); + } + } + return this; + } + + protected List<String> getPropertiesToLoad() { + if (propertiesToLoad == null) { + propertiesToLoad = new ArrayList<String>(); + } + return propertiesToLoad; + } + + /** + * @param where Where statement to add + * @return TopiaQuery + * @deprecated since 2.3.4, use {@link #addWhere(String)} instead + */ + @Deprecated + public TopiaQuery add(String where) { + return addWhere(where); + } + + /** + * Add a where element to the Query. Could be anything. Parentheses are + * added automatically (even if there are not needed). + * + * @param where element to add + * @return the TopiaQuery + * @since 2.3.4 + */ + public TopiaQuery addWhere(String where) { + if (StringUtils.isNotEmpty(where)) { + if (wheres == null) { + wheres = new ArrayList<String>(); + } + wheres.add(where); +// // Reinitialize parentheses boolean for next add call +// parentheses = true; + } +// if (this.where == null) { +// this.where = new StringBuilder(" WHERE "); +// } else { +// this.where.append(" AND "); +// } +// if (parentheses) { +// this.where.append('('); +// } +// this.where.append(where); +// if (parentheses) { +// this.where.append(')'); +// } +// // Reinitialize parentheses boolean for next add call +// parentheses = true; + return this; + } + + /** + * @param paramName name of the parameter to add + * @param constraint constraint to use + * @param paramValue value of this parameter + * @return TopiaQuery + * @deprecated since 2.3.4, use {@link #addWhere(String, Op, Object)} instead + */ + @Deprecated + public TopiaQuery add(String paramName, Op constraint, Object paramValue) { + return addWhere(paramName, constraint, paramValue); + } + + /** + * Add an element to the query. The parameter will be automatically added. + * The {@code operator} is needed to determine what type of operation it is. + * Ex : add("boat", Op.EQ, boat) means -> boat = :boat. Also if the paramValue + * is Null, the paramName will be added to the query with the constraint + * null (IS NULL). + * <p/> + * TODO-fdesbois-2010-05-26 : maybe manage more than one paramValue with Object... Depends on operator + * + * @param paramName the name of the parameter in the query (attribute of + * the entity) + * @param operator the operation concerned + * @param paramValue the value of the parameter (an other entity, a String, + * ...) + * @return the TopiaQuery + * @since 2.3.4 + */ + public TopiaQuery addWhere(String paramName, Op operator, Object paramValue) { + StringBuilder result = new StringBuilder(paramName).append(' '); + if (log.isTraceEnabled()) { + log.trace("paramValue = " + paramValue); + } + if (paramValue == null) { + result.append(Op.NULL); + } else { + String valueName = getValueName(paramName); + if (Op.IN == operator || Op.NOT_IN == operator) { + + result.append(operator).append(" (:").append(valueName).append(')'); + } else { + result.append(operator).append(" :").append(valueName); + } + addParam(valueName, paramValue); + } +// parentheses = false; + return addWhere(result.toString()); + + } + + protected String getValueName(String paramName) { + int dot = paramName.lastIndexOf('.'); + String valueName = paramName; + if (dot != -1) { + valueName = paramName.substring(dot + 1); + } + + // If the paramName contains a function, escape parenthesis + valueName = valueName.replace('(', '_'); + valueName = valueName.replace(')', '_'); + + if (getParams().contains(valueName)) { + valueName = valueName + "_" + + RandomStringUtils.randomAlphanumeric(4); + } + return valueName; + } + + /** + * @param paramName name of the parameter to add + * @param paramValue value of this parameter + * @return TopiaQuery + * @since 2.3.1 + * @deprecated since 2.3.4, use {@link #addEquals(String, Object...)} instead + */ + @Deprecated + public TopiaQuery add(String paramName, Object... paramValue) { + return addEquals(paramName, paramValue); + } + + /** + * Add an element to the query. The parameter will be automatically added. + * The default constrainst operation is Op.EQ for EQUALS. Ex : add("boat", + * boat) means -> boat = :boat. If you add more than one values, the + * statement IN will be used. You can also have a null value in the {@code + * paramValue} list (except if it's the only one value, it's ambiguous). + * Note : this method do nothing if the {@code paramValue} is not defined. + * You can also set {@code paramValue} to null if you want the {@code + * paramName} to be null in the query. + * + * @param paramName name of the parameter in the query + * @param paramValue values of the parameter + * @return the TopiaQuery + * @see TopiaQuery#addWhere(String, Op, Object) + * @since 2.3.4 + */ + public TopiaQuery addEquals(String paramName, Object... paramValue) { + if (paramValue == null) { + return addWhere(paramName, Op.EQ, null); + } + int length = paramValue.length; + // Do nothing if there is no value defined + if (length == 0) { + return this; + } + // Only one paramValue + if (length == 1) { + if (log.isTraceEnabled()) { + log.trace("Only one value " + Arrays.toString(paramValue)); + } + return addWhere(paramName, Op.EQ, paramValue[0]); + } + // Multiple values is defined + StringBuilder values = new StringBuilder(); + int count = 1; + // Used if one of the value is null + boolean addNull = false; + for (Object value : paramValue) { + if (value != null) { + // Add the valueName to the values list for IN statement + String valueName = getValueName(paramName + count); + if (count != 1) { + values.append(", "); + } + values.append(':').append(valueName); + addParam(valueName, value); + count++; + } else { + addNull = true; + } + } + // Create buffer for IN statement with values + StringBuilder buffer = new StringBuilder(); + buffer.append(paramName).append(" IN (").append(values).append(")"); + // Add the OR statement for null value if needed + if (addNull) { + buffer.append(" OR "). + append(paramName).append(' ').append(Op.NULL.toString()); + } else { +// // no parentheses needed in this case (no OR statement) +// parentheses = false; + } + return addWhere(buffer.toString()); + } + + /** + * @param properties map of the properties to add + * @return TopiaQuery + * @deprecated since 2.3.4 use {@link #addEquals(Map)} + */ + @Deprecated + public TopiaQuery add(Map<String, Object> properties) { + return addEquals(properties); + } + + /** + * Add a map of properties to the where clause of the query. Each property + * will be added to the query with Op.EQ operation, the key in the map is + * the property name, and the value is the value of the parameter in the + * query. + * + * @param properties to add to the query + * @return the TopiaQuery + * @since 2.3.4 + */ + public TopiaQuery addEquals(Map<String, Object> properties) { + for (String key : properties.keySet()) { + addEquals(key, properties.get(key)); + } + return this; + } + + /** + * Add an element to the query with the constraint Not null. + * + * @param paramName name of the parameter in the query + * @return the TopiaQuery + */ + public TopiaQuery addNotNull(String paramName) { + StringBuilder result = + new StringBuilder(paramName).append(' ').append(Op.NOT_NULL); +// parentheses = false; + addWhere(result.toString()); +// parentheses = true; + return this; + } + + /** + * Add an element to the query. The nullity is tested or a constraint is + * added for that element. Ex : addNullOr("begin", Op.GT, new Date()) means + * begin IS NULL OR begin > :begin (where :begin = new Date()). + * + * @param paramName the name of the parameter in the query (attribute of + * the entity) + * @param constraint the operation concerned by the or + * @param paramValue the value of the parameter (an other entity, a String, + * ...) + * @return the TopiaQuery + */ + public TopiaQuery addNullOr(String paramName, Op constraint, + Object paramValue) { + String valueName = getValueName(paramName); + StringBuilder result = + new StringBuilder(paramName).append(' ').append(Op.NULL). + append(" OR ").append(paramName).append(constraint). + append(" :").append(valueName); + addParam(valueName, paramValue); + return addWhere(result.toString()); + } + + /** + * Add an element to the query with the constraint null. + * + * @param paramName name of the parameter in the query + * @return the TopiaQuery + */ + public TopiaQuery addNull(String paramName) { + addWhere(paramName, Op.EQ, null); + return this; + } + + /** + * Add an element with BETWEEN operation. The {@code paramName} will be + * found between {@code value1} and {@code value2}. Useful for date + * manipulations. + * + * @param paramName The name of the parameter in the query (entity property) + * @param value1 First value + * @param value2 Second value + * @return the TopiaQuery + */ + public TopiaQuery addBetween(String paramName, Object value1, Object value2) { + + String valueName = getValueName(paramName); + String valueName1 = new StringBuilder(valueName).append('1').toString(); + String valueName2 = new StringBuilder(valueName).append('2').toString(); + + addParam(valueName1, value1); + addParam(valueName2, value2); + + StringBuilder builder = + new StringBuilder(paramName). + append(" BETWEEN "). + append(':').append(valueName1). + append(" AND "). + append(':').append(valueName2); + +// parentheses = false; + addWhere(builder.toString()); +// parentheses = true; + return this; + } + + + /** + * Add link constraint between two properties. {@code elementProperty} is in + * elements of {@code containerProperty} which is a collection with same type + * than {@code elementProperty}. (HQL : elementProperty IN elements + * (containerProperty)) + * + * @param elementProperty contains in containerProperty collection + * @param containerProperty collection which contains elementProperty + * @return the TopiaQuery + * @since 2.3.4 + */ + public TopiaQuery addInElements(String elementProperty, String containerProperty) { + StringBuilder builder = new StringBuilder(elementProperty). + append(" IN elements(").append(containerProperty).append(')'); +// parentheses = false; + addWhere(builder.toString()); +// parentheses = true; + return this; + } + + /** + * Method used to add a subquery in an existing query. The params will be + * automatically checked and copied from the subquery to the current one. + * This method is used to inject {@code subquery} in WHERE part of the + * query. The {@code queryPart} is the element in the query to bind with the + * {@code subquery}. The ? character is used to inject the subquery into the + * {@code queryPart}. Ex : + * <pre> + * // Add a SUB_ELMT = (subquery) into the query + * query.addSubQuery("SUB_ELMT = (?)", subquery, false); + * </pre> + * + * @param queryPart part of the query where subquery need to be injected + * @param subquery existing topiaQuery as subquery + * @return the TopiaQuery + * @see TopiaQuery#getValueName(String) + * @since 2.3.4 + */ + public TopiaQuery addSubQuery(String queryPart, + TopiaQuery subquery) { + + List<Object> subqueryParams = subquery.getParams(); + String subqueryString = subquery.fullQuery(); + + // If no params is still defined, use those from subquery. + if (CollectionUtils.isEmpty(params)) { + addParams(subqueryParams); + } else { + for (int i = 0; i < subqueryParams.size(); i += 2) { + + String paramName = (String) subqueryParams.get(i); + Object paramValue = subqueryParams.get(i + 1); + + // Check existence of paramName + int index = params.indexOf(paramName); + + if (index == -1) { // if not defined, param is only used + // in sub-query, so add it in the whole query + addParam(paramName, paramValue); + } else { // If already defined + + Object existingValue = params.get(index + 1); + + // Only change paramName in not equals case + if (!ObjectUtils.equals(existingValue, paramValue)) { + String newParamName = getValueName(paramName); + // Replace old paramName in subquery + subqueryString = + subqueryString.replace(":" + paramName, + ":" + newParamName); + + // Add the param to the current query + addParam(newParamName, paramValue); + } + } + } + } + + // Replace ? injection by the subquery + String result = queryPart.replace("?", subqueryString); + return addWhere(result); + } + + /** + * Add an element to the select in the query. Depends on the result wanted + * in execute methods. The main entity will be automatically added only if + * an alias is initialize from constructor. If you want only this select + * element, use {@link #setSelect(String...) } method instead. + * + * @param select element to add + * @return the TopiaQuery + */ + public TopiaQuery addSelect(String... select) { + +// if (mainAlias != null && +// CollectionUtils.isNotEmpty(userSelects) && +// userSelects.contains(mainAlias)) { +// // if select is the mainAlias, do nothing +// return this; +// } +// String str = convertStringArray(select); +// // if select is the mainAlias, do nothing +// if (mainAlias != null && str.equals(mainAlias)) { +// return this; +// } + + if (userSelects == null) { + + userSelects = new ArrayList<String>(); + + if (mainAlias != null) { + + // if mainAlias is not null, add it before adding the select in argument + userSelects.add(mainAlias); + } + } + + userSelects.addAll(Arrays.asList(select)); + +// // if select is not null, add the new element to the select +// if (this.select != null) { +// this.select.append(", "); +// // if mainAlias is not null, add it before adding the select in argument +// } else if (mainAlias != null) { +// this.select = new StringBuilder(mainAlias).append(", "); +// } else { +// this.select = new StringBuilder(); +// } +// this.select.append(convertStringArray(select)); + return this; + } + + /** + * Set the select in the query. Depends on the result wanted in execute + * methods. + * + * @param select element to set + * @return the TopiaQuery + */ + public TopiaQuery setSelect(String... select) { + userSelects = new ArrayList<String>(Arrays.asList(select)); +// this.select = new StringBuilder(convertStringArray(select)); + return this; + } + + /** + * Add the distinct key word in the query. The result will not have multiple + * same values. + * + * @return the TopiaQuery + */ + public TopiaQuery addDistinct() { + distinct = true; + return this; + } + + /** + * Add an element to the order in the query. Used to add some parameters to + * order by. + * + * @param order element to add + * @return the TopiaQuery + */ + public TopiaQuery addOrder(String... order) { + if (orderBys == null) { + orderBys = new ArrayList<String>(); + } + Collections.addAll(orderBys, order); +// if (orderBy == null) { +// orderBy = new StringBuilder(" ORDER BY "); +// } else { +// orderBy.append(", "); +// } +// orderBy.append(convertStringArray(order)); + return this; + } + + public TopiaQuery addOrderDesc(String order) { + return addOrder(order + " DESC"); + } + + /** + * Add an element to the group of the query. Used to add some paramters to + * group by. + * + * @param group element to add + * @return the TopiaQuery + */ + public TopiaQuery addGroup(String... group) { + if (groupBys == null) { + groupBys = new ArrayList<String>(); + } + Collections.addAll(groupBys, group); +// if (groupBy == null) { +// groupBy = new StringBuilder(" GROUP BY "); +// } else { +// groupBy.append(", "); +// } +// groupBy.append(convertStringArray(group)); + return this; + } + +// /** +// * Helper method for array type. Each value will be separated by a comma. +// * TODO-fdesbois-2010-05-25 : replace this algo by StringUtil.join() +// * +// * @param array of String +// * @return a String with values of the array separated by a comma +// */ +// protected String convertStringArray(String... array) { +// StringBuilder result = new StringBuilder(); +// for (String value : array) { +// result.append(", ").append(value); +// } +// String str = ""; +// if (result.length() > 0) { +// str = result.substring(2); +// } +// return str; +// } + + /** + * Limit the result of the query with startIndex and endIndex. + * + * @param start first index to get from the results + * @param end last index to get from the results + * @return the TopiaQuery + */ + public TopiaQuery setLimit(int start, int end) { + startIndex = start; + endIndex = end; + return this; + } + + /** + * Remove limits previously set + * + * @return the TopiaQuery + */ + public TopiaQuery resetLimit() { + startIndex = null; + endIndex = null; + return this; + } + + /** + * Set the max results wanted for the query. + * + * @param max the number of elements wanted + * @return the TopiaQuery + */ + public TopiaQuery setMaxResults(int max) { + return setLimit(0, max - 1); + } + + /** + * Add a {@code filter} to the query that contains limit indexes, + * orderBy condition and referenceId if needed. The referenceProperty is + * necessary to use the referenceId of the {@code filter}. The filter will + * be applied on the main entity in the query (using the mainAlias if + * necessary). + * <p/> + * Note : the default orderBy is the topiaCreateDate ordered desc (the most + * recent in first) + * + * @param filter Filter to apply on the query + * @return the TopiaQuery + * @throws IllegalArgumentException if referenceId is defined but no + * referenceProperty was set + * @see #addFilter(EntityFilter, String) + */ + public TopiaQuery addFilter(EntityFilter filter) + throws IllegalArgumentException { + return addFilter(filter, null); + } + + /** + * Add a {@code filter} to the query that contains limit indexes, + * orderBy condition and referenceId if needed. In some case it's necessary + * to specify explicitely the {@code propertyToFilter} in complex queries. + * The referenceProperty need to be specifie in {@code filter} to have a + * correspondance between the referenceId and it's property in the query. By + * default, the {@code propertyToFilter} is the mainAlias of the query. + * <p/> + * Note : the default orderBy is the topiaCreateDate ordered desc (the most + * recent in first) + * + * @param filter Filter to apply on the query + * @param propertyToFilter Explicit property to filter + * @return the TopiaQuery + * @throws IllegalArgumentException if referenceId is defined but no + * referenceProperty was set + */ + public TopiaQuery addFilter(EntityFilter filter, + String propertyToFilter) + throws IllegalArgumentException { + + if (propertyToFilter == null) { + propertyToFilter = mainAlias; + } + + Integer startIndex = filter.getStartIndex(); + Integer endIndex = filter.getEndIndex(); + String orderBy = filter.getOrderBy(); + String referenceId = filter.getReferenceId(); + String referenceProperty = filter.getReferenceProperty(); + + if (log.isDebugEnabled()) { + log.debug("Filter added to the query : " + filter); + } + + // Add limits. Only startIndex do nothing. + // startIndex + endIndex provides the limit + if (startIndex != null && endIndex != null) { + setLimit(startIndex, endIndex); + + // endIndex only provides the maxResults wanted + } else if (endIndex != null) { + setMaxResults(endIndex); + } + + // Add order to the main entity in the query, splitted by comma + if (orderBy != null) { + List<String> order = new ArrayList<String>(); + for (String elmt : orderBy.split(",")) { + String property = + TopiaQuery.getProperty(propertyToFilter, elmt.trim()); + order.add(property); + } + addOrder(order.toArray(new String[order.size()])); + + // Default order by creation date + } else { + addOrderDesc(getPropertyCreateDate(propertyToFilter)); + } + + if (filter.hasReference()) { + if (referenceProperty == null) { + throw new IllegalArgumentException("Reference property need" + + " to be defined in filter to use referenceId = " + + referenceId); + } + addEquals(getPropertyId(referenceProperty), referenceId); + } + + return this; + } + + /** + * Simple execution of the query. This method use directly the find method + * in TopiaContext interface. + * + * @param transaction the TopiaContext to use for execution + * @return a List of results + * @throws TopiaException for error on query execution + * @see TopiaContext#findAll(String, Object...) + */ + public List execute(TopiaContext transaction) throws TopiaException { + String query = fullQuery(); + if (log.isDebugEnabled()) { + log.debug(this); + } + List result; + if (startIndex != null && endIndex != null) { + result = transaction.find(query, startIndex, endIndex, + getParams().toArray()); + } else { + result = transaction.findAll(query, getParams().toArray()); + } + return result; + } + + /** + * Execute the query and get a List of entity. Some properties will be + * loaded if they are prealably set using ${@link #addLoad(String...) }. + * + * @param <E> entity type + * @param transaction the TopiaContext to use for execution + * @param entityClass used to check return type of execution results + * @return a List of TopiaEntity corresponding to the entityClass in + * argument + * @throws TopiaException for error on query execution + * @throws ClassCastException if entityClass doesn't match to results + */ + public <E extends TopiaEntity> List<E> executeToEntityList( + TopiaContext transaction, Class<E> entityClass) + throws TopiaException, ClassCastException { + List res = execute(transaction); + if (log.isTraceEnabled()) { + log.trace("Properties to load : " + getPropertiesToLoad()); + } + List<E> results = new ArrayList<E>(); + for (Object o : res) { + if (o == null) { + continue; + } + if (o instanceof Object[]) { + // If it's an array, we want only the first element wich is the + // entity wanted + // We know that the array have at least one element + o = ((Object[]) o)[0]; + } + if (!entityClass.isAssignableFrom(o.getClass())) { + throw new ClassCastException(o.getClass().getName() + + " can't be cast to " + entityClass.getName() + + " o : " + o); + } + E entity = (E) o; + // Check distinct constraint for complex query where o is firstly an + // Object[] (potentially distinct results with existing entity to add) + if (!(distinct && results.contains(entity))) { + if (!getPropertiesToLoad().isEmpty()) { + loadProperties(entity); + } + results.add(entity); + } + } + return results; + } + + /** + * Execute the query and get a Map of entity with key type in argument. Some + * properties will be loaded if they are prealably set using ${@link + * #addLoad(String...) }. + * + * @param <E> entity type + * @param <K> the type of the map key + * @param transaction the TopiaContext to use for execution + * @param entityClass needed to execute the query + * @param keyName the property name of the key in the entity + * @param keyClass the key class for the result map + * @return a Map with the key type defined and the entity in value + * @throws TopiaException for error on query execution + * @throws ClassCastException if entityClass doesn't match to results + */ + public <E extends TopiaEntity, K> Map<K, E> executeToEntityMap( + TopiaContext transaction, Class<E> entityClass, + String keyName, Class<K> keyClass) + throws TopiaException, ClassCastException { + + // Use LinkedHashMap to keep insert order from list results which + // can be ordered + Map<K, E> results = new LinkedHashMap<K, E>(); + List<E> list = executeToEntityList(transaction, entityClass); + for (E elmt : list) { + Object value = loadProperty(elmt, keyName); + if (value != null && !keyClass.isAssignableFrom(value.getClass())) { + throw new ClassCastException(value.getClass().getName() + + " can't be cast to " + keyClass.getName()); + } + results.put((K) value, elmt); + } + return results; + } + + /** + * Execute the query and get a Map of entity with topiaId in key. Some + * properties will be loaded if they are prealably set using ${@link + * #addLoad(String...) }. + * + * @param <E> entity type + * @param transaction the TopiaContext to use for execution + * @param entityClass used to check return type of execution results + * @return a Map with the key type defined and the entity in value + * @throws TopiaException for error on query execution + * @throws ClassCastException if entityClass doesn't match to results + */ + public <E extends TopiaEntity> Map<String, E> executeToEntityMap( + TopiaContext transaction, Class<E> entityClass) + throws TopiaException, ClassCastException { + return executeToEntityMap(transaction, entityClass, + TopiaEntity.TOPIA_ID, String.class); + } + + /** + * Execute the query and get the first result entity. Some properties will + * be loaded if they are prealably set using ${@link #addLoad(String...) }. + * + * @param <E> entity type + * @param transaction the TopiaContext to use for execution + * @param entityClass used to check return type of execution results + * @return a TopiaEntity corresponding to the entityClass in argument + * @throws TopiaException for error on query execution + * @throws ClassCastException if entityClass doesn't match to results + */ + public <E extends TopiaEntity> E executeToEntity(TopiaContext transaction, + Class<E> entityClass) + throws TopiaException, ClassCastException { + setMaxResults(1); + List<E> results = executeToEntityList(transaction, entityClass); + resetLimit(); + return !results.isEmpty() ? results.get(0) : null; + } + + /** + * Execute the query and get an Object for result. The select is overriden + * to get only the right value for return. + * + * @param transaction the TopiaContext to use for execution + * @param select the Select overriden + * @return an Object + * @throws TopiaException for error on query execution + */ + public Object executeToObject(TopiaContext transaction, String select) + throws TopiaException { + List<String> oldValue = this.userSelects; +// StringBuilder oldValue = this.select; + if (!StringUtils.isEmpty(select)) { + setSelect(select); + } + Object result = null; + setMaxResults(1); + List results = execute(transaction); + if (!results.isEmpty()) { + result = results.get(0); + } +// this.select = oldValue; + userSelects = oldValue; + resetLimit(); + return result; + } + + /** + * Execute the query and get an Integer for result. Used only for query with + * aggration select which return a Long : COUNT, SUM ... The select is + * overriden to get only the right value for return. + * + * @param transaction the TopiaContext to use for execution + * @param select the Select overriden (ex : SUM(myParam)) + * @return an Integer + * @throws TopiaException for error on query execution + */ + public int executeToInteger(TopiaContext transaction, String select) + throws TopiaException { + Long res = (Long) executeToObject(transaction, select); + return res != null ? res.intValue() : 0; + } + + /** + * Execute the query and get a String for result. Used for query with MAX, + * ... The select is overriden to get only the right value for return. + * + * @param transaction the TopiaContext to use for execution + * @param select the Select overriden (ex : MAX(myParam)) + * @return a String + * @throws TopiaException for error on query execution + */ + public String executeToString(TopiaContext transaction, String select) + throws TopiaException { + Object res = executeToObject(transaction, select); + return res != null ? (String) res : ""; + } + + /** + * Execute a simple count on the query, i.e. the number of results get from + * the query. The order is not considered to count the elements and will be + * temporarly disabled. The distinct constraint will be manage if necessary + * : + * <pre> + * COUNT(DISTINCT mainAlias) + * </pre> + * + * @param transaction the TopiaContext to use for execution + * @return an int corresponding to the number of result in the query + * @throws TopiaException for error on query execution + */ + public int executeCount(TopiaContext transaction) throws TopiaException { + List<String> oldOrderBys = orderBys; + orderBys = null; +// StringBuilder oldOrder = orderBy; +// orderBy = null; + StringBuilder count = new StringBuilder("COUNT("); + // Ano #560 : manage distinct case when the alias is set (otherwise + // no need the distinct keyword because the entity type in query is + // unique) + + //Note Ano #1930 tchemit 2012-02-01 this code does not work if a setSelect was done + +// if (distinct && StringUtils.isNotEmpty(mainAlias) && +// isUserSelectEqualsMainAlias()) { +// count.append("DISTINCT ").append(mainAlias); +// // When distinct is not set, use <pre>*</pre> +// } else { +// count.append('*'); +// } + + String mainSelect = null; + + if (distinct) { + + if (CollectionUtils.isNotEmpty(userSelects)) { + + if (userSelects.size() > 1) { + + // can not count this on multiple select... + throw new TopiaException( + "To count, can not have more than one select, but found " + + userSelects); + } + mainSelect = userSelects.get(0); + } else if (StringUtils.isNotEmpty(mainAlias)) { + + // use main alias + mainSelect = mainAlias; + } + } + + if (mainSelect != null) { + count.append("DISTINCT ").append(mainSelect); + } else { + count.append('*'); + } + count.append(')'); + int result = executeToInteger(transaction, count.toString()); + orderBys = oldOrderBys; +// orderBy = oldOrder; + return result; + } + + protected boolean isUserSelectEqualsMainAlias() { + boolean result = !CollectionUtils.isEmpty(userSelects) && + userSelects.size() == 1 && + userSelects.get(0).equals(mainAlias); + return result; + } + + /** + * Load all properties for the entity. + * + * @param entity used to load properties + * @throws TopiaException for error on query execution + */ + protected void loadProperties(TopiaEntity entity) + throws TopiaException { + for (String prop : getPropertiesToLoad()) { + if (log.isTraceEnabled()) { + log.trace("load property " + prop + " ..."); + } + List<String> str = Arrays.asList(prop.split("\\.")); + Iterator<String> it = str.iterator(); + TopiaEntity currEntity = entity; + while (it.hasNext()) { + String s = it.next(); + if (mainAlias != null && s.equals(mainAlias)) { + if (log.isTraceEnabled()) { + log.trace("Skip alias : " + mainAlias); + } + continue; + } + if (log.isTraceEnabled()) { + log.trace("Current entity : " + + currEntity.getClass().getSimpleName()); + log.trace("Current loading : " + s); + } + if (it.hasNext()) { + currEntity = loadEntityProperty(currEntity, s); + } else { + loadProperty(currEntity, s); + } + } + } + } + + /** + * Load a property of type TopiaEntity from an other entity. + * + * @param <T> type of the entity extends TopiaEntity + * @param entity used to load the property + * @param property name of the property in the entity + * @return a TopiaEntity corresponding to the property loaded + * @throws TopiaException for error on query execution + */ + protected <T extends TopiaEntity> TopiaEntity loadEntityProperty(T entity, + String property) + throws TopiaException { + return (TopiaEntity) loadProperty(entity, property); + } + + /** + * Load a property from an entity. + * + * @param <T> type of the entity extends TopiaEntity + * @param entity used to load the property + * @param property name of the property in the entity + * @return an Object corresponding to the property loaded + * @throws TopiaException for error loading property (encapsulate + * IllegalACessException, InvocationTargetException, + * NoSuchMethodException) + */ + protected <T extends TopiaEntity> Object loadProperty(T entity, + String property) + throws TopiaException { + try { + Object res = PropertyUtils.getProperty(entity, property); + if (log.isDebugEnabled()) { + log.debug("load property '" + property + "' for '" + + entity.getClass().getSimpleName() + "'"); + } + if (res != null && Collection.class.isAssignableFrom(res.getClass())) { + Collection<?> list = (Collection<?>) res; + list.size(); + } + return res; + } catch (IllegalAccessException eee) { + throw new TopiaException("Illegal access on property " + + property + " from entity " + + entity.getClass().getName(), eee); + } catch (InvocationTargetException eee) { + throw new TopiaException("Invocation error on entity " + + entity.getClass().getName() + " for property " + + property, eee); + } catch (NoSuchMethodException eee) { + throw new TopiaException("Getter method does not exist for" + + " property " + property + " from entity " + + entity.getClass().getName(), eee); + } + } + + /** + * This method is used to concat properties from entities. Ex in HQL you can + * have boat.shipOwner.name, these properties are defined as constants in + * each entity associated (SHIP_OWNER in Boat entity, NAME in ShipOwner + * entity) so you just have to call this method as : + * <pre> + * getProperty("boat", Boat.SHIP_OWNER, ShipOwner.NAME); + * // will return boat.shipOwner.name + * </pre> + * <p/> + * It's better to use constants instead of directly the string chain to + * avoid problems on changing property name in model. Furthermore it's + * better to use this method instead of doing : + * <pre> + * "boat." + Boat.SHIP_OWNER + "." + ShipOwner.NAME + * </pre> + * + * @param entityProperty to concat + * @return the string chain with properties separated with a dot + */ + public static String getProperty(String... entityProperty) { + List<String> list = Arrays.asList(entityProperty); + String result = StringUtil.join(list, ".", false); + return result; + } + + public String getPropertyId(String alias) { + return getProperty(alias, TopiaEntity.TOPIA_ID); + } + + public String getPropertyCreateDate(String alias) { + return getProperty(alias, TopiaEntity.TOPIA_CREATE_DATE); + } + + public String getPropertyVersion(String alias) { + return getProperty(alias, TopiaEntity.TOPIA_VERSION); + } + + @Override + protected void finalize() throws Throwable { + // Clean StringBuilder statements +// select = null; + from = null; +// where = null; +// orderBy = null; +// groupBy = null; + super.finalize(); + } + + @Override + public String toString() { + StringBuilder result = new StringBuilder(fullQuery()). + append("; (PARAMS : "). + append(getParams()). + append("); (LIMIT : "). + append(startIndex). + append(", "). + append(endIndex). + append(')'); + return result.toString(); + } + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaSQLQuery.java b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaSQLQuery.java new file mode 100644 index 0000000..8b14d9c --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaSQLQuery.java @@ -0,0 +1,248 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.framework; + +import org.hibernate.jdbc.Work; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaRuntimeException; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.ResultSetMetaData; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * An executor of sql query which permits to obtain a single result via + * the method {@link #findSingleResult(TopiaContextImplementor)} + * or a multiple result with method {@link #findMultipleResult(TopiaContextImplementor)}. + * + * @param <O> the type of result data + * @since 2.5 + */ +public abstract class TopiaSQLQuery<O> { + + /** + * Prepare the statement used to do the sql query. + * + * @param connection jdbc connection to use + * @return the statement containing the query to execute + * @throws SQLException if any problem + */ + protected abstract PreparedStatement prepareQuery(Connection connection) throws SQLException; + + /** + * given a result set, extract the data. + * + * @param set the result set + * @return the data extracted from the current set, or {@code null} + * @throws SQLException if any prob + */ + protected abstract O prepareResult(ResultSet set) throws SQLException; + + /** + * Obtain a single result from the builded sql query. + * + * @param tx the transaction used to execute the query. + * @return the single result or {@code null} if none found. + * @throws TopiaException for any pb + */ + public O findSingleResult(TopiaContextImplementor tx) throws TopiaException { + final List<O> result = new ArrayList<O>(); + + tx.getHibernate().doWork(new Work() { + + @Override + public void execute(Connection connection) throws SQLException { + + PreparedStatement ps = prepareQuery(connection); + + try { + ResultSet set = ps.executeQuery(); + + findSingleResult(result, set); + + } catch (Exception e) { + throw new TopiaRuntimeException("Could not execute query", e); + } finally { + ps.close(); + } + } + }); + return result.isEmpty() ? null : result.get(0); + } + + /** + * Obtain a multiple results fro the builded sql query. + * + * @param tx the transaction used to execute the query. + * @return the list of results (the list is empty if non result is found). + * @throws TopiaException for any pb + */ + public List<O> findMultipleResult(TopiaContextImplementor tx) throws TopiaException { + final List<O> result = new ArrayList<O>(); + + tx.getHibernate().doWork(new Work() { + + @Override + public void execute(Connection connection) throws SQLException { + + PreparedStatement ps = prepareQuery(connection); + try { + ResultSet set = ps.executeQuery(); + + findMultipleResult(result, set); + + } catch (Exception e) { + throw new TopiaRuntimeException("Could not execute query", e); + } finally { + ps.close(); + } + } + }); + return result; + } + + /** + * A hook to obtain the result set just after the query execute. + * + * @param set the result set just obtained + * @throws SQLException if any prob + * @since 2.6.4 + */ + protected void afterExecuteQuery(ResultSet set) throws SQLException { + // by default do nothing + } + + /** + * Obtain a single result given the result set and push in in the result list. + * + * @param result the result list + * @param set the set of the executed sql query + * @throws SQLException if any pb + * @since 2.6.4 + */ + protected void findSingleResult(List<O> result, + ResultSet set) throws SQLException { + + afterExecuteQuery(set); + + if (set.next()) { + O singleResult = prepareResult(set); + if (singleResult != null) { + result.add(singleResult); + } + } + } + + /** + * Obtain a multi result given the result set and push in in the result list. + * + * @param result the result list + * @param set the set of the executed sql query + * @throws SQLException if any pb + * @since 2.6.4 + */ + protected void findMultipleResult(List<O> result, + ResultSet set) throws SQLException { + + afterExecuteQuery(set); + + while (set.next()) { + O singleResult = prepareResult(set); + + if (singleResult != null) { + result.add(singleResult); + } + } + } + + /** + * Obtain the column names of a given result set using his metadata. + * + * @param set the result set to inspect + * @return the column names of the result set + * @throws SQLException if any pb + * @since 2.6.4 + */ + protected String[] getColumnNames(ResultSet set) throws SQLException { + ResultSetMetaData metaData = set.getMetaData(); + int columnCount = metaData.getColumnCount(); + String[] result = new String[columnCount]; + for (int i = 0; i < columnCount; i++) { + result[i] = metaData.getColumnName(i + 1); + } + return result; + } + + /** + * From a given result set, let's count his number of row. + * <p/> + * <strong>Note:</strong> the result set must be scrollable to go back to + * before first row. + * + * @param set the result set to inspect + * @return the number of row of the given result set + * @throws SQLException if any pb + * @since 2.6.4 + */ + protected long getNbRows(ResultSet set) throws SQLException { + + long nbRows = 0; + while (set.next()) { + nbRows++; + } + // go back before first row (be ware the resultset must be scrollable) + set.beforeFirst(); + return nbRows; + } + + /** + * Given the column names of the result set, transform the row of the + * result set to a map with column name as key. + * + * @param columnNames column names of the result set + * @param set the set to inspect + * @return the map for the given row of the result set + * @throws SQLException if any pb + * @since 2.6.4 + */ + protected Map<String, Object> getRowAsMap(String[] columnNames, + ResultSet set) throws SQLException { + + Map<String, Object> result = new LinkedHashMap<String, Object>(); + int length = columnNames.length; + for (int i = 0; i < length; i++) { + String name = columnNames[i]; + Object value = set.getObject(i + 1); + result.put(name, value); + } + return result; + } + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaService.java b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaService.java new file mode 100644 index 0000000..1b2ca30 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaService.java @@ -0,0 +1,85 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * + * TopiaService.java + * + * Created: 8 oct. 06 17:15:52 + * + * @author poussin <poussin@codelutin.com> + * @version $Revision$ + * + * Last update: $Date$ + * by : $Author$ + */ + +package org.nuiton.topia.framework; + +/** + * Used to implement a service for Topia. You have to provide a static property + * called SERVICE_NAME that identify the service : + * <p/> + * <li>public static final String SERVICE_NAME = "monservice"; + * <p/> + * The value of this attribute need to be returned when using + * {@link #getServiceName()} method. + * + * @author poussin <poussin@codelutin.com> + * @version $Id$ + */ +public interface TopiaService { + + /** + * Return the name of the service, this name need to match with + * configuration file, for example for index service, we need to have : + * "topia.service.index" and this method will returned "index". + * + * @return the service name + */ + String getServiceName(); + + /** + * Retrieve entities of this service needed for persistence. + * + * @return List of entities full qualified name separated by a comma + */ + Class<?>[] getPersistenceClasses(); + + /** + * Initiliaze the service before create the {@code context}. + * + * @param context TopiaContextImplementor + * @return true if service need to be activated or not + */ + boolean preInit(TopiaContextImplementor context); + + /** + * Initiliaze the service after create the {@code context}. + * + * @param context TopiaContextImplementor + * @return true if service need to be activated or not + */ + boolean postInit(TopiaContextImplementor context); + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaTransactionAware.java b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaTransactionAware.java new file mode 100644 index 0000000..4e2143b --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaTransactionAware.java @@ -0,0 +1,57 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin, Tony Chemit + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.framework; + +import org.nuiton.topia.TopiaContext; + +/** + * Use this contract on a object which use a {@code TopiaContext} as a + * transaction. + * <p/> + * The method {@link #getTransaction()} returns the internal transaction used. + * <p/> + * the method {@link #setTransaction(TopiaContext)} put the internal + * transaction. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.1 + */ +public interface TopiaTransactionAware { + + /** + * Obtains the internal transaction. + * <p/> + * If no transaction was opened, can return the {@code null} object. + * + * @return the current transaction (can be null or closed...). + */ + TopiaContext getTransaction(); + + /** + * Put in the instance, the given transaction. + * + * @param transaction the transaction to push + */ + void setTransaction(TopiaContext transaction); +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaUtil.java b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaUtil.java new file mode 100644 index 0000000..7bdcdbc --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/framework/TopiaUtil.java @@ -0,0 +1,523 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin, Chatellier Eric + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.framework; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.SessionFactory; +import org.hibernate.boot.registry.StandardServiceRegistry; +import org.hibernate.boot.registry.StandardServiceRegistryBuilder; +import org.hibernate.cfg.Configuration; +import org.hibernate.dialect.Dialect; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.engine.spi.SessionFactoryImplementor; +import org.hibernate.mapping.PersistentClass; +import org.hibernate.mapping.Table; +import org.hibernate.service.Service; +import org.hibernate.service.ServiceRegistry; +import org.hibernate.service.spi.SessionFactoryServiceRegistry; +import org.hibernate.tool.hbm2ddl.DatabaseMetadata; +import org.hibernate.tool.hbm2ddl.TableMetadata; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaContextFactory; +import org.nuiton.topia.TopiaNotFoundException; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.util.RecursiveProperties; +import org.nuiton.util.Resource; + +import java.io.Closeable; +import java.io.IOException; +import java.net.URL; +import java.sql.Connection; +import java.sql.SQLException; +import java.util.Iterator; +import java.util.Properties; +import java.util.StringTokenizer; +import java.util.regex.Pattern; + +import com.google.common.base.Supplier; + +/** + * TODO-fdesbois-20100507 : Need javadoc + translations for existing methods. + * + * @author bpoussin <poussin@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @version $Id$ + */ +public class TopiaUtil { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(TopiaUtil.class); + + /** @deprecated since 2.5.3, use now the constant {@link TopiaContextFactory#CONFIG_DEFAULT_SCHEMA}. */ + @Deprecated + public final static String HIBERNATE_DEFAULT_SCHEMA = + TopiaContextFactory.CONFIG_DEFAULT_SCHEMA; + + /** @deprecated since 2.5.3, use the already present {@link TopiaContextImpl#TOPIA_PERSISTENCE_CLASSES}. */ + @Deprecated + public final static String TOPIA_PERSISTENCE_CLASSES = TopiaContextImpl.TOPIA_PERSISTENCE_CLASSES; + + /** + * Permet de récupérer le fichier de propriété ayant le nom passé en + * argument. + * + * @param pathOrUrl le nom du fichier de propriété à charger, s'il est null + * ou vide retourne un objet Properties vide. + * @return Un nouvel objet de propriete + * @throws TopiaNotFoundException Si pathOrUrl n'est pas null ou vide et que + * le fichier devant contenir les propriétés + * n'est pas retrouvé. + */ + static public Properties getProperties(String pathOrUrl) + throws TopiaNotFoundException { + return getProperties(null, pathOrUrl); + } + + /** + * Permet de récupérer le fichier de propriété ayant le nom passé en + * argument. + * + * @param parent l'objet properties utilisé comme parent de l'objet + * retourné + * @param pathOrUrl le nom du fichier de propriété à charger, s'il est null + * ou vide retourne un objet Properties vide. + * @return Un nouvel objet de propriete + * @throws TopiaNotFoundException Si pathOrUrl n'est pas null ou vide et que + * le fichier devant contenir les propriétés + * n'est pas retrouvé. + */ + static public Properties getProperties(Properties parent, String pathOrUrl) + throws TopiaNotFoundException { + Properties result = new RecursiveProperties(parent); + + // load properties for helper + if (pathOrUrl != null && !pathOrUrl.equals("")) { + try { + URL propURL = Resource.getURL(pathOrUrl); + log.info("Properties file used for " + pathOrUrl + " is: " + propURL); + result.load(propURL.openStream()); + } catch (Exception eee) { + throw new TopiaNotFoundException( + "Properties file can't be found: " + pathOrUrl, eee); + } + } + return result; + } + + /** + * Compute a regex pattern given a format string. + * <p/> + * A {@link String#format(String, Object...)} will be apply to + * <code>format</code>, with for parameters the list of <code>klass</code> + * transformed in topia pattern via method {@link #getTopiaIdPattern(Class)} + * ready to be capture (enclosed by ()). + * + * @param format the format + * @param classes the list of class to use + * @return the pattern computed + */ + public static Pattern getTopiaPattern(String format, + Class<? extends TopiaEntity>... classes) { + String[] entityPatterns = new String[classes.length]; + for (int i = 0; i < classes.length; i++) { + Class<? extends TopiaEntity> aClass = classes[i]; + entityPatterns[i] = "(" + getTopiaIdPattern(aClass) + ")"; + } + String s = String.format(format, (Object[]) entityPatterns); + if (log.isDebugEnabled()) { + log.debug(s); + } + return Pattern.compile(s); + } + + /** + * Compute the pattern to be used to capture a topia id for a given entity + * class. + * + * @param klass the entity class + * @return the pattern to capture a topia id for the given entity class. + */ + public static String getTopiaIdPattern(Class<? extends TopiaEntity> klass) { + StringBuilder buffer = new StringBuilder(); + StringTokenizer stk = new StringTokenizer(klass.getName(), "."); + while (stk.hasMoreTokens()) { + buffer.append("\\.").append(stk.nextToken()); + } + buffer.append("#(?:\\d+?)#(?:\\d+)\\.(?:\\d+)"); + return buffer.substring(2); + } + + /** + * Test si une entite donnee correspondant a une configuration existe en + * base. + * + * @param tx la session topia + * @param entityName le nom de l'entite a tester + * @return <tt>true</tt> si le schema de la table existe + * @since 2.6.4 + */ + public static boolean isSchemaExist(TopiaContext tx, + String entityName) { + + TopiaContextImplementor txi = (TopiaContextImplementor) tx; + + ConnectionProviderSupplier connectionProviderSupplier = null; + + boolean exist = false; + + try { + connectionProviderSupplier = new ConnectionProviderSupplier(txi); + + Configuration configuration = txi.getHibernateConfiguration(); + PersistentClass classMapping = + configuration.getClassMapping(entityName); + if (classMapping == null) { + if (log.isInfoEnabled()) { + Iterator<?> itr = configuration.getClassMappings(); + while (itr.hasNext()) { + log.info("available mapping " + itr.next()); + } + } + throw new IllegalArgumentException( + "could not find entity with name " + entityName); + } + Table testTable = classMapping.getTable(); + + if (testTable == null) { + throw new IllegalArgumentException( + "could not find entity with name " + entityName); + } + + ConnectionProvider connectionProvider = connectionProviderSupplier.get(); + + Dialect dialect = Dialect.getDialect(configuration.getProperties()); + + Connection connection = null; + try { + connection = connectionProvider.getConnection(); + + DatabaseMetadata meta = new DatabaseMetadata(connection, dialect); + + TableMetadata tmd = meta.getTableMetadata( + testTable.getName(), testTable.getSchema(), + testTable.getCatalog(), testTable.isQuoted()); + + if (tmd != null) { + //table exist + exist = true; + } + } finally { + if (connection != null) { + connection.close(); + } + } + + } catch (SQLException e) { + log.error("Cant connect to database", e); + } catch (TopiaNotFoundException e) { + log.error("Cant connect to database", e); + } finally { + if (connectionProviderSupplier != null) { + try { + connectionProviderSupplier.close(); + } catch (IOException e) { + log.error("Cant close connection provider", e); + } + } + } + + return exist; + } + + /** + * Test si une entite donnee correspondant a une configuration existe en + * base. + * + * @param configuration la configuration hibernate + * @param entityName le nom de l'entite a tester + * @return <tt>true</tt> si le schema de la table existe + */ + public static boolean isSchemaExist(Configuration configuration, + String entityName) { + + ConnectionProviderSupplier connectionProviderSupplier = + new ConnectionProviderSupplier(configuration); + + boolean exist = false; + + try { + PersistentClass classMapping = + configuration.getClassMapping(entityName); + if (classMapping == null) { + if (log.isInfoEnabled()) { + Iterator<?> itr = configuration.getClassMappings(); + while (itr.hasNext()) { + log.info("available mapping " + itr.next()); + } + } + throw new IllegalArgumentException( + "could not find entity with name " + entityName); + } + Table testTable = classMapping.getTable(); + + if (testTable == null) { + throw new IllegalArgumentException( + "could not find entity with name " + entityName); + } + + ConnectionProvider connectionProvider = connectionProviderSupplier.get(); + + Dialect dialect = Dialect.getDialect(configuration.getProperties()); + + Connection connection = null; + try { + connection = connectionProvider.getConnection(); + + DatabaseMetadata meta = new DatabaseMetadata(connection, dialect); + + TableMetadata tmd = meta.getTableMetadata( + testTable.getName(), testTable.getSchema(), + testTable.getCatalog(), testTable.isQuoted()); + + if (tmd != null) { + //table exist + exist = true; + } + } finally { + if (connection != null) { + connection.close(); + } + } + + } catch (SQLException e) { + log.error("Cant connect to database", e); + } finally { + try { + connectionProviderSupplier.close(); + } catch (IOException e) { + log.error("Cant close connection provider", e); + } + } + + return exist; + } + + /** + * Test if the db associated to the given {@code configuration} contaisn any of + * the dealed entities. + * + * @param configuration hibernate db configuration + * @return {@code true} if there is no schema for any of the dealed entities, + * {@code false} otherwise. + * @since 2.5.3 + */ + public static boolean isSchemaEmpty(Configuration configuration) { + + ConnectionProviderSupplier connectionProviderSupplier = + new ConnectionProviderSupplier(configuration); + + try { + + ConnectionProvider connectionProvider = connectionProviderSupplier.get(); + + Dialect dialect = Dialect.getDialect(configuration.getProperties()); + + Connection connection = null; + try { + connection = connectionProvider.getConnection(); + + DatabaseMetadata meta = new DatabaseMetadata(connection, dialect); + + Iterator<?> itr = configuration.getClassMappings(); + while (itr.hasNext()) { + PersistentClass classMapping = (PersistentClass) itr.next(); + Table testTable = classMapping.getTable(); + + if (testTable == null) { + throw new IllegalArgumentException( + "could not find entity with name " + + classMapping.getClassName()); + } + + + TableMetadata tmd = meta.getTableMetadata( + testTable.getName(), testTable.getSchema(), + testTable.getCatalog(), testTable.isQuoted()); + + if (tmd != null) { + //table exist + + + if (log.isDebugEnabled()) { + log.debug("Existing table found " + + testTable.getName() + " for entity " + + classMapping.getClassName() + + ", db is not empty."); + } + + return false; + } + } + + } finally { + if (connection != null) { + connection.close(); + } + } + + } catch (SQLException e) { + log.error("Cant connect to database", e); + } finally { + try { + connectionProviderSupplier.close(); + } catch (IOException e) { + log.error("Cant close connection provider", e); + } + } + + return true; + } + + /** + * Method to extract from the given Hibernate SessionFactory a working instance of StandardServiceRegistry + * <p/> + * IMPORTANT : As much as possible, prefer using the + * {@link #getSessionFactoryServiceRegistry(org.hibernate.SessionFactory)} mthod instead of the current one because + * the SessionFactoryServiceRegistry is a child of the StandardServiceRegistry + * <p/> + * NB: This method is static to make sure it does not depend on the current instance + * + * @param sessionFactory the Hibernate's SessionFactory instance + * @return the StandardServiceRegistry instance used by the given SessionFactory + */ + protected static StandardServiceRegistry getStandardServiceRegistry(SessionFactory sessionFactory) { + + // AThimel 03/04/14 The next two lines are the good way to get the StandardServiceRegistry in Hibernate 4.3 + SessionFactory.SessionFactoryOptions sessionFactoryOptions = sessionFactory.getSessionFactoryOptions(); + StandardServiceRegistry result = sessionFactoryOptions.getServiceRegistry(); + + return result; + } + + /** + * Method to extract from the given Hibernate SessionFactory a working instance of SessionFactoryServiceRegistry + * <p/> + * IMPORTANT : If possible, prefer using this method instead of + * {@link #getStandardServiceRegistry(org.hibernate.SessionFactory)} because the SessionFactoryServiceRegistry is a + * child of the StandardServiceRegistry + * <p/> + * NB: This method is static to make sure it does not depend on the current instance + * + * @param sessionFactory the Hibernate's SessionFactory instance + * @return the SessionFactoryServiceRegistry instance used by the given SessionFactory + */ + protected static SessionFactoryServiceRegistry getSessionFactoryServiceRegistry(SessionFactory sessionFactory) { + + // AThimel 03/04/14 The next two lines are the good way to get the SessionFactoryServiceRegistry in Hibernate 4.3 + SessionFactoryImplementor sessionFactoryImplementor = (SessionFactoryImplementor) sessionFactory; + SessionFactoryServiceRegistry result = (SessionFactoryServiceRegistry)sessionFactoryImplementor.getServiceRegistry(); + + return result; + } + + /** + * Method to get an Hibernate service instance from a given Hibernate SessionFactory + * <p/> + * NB: This method is static to make sure it does not depend on the current instance + * + * @param sessionFactory the Hibernate's SessionFactory instance + * @param serviceClass the expected service class + * @return the found service instance + * @throws org.hibernate.service.UnknownServiceException Indicates the service was not known. + * @see org.hibernate.service.ServiceRegistry#getService(Class) + */ + public static <S extends Service> S getHibernateService(SessionFactory sessionFactory, Class<S> serviceClass) { + + // Hibernate 4.3.x : prefer using the SessionFactoryServiceRegistry method instead of StandardServiceRegistry + // because SessionFactoryServiceRegistry is a child of the StandardServiceRegistry + ServiceRegistry serviceRegistry = getSessionFactoryServiceRegistry(sessionFactory); + + S result = serviceRegistry.getService(serviceClass); + return result; + } + + /** + * Hibernate 4.3.x compatible Supplier<ConnectionProvider>. The provider will choose the best way to find the + * ConnectionProvider depending on the way is has been created. + */ + public static class ConnectionProviderSupplier implements Supplier<ConnectionProvider>, Closeable { + + /** + * StandardServiceRegistry will be used if no SessionFactory is provided + */ + protected StandardServiceRegistry standardServiceRegistry; + + protected ConnectionProvider connectionProvider; + + protected boolean embeddedRegistry; + + public ConnectionProviderSupplier(TopiaContextImplementor topiaContextImplementor) throws TopiaNotFoundException { + this(topiaContextImplementor.getHibernateConfiguration()); + embeddedRegistry = false; + } + + public ConnectionProviderSupplier(Configuration configuration) { + Properties properties = configuration.getProperties(); + StandardServiceRegistryBuilder builder = new StandardServiceRegistryBuilder(); + this.standardServiceRegistry = builder.applySettings(properties).build(); + embeddedRegistry = true; + } + + @Override + public ConnectionProvider get() { + if (connectionProvider == null) { + // otherwise use the StandardServiceRegistry + connectionProvider = standardServiceRegistry.getService(ConnectionProvider.class); + } + return connectionProvider; + } + + @Override + public void close() throws IOException { + // Do not close the SessionFactory, it is probably used somewhere else + + // On the over hand, if standardServiceRegistry is provided, that means the its has been created explicitly + // for the current instance, close it + if (standardServiceRegistry != null && embeddedRegistry) { + StandardServiceRegistryBuilder.destroy(standardServiceRegistry); + } + } + } + + /** + * Return hibernate schema name + * + * @param config of hibernate + * @return schema name + */ + public static String getSchemaName(Configuration config) { + return config.getProperty(TopiaContextFactory.CONFIG_DEFAULT_SCHEMA); + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/framework/package-info.java b/topia-persistence/src/main/java/org/nuiton/topia/framework/package-info.java new file mode 100644 index 0000000..813971b --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/framework/package-info.java @@ -0,0 +1,41 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +/** + * Contains two interfaces, one {@link org.nuiton.topia.framework.TopiaContextImplementor} for internal + * manipulations of {@link org.nuiton.topia.TopiaContext} and one {@link org.nuiton.topia.framework.TopiaService} for topia + * services. This package contains also the main implementation {@link org.nuiton.topia.framework.TopiaContextImpl} + * for both {@link org.nuiton.topia.TopiaContext} for final applications and + * {@link org.nuiton.topia.framework.TopiaContextImplementor} for internal. + * <p /> + * You can also use {@link org.nuiton.topia.framework.TopiaQuery} class for query manipulation with + * {@link org.nuiton.topia.persistence.TopiaDAO}. + * <p /> + * {@link org.nuiton.topia.framework.TopiaUtil} is a helper class used for TopiaContext manipulations. + * + * @author fdesbois <fdesbois@codelutin.com> + * @version $Id$ + * @see org.nuiton.topia.TopiaContext + * @see org.nuiton.topia.persistence.TopiaDAO + */ +package org.nuiton.topia.framework; diff --git a/topia-persistence/src/main/java/org/nuiton/topia/generator/BinderHelperTransformer.java b/topia-persistence/src/main/java/org/nuiton/topia/generator/BinderHelperTransformer.java new file mode 100644 index 0000000..4cb1da0 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/generator/BinderHelperTransformer.java @@ -0,0 +1,273 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.generator; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.eugene.GeneratorUtil; +import org.nuiton.eugene.java.ObjectModelTransformerToJava; +import org.nuiton.eugene.models.object.*; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.util.TopiaEntityBinder; +import org.nuiton.topia.persistence.util.TopiaEntityHelper; +import org.nuiton.util.beans.BinderModelBuilder; +import org.nuiton.util.beans.BinderFactory; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + + +/*{generator option: parentheses = false}*/ +/*{generator option: writeString = +}*/ + +/** + * A template to generate a helper for {@link TopiaEntityBinder}. + * + * @author tchemit <chemit@codelutin.com> + * @version $Id$ + * @plexus.component role="org.nuiton.eugene.Template" role-hint="org.nuiton.topia.generator.BinderHelperTransformer" + * @since 2.3.1 + */ +public class BinderHelperTransformer extends ObjectModelTransformerToJava { + + private static final Log log = + LogFactory.getLog(BinderHelperTransformer.class); + + + @Override + public void transformFromModel(ObjectModel model) { + ObjectModelClass resultClass; + + List<ObjectModelClass> classes = TopiaGeneratorUtil.getEntityClasses(model, true); + + if (CollectionUtils.isEmpty(classes)) { + + // no entity classes, so no generation + log.warn("No entity to generate, " + getClass().getName() + " is skipped"); + return; + } + + String packageName = getDefaultPackageName(); + String modelName = model.getName(); + String binderHelperClazzName = modelName + "BinderHelper"; + String daoHelperClazzName = modelName + "DAOHelper"; + + resultClass = createClass(binderHelperClazzName, packageName); + + setSuperClass(resultClass, BinderFactory.class); + + + addImport(resultClass, TopiaEntityBinder.class); + addImport(resultClass, TopiaEntityHelper.class); + addImport(resultClass, TopiaEntity.class); + addImport(resultClass, BinderModelBuilder.class); + + ObjectModelOperation op; + + op = addOperation(resultClass, + "getTopiaBinder", + "<E extends TopiaEntity> TopiaEntityBinder<E>", + ObjectModelJavaModifier.PUBLIC, + ObjectModelJavaModifier.STATIC); + addParameter(op, "Class<E>", "entityClass"); + addParameter(op, "String", "contextName"); + setOperationBody(op, "" +/*{ + return (TopiaEntityBinder<E>) newBinder(entityClass, entityClass, contextName, TopiaEntityBinder.class); + }*/ + ); + + op = addOperation(resultClass, + "getSimpleTopiaBinder", + "<E extends TopiaEntity> TopiaEntityBinder<E>", + ObjectModelJavaModifier.PUBLIC, + ObjectModelJavaModifier.STATIC); + addParameter(op, "Class<E>", "entityClass"); + setOperationBody(op, "" +/*{ + return getTopiaBinder(entityClass, "<%=modelName%>"); + }*/ + ); + + op = addOperation(resultClass, + "registerTopiaBinder", + "void", + ObjectModelJavaModifier.PUBLIC, + ObjectModelJavaModifier.STATIC); + addParameter(op, "BinderModelBuilder", "builder"); + addParameter(op, "String", "contextName"); + setOperationBody(op, "" +/*{ + registerBinderModel(builder, contextName); + }*/ + ); + + op = addOperation(resultClass, + "registerTopiaBinder", + "<E extends TopiaEntity> TopiaEntityBinder<E>", + ObjectModelJavaModifier.PUBLIC, + ObjectModelJavaModifier.STATIC); + addParameter(op, "Class<E>", "entityClass"); + addParameter(op, "BinderModelBuilder", "builder"); + addParameter(op, "String", "contextName"); + setOperationBody(op, "" +/*{ + registerBinderModel(builder, contextName); + return getTopiaBinder(entityClass, contextName); + }*/ + ); + + op = addOperation(resultClass, + "copy", + "<E extends TopiaEntity> void", + ObjectModelJavaModifier.PUBLIC, + ObjectModelJavaModifier.STATIC); + addParameter(op, "String", "contextName"); + addParameter(op, "E", "source"); + addParameter(op, "E", "target"); + addParameter(op, "boolean", "tech"); + setOperationBody(op, "" +/*{ + Class<E> entityClass = (Class<E>) TopiaEntityHelper.getContractClass(<%=daoHelperClazzName%>.getContracts(), target.getClass()); + TopiaEntityBinder<E> binder = getTopiaBinder(entityClass, contextName); + if (binder == null) { + throw new NullPointerException("could not find a simple topia binder of type : " + target.getClass()); + } + binder.load(source, target, tech); + }*/ + ); + + op = addOperation(resultClass, + "simpleCopy", + "<E extends TopiaEntity> void", + ObjectModelJavaModifier.PUBLIC, + ObjectModelJavaModifier.STATIC); + addParameter(op, "E", "source"); + addParameter(op, "E", "target"); + addParameter(op, "boolean", "tech"); + setOperationBody(op, "" +/*{ + Class<E> entityClass = (Class<E>) TopiaEntityHelper.getContractClass(<%=daoHelperClazzName%>.getContracts(), target.getClass()); + TopiaEntityBinder<E> binder = getSimpleTopiaBinder(entityClass); + if (binder == null) { + throw new NullPointerException("could not find a simple topia binder of type : " + target.getClass()); + } + binder.load(source, target, tech); + }*/ + ); + + StringBuilder initCode = new StringBuilder(); + + for (ObjectModelClass clazz : classes) { + + String prefix = getConstantPrefix(clazz); + + if (StringUtils.isEmpty(prefix)) { + + // no specific prefix, so no prefix + if (log.isWarnEnabled()) { + log.warn("[" + clazz.getName() + "] Will generate constants with NO prefix, not a good idea..."); + } + } + + setConstantPrefix(prefix); + + generateBinder(modelName, clazz, resultClass, initCode); + + } + + op = addOperation(resultClass, "initBinders", "void", ObjectModelJavaModifier.PROTECTED, ObjectModelJavaModifier.STATIC); + setOperationBody(op, initCode.toString()); + + op = addOperation(resultClass, null, (String) null, ObjectModelJavaModifier.STATIC); + setOperationBody(op, "" + /*{ + initBinders(); +}*/ + ); + } + + protected void generateBinder(String modelName, + ObjectModelClass clazz, + ObjectModelClass resultClass, + StringBuilder initCode) { + + List<ObjectModelAttribute> list = new ArrayList<ObjectModelAttribute>(); + for (ObjectModelAttribute attr : clazz.getAttributes()) { + if (!attr.isNavigable()) { + continue; + } + + if (GeneratorUtil.isNMultiplicity(attr)) { + // not dealing with association + continue; + } + + list.add(attr); + } + + String clazzName = clazz.getName(); + + if (list.isEmpty()) { + // no attribute, do nothing + if (log.isDebugEnabled()) { + log.debug("no attribute to add in a binder for " + clazzName + + ", will not generate it."); + } + return; + } + + if (log.isDebugEnabled()) { + log.debug("generate simple binder for " + clazzName); + } + addImport(resultClass, clazz); + initCode.append("" +/*{ + BinderModelBuilder<<%=clazzName%>, <%=clazzName%>> builder<%=clazzName%> = + BinderModelBuilder.newEmptyBuilder(<%=clazzName%>.class); + builder<%=clazzName%>.addSimpleProperties( +}*/ + ); + Iterator<ObjectModelAttribute> itr = list.iterator(); + while (itr.hasNext()) { + ObjectModelAttribute attr = itr.next(); + String attrName = attr.getName(); + boolean hasNext = itr.hasNext(); + initCode.append("" +/*{ <%=clazzName%>.<%=getConstantName(attrName)%><%=(hasNext?",\n":"")%>}*/ + ); + } + initCode.append("" +/*{ + ); + registerTopiaBinder(builder<%=clazzName%>, "<%=modelName%>"); + }*/ + ); + } +} + diff --git a/topia-persistence/src/main/java/org/nuiton/topia/generator/DAOAbstractTransformer.java b/topia-persistence/src/main/java/org/nuiton/topia/generator/DAOAbstractTransformer.java new file mode 100644 index 0000000..f1f9d91 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/generator/DAOAbstractTransformer.java @@ -0,0 +1,1027 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.generator; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.eugene.GeneratorUtil; +import org.nuiton.eugene.java.ObjectModelTransformerToJava; +import org.nuiton.eugene.models.object.ObjectModel; +import org.nuiton.eugene.models.object.ObjectModelAssociationClass; +import org.nuiton.eugene.models.object.ObjectModelAttribute; +import org.nuiton.eugene.models.object.ObjectModelClass; +import org.nuiton.eugene.models.object.ObjectModelClassifier; +import org.nuiton.eugene.models.object.ObjectModelDependency; +import org.nuiton.eugene.models.object.ObjectModelInterface; +import org.nuiton.eugene.models.object.ObjectModelJavaModifier; +import org.nuiton.eugene.models.object.ObjectModelModifier; +import org.nuiton.eugene.models.object.ObjectModelOperation; +import org.nuiton.eugene.models.object.ObjectModelParameter; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaDAO; +import org.nuiton.topia.persistence.TopiaDAOLegacy; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.util.StringUtil; + +import java.security.Permission; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + + +/*{generator option: parentheses = false}*/ +/*{generator option: writeString = +}*/ + +/** + * Created: 13 déc. 2009 + * + * @author tchemit <chemit@codelutin.com> + * @version $Id: DAOAbstractTransformer.java 1960 2010-05-13 17:18:23Z tchemit$ + * @plexus.component role="org.nuiton.eugene.Template" role-hint="org.nuiton.topia.generator.DAOAbstractTransformer" + * @since 2.3.0 + * @deprecated 2.5.4, prefer use the transformer {@link EntityDAOTransformer} + */ +@Deprecated +public class DAOAbstractTransformer extends ObjectModelTransformerToJava { + + /** Logger. */ + private static final Log log = LogFactory.getLog( + DAOAbstractTransformer.class); + + /** TODO */ + protected Map<ObjectModelClass, Set<ObjectModelClass>> usages; + + /** + * All entities fqn of the model (used to detect if an attribute is not + * an entity). + */ + Set<String> allEntitiesFqn; + + /** + * The class of abstract dao to use. + * @since 2.5 + */ + protected Class<?> daoImplementation; + + /** + * Map of extra operations for DAO. The key of the map is the qualified + * name of the entity relative to the DAO. + */ + Map<String, Collection<ObjectModelOperation>> extraOperations = + new HashMap<String, Collection<ObjectModelOperation>>(); + + @Override + public void transformFromModel(ObjectModel model) { + + usages = TopiaGeneratorUtil.searchDirectUsages(model); + boolean extendLegacyDAO = Boolean.valueOf(model.getTagValue(TopiaTagValues.TAG_USE_LEGACY_DAO)); + if (extendLegacyDAO) { + log.warn("Using a deprecated tag value "+ + TopiaTagValues.TAG_USE_LEGACY_DAO+", prefer use the tag value "+TopiaTagValues.TAG_DAO_IMPLEMENTATION); + daoImplementation = TopiaDAOLegacy.class; + } else { + daoImplementation = TopiaGeneratorUtil.getDAOImplementation(model); + } + + List<ObjectModelClass> allEntities = TopiaGeneratorUtil.getEntityClasses(model, true); + allEntitiesFqn = new HashSet<String>(allEntities.size()); + for (ObjectModelClass entity : allEntities) { + allEntitiesFqn.add(entity.getQualifiedName()); + } + } + + @Override + public void transformFromInterface(ObjectModelInterface interfacez) { + if (!TopiaGeneratorUtil.hasDaoStereotype(interfacez)) { + return; + } + + // Extra operations from <<dao>> interfacez + collectExtraOperations(interfacez); + } + + /** + * EVO #636 : Manage extra operations for DAO from "dao" dependency + * between an interface with stereotype <<dao>> (dependency client) and + * a class with stereotype <<entity>> (dependency supplier). + * + * @param interfacez The interface with <<dao>> stereotype + */ + protected void collectExtraOperations(ObjectModelInterface interfacez) { + ObjectModelDependency dependency = + interfacez.getDependency(TopiaGeneratorUtil.DEPENDENCIES_DAO); + + if (dependency == null) { + if (log.isWarnEnabled()) { + log.warn("Could not find dependency " + + TopiaGeneratorUtil.DEPENDENCIES_DAO + + " but DAO stereotype was placed on the interface " + + interfacez.getName()); + + } + return; + } + ObjectModelClassifier classifier = dependency.getSupplier(); + + if (TopiaGeneratorUtil.isEntity(classifier)) { + + // Only direct operations will be used. No need to have more + // operations. + Collection<ObjectModelOperation> operations = + interfacez.getOperations(); + + if (log.isDebugEnabled()) { + log.debug("add extra operations for DAO"); + } + + extraOperations.put(classifier.getQualifiedName(), operations); + } + } + + @Override + public void transformFromClass(ObjectModelClass clazz) { + if (!TopiaGeneratorUtil.isEntity(clazz)) { + return; + } + + String clazzName = clazz.getName(); + + ObjectModelClass result = createAbstractClass( + clazzName + "DAOAbstract<E extends " + clazzName + '>', + clazz.getPackageName()); + + // super class + + String extendClass = ""; + for (ObjectModelClass parent : clazz.getSuperclasses()) { + extendClass = parent.getQualifiedName(); + if (TopiaGeneratorUtil.isEntity(parent)) { + extendClass += "DAOImpl<E>"; + // in java no multi-inheritance + break; + } + } + if (extendClass.length() == 0) { + extendClass = daoImplementation.getName() + "<E>"; + } + if (log.isDebugEnabled()) { + log.debug("super class = " + extendClass); + } + setSuperClass(result, extendClass); + + addInterface(result, TopiaDAO.class.getName() + "<E>"); + + String prefix = getConstantPrefix(clazz); + setConstantPrefix(prefix); + + // imports + + Collection<ObjectModelOperation> DAOoperations = getDAOOperations(clazz); + if (isCollectionNeeded(DAOoperations)) { + addImport(result, Collection.class); + } + if (isSetNeeded(DAOoperations)) { + addImport(result, Set.class); + } + addImport(result, List.class); + addImport(result, Arrays.class); + addImport(result, TopiaException.class); + addImport(result, TopiaContextImplementor.class); + + boolean enableSecurity = TopiaGeneratorUtil.isClassWithSecurity(clazz); + + if (enableSecurity) { + addImport(result, ArrayList.class); + addImport(result, Permission.class); + addImport(result, "org.nuiton.topia.taas.entities.TaasAuthorizationImpl"); + addImport(result, "org.nuiton.topia.taas.jaas.TaasPermission"); + addImport(result, "org.nuiton.topia.taas.TaasUtil"); + addImport(result, TopiaDAO.class); + + //FIXME : how to do static imports ? +//import static org.nuiton.topia.taas.TaasUtil.CREATE; +//import static org.nuiton.topia.taas.TaasUtil.DELETE; +//import static org.nuiton.topia.taas.TaasUtil.LOAD; +//import static org.nuiton.topia.taas.TaasUtil.UPDATE; + } + + ObjectModelOperation op; + + // getEntityClass + + op = addOperation(result, + "getEntityClass", + "Class<E>", + ObjectModelJavaModifier.PUBLIC); + setOperationBody(op, "" +/*{ + return (Class<E>)<%=clazzName%>.class; + }*/ + ); + + + generateDAOOperations(result, DAOoperations); + + generateDelete(clazz, result); + + generateNaturalId(result, clazz); + + for (ObjectModelAttribute attr : clazz.getAttributes()) { + if (!attr.isNavigable()) { + continue; + } + + if (!GeneratorUtil.isNMultiplicity(attr)) { + generateNoNMultiplicity(clazzName, result, attr, false); + } else { + generateNMultiplicity(clazzName, result, attr); + } + } + + if (clazz instanceof ObjectModelAssociationClass) { + ObjectModelAssociationClass assocClass = + (ObjectModelAssociationClass) clazz; + for (ObjectModelAttribute attr : assocClass.getParticipantsAttributes()) { + if (attr != null) { + if (!GeneratorUtil.isNMultiplicity(attr)) { + generateNoNMultiplicity(clazzName, result, attr, true); + } else { + generateNMultiplicity(clazzName, result, attr); + } + } + } + } + + if (enableSecurity) { + + // getRequestPermission + + op = addOperation(result, + "getRequestPermission", + "List<Permission>", + ObjectModelJavaModifier.PUBLIC); + setDocumentation(op, "Retourne les permissions a verifier pour " + + "l'acces a l'entite pour le service Taas"); + addException(op, TopiaException.class); + addParameter(op, String.class, "topiaId"); + addParameter(op, int.class, "actions"); + StringBuilder buffer = new StringBuilder(); + buffer.append("" +/*{ + List<Permission> resultPermissions = new ArrayList<Permission>(); + if ((actions & TaasUtil.CREATE) == TaasUtil.CREATE) { +}*/ + ); + buffer.append(generateSecurity(result, clazz, + TopiaGeneratorUtil.getSecurityCreateTagValue(clazz))); + buffer.append("" +/*{ + } + if ((actions & TaasUtil.LOAD) == TaasUtil.LOAD) { +}*/ + ); + buffer.append(generateSecurity(result, clazz, + TopiaGeneratorUtil.getSecurityLoadTagValue(clazz))); + buffer.append("" +/*{ + } + if ((actions & TaasUtil.UPDATE) == TaasUtil.UPDATE) { +}*/ + ); + buffer.append(generateSecurity(result, clazz, + TopiaGeneratorUtil.getSecurityUpdateTagValue(clazz))); + buffer.append("" +/*{ + } + if ((actions & TaasUtil.DELETE) == TaasUtil.DELETE) { +}*/ + ); + buffer.append(generateSecurity(result, clazz, + TopiaGeneratorUtil.getSecurityDeleteTagValue(clazz))); + buffer.append("" +/*{ + } + return resultPermissions; + }*/ + ); + + setOperationBody(op, buffer.toString()); + + // THIMEL : Le code suivant doit pouvoir être déplacé dans DAODelegator ? + + // getRequestPermission + + + op = addOperation(result, + "getRequestPermission", + "List<Permission>", + ObjectModelJavaModifier.PROTECTED); + addParameter(op, String.class, "topiaId"); + addParameter(op, int.class, "actions"); + addParameter(op, String.class, "query"); + addParameter(op, Class.class, "daoClass"); + addException(op, TopiaException.class); + setDocumentation(op, "Retourne les permissions a verifier pour " + + "l'acces a l'entite pour le service Taas"); + setOperationBody(op, "" +/*{ TopiaContextImplementor context = getContext(); + List<String> result = context.findAll(query, "id", topiaId); + + List<Permission> resultPermissions = new ArrayList<Permission>(); + for (String topiaIdPermission : result) { + TopiaDAO dao = context.getDAO(daoClass); + List<Permission> permissions = dao.getRequestPermission(topiaIdPermission, actions); + if(permissions != null) { + resultPermissions.addAll(permissions); + } else { + TaasPermission permission = new TaasPermission(topiaIdPermission, actions); + resultPermissions.add(permission); + } + } + return resultPermissions; + }*/ + ); + } + + Set<ObjectModelClass> usagesForclass = usages.get(clazz); + generateFindUsages(clazz, result, usagesForclass); + } + + protected void generateDelete(ObjectModelClass clazz, + ObjectModelClass result) { + ObjectModelOperation op; + op = addOperation(result, "delete", "void", ObjectModelJavaModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, "E", "entity"); + StringBuilder body = new StringBuilder(); + String modelName = StringUtils.capitalize(model.getName()); + String providerFQN = getOutputProperties().getProperty( + PROP_DEFAULT_PACKAGE) + '.' + modelName + + "DAOHelper.getImplementationClass"; + + for (ObjectModelAttribute attr : clazz.getAttributes()) { + + String attrType = GeneratorUtil.getSimpleName(attr.getType()); + + String reverseAttrName = attr.getReverseAttributeName(); + ObjectModelAttribute reverse = attr.getReverseAttribute(); + if (attr.hasAssociationClass() || + reverse == null || !reverse.isNavigable()) { + + // never treate a non reverse and navigable attribute + // never treate an association class attribute + continue; + } + + // at this point we are sure to have a attribute which is + // - reverse + // - navigable + // - not from an association class + if (!allEntitiesFqn.contains(attr.getType())) { + + // this attribute is not from an entity, don't treate it + if (log.isDebugEnabled()) { + log.debug("[" + result.getName() + "] Skip attribute [" + + attr.getName() + "] with type " + attr.getType()); + } + continue; + } + + // At this point, the attribute type is a entity + if (GeneratorUtil.isNMultiplicity(attr) && + GeneratorUtil.isNMultiplicity(reverse)) { + // On doit absolument supprimer pour les relations many-to-many + // le this de la collection de l'autre cote + + String attrDBName = TopiaGeneratorUtil.getDbName(attr); + String attrClassifierDBName = TopiaGeneratorUtil.getDbName(attr.getClassifier()); + String attrJoinTableName = TopiaGeneratorUtil.getManyToManyTableName(attr); + String attrReverseDBName = TopiaGeneratorUtil.getReverseDbName(attr); + + //FIXME_-FC-20100413 Use a TopiaQuery (use HQLin elements) +// // Add DAOHelper +// String daoHelper = modelName + "DAOHelper"; +// String daoHelperFQN = getOutputProperties(). +// getProperty(PROP_DEFAULT_PACKAGE) + '.' + daoHelper; +// addImport(result, daoHelperFQN); +// +// // Add import for TopiaQuery +// addImport(result, TopiaQuery.class); +// +// // Entity DAO and reversePropertyName +// String entityDAO = attrType + "DAO"; +// String reverseAttrNameProperty = +// attrType + "." + getConstantName(reverseAttrName); +// +// +// <%=entityDAO%> dao = <%=daoHelper%>.get<%=entityDAO%>(getContext()); +// TopiaQuery query = dao.createQuery("B"). +// addFrom(entity.getClass(), "A"). +// add("A", entity). +// addInElements("A", "B." + <%=reverseAttrNameProperty%>); +// +// System.out.println("Query : " + query); +// List<<%=attrType%>> list = dao.findAllByQuery(query); + + + body.append("" +/*{ + { + List<<%=attrType%>> list = getContext().getHibernate().createSQLQuery( + "SELECT main.topiaid " + + "from <%=attrClassifierDBName%> main, <%=attrJoinTableName%> secondary " + + "where main.topiaid=secondary.<%=attrDBName%>" + + " and secondary.<%=attrReverseDBName%>='" + entity.getTopiaId() + "'") + .addEntity("main", <%=providerFQN%>(<%=attrType%>.class)).list(); + + for (<%=attrType%> item : list) { + item.remove<%=StringUtils.capitalize(reverseAttrName)%>(entity); + } + } +}*/ + ); + } else if (!GeneratorUtil.isNMultiplicity(reverse)) { + // On doit mettre a null les attributs qui ont cet objet sur les + // autres entites en one-to-* + // TODO peut-etre qu'hibernate est capable de faire ca tout seul ? + // THIMEL: J'ai remplacé reverse.getName() par reverseAttrName sans certitude + builder.addImport(result, attrType); + String attrSimpleType = TopiaGeneratorUtil.getClassNameFromQualifiedName(attrType); + + body.append("" + /*{ + { + List<<%=attrSimpleType%>> list = getContext() + .getDAO(<%=attrSimpleType%>.class) + .findAllByProperties(<%=attrSimpleType%>.<%=getConstantName(reverseAttrName)%>, entity); + for (<%=attrSimpleType%> item : list) { + item.set<%=StringUtils.capitalize(reverseAttrName)%>(null); + }*/ + ); + if (attr.isAggregate()) { + body.append("" +/*{ + getContext().getDAO(<%=attrSimpleType%>.class).delete(item); + //item.delete(); +}*/ + ); + } + body.append("" +/*{ + } + } +}*/ + ); + + } + } + body.append("" +/*{ + super.delete(entity); + }*/ + ); + + setOperationBody(op, body.toString()); + } + + private void generateFindUsages(ObjectModelClass clazz, + ObjectModelClass result, + Set<ObjectModelClass> usagesForclass) { + + builder.addImport(result, ArrayList.class.getName()); + builder.addImport(result, Map.class.getName()); + builder.addImport(result, HashMap.class.getName()); + builder.addImport(result, TopiaEntity.class.getName()); + + if (clazz instanceof ObjectModelAssociationClass || usagesForclass.isEmpty()) { + // not for an association class + // just let a null method + ObjectModelOperation operation; + operation = addOperation(result, + "findUsages", + "<U extends TopiaEntity> List<U>", + ObjectModelJavaModifier.PUBLIC); + + addParameter(operation, "Class<U>", "type"); + addParameter(operation, "E", "entity"); + addException(operation, TopiaException.class); + addAnnotation(result, operation, "Override"); + setOperationBody(operation, "" +/*{ + return new ArrayList<U>(); + }*/ + ); + + operation = addOperation(result, + "findAllUsages", + "Map<Class<? extends TopiaEntity>, List<? extends TopiaEntity>>", + ObjectModelJavaModifier.PUBLIC); + + addParameter(operation, "E", "entity"); + addException(operation, TopiaException.class); + addAnnotation(result, operation, "Override"); + setOperationBody(operation, "" +/*{ + return new HashMap<Class<? extends TopiaEntity>, List<? extends TopiaEntity>>(); + }*/ + ); + + return; + } + List<ObjectModelClass> allEntities; + Map<String, ObjectModelClass> allEntitiesByFQN; + + allEntities = TopiaGeneratorUtil.getEntityClasses(model, true); + allEntitiesByFQN = new TreeMap<String, ObjectModelClass>(); + + // prepare usages map and fill allEntitiesByFQN map + for (ObjectModelClass klass : allEntities) { + allEntitiesByFQN.put(klass.getQualifiedName(), klass); + } + + ObjectModelOperation operation; + operation = addOperation(result, + "findUsages", + "<U extends TopiaEntity> List<U>", + ObjectModelJavaModifier.PUBLIC); + + addParameter(operation, "Class<U>", "type"); + addParameter(operation, "E", "entity"); + addException(operation, TopiaException.class); + addAnnotation(result, operation, "Override"); + StringBuilder buffer = new StringBuilder(300); + buffer.append("" +/*{ + List<?> result = new ArrayList(); + List tmp; +}*/ + ); + + for (ObjectModelClass usageClass : usagesForclass) { + String usageType = usageClass.getQualifiedName(); + builder.addImport(result, usageType); + String usageSimpleType = + TopiaGeneratorUtil.getClassNameFromQualifiedName(usageType); + String usageSimplePropertyMethod = "findAllBy" + usageSimpleType; + String usageCollectionPropertyMethod = "findAllContaining" + usageSimpleType; + for (ObjectModelAttribute attr : usageClass.getAttributes()) { + if (!attr.isNavigable()) { + // skip this case + continue; + } + String type; + String attrName = attr.getName(); + if (attr.hasAssociationClass()) { + //FIXME-TC20100224 dont known how to do this ? + continue; +// type = attr.getAssociationClass().getQualifiedName(); +// //FIXME-TC20100224 : this is crazy ??? must find the good name +// // Perhaps need to make different cases? +// attrName = attrName + "_" + TopiaGeneratorUtil.toLowerCaseFirstLetter(attr.getAssociationClass().getName()); + } else { + type = attr.getType(); + } + if (!allEntitiesByFQN.containsKey(type)) { + // not a entity, can skip for this attribute + continue; + } + ObjectModelClass targetEntity = allEntitiesByFQN.get(type); +// if (!type.equals(clazz.getQualifiedName())) { + if (!targetEntity.equals(clazz)) { + // not a good attribute reference + continue; + } + // found something to seek + + String methodNameSuffix = StringUtils.capitalize(attrName); + String methodName; + if (TopiaGeneratorUtil.isNMultiplicity(attr)) { + methodName = "findAllContains" + methodNameSuffix; + } else { + methodName = "findAllBy" + methodNameSuffix; + } + String daoName = StringUtils.capitalize(usageSimpleType) + "DAO"; + + builder.addImport(result, usageClass.getPackageName() + '.' + daoName); + + buffer.append("" +/*{ + if (type == <%=usageSimpleType%>.class) { + <%=daoName%> dao = (<%=daoName%>) + getContext().getDAO(<%=usageSimpleType%>.class); + tmp = dao.<%=methodName%>(entity); + result.addAll(tmp); + } +}*/ + ); + } + } + + buffer.append("" +/*{ + return (List<U>) result; + }*/ + ); + setOperationBody(operation, buffer.toString()); + + operation = addOperation(result, + "findAllUsages", + "Map<Class<? extends TopiaEntity>, List<? extends TopiaEntity>>", + ObjectModelJavaModifier.PUBLIC); + + addParameter(operation, "E", "entity"); + addException(operation, TopiaException.class); + addAnnotation(result, operation, "Override"); + + buffer = new StringBuilder(300); + buffer.append("" +/*{ + Map<Class<? extends TopiaEntity>,List<? extends TopiaEntity>> result; + result = new HashMap<Class<? extends TopiaEntity>, List<? extends TopiaEntity>>(<%=usagesForclass.size()%>); + + List<? extends TopiaEntity> list; +}*/ + ); + for (ObjectModelClass usageClass : usagesForclass) { + + String fqn = usageClass.getName(); + buffer.append("" +/*{ + list = findUsages(<%=fqn%>.class, entity); + if (!list.isEmpty()) { + result.put(<%=fqn%>.class, list); + } +}*/ + ); + + } + buffer.append("" +/*{ + return result; + }*/ + ); + + setOperationBody(operation, buffer.toString()); + } + + /** + * Generation of DAO operations signatures from class. These operations are + * abstract and identified by <<dao>> stereotype in the model. The + * developper must defined these methods in the DAOImpl associated to this + * DAOAbstract. + * + * @param result clazz where to add operations + * @param operations operations to generate + */ + private void generateDAOOperations(ObjectModelClass result, + Collection<ObjectModelOperation> + operations) { + for (ObjectModelOperation op : operations) { + + //TODO: add to transformer cloneOperation + + ObjectModelOperation op2; + op2 = addOperation(result, + op.getName(), + op.getReturnType(), + ObjectModelJavaModifier.ABSTRACT, + ObjectModelJavaModifier.fromVisibility(op.getVisibility())); + setDocumentation(op2, op.getDocumentation()); + + // parameters + + for (ObjectModelParameter param : op.getParameters()) { + ObjectModelParameter param2 = addParameter(op2, + param.getType(), param.getName()); + setDocumentation(param2, param.getDocumentation()); + } + + // exceptions + Set<String> exceptions = op.getExceptions(); + exceptions.add(TopiaException.class.getName()); + for (String exception : exceptions) { + addException(op2, exception); + } + } + } + + + private String generateSecurity(ObjectModelClass result, + ObjectModelClass clazz, + String tagValue) { + StringBuilder buffer = new StringBuilder(); + + if (StringUtils.isNotEmpty(tagValue)) { + String security = tagValue; + Pattern propertiesPattern = Pattern + .compile("((?:[_a-zA-Z0-9]+\\.)+(?:_?[A-Z][_a-zA-Z0-9]*\\.)+)attribute\\.(?:([_a-z0-9][_a-zA-Z0-9]*))#(?:(create|load|update|delete))"); + String[] valuesSecurity = security.split(":"); + + for (String valueSecurity : valuesSecurity) { + Matcher matcher = propertiesPattern.matcher(valueSecurity); + matcher.find(); + // className is fully qualified name of class + String className = matcher.group(1); + className = StringUtil.substring(className, 0, -1); // remove ended + // . + // target is class, attribute or operation + String attributeName = matcher.group(2); + String actions = matcher.group(3).toUpperCase(); + + String query = ""; + String daoClass = ""; + if (className.equals(clazz.getQualifiedName())) { + query = "select " + attributeName + ".topiaId from " + clazz.getQualifiedName() + " where topiaId = :id"; + daoClass = clazz.getAttribute(attributeName).getClassifier().getQualifiedName(); + } else { + query = "select at.topiaId from " + className + " at inner join at." + attributeName + " cl where cl.topiaId = :id"; + daoClass = className; + } + buffer.append("" +/*{ + resultPermissions.addAll(getRequestPermission(topiaId, + <%=actions%>, + "<%=query%>", + <%=daoClass%>.class)); +}*/ + ); + } + } else { + buffer.append("" +/*{ return null; + }*/ + ); + } + return buffer.toString(); + } + + protected void generateNoNMultiplicity(String clazzName, + ObjectModelClass result, + ObjectModelAttribute attr, + boolean isAssoc) { + String attrName = attr.getName(); + String attrType = attr.getType(); + String propertyName = attrName; + if (!isAssoc && attr.hasAssociationClass()) { + propertyName = TopiaGeneratorUtil.toLowerCaseFirstLetter( + attr.getAssociationClass().getName()) + '.' + propertyName; + } + ObjectModelOperation op; + op = addOperation(result, + "findBy" + StringUtils.capitalize(attrName), + "E", + ObjectModelJavaModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, attrType, "v"); + setDocumentation(op, "Retourne le premier élément trouvé ayant comme valeur pour l'attribut " + attrName + " le paramètre."); + setOperationBody(op, "" +/*{ + E result = findByProperty(<%=clazzName + "." + getConstantName(propertyName)%>, v); + return result; + }*/ + ); + + op = addOperation(result, + "findAllBy" + StringUtils.capitalize(attrName), + "List<E>", + ObjectModelJavaModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, attrType, "v"); + setDocumentation(op, "Retourne les éléments ayant comme valeur pour " + + "l'attribut " + attrName + " le paramètre."); + setOperationBody(op, "" +/*{ + List<E> result = findAllByProperty(<%=clazzName + "." + getConstantName(propertyName)%>, v); + return result; + }*/ + ); + + if (attr.hasAssociationClass()) { + String assocClassName = attr.getAssociationClass().getName(); + String assocClassFQN = attr.getAssociationClass().getQualifiedName(); + op = addOperation(result, + "findBy" + StringUtils.capitalize(assocClassName), + "E", + ObjectModelJavaModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, assocClassFQN, "value"); + setDocumentation(op, "Retourne le premier élément trouvé ayant " + + "comme valeur pour l'attribut " + + TopiaGeneratorUtil.toLowerCaseFirstLetter(assocClassName) + + " le paramètre."); + setOperationBody(op, "" +/*{ + E result = findByProperty(<%=clazzName + "." + getConstantName(TopiaGeneratorUtil.toLowerCaseFirstLetter(assocClassName))%>, value); + return result; + }*/ + ); + + op = addOperation(result, + "findAllBy" + StringUtils.capitalize(assocClassName), + "List<E>", + ObjectModelJavaModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, assocClassFQN, "value"); + setDocumentation(op, "Retourne les éléments ayant comme valeur pour" + + " l'attribut " + + TopiaGeneratorUtil.toLowerCaseFirstLetter(assocClassName) + + " le paramètre."); + setOperationBody(op, "" +/*{ + List<E> result = findAllByProperty(<%=clazzName + "." + getConstantName(TopiaGeneratorUtil.toLowerCaseFirstLetter(assocClassName))%>, value); + return result; + }*/ + ); + } + } + + protected void generateNMultiplicity(String clazzName, ObjectModelClass result, ObjectModelAttribute attr) { + String attrName = attr.getName(); + String attrType = attr.getType(); + if (attr.hasAssociationClass()) { + // do nothing for association class, too complex... + return; + } + ObjectModelOperation op; + // Since 2.4 do nothing, findContains and findAllContains are not generated anymore + op = addOperation(result, + "findContains" + StringUtils.capitalize(attrName), + "E", + ObjectModelJavaModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, attrType, "v"); + setDocumentation(op, "Retourne le premier élément ayant comme valeur pour" + + " l'attribut " + + TopiaGeneratorUtil.toLowerCaseFirstLetter(attrName) + + " le paramètre."); + setOperationBody(op, "" +/*{ + E result = findContains(<%=clazzName + "." + getConstantName(attrName)%>, v); + return result; + }*/ + ); + + op = addOperation(result, + "findAllContains" + StringUtils.capitalize(attrName), + "List<E>", + ObjectModelJavaModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, attrType, "v"); + setDocumentation(op, "Retourne les éléments ayant comme valeur pour" + + " l'attribut " + + TopiaGeneratorUtil.toLowerCaseFirstLetter(attrName) + + " le paramètre."); + setOperationBody(op, "" +/*{ + List<E> result = findAllContains(<%=clazzName + "." + getConstantName(attrName)%>, v); + return result; + }*/ + ); + } + + private boolean isCollectionNeeded( + Collection<ObjectModelOperation> operations) { + return isImportNeeded(operations, "Collection"); + } + + private boolean isSetNeeded(Collection<ObjectModelOperation> operations) { + return isImportNeeded(operations, "Set"); + } + + private boolean isImportNeeded(Collection<ObjectModelOperation> operations, + String importName) { + for (ObjectModelOperation op : operations) { + if (op.getReturnType().contains(importName)) { + return true; + } + for (ObjectModelParameter param : op.getParameters()) { + if (param.getType().contains(importName)) { + return true; + } + } + } + return false; + } + + public Collection<ObjectModelOperation> getDAOOperations( + ObjectModelClass clazz) { + // Note : this collection will contains extra operations for DAO. + // Overriding existing generated methods is not managed yet + Collection<ObjectModelOperation> results = + new ArrayList<ObjectModelOperation>(); + + // This code will be deprecated + for (ObjectModelOperation op : clazz.getOperations()) { + if (TopiaGeneratorUtil.hasDaoStereotype(op)) { + results.add(op); + } + } + // New method : interface dependency + Collection<ObjectModelOperation> extra = + extraOperations.get(clazz.getQualifiedName()); + + if (extra != null) { + for (ObjectModelOperation op : extra) { + results.add(op); + } + } + + return results; + } + + private void generateNaturalId(ObjectModelClass result, + ObjectModelClass clazz) { + Set<ObjectModelAttribute> props = + TopiaGeneratorUtil.getNaturalIdAttributes(clazz); + + if (!props.isEmpty()) { + + if (log.isDebugEnabled()) { + log.debug("generateNaturalId for " + props); + } + ObjectModelOperation findByNaturalId = addOperation(result, + "findByNaturalId", "E", ObjectModelJavaModifier.PUBLIC); + addException(findByNaturalId, TopiaException.class); + + ObjectModelOperation existByNaturalId = addOperation(result, + "existByNaturalId", "boolean", ObjectModelJavaModifier.PUBLIC); + addException(existByNaturalId, TopiaException.class); + + ObjectModelOperation create = addOperation(result, + "create", "E", ObjectModelJavaModifier.PUBLIC); + addException(create, TopiaException.class); + + // used for calling findByProperties in findByNaturalId + String searchProperties = ""; + // used for calling findByNaturalId in existByNaturalId + String params = ""; + String clazzName = clazz.getName(); + for (ObjectModelAttribute attr : props) { + String propName = attr.getName(); + // add property as param in both methods + addParameter(findByNaturalId, attr.getType(), propName); + addParameter(existByNaturalId, attr.getType(), propName); + addParameter(create, attr.getType(), propName); + + searchProperties += + ", " + clazzName + '.' + getConstantName(propName) + + ", " + propName; + //params += ", " + propName; + } + searchProperties = searchProperties.substring(2); + //params = params.substring(2); + + setOperationBody(findByNaturalId, "" +/*{ + return findByProperties(<%=searchProperties%>); + }*/ + ); + + setOperationBody(existByNaturalId, "" +/*{ + return existByProperties(<%=searchProperties%>); + }*/ + ); + + setOperationBody(create, "" +/*{ + return create(<%=searchProperties%>); + }*/ + ); + } + + + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/generator/DAOHelperTransformer.java b/topia-persistence/src/main/java/org/nuiton/topia/generator/DAOHelperTransformer.java new file mode 100644 index 0000000..e3d41f8 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/generator/DAOHelperTransformer.java @@ -0,0 +1,560 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.generator; + +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Set; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterables; +import com.google.common.collect.Sets; +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.eugene.models.object.ObjectModelType; +import org.nuiton.eugene.java.ObjectModelTransformerToJava; +import org.nuiton.eugene.models.object.*; +import org.nuiton.eugene.models.object.xml.ObjectModelAttributeImpl; +import org.nuiton.eugene.models.object.xml.ObjectModelEnumerationImpl; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaRuntimeException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaDAO; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.util.EntityOperator; +import org.nuiton.topia.persistence.util.EntityOperatorStore; +import org.nuiton.topia.persistence.util.TopiaEntityHelper; + +import java.lang.reflect.Array; +import java.util.Arrays; +import java.util.List; + + +/*{generator option: parentheses = false}*/ + +/*{generator option: writeString = +}*/ + +/** + * Created: 13 nov. 2009 09:05:17 + * + * @author tchemit <chemit@codelutin.com> + * @version $Id$ + * @since 2.3.0 + * @plexus.component role="org.nuiton.eugene.Template" role-hint="org.nuiton.topia.generator.DAOHelperTransformer" + */ +public class DAOHelperTransformer extends ObjectModelTransformerToJava { + + private static final Log log = + LogFactory.getLog(DAOHelperTransformer.class); + + @Override + public void transformFromModel(ObjectModel model) { + String packageName = getDefaultPackageName(); + String modelName = model.getName(); + String daoHelperClazzName = modelName + "DAOHelper"; + String entityEnumName = modelName + "EntityEnum"; + + List<ObjectModelClass> classes = + TopiaGeneratorUtil.getEntityClasses(model, true); + + boolean generateOperator = + TopiaGeneratorUtil.shouldGenerateOperatorForDAOHelper(model); + + boolean generateStandaloneEnum = + TopiaGeneratorUtil.shouldGenerateStandaloneEnumForDAOHelper(model); + + ObjectModelClass daoHelper = createClass(daoHelperClazzName, + packageName); + + ObjectModelEnumeration entityEnum; + + if (generateStandaloneEnum) { + if (log.isDebugEnabled()) { + log.debug("Will generate standalone " + entityEnumName + + " in package " + packageName); + } + entityEnum = createEnumeration(entityEnumName, packageName); + addImport(entityEnum, TopiaEntity.class); + addImport(entityEnum, EntityOperatorStore.class); + addImport(entityEnum, Arrays.class); + addImport(entityEnum, TopiaRuntimeException.class); + addImport(entityEnum, ArrayUtils.class); + addImport(entityEnum, TopiaEntityHelper.class); + + } else { + entityEnum = (ObjectModelEnumerationImpl) + addInnerClassifier(daoHelper, + ObjectModelType.OBJECT_MODEL_ENUMERATION, + entityEnumName + ); + addImport(daoHelper, TopiaRuntimeException.class); + addImport(daoHelper, TopiaEntityEnum.class); + addImport(daoHelper, EntityOperatorStore.class); + addImport(daoHelper, Arrays.class); + addImport(daoHelper, ArrayUtils.class); + addImport(daoHelper, TopiaEntityHelper.class); + } + + // generate DAOHelper + createDAOHelper(model, + daoHelper, + daoHelperClazzName, + entityEnumName, + generateOperator, + classes + ); + + // generate TopiaEntityEnum + createEntityEnum(entityEnum, + daoHelperClazzName, + entityEnumName, + generateOperator, + generateStandaloneEnum, + classes + ); + } + + protected void createDAOHelper(ObjectModel model, + ObjectModelClass daoHelper, + String daoHelperClazzName, + String entityEnumName, + boolean generateOperator, + List<ObjectModelClass> classes) { + + String modelName = model.getName(); + String modelVersion = model.getVersion(); + + addImport(daoHelper, TopiaContextImplementor.class); + addImport(daoHelper, TopiaDAO.class); + addImport(daoHelper, TopiaEntity.class); + addImport(daoHelper, TopiaContext.class); + addImport(daoHelper, Array.class); + + if (generateOperator) { + addImport(daoHelper,EntityOperator.class); + addImport(daoHelper, EntityOperatorStore.class); + } + + // add non public constructor + ObjectModelOperation constructor = + addConstructor(daoHelper, ObjectModelJavaModifier.PROTECTED); + setOperationBody(constructor," "); + + ObjectModelOperation op; + + // getModelVersion method + op = addOperation(daoHelper, "getModelVersion", "String", ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC); + setOperationBody(op, "" +/*{ + return "<%=modelVersion%>"; + }*/ + ); + + // getModelName method + op = addOperation(daoHelper, "getModelName", "String", ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC); + setOperationBody(op, "" +/*{ + return "<%=modelName%>"; + }*/ + ); + + + for (ObjectModelClass clazz : classes) { + String clazzName = clazz.getName(); + String daoClazzName = clazzName + "DAO"; + + // specialized getXXXDao method + op = addOperation(daoHelper, "get" + daoClazzName, clazz.getPackageName() + '.' + daoClazzName, ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC); + addParameter(op, TopiaContext.class, "context"); + addImport(daoHelper, clazz); + addException(op, TopiaException.class); + setOperationBody(op, "" +/*{ + TopiaContextImplementor ci = (TopiaContextImplementor) context; + <%=daoClazzName%> result = ci.getDAO(<%=clazzName%>.class, <%=daoClazzName%>.class); + return result; + }*/ + ); + + } + + // generic getDao method + op = addOperation(daoHelper, "getDAO", "<T extends TopiaEntity, D extends TopiaDAO<? super T>> D", ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC); + addParameter(op, TopiaContext.class, "context"); + addParameter(op, "Class<T>", "klass"); + addException(op, TopiaException.class); + setOperationBody(op, "" +/*{ + TopiaContextImplementor ci = (TopiaContextImplementor) context; + <%=entityEnumName%> constant = <%=entityEnumName%>.valueOf(klass); + D dao = (D) ci.getDAO(constant.getContract()); + return dao; + }*/ + ); + + op = addOperation(daoHelper, "getDAO", "<T extends TopiaEntity, D extends TopiaDAO<? super T>> D", ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC); + addParameter(op, TopiaContext.class, "context"); + addParameter(op, "T", "entity"); + addException(op, TopiaException.class); + setOperationBody(op, "" +/*{ + TopiaContextImplementor ci = (TopiaContextImplementor) context; + <%=entityEnumName%> constant = <%=entityEnumName%>.valueOf(entity); + D dao = (D) ci.getDAO(constant.getContract()); + return dao; + }*/ + ); + + // getContractClass method + op = addOperation(daoHelper, "getContractClass", "<T extends TopiaEntity> Class<T>", ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC); + addParameter(op, "Class<T>", "klass"); + setOperationBody(op, "" +/*{ + <%=entityEnumName%> constant = <%=entityEnumName%>.valueOf(klass); + return (Class<T>) constant.getContract(); + }*/ + ); + + // getImplementationClass method + op = addOperation(daoHelper, "getImplementationClass", "<T extends TopiaEntity> Class<T>", ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC); + addParameter(op, "Class<T>", "klass"); + setOperationBody(op, "" +/*{ + <%=entityEnumName%> constant = <%=entityEnumName%>.valueOf(klass); + return (Class<T>) constant.getImplementation(); + }*/ + ); + + // getContractClasses method + op = addOperation(daoHelper, "getContractClasses", "Class<? extends TopiaEntity>[]", ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC); + setOperationBody(op, "" +/*{ + <%=entityEnumName%>[] values = <%=entityEnumName%>.values(); + Class<? extends TopiaEntity>[] result = (Class<? extends TopiaEntity>[]) Array.newInstance(Class.class, values.length); + for (int i = 0; i < values.length; i++) { + result[i] = values[i].getContract(); + } + return result; + }*/ + ); + + // getImplementationClasses method + op = addOperation(daoHelper, "getImplementationClasses", "Class<? extends TopiaEntity>[]", ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC); + setOperationBody(op, "" +/*{ + <%=entityEnumName%>[] values = <%=entityEnumName%>.values(); + Class<? extends TopiaEntity>[] result = (Class<? extends TopiaEntity>[]) Array.newInstance(Class.class, values.length); + for (int i = 0; i < values.length; i++) { + result[i] = values[i].getImplementation(); + } + return result; + }*/ + ); + + // getImplementationClassesAsString method + op = addOperation(daoHelper, "getImplementationClassesAsString", "String", ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC); + setOperationBody(op, "" +/*{ + StringBuilder buffer = new StringBuilder(); + for (Class<? extends TopiaEntity> aClass : getImplementationClasses()) { + buffer.append(',').append(aClass.getName()); + } + return buffer.substring(1); + }*/ + ); + + // getContracts method + op = addOperation(daoHelper, "getContracts", entityEnumName+"[]", ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC); + setOperationBody(op, "" +/*{ + return <%=entityEnumName%>.values(); + }*/ + ); + + if (generateOperator) { + // getOperator method + op = addOperation(daoHelper, "getOperator", "<T extends TopiaEntity> EntityOperator<T>", ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC); + addParameter(op,"Class<T>","klass"); + setOperationBody(op, "" +/*{ + <%=entityEnumName%> constant = <%=entityEnumName%>.valueOf(klass); + return EntityOperatorStore.getOperator(constant); + }*/ + ); + } + } + + protected void createEntityEnum(ObjectModelEnumeration entityEnum, + String daoHelperClazzName, + String entityEnumName, + boolean generateOperator, + boolean generateStandaloneEnum, + List<ObjectModelClass> classes) { + + ObjectModelAttributeImpl attr; + ObjectModelOperation op; + + addInterface(entityEnum, TopiaEntityEnum.class); + + for (ObjectModelClass clazz : classes) { + String clazzName = clazz.getName(); + + boolean withNatural = false; + boolean withNotNull = false; + StringBuilder naturalIdsParams = new StringBuilder(); + StringBuilder notNullParams = new StringBuilder(); + + Set<ObjectModelAttribute> naturalIdsAttributes = TopiaGeneratorUtil.getNaturalIdAttributes(clazz); + for (ObjectModelAttribute attribute: naturalIdsAttributes) { + withNatural = true; + // attribut metier + naturalIdsParams.append(", \"").append(attribute.getName()).append("\""); + } + Set<ObjectModelAttribute> notNullIdsAttributes = TopiaGeneratorUtil.getNotNullAttributes(clazz); + for (ObjectModelAttribute attribute : notNullIdsAttributes) { + withNotNull = true; + // attribut not-null + notNullParams.append(", \"").append(attribute.getName()).append("\""); + } + + StringBuilder params = new StringBuilder(clazzName + ".class"); + + String dbSchema = TopiaGeneratorUtil.getDbSchemaNameTagValue(clazz, model); + if (dbSchema == null) { + params.append(", null"); + } else { + params.append(", \"").append(dbSchema.toLowerCase()).append("\""); + } + + String dbTable = TopiaGeneratorUtil.getDbName(clazz); + params.append(", \"").append(dbTable.toLowerCase()).append("\""); + + if (withNotNull) { + params.append(", new String[]{ ").append(notNullParams.substring(2)).append(" }"); + } else { + params.append(", ArrayUtils.EMPTY_STRING_ARRAY"); + } + if (withNatural) { + params.append(", ").append(naturalIdsParams.substring(2)); + } + addLiteral(entityEnum, clazzName + '(' + params.toString() + ')'); + + if (generateStandaloneEnum) { + addImport(entityEnum, clazz); + } + } + + attr = (ObjectModelAttributeImpl) addAttribute(entityEnum, "contract", "Class<? extends TopiaEntity>"); + attr.setDocumentation("The contract of the entity."); + + attr = (ObjectModelAttributeImpl) addAttribute(entityEnum, "dbSchemaName", "String"); + attr.setDocumentation("The optional name of database schema of the entity (if none was filled, will be {@code null})."); + + attr = (ObjectModelAttributeImpl) addAttribute(entityEnum, "dbTableName", "String"); + attr.setDocumentation("The name of the database table for the entity."); + + attr = (ObjectModelAttributeImpl) addAttribute(entityEnum, "implementationFQN", "String"); + attr.setDocumentation("The fully qualified name of the implementation of the entity."); + + attr = (ObjectModelAttributeImpl) addAttribute(entityEnum, "implementation", "Class<? extends TopiaEntity>"); + attr.setDocumentation("The implementation class of the entity (will be lazy computed at runtime)."); + + attr = (ObjectModelAttributeImpl) addAttribute(entityEnum, "naturalIds", "String[]"); + attr.setDocumentation("The array of property involved in the natural key of the entity."); + + attr = (ObjectModelAttributeImpl) addAttribute(entityEnum, "notNulls", "String[]"); + attr.setDocumentation("The array of not null properties of the entity."); + + // constructor + op = addConstructor(entityEnum, ObjectModelJavaModifier.PACKAGE); + addParameter(op,"Class<? extends TopiaEntity >","contract"); + addParameter(op,"String","dbSchemaName"); + addParameter(op,"String","dbTableName"); + addParameter(op,"String[]","notNulls"); + addParameter(op,"String...","naturalIds"); + setOperationBody(op, "" +/*{ + this.contract = contract; + this.dbSchemaName = dbSchemaName; + this.dbTableName = dbTableName; + this.notNulls = notNulls; + this.naturalIds = naturalIds; + implementationFQN = contract.getName() + "Impl"; + }*/ + ); + + // getContract method + op = addOperation(entityEnum, "getContract", "Class<? extends TopiaEntity>", ObjectModelJavaModifier.PUBLIC); + addAnnotation(entityEnum,op,Override.class.getSimpleName()); + setOperationBody(op, "" +/*{ + return contract; + }*/ + ); + + // dbSchemaName method + op = addOperation(entityEnum, "dbSchemaName", String.class, ObjectModelJavaModifier.PUBLIC); + addAnnotation(entityEnum,op,Override.class.getSimpleName()); + setOperationBody(op, "" +/*{ + return dbSchemaName; + }*/ + ); + + // dbTableName method + op = addOperation(entityEnum, "dbTableName", String.class, ObjectModelJavaModifier.PUBLIC); + addAnnotation(entityEnum,op,Override.class.getSimpleName()); + setOperationBody(op, "" +/*{ + return dbTableName; + }*/ + ); + + // getNaturalIds method + op = addOperation(entityEnum, "getNaturalIds", "String[]", ObjectModelJavaModifier.PUBLIC); + addAnnotation(entityEnum,op,Override.class.getSimpleName()); + setOperationBody(op, "" +/*{ + return naturalIds; + }*/ + ); + + // isUseNaturalIds method + op = addOperation(entityEnum, "isUseNaturalIds", "boolean", ObjectModelJavaModifier.PUBLIC); + addAnnotation(entityEnum,op,Override.class.getSimpleName()); + setOperationBody(op, "" +/*{ + return naturalIds.length > 0; + }*/ + ); + + // getNotNulls method + op = addOperation(entityEnum, "getNotNulls", "String[]", ObjectModelJavaModifier.PUBLIC); + addAnnotation(entityEnum,op,Override.class.getSimpleName()); + setOperationBody(op, "" +/*{ + return notNulls; + }*/ + ); + + // isUseNotNulls method + op = addOperation(entityEnum, "isUseNotNulls", "boolean", ObjectModelJavaModifier.PUBLIC); + addAnnotation(entityEnum,op,Override.class.getSimpleName()); + setOperationBody(op, "" +/*{ + return notNulls.length > 0; + }*/ + ); + + // getImplementationFQN method + op = addOperation(entityEnum, "getImplementationFQN","String",ObjectModelJavaModifier.PUBLIC); + addAnnotation(entityEnum,op,Override.class.getSimpleName()); + setOperationBody(op, "" +/*{ + return implementationFQN; + }*/ + ); + + // setImplementationFQN method + op = addOperation(entityEnum, "setImplementationFQN","void",ObjectModelJavaModifier.PUBLIC); + addAnnotation(entityEnum,op,Override.class.getSimpleName()); + addParameter(op,"String","implementationFQN"); + if (generateOperator) { + setOperationBody(op, "" +/*{ + this.implementationFQN = implementationFQN; + implementation = null; + // reinit the operators store + EntityOperatorStore.clear(); + }*/ + ); + } else { + setOperationBody(op, "" +/*{ + this.implementationFQN = implementationFQN; + this.implementation = null; + }*/ + ); + } + + // accept method + op = addOperation(entityEnum, "accept","boolean",ObjectModelJavaModifier.PUBLIC); + addAnnotation(entityEnum,op,Override.class.getSimpleName()); + addParameter(op,"Class<? extends TopiaEntity>","klass"); + setOperationBody(op, "" +/*{ + return <%=daoHelperClazzName%>.getContractClass(klass) == contract; + }*/ + ); + + // getImplementation method + op = addOperation(entityEnum, "getImplementation","Class<? extends TopiaEntity>",ObjectModelJavaModifier.PUBLIC); + addAnnotation(entityEnum,op,Override.class.getSimpleName()); + setOperationBody(op, "" +/*{ + if (implementation == null) { + try { + implementation = (Class<? extends TopiaEntity>) Class.forName(implementationFQN); + } catch (ClassNotFoundException e) { + throw new TopiaRuntimeException("could not find class " + implementationFQN, e); + } + } + return implementation; + }*/ + ); + + // valueOf method + op = addOperation(entityEnum, "valueOf", entityEnumName, ObjectModelJavaModifier.PUBLIC,ObjectModelJavaModifier.STATIC); + addParameter(op,"TopiaEntity","entity"); + setOperationBody(op, "" +/*{ + return valueOf(entity.getClass()); + }*/ + ); + + // valueOf method + op = addOperation(entityEnum, "valueOf", entityEnumName, ObjectModelJavaModifier.PUBLIC,ObjectModelJavaModifier.STATIC); + addParameter(op,"Class<?>","klass"); + setOperationBody(op, "" +/*{ + if (klass.isInterface()) { + return valueOf(klass.getSimpleName()); + } + + Class<?> contractClass = TopiaEntityHelper.getContractClass(<%=entityEnumName%>.values(), (Class) klass); + + if (contractClass != null) { + + return valueOf(contractClass.getSimpleName()); + } + + throw new IllegalArgumentException("no entity defined for the class " + klass + " in : " + Arrays.toString(<%=entityEnumName%>.values())); + }*/ + ); + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/generator/DAOImplTransformer.java b/topia-persistence/src/main/java/org/nuiton/topia/generator/DAOImplTransformer.java new file mode 100644 index 0000000..06265dd --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/generator/DAOImplTransformer.java @@ -0,0 +1,126 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.generator; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.eugene.java.ObjectModelTransformerToJava; +import org.nuiton.eugene.models.object.ObjectModelClass; +import org.nuiton.eugene.models.object.ObjectModelClassifier; +import org.nuiton.eugene.models.object.ObjectModelDependency; +import org.nuiton.eugene.models.object.ObjectModelInterface; +import org.nuiton.eugene.models.object.ObjectModelOperation; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created: 14 déc. 2009 + * + * @author tchemit <chemit@codelutin.com> + * @version $Id$ + * @since 2.3.0 + * @plexus.component role="org.nuiton.eugene.Template" role-hint="org.nuiton.topia.generator.DAOImplTransformer" + * @deprecated 2.5.4, prefer use the transformer {@link EntityDAOTransformer} + */ +@Deprecated +public class DAOImplTransformer extends ObjectModelTransformerToJava { + + /** Logger. */ + static Log log = LogFactory.getLog(DAOImplTransformer.class); + + /** + * Collection used to identify entities full qualified name that have + * a dependency for DAO extra operations. So no need to generate the + * DAOImpl in this case, it will be created by the developper. + */ + List<String> noGenerationNeeded = new ArrayList<String>(); + + @Override + public void transformFromInterface(ObjectModelInterface interfacez) { + if (!TopiaGeneratorUtil.hasDaoStereotype(interfacez)) { + return; + } + + /** + * EVO #636 : Manage extra operations for DAO from "dao" dependency + * between an interface with stereotype <<dao>> (dependency client) and + * a class with stereotype <<entity>> (dependency supplier). + */ + + ObjectModelDependency dependency = + interfacez.getDependency(TopiaGeneratorUtil.DEPENDENCIES_DAO); + + if (dependency == null) { + if (log.isWarnEnabled()) { + log.warn("Could not find dependency " + + TopiaGeneratorUtil.DEPENDENCIES_DAO + + " but DAO stereotype was placed on the interface " + + interfacez.getName()); + + } + return; + } + ObjectModelClassifier classifier = dependency.getSupplier(); + + if (TopiaGeneratorUtil.isEntity(classifier)) { + noGenerationNeeded.add(classifier.getQualifiedName()); + } + } + + @Override + public void transformFromClass(ObjectModelClass clazz) { + if (!TopiaGeneratorUtil.isEntity(clazz) || hasDAOOperations(clazz)) { + return; + } + String clazzName = clazz.getName(); + String clazzFQN = clazz.getQualifiedName(); + ObjectModelClass result = createClass(clazzName + "DAOImpl<E extends " + clazzName + ">", clazz.getPackageName()); + setDocumentation(result, "/**\n" + + " Implantation du DAO pour l'entité " + clazzName + ".\n" + + " * L'utilisateur peut remplacer cette classe par la sienne en la mettant \n" + + " * simplement dans ces sources. Cette classe générée sera alors simplement\n" + + " * écrasée\n" + + " */"); + setSuperClass(result, clazzFQN + "DAOAbstract<E>"); + } + + /** + * Detect if the class has DAO operations identified with <<dao>> stereotype. + * + * @param clazz The ObjectModelClass with operations (Corresponding to the Entity) + * @return true if the class has some dao operations, false if not + */ + public boolean hasDAOOperations(ObjectModelClass clazz) { + // This code will be deprecated + for (ObjectModelOperation op : clazz.getOperations()) { + if (TopiaGeneratorUtil.hasDaoStereotype(op)) { + return true; + } + } + // New method : interface dependency + return noGenerationNeeded.contains(clazz.getQualifiedName()); + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/generator/DAOTransformer.java b/topia-persistence/src/main/java/org/nuiton/topia/generator/DAOTransformer.java new file mode 100644 index 0000000..3cb3a61 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/generator/DAOTransformer.java @@ -0,0 +1,62 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.generator; + +import org.nuiton.eugene.java.ObjectModelTransformerToJava; +import org.nuiton.eugene.models.object.ObjectModelClass; + +/*{generator option: parentheses = false}*/ + +/*{generator option: writeString = +}*/ +/** + * Created: 13 déc. 2009 + * + * @author tchemit <chemit@codelutin.com> + * @version $Id$ + * @since 2.3.0 + * @plexus.component role="org.nuiton.eugene.Template" role-hint="org.nuiton.topia.generator.DAOTransformer" + * @deprecated 2.5.4, prefer use the transformer {@link EntityDAOTransformer} + */ +@Deprecated +public class DAOTransformer extends ObjectModelTransformerToJava { + + @Override + public void transformFromClass(ObjectModelClass clazz) { + if (!TopiaGeneratorUtil.isEntity(clazz)) { + return; + } + String clazzName = clazz.getName(); + String clazzFQN = clazz.getQualifiedName(); + ObjectModelClass result = createClass(clazzName + "DAO", clazz.getPackageName()); + setDocumentation(result, "/**\n" + + " * Cette classe etend le DAOImpl pour parametrer la classe avec le bon type\n" + + " * Cette classe est marque finale car l'heritage entre les DAO se fait\n" + + " * sur les DOAImpl, c-a-d que DAOAbstract peut etendre le DAOImpl\n" + + " */"); + setSuperClass(result, clazzFQN + "DAOImpl<" + clazzName + ">"); + } + + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/generator/DTOTransformer.java b/topia-persistence/src/main/java/org/nuiton/topia/generator/DTOTransformer.java new file mode 100644 index 0000000..dca8d8a --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/generator/DTOTransformer.java @@ -0,0 +1,461 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.generator; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.eugene.GeneratorUtil; +import org.nuiton.eugene.java.ObjectModelTransformerToJava; +import org.nuiton.eugene.models.object.*; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.Serializable; +import java.util.Collection; +import java.util.List; + + +/*{generator option: parentheses = false}*/ + +/*{generator option: writeString = +}*/ + +/** + * Created: 20 déc. 2009 + * + * @author tchemit <chemit@codelutin.com> + * @version $Id$ + * @since 2.3.0 + * @plexus.component role="org.nuiton.eugene.Template" role-hint="org.nuiton.topia.generator.DTOTransformer" + */ +public class DTOTransformer extends ObjectModelTransformerToJava { + + /** + * Logger + */ + private static final Log log = LogFactory.getLog(DTOTransformer.class); + + @Override + public void transformFromClass(ObjectModelClass clazz) { + if (!TopiaGeneratorUtil.hasDtoStereotype(clazz)) { + return; + } + String clazzName = clazz.getName(); + ObjectModelClass result; + result = createClass(clazzName + "DTO", clazz.getPackageName()); + addImport(result, ToStringBuilder.class); + addImport(result, PropertyChangeListener.class); + + setDocumentation(result, "Implantation DTO pour l'entité " + StringUtils.capitalize(clazzName) + "."); + String extendClass = ""; + for (ObjectModelClass parent : clazz.getSuperclasses()) { + extendClass = parent.getQualifiedName() + "DTO"; + // no multi-inheritance in java + break; + } + if (extendClass.length() > 0) { + setSuperClass(result, extendClass); + } + + addInterface(result, Serializable.class); + for (ObjectModelInterface parentInterface : clazz.getInterfaces()) { + if (TopiaGeneratorUtil.hasDtoStereotype(parentInterface)) { + addInterface(result, parentInterface.getName() + "DTO"); + } else { + addInterface(result, parentInterface.getName()); + } + } + + addAttributes(result, clazz); + + addOperations(result, clazz); + } + + protected void addAttributes(ObjectModelClass result, ObjectModelClass clazz) { + + String svUID = TopiaGeneratorUtil.findTagValue("dto-serialVersionUID", clazz, model); + if (StringUtils.isNotEmpty(svUID)) { + addAttribute(result, "serialVersionUID", long.class, svUID, ObjectModelJavaModifier.FINAL, ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC); + } + + addAttribute(result, "p", PropertyChangeSupport.class, null, ObjectModelJavaModifier.PROTECTED); + +/* +* Définition des attributs +*/ + ObjectModelAttribute attr2; + for (ObjectModelAttribute attr : clazz.getAttributes()) { + ObjectModelAttribute reverse = attr.getReverseAttribute(); + + String attributeName; + String attributeType; + if (!(attr.isNavigable() + || attr.hasAssociationClass())) { + continue; + } + + String attrName = attr.getName(); + String attrVisibility = attr.getVisibility(); + String attrType = attr.getType(); + if (!GeneratorUtil.isNMultiplicity(attr)) { + if (!attr.hasAssociationClass()) { + if (isDTO(attrType)) { + attrType += "DTO"; + } + attributeType = attrType; + attributeName = attrName; + } else { + String assocAttrName = TopiaGeneratorUtil.getAssocAttrName(attr); + attributeType = attr.getAssociationClass().getQualifiedName(); + attributeName = GeneratorUtil.toLowerCaseFirstLetter(assocAttrName); + } + } else { + if (!attr.hasAssociationClass()) { + String nMultType; + if (attr.isOrdered()) { + nMultType = List.class.getName() + "<"; + } else { + nMultType = Collection.class.getName() + "<"; + } + nMultType += attrType; + if (isDTO(attrType)) { + nMultType += "DTO"; + } + nMultType += ">"; + + attributeType = nMultType; + attributeName = attrName; + } else { + String assocAttrName = TopiaGeneratorUtil.getAssocAttrName(attr); + String assocClassFQN = attr.getAssociationClass().getQualifiedName(); + String nMultType; + if (attr.isOrdered()) { + nMultType = List.class.getName() + "<"; + } else { + nMultType = Collection.class.getName() + "<"; + } + nMultType += assocClassFQN; + if (isDTO(attrType)) { + nMultType += "DTO"; + } + nMultType += ">"; + attributeType = nMultType; + attributeName = GeneratorUtil.toLowerCaseFirstLetter(assocAttrName); + } + } + + attr2 = addAttribute(result, attributeName, attributeType, null, ObjectModelJavaModifier.PROTECTED); + + if (attr2 != null) { + if (TopiaGeneratorUtil.hasDocumentation(attr)) { + setDocumentation(attr2, attr.getDocumentation()); + } + String annotation = TopiaGeneratorUtil.getAnnotationTagValue(attr); + if (StringUtils.isNotEmpty(annotation)) { + addAnnotation(result, attr2, annotation); + } + } + } /* end for*/ + + //Déclaration des attributs d'une classe d'associations + if (clazz instanceof ObjectModelAssociationClass) { + ObjectModelAssociationClass assoc = (ObjectModelAssociationClass) clazz; + for (ObjectModelAttribute attr : assoc.getParticipantsAttributes()) { + if (attr != null) { + String attrName = attr.getName(); + String attrVisibility = attr.getVisibility(); + String attrType = attr.getType(); + if (isDTO(attrType)) { + attrType += "DTO"; + } + addAttribute(result, GeneratorUtil.toLowerCaseFirstLetter(attrName), attrType); + } + } + } + + } + + protected void addOperations(ObjectModelClass result, ObjectModelClass clazz) { + ObjectModelOperation op; + op = addOperation(result, "addPropertyChangeListener", "void", ObjectModelJavaModifier.PUBLIC); + addParameter(op, PropertyChangeListener.class, "listener"); + setOperationBody(op, "" +/*{ + p.addPropertyChangeListener(listener); + }*/ + ); + + op = addOperation(result, "addPropertyChangeListener", "void", ObjectModelJavaModifier.PUBLIC); + addParameter(op, String.class, "propertyName"); + addParameter(op, PropertyChangeListener.class, "listener"); + setOperationBody(op, "" +/*{ + p.addPropertyChangeListener(propertyName, listener); + }*/ + ); + + op = addOperation(result, "removePropertyChangeListener", "void", ObjectModelJavaModifier.PUBLIC); + addParameter(op, PropertyChangeListener.class, "listener"); + setOperationBody(op, "" +/*{ + p.removePropertyChangeListener(listener); + }*/ + ); + + op = addOperation(result, "removePropertyChangeListener", "void", ObjectModelJavaModifier.PUBLIC); + addParameter(op, String.class, "propertyName"); + addParameter(op, PropertyChangeListener.class, "listener"); + setOperationBody(op, "" +/*{ + p.removePropertyChangeListener(propertyName, listener); + }*/ + ); + /* + * Définition des getteurs et setteurs + */ + for (ObjectModelAttribute attr : clazz.getAttributes()) { + + ObjectModelAttribute reverse = attr.getReverseAttribute(); + +// if (!(attr.isNavigable() || hasUnidirectionalRelationOnAbstractType(reverse, model))) { + if (!attr.isNavigable()) { + continue; + } + + String attrName = attr.getName(); + String attrType = attr.getType(); + String attrTypeDTO = attr.getType(); + if (isDTO(attrType)) { + attrTypeDTO += "DTO"; + } + + if (!GeneratorUtil.isNMultiplicity(attr)) { + if (!attr.hasAssociationClass()) { + op = addOperation(result, "set" + StringUtils.capitalize(attrName), "void", ObjectModelJavaModifier.PUBLIC); + addParameter(op, attrTypeDTO, "value"); + setOperationBody(op, "" +/*{ + <%=attrTypeDTO%> oldValue = this.<%=attrName%>; + this.<%=attrName%> = value; + p.firePropertyChange("<%=attrName%>", oldValue, value); + }*/ + ); + + op = addOperation(result, "get" + StringUtils.capitalize(attrName), attrTypeDTO, ObjectModelJavaModifier.PUBLIC); + setOperationBody(op, "" +/*{ + return <%=attrName%>; + }*/ + ); + + } else { + String assocAttrName = TopiaGeneratorUtil.getAssocAttrName(attr); + String assocClassFQN = attr.getAssociationClass().getQualifiedName(); + if (log.isTraceEnabled()) { + log.trace("assocAttrName: " + assocAttrName); + } + op = addOperation(result, "set" + StringUtils.capitalize(assocAttrName), "void", ObjectModelJavaModifier.PUBLIC); + addParameter(op, assocClassFQN + "DTO", "association"); + setOperationBody(op, "" +/*{ + <%=assocClassFQN%>DTO oldAssocation = this.<%=GeneratorUtil.toLowerCaseFirstLetter(assocAttrName)%>; + this.<%=GeneratorUtil.toLowerCaseFirstLetter(assocAttrName)%> = association; + p.firePropertyChange("<%=attrName%>", oldAssocation, assocation); + }*/ + ); + + op = addOperation(result, "get" + StringUtils.capitalize(assocAttrName), assocClassFQN + "DTO", ObjectModelJavaModifier.PUBLIC); + setOperationBody(op, "" +/*{ + return <%=GeneratorUtil.toLowerCaseFirstLetter(assocAttrName)%>; + }*/ + ); + } + } else { //NMultiplicity + if (!attr.hasAssociationClass()) { //Méthodes remplacées par des accesseurs sur les classes d'assoc + + String nMultType; + if (attr.isOrdered()) { + nMultType = List.class.getName() + "<" + attrTypeDTO + ">"; + } else { + nMultType = Collection.class.getName() + "<" + attrTypeDTO + ">"; + } + op = addOperation(result, "set" + StringUtils.capitalize(attrName), "void", ObjectModelJavaModifier.PUBLIC); + addParameter(op, nMultType, "values"); + setOperationBody(op, "" +/*{ + <%=nMultType%> oldValues = this.<%=attrName%>; + this.<%=attrName%> = values; + p.firePropertyChange("<%=attrName%>", oldValues, values); + }*/ + ); + + op = addOperation(result, "addChild" + StringUtils.capitalize(attrName), attrTypeDTO, ObjectModelJavaModifier.PUBLIC); + addParameter(op, attrTypeDTO, attrName); + StringBuilder buffercode = new StringBuilder(); + + buffercode.append("" +/*{ + this.<%=attrName%>.add(<%=attrName%>); + }*/ + ); + + if (reverse != null && reverse.isNavigable()) { + String reverseAttrName = reverse.getName(); + buffercode.append("" +/*{ <%=attrName%>.set<%=StringUtils.capitalize(reverseAttrName)%>(this); + }*/ + ); + } + buffercode.append("" +/*{ return <%=attrName%>; + }*/ + ); + setOperationBody(op, buffercode.toString()); + + op = addOperation(result, "removeChild", "void"); + addParameter(op, attrTypeDTO, attrName); + + buffercode = new StringBuilder(); + buffercode.append("" +/*{ + this.<%=attrName%>.remove(<%=attrName%>); + }*/ + ); + + if (reverse != null && reverse.isNavigable()) { + String reverseAttrName = reverse.getName(); + buffercode.append("" +/*{ <%=attrName%>.set<%=StringUtils.capitalize(reverseAttrName)%>(null); + }*/ + ); + } + setOperationBody(op, buffercode.toString()); + + } else { + String assocAttrName = TopiaGeneratorUtil.getAssocAttrName(attr); + String assocClassFQN = attr.getAssociationClass().getQualifiedName(); + String nMultType; + if (attr.isOrdered()) { + nMultType = List.class.getName() + "<" + assocClassFQN + "DTO>"; + } else { + nMultType = Collection.class.getName() + "<" + assocClassFQN + "DTO>"; + } + if (log.isTraceEnabled()) { + log.trace("assocAttrName: " + assocAttrName); + } + op = addOperation(result, "set" + StringUtils.capitalize(assocAttrName), "void"); + addParameter(op, nMultType, "values"); + setOperationBody(op, "" +/*{ + <%=nMultType%> oldValues = this.<%=GeneratorUtil.toLowerCaseFirstLetter(assocAttrName)%>; + this.<%=GeneratorUtil.toLowerCaseFirstLetter(assocAttrName)%> = values; + p.firePropertyChange("<%=attrName%>", oldValues, values); + }*/ + ); + } + if (!attr.hasAssociationClass()) { + String nMultType; + if (attr.isOrdered()) { + nMultType = List.class.getName() + "<" + attrTypeDTO + ">"; + } else { + nMultType = Collection.class.getName() + "<" + attrTypeDTO + ">"; + } + op = addOperation(result, "get" + StringUtils.capitalize(attrName), nMultType); + setOperationBody(op, "" +/*{ + return this.<%=attrName%>; + }*/ + ); + } else { + String assocAttrName = TopiaGeneratorUtil.getAssocAttrName(attr); + String assocClassFQN = attr.getAssociationClass().getQualifiedName(); + String nMultType; + if (attr.isOrdered()) { + nMultType = List.class.getName() + "<" + assocClassFQN + "DTO>"; + } else { + nMultType = Collection.class.getName() + "<" + assocClassFQN + "DTO>"; + } + if (log.isTraceEnabled()) { + log.trace("assocAttrName: " + assocAttrName); + } + op = addOperation(result, "get" + StringUtils.capitalize(assocAttrName), nMultType); + setOperationBody(op, "" +/*{ + return this.<%=GeneratorUtil.toLowerCaseFirstLetter(assocAttrName)%>; + }*/ + ); + } + } + } + + op = addOperation(result, "toString", String.class, ObjectModelJavaModifier.PUBLIC); + StringBuilder buffer = new StringBuilder(); + + buffer.append("" +/*{ + String result = new ToStringBuilder(this). +}*/ + ); + + for (Object o : clazz.getAttributes()) { + ObjectModelAttribute attr = (ObjectModelAttribute) o; + if (!(attr.isNavigable() + || attr.hasAssociationClass())) { + continue; + } + //FIXME possibilité de boucles (non directes) + ObjectModelClass attrEntity = null; + if (model.hasClass(attr.getType())) { + attrEntity = model.getClass(attr.getType()); + } + boolean isDTO = attrEntity != null && + TopiaGeneratorUtil.isEntity(attrEntity); //THIMEL : STEREOTYPE ENTITY ??? + ObjectModelAttribute reverse = attr.getReverseAttribute(); + if (isDTO && (reverse == null || !reverse.isNavigable()) && !attr.hasAssociationClass() || !isDTO) { + String attrName = attr.getName(); + buffer.append("" +/*{ append("<%=attrName%>", this.<%=attrName%>). +}*/ + ); + } + } + buffer.append("" +/*{ toString(); + return result; + }*/ + ); + setOperationBody(op, buffer.toString()); + } + + public boolean isDTO(String type) { + ObjectModelClassifier clazz = model.getClassifier(type); + return clazz != null && TopiaGeneratorUtil.hasDtoStereotype(clazz); + } + + +} + diff --git a/topia-persistence/src/main/java/org/nuiton/topia/generator/EntityDAOTransformer.java b/topia-persistence/src/main/java/org/nuiton/topia/generator/EntityDAOTransformer.java new file mode 100644 index 0000000..3604621 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/generator/EntityDAOTransformer.java @@ -0,0 +1,1233 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.generator; + +/*{generator option: parentheses = false}*/ +/*{generator option: writeString = +}*/ + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.eugene.GeneratorUtil; +import org.nuiton.eugene.java.ObjectModelTransformerToJava; +import org.nuiton.eugene.models.object.ObjectModel; +import org.nuiton.eugene.models.object.ObjectModelAssociationClass; +import org.nuiton.eugene.models.object.ObjectModelAttribute; +import org.nuiton.eugene.models.object.ObjectModelClass; +import org.nuiton.eugene.models.object.ObjectModelClassifier; +import org.nuiton.eugene.models.object.ObjectModelDependency; +import org.nuiton.eugene.models.object.ObjectModelInterface; +import org.nuiton.eugene.models.object.ObjectModelJavaModifier; +import org.nuiton.eugene.models.object.ObjectModelModifier; +import org.nuiton.eugene.models.object.ObjectModelOperation; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaDAO; +import org.nuiton.topia.persistence.TopiaDAOLegacy; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.util.StringUtil; + +import java.security.Permission; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * To generate all <code>DAO</code> related classes for a given entity. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.4 + * @plexus.component role="org.nuiton.eugene.Template" role-hint="org.nuiton.topia.generator.EntityDAOTransformer" + */ +public class EntityDAOTransformer extends ObjectModelTransformerToJava { + + /** Logger. */ + private static final Log log = + LogFactory.getLog(EntityDAOTransformer.class); + + /** + * map of direct usages (values) for each entity (key). + * <p/> + * This map is used to generate the findUsages methods for DAOAbstract. + */ + protected Map<ObjectModelClass, Set<ObjectModelClass>> usages; + + /** + * All entities fqn of the model (used to detect if an attribute is not + * an entity). + */ + Set<String> allEntitiesFqn; + + /** + * The class of abstract dao to use. + * @since 2.5 + */ + protected Class<?> daoImplementation; + + protected String entityEnumName; + + protected String entityEnumPackage; + /** + * Map of extra operations for DAO. The key of the map is the qualified + * name of the entity relative to the DAO. + */ + Map<String, Collection<ObjectModelOperation>> extraOperations = + new HashMap<String, Collection<ObjectModelOperation>>(); + + @Override + public void transformFromModel(ObjectModel model) { + + boolean generateStandaloneEnum = + TopiaGeneratorUtil.shouldGenerateStandaloneEnumForDAOHelper(model); + String modelName = model.getName(); + + entityEnumName = modelName + "EntityEnum"; + + String packageName = getDefaultPackageName(); + + if (generateStandaloneEnum) { + entityEnumPackage = packageName + "." + entityEnumName; + } else { + String daoHelperClazzName = modelName + "DAOHelper"; + entityEnumPackage = packageName + "." + daoHelperClazzName + "." + entityEnumName; + } + + usages = TopiaGeneratorUtil.searchDirectUsages(model); + boolean extendLegacyDAO = + Boolean.valueOf(model.getTagValue(TopiaTagValues.TAG_USE_LEGACY_DAO)); + if (extendLegacyDAO) { + log.warn("Using a deprecated tag value " + + TopiaTagValues.TAG_USE_LEGACY_DAO + + ", prefer use the tag value " + + TopiaTagValues.TAG_DAO_IMPLEMENTATION); + daoImplementation = TopiaDAOLegacy.class; + } else { + daoImplementation = + TopiaGeneratorUtil.getDAOImplementation(model); + } + + // keep all classifiers on the model which are entities + List<ObjectModelClass> allEntities = + TopiaGeneratorUtil.getEntityClasses(model, true); + allEntitiesFqn = new HashSet<String>(allEntities.size()); + for (ObjectModelClass entity : allEntities) { + String fqn = entity.getQualifiedName(); + allEntitiesFqn.add(fqn); + Collection<ObjectModelOperation> daoOperations = + new HashSet<ObjectModelOperation>(); + for (ObjectModelOperation op : entity.getOperations()) { + if (TopiaGeneratorUtil.hasDaoStereotype(op)) { + daoOperations.add(op); + } + } + + if (daoOperations.isEmpty()) { + + // found some dao operations + extraOperations.put(fqn, daoOperations); + } + } + } + + @Override + public void transformFromInterface(ObjectModelInterface interfacez) { + if (!TopiaGeneratorUtil.hasDaoStereotype(interfacez)) { + return; + } + + /** + * EVO #636 : Manage extra operations for DAO from "dao" dependency + * between an interface with stereotype <<dao>> (dependency client) and + * a class with stereotype <<entity>> (dependency supplier). + */ + + ObjectModelDependency dependency = + interfacez.getDependency(TopiaGeneratorUtil.DEPENDENCIES_DAO); + + if (dependency == null) { + if (log.isWarnEnabled()) { + log.warn("Could not find dependency " + + TopiaGeneratorUtil.DEPENDENCIES_DAO + + " but DAO stereotype was placed on the interface " + + interfacez.getName()); + } + return; + } + + ObjectModelClassifier classifier = dependency.getSupplier(); + + if (!TopiaGeneratorUtil.isEntity(classifier)) { + + // dependency supplier is not an entity... + if (log.isWarnEnabled()) { + log.warn("Dependency supplier " + + classifier.getQualifiedName() + + " is not an entity."); + } + return; + } + + // keep only direct operations + Collection<ObjectModelOperation> operations = + interfacez.getOperations(); + + if (CollectionUtils.isEmpty(operations)) { + + // no operations on interface, this is not normal + if (log.isWarnEnabled()) { + log.warn("No operation found on interface with DAO " + + "stereotype "+classifier.getQualifiedName()); + } + return; + } + if (log.isDebugEnabled()) { + log.debug("add "+operations.size()+" extra operation(s) for DAO"); + } + + extraOperations.put(classifier.getQualifiedName(), operations); + } + + @Override + public void transformFromClass(ObjectModelClass clazz) { + if (!TopiaGeneratorUtil.isEntity(clazz)) { + // not an entity + return; + } + String clazzName = clazz.getName(); + String clazzFQN = clazz.getQualifiedName(); + + if (isGenerateDAO(clazz)) { + + // generate DAO + generateDAOClass(clazz, clazzName, clazzFQN); + + } + if (isGenerateImpl(clazz)) { + + // generate DAOImpl + generateDAOImpl(clazz, clazzName, clazzFQN); + } + + if (isGenerateDAOAbstract(clazz)) { + + // generate DAOAbstract + generateDAOAbstract(clazz, clazzName, clazzFQN); + } + } + + protected boolean isGenerateDAO(ObjectModelClass input) { + + String fqn = input.getQualifiedName() + "DAO"; + + if (isInClassPath(fqn)) { + + // already in class-path + return false; + } + + // can safely generate the dao impl + return true; + } + + protected boolean isGenerateDAOAbstract(ObjectModelClass input) { + + String fqn = input.getQualifiedName() + "DAOAbstract"; + + if (isInClassPath(fqn)) { + + // already in class-path + return false; + } + + // can safely generate the dao impl + return true; + } + + protected boolean isGenerateImpl(ObjectModelClass input) { + + String fqn = input.getQualifiedName() + "DAOImpl"; + + if (isInClassPath(fqn)) { + + // already in class-path + return false; + } + + Collection<ObjectModelOperation> moreOperations = + extraOperations.get(input.getQualifiedName()); + + if (CollectionUtils.isNotEmpty(moreOperations)) { + + // no user operations, can generate it + return false; + } + + // can safely generate the dao impl + return true; + } + + protected void generateDAOClass(ObjectModelClass clazz, String clazzName, String clazzFQN) { + ObjectModelClass daoClass = createClass(clazzName + "DAO", clazz.getPackageName()); + setDocumentation(daoClass, "/**\n" + + " * Cette classe etend le DAOImpl pour parametrer la classe avec le bon type\n" + + " * Cette classe est marque finale car l'heritage entre les DAO se fait\n" + + " * sur les DOAImpl, c-a-d que DAOAbstract peut etendre le DAOImpl\n" + + " */"); + setSuperClass(daoClass, clazzFQN + "DAOImpl<" + clazzName + ">"); + } + + protected void generateDAOImpl(ObjectModelClass clazz, + String clazzName, + String clazzFQN) { + + Collection<ObjectModelOperation> moreOperations = + extraOperations.get(clazz.getQualifiedName()); + + if (CollectionUtils.isEmpty(moreOperations)) { + + // no business dao found, can safely generate the daoImpl class + + ObjectModelClass daoImplClass = createClass(clazzName + "DAOImpl<E extends " + clazzName + ">", clazz.getPackageName()); + setDocumentation(daoImplClass, "/**\n" + + " Implantation du DAO pour l'entité " + clazzName + ".\n" + + " * L'utilisateur peut remplacer cette classe par la sienne en la mettant \n" + + " * simplement dans ces sources. Cette classe générée sera alors simplement\n" + + " * écrasée\n" + + " */"); + setSuperClass(daoImplClass, clazzFQN + "DAOAbstract<E>"); + } + } + + protected void generateDAOAbstract(ObjectModelClass clazz, + String clazzName, + String clazzFQN) { + ObjectModelClass daoAbstractClass = createAbstractClass( + clazzName + "DAOAbstract<E extends " + clazzName + '>', + clazz.getPackageName()); + + // super class + + String extendClass = ""; + for (ObjectModelClass parent : clazz.getSuperclasses()) { + extendClass = parent.getQualifiedName(); + if (TopiaGeneratorUtil.isEntity(parent)) { + extendClass += "DAOImpl<E>"; + // in java no multi-inheritance + break; + } + } + if (extendClass.length() == 0) { + extendClass = daoImplementation.getName() + "<E>"; + } + if (log.isDebugEnabled()) { + log.debug("super class = " + extendClass); + } + setSuperClass(daoAbstractClass, extendClass); + + String prefix = getConstantPrefix(clazz); + setConstantPrefix(prefix); + + // imports + + Collection<ObjectModelOperation> DAOoperations = + getDAOOperations(clazz); + + if (TopiaGeneratorUtil.isCollectionNeeded(DAOoperations)) { + addImport(daoAbstractClass, Collection.class); + } + if (TopiaGeneratorUtil.isSetNeeded(DAOoperations)) { + addImport(daoAbstractClass, Set.class); + } + addImport(daoAbstractClass, List.class); + addImport(daoAbstractClass, TopiaException.class); + + boolean enableSecurity = TopiaGeneratorUtil.isClassWithSecurity(clazz); + + if (enableSecurity) { + addImport(daoAbstractClass, TopiaContextImplementor.class); + addImport(daoAbstractClass, ArrayList.class); + addImport(daoAbstractClass, Permission.class); + addImport(daoAbstractClass, "org.nuiton.topia.taas.entities.TaasAuthorizationImpl"); + addImport(daoAbstractClass, "org.nuiton.topia.taas.jaas.TaasPermission"); + addImport(daoAbstractClass, "org.nuiton.topia.taas.TaasUtil"); + addImport(daoAbstractClass, TopiaDAO.class); + + //FIXME : how to do static imports ? +//import static org.nuiton.topia.taas.TaasUtil.CREATE; +//import static org.nuiton.topia.taas.TaasUtil.DELETE; +//import static org.nuiton.topia.taas.TaasUtil.LOAD; +//import static org.nuiton.topia.taas.TaasUtil.UPDATE; + } + + ObjectModelOperation op; + + // getEntityClass + + op = addOperation(daoAbstractClass, + "getEntityClass", + "Class<E>", + ObjectModelJavaModifier.PUBLIC); + addAnnotation(daoAbstractClass, op,Override.class.getSimpleName()); + setOperationBody(op, "" +/*{ + return (Class<E>)<%=clazzName%>.class; + }*/ + ); + + // getTopiaEntityEnum + addImport(daoAbstractClass, entityEnumPackage); + op = addOperation(daoAbstractClass, + "getTopiaEntityEnum", + entityEnumName, + ObjectModelJavaModifier.PUBLIC); + addAnnotation(daoAbstractClass, op,Override.class.getSimpleName()); + setOperationBody(op, "" +/*{ + return <%=entityEnumName%>.<%=clazzName%>; + }*/ + ); + + generateDAOOperations(daoAbstractClass, DAOoperations); + + generateDelete(clazz, daoAbstractClass); + + generateNaturalId(daoAbstractClass, clazz); + + generateNotNull(daoAbstractClass, clazz); + + for (ObjectModelAttribute attr : clazz.getAttributes()) { + if (!attr.isNavigable()) { + continue; + } + + if (!GeneratorUtil.isNMultiplicity(attr)) { + generateNoNMultiplicity(clazzName, daoAbstractClass, attr, false); + } else { + generateNMultiplicity(clazzName, daoAbstractClass, attr); + } + } + + if (clazz instanceof ObjectModelAssociationClass) { + ObjectModelAssociationClass assocClass = + (ObjectModelAssociationClass) clazz; + for (ObjectModelAttribute attr : assocClass.getParticipantsAttributes()) { + if (attr != null) { + if (!GeneratorUtil.isNMultiplicity(attr)) { + generateNoNMultiplicity(clazzName, daoAbstractClass, attr, true); + } else { + generateNMultiplicity(clazzName, daoAbstractClass, attr); + } + } + } + } + + if (enableSecurity) { + + // getRequestPermission + + op = addOperation(daoAbstractClass, + "getRequestPermission", + "List<Permission>", + ObjectModelJavaModifier.PUBLIC); +// setDocumentation(op, "Retourne les permissions a verifier pour " + +// "l'acces a l'entite pour le service Taas"); + addException(op, TopiaException.class); + addParameter(op, String.class, "topiaId"); + addParameter(op, int.class, "actions"); + StringBuilder buffer = new StringBuilder(); + buffer.append("" +/*{ + List<Permission> resultPermissions = new ArrayList<Permission>(); + if ((actions & TaasUtil.CREATE) == TaasUtil.CREATE) { +}*/ + ); + buffer.append(generateSecurity(daoAbstractClass, clazz, + TopiaGeneratorUtil.getSecurityCreateTagValue(clazz))); + buffer.append("" +/*{ + } + if ((actions & TaasUtil.LOAD) == TaasUtil.LOAD) { +}*/ + ); + buffer.append(generateSecurity(daoAbstractClass, clazz, + TopiaGeneratorUtil.getSecurityLoadTagValue(clazz))); + buffer.append("" +/*{ + } + if ((actions & TaasUtil.UPDATE) == TaasUtil.UPDATE) { +}*/ + ); + buffer.append(generateSecurity(daoAbstractClass, clazz, + TopiaGeneratorUtil.getSecurityUpdateTagValue(clazz))); + buffer.append("" +/*{ + } + if ((actions & TaasUtil.DELETE) == TaasUtil.DELETE) { +}*/ + ); + buffer.append(generateSecurity(daoAbstractClass, clazz, + TopiaGeneratorUtil.getSecurityDeleteTagValue(clazz))); + buffer.append("" +/*{ + } + return resultPermissions; + }*/ + ); + + setOperationBody(op, buffer.toString()); + + // THIMEL : Le code suivant doit pouvoir être déplacé dans DAODelegator ? + + // getRequestPermission + + + op = addOperation(daoAbstractClass, + "getRequestPermission", + "List<Permission>", + ObjectModelJavaModifier.PROTECTED); + addParameter(op, String.class, "topiaId"); + addParameter(op, int.class, "actions"); + addParameter(op, String.class, "query"); + addParameter(op, Class.class, "daoClass"); + addException(op, TopiaException.class); +// setDocumentation(op, "Retourne les permissions a verifier pour " + +// "l'acces a l'entite pour le service Taas"); + setOperationBody(op, "" +/*{ TopiaContextImplementor context = getContext(); + List<String> result = context.findAll(query, "id", topiaId); + + List<Permission> resultPermissions = new ArrayList<Permission>(); + for (String topiaIdPermission : result) { + TopiaDAO dao = context.getDAO(daoClass); + List<Permission> permissions = dao.getRequestPermission(topiaIdPermission, actions); + if(permissions != null) { + resultPermissions.addAll(permissions); + } else { + TaasPermission permission = new TaasPermission(topiaIdPermission, actions); + resultPermissions.add(permission); + } + } + return resultPermissions; + }*/ + ); + } + + Set<ObjectModelClass> usagesForclass = usages.get(clazz); + generateFindUsages(clazz, daoAbstractClass, usagesForclass); + } + + protected void generateDelete(ObjectModelClass clazz, + ObjectModelClass result) { + + StringBuilder body = new StringBuilder(); + String modelName = StringUtils.capitalize(model.getName()); + String providerFQN = getDefaultPackageName() + '.' + modelName + + "DAOHelper.getImplementationClass"; + + for (ObjectModelAttribute attr : clazz.getAttributes()) { + + String attrType = GeneratorUtil.getSimpleName(attr.getType()); + + String reverseAttrName = attr.getReverseAttributeName(); + ObjectModelAttribute reverse = attr.getReverseAttribute(); + if (attr.hasAssociationClass() || + reverse == null || !reverse.isNavigable()) { + + // never treate a non reverse and navigable attribute + // never treate an association class attribute + continue; + } + + // at this point we are sure to have a attribute which is + // - reverse + // - navigable + // - not from an association class + if (!allEntitiesFqn.contains(attr.getType())) { + + // this attribute is not from an entity, don't treate it + if (log.isDebugEnabled()) { + log.debug("[" + result.getName() + "] Skip attribute [" + + attr.getName() + "] with type " + attr.getType()); + } + continue; + } + + // At this point, the attribute type is a entity + if (GeneratorUtil.isNMultiplicity(attr) && + GeneratorUtil.isNMultiplicity(reverse)) { + // On doit absolument supprimer pour les relations many-to-many + // le this de la collection de l'autre cote + + String attrDBName = TopiaGeneratorUtil.getDbName(attr); + String attrClassifierDBName = TopiaGeneratorUtil.getDbName(attr.getClassifier()); + String attrJoinTableName = TopiaGeneratorUtil.getManyToManyTableName(attr); + String attrReverseDBName = TopiaGeneratorUtil.getReverseDbName(attr); + + //FIXME_-FC-20100413 Use a TopiaQuery (use HQLin elements) +// // Add DAOHelper +// String daoHelper = modelName + "DAOHelper"; +// String daoHelperFQN = getOutputProperties(). +// getProperty(PROP_DEFAULT_PACKAGE) + '.' + daoHelper; +// addImport(result, daoHelperFQN); +// +// // Add import for TopiaQuery +// addImport(result, TopiaQuery.class); +// +// // Entity DAO and reversePropertyName +// String entityDAO = attrType + "DAO"; +// String reverseAttrNameProperty = +// attrType + "." + getConstantName(reverseAttrName); +// +// +// <%=entityDAO%> dao = <%=daoHelper%>.get<%=entityDAO%>(getContext()); +// TopiaQuery query = dao.createQuery("B"). +// addFrom(entity.getClass(), "A"). +// add("A", entity). +// addInElements("A", "B." + <%=reverseAttrNameProperty%>); +// +// System.out.println("Query : " + query); +// List<<%=attrType%>> list = dao.findAllByQuery(query); + + String removeName = getJavaBeanMethodName("remove", reverseAttrName); + body.append("" +/*{ + { + List<<%=attrType%>> list = getContext().getHibernate().createSQLQuery( + "SELECT main.topiaid " + + "from <%=attrClassifierDBName%> main, <%=attrJoinTableName%> secondary " + + "where main.topiaid=secondary.<%=attrDBName%>" + + " and secondary.<%=attrReverseDBName%>='" + entity.getTopiaId() + "'") + .addEntity("main", <%=providerFQN%>(<%=attrType%>.class)).list(); + + for (<%=attrType%> item : list) { + item.<%=removeName%>(entity); + } + } +}*/ + ); + } else if (!GeneratorUtil.isNMultiplicity(reverse)) { + // On doit mettre a null les attributs qui ont cet objet sur les + // autres entites en one-to-* + // TODO peut-etre qu'hibernate est capable de faire ca tout seul ? + // THIMEL: J'ai remplacé reverse.getName() par reverseAttrName sans certitude + addImport(result, attrType); + String attrSimpleType = TopiaGeneratorUtil.getClassNameFromQualifiedName(attrType); + String getName = getJavaBeanMethodName("get", reverseAttrName); + String setName = getJavaBeanMethodName("set", reverseAttrName); + + body.append("" + /*{ + { + List<<%=attrSimpleType%>> list = getContext() + .getDAO(<%=attrSimpleType%>.class) + .findAllByProperties(<%=attrSimpleType%>.<%=getConstantName(reverseAttrName)%>, entity); + for (<%=attrSimpleType%> item : list) { + + // sletellier : Set null only if target is concerned by deletion + if (entity.equals(item.<%=getName%>())) { + item.<%=setName%>(null); + } + }*/ + ); + if (attr.isAggregate()) { + body.append("" +/*{ + getContext().getDAO(<%=attrSimpleType%>.class).delete(item); + //item.delete(); +}*/ + ); + } + body.append("" +/*{ + } + } +}*/ + ); + + } + } + + if (body.length()>0) { + // something specific was done, need to generate the method + ObjectModelOperation op; + op = addOperation(result, "delete", "void", ObjectModelJavaModifier.PUBLIC); + addAnnotation(result, op,Override.class.getSimpleName()); + addException(op, TopiaException.class); + addParameter(op, "E", "entity"); + body.append("" +/*{ + super.delete(entity); + }*/ + ); + setOperationBody(op, body.toString()); + } + + + + } + + protected void generateFindUsages(ObjectModelClass clazz, + ObjectModelClass result, + Set<ObjectModelClass> usagesForclass) { + + builder.addImport(result, ArrayList.class.getName()); + builder.addImport(result, Map.class.getName()); + builder.addImport(result, HashMap.class.getName()); + builder.addImport(result, TopiaEntity.class.getName()); + + if (clazz instanceof ObjectModelAssociationClass || + usagesForclass.isEmpty()) { + // not for an association class + // just let a null method + ObjectModelOperation operation; + operation = addOperation(result, + "findUsages", + "<U extends TopiaEntity> List<U>", + ObjectModelJavaModifier.PUBLIC); + + addParameter(operation, "Class<U>", "type"); + addParameter(operation, "E", "entity"); + addException(operation, TopiaException.class); + addAnnotation(result, operation, "Override"); + setOperationBody(operation, "" +/*{ + return new ArrayList<U>(); + }*/ + ); + + operation = addOperation(result, + "findAllUsages", + "Map<Class<? extends TopiaEntity>, List<? extends TopiaEntity>>", + ObjectModelJavaModifier.PUBLIC); + + addParameter(operation, "E", "entity"); + addException(operation, TopiaException.class); + addAnnotation(result, operation, "Override"); + setOperationBody(operation, "" +/*{ + return new HashMap<Class<? extends TopiaEntity>, List<? extends TopiaEntity>>(); + }*/ + ); + + return; + } + List<ObjectModelClass> allEntities; + Map<String, ObjectModelClass> allEntitiesByFQN; + + allEntities = TopiaGeneratorUtil.getEntityClasses(model, true); + allEntitiesByFQN = new TreeMap<String, ObjectModelClass>(); + + // prepare usages map and fill allEntitiesByFQN map + for (ObjectModelClass klass : allEntities) { + allEntitiesByFQN.put(klass.getQualifiedName(), klass); + } + + ObjectModelOperation operation; + operation = addOperation(result, + "findUsages", + "<U extends TopiaEntity> List<U>", + ObjectModelJavaModifier.PUBLIC); + + addParameter(operation, "Class<U>", "type"); + addParameter(operation, "E", "entity"); + addException(operation, TopiaException.class); + addAnnotation(result, operation, "Override"); + StringBuilder buffer = new StringBuilder(300); + buffer.append("" +/*{ + List<?> result = new ArrayList(); + List tmp; +}*/ + ); + + for (ObjectModelClass usageClass : usagesForclass) { + String usageType = usageClass.getQualifiedName(); + builder.addImport(result, usageType); + String usageSimpleType = + TopiaGeneratorUtil.getClassNameFromQualifiedName(usageType); + String usageSimplePropertyMethod = "findAllBy" + usageSimpleType; + String usageCollectionPropertyMethod = "findAllContaining" + usageSimpleType; + for (ObjectModelAttribute attr : usageClass.getAttributes()) { + if (!attr.isNavigable()) { + // skip this case + continue; + } + String type; + String attrName = attr.getName(); + if (attr.hasAssociationClass()) { + //FIXME-TC20100224 dont known how to do this ? + continue; +// type = attr.getAssociationClass().getQualifiedName(); +// //FIXME-TC20100224 : this is crazy ??? must find the good name +// // Perhaps need to make different cases? +// attrName = attrName + "_" + TopiaGeneratorUtil.toLowerCaseFirstLetter(attr.getAssociationClass().getName()); + } else { + type = attr.getType(); + } + if (!allEntitiesByFQN.containsKey(type)) { + // not a entity, can skip for this attribute + continue; + } + ObjectModelClass targetEntity = allEntitiesByFQN.get(type); +// if (!type.equals(clazz.getQualifiedName())) { + if (!targetEntity.equals(clazz)) { + // not a good attribute reference + continue; + } + // found something to seek + + String methodName; + if (TopiaGeneratorUtil.isNMultiplicity(attr)) { + methodName = getJavaBeanMethodName("findAllContains", attrName); + } else { + methodName = getJavaBeanMethodName("findAllBy", attrName); + } + String daoName = StringUtils.capitalize(usageSimpleType) + "DAO"; + + builder.addImport(result, usageClass.getPackageName() + '.' + daoName); + + buffer.append("" +/*{ + if (type == <%=usageSimpleType%>.class) { + <%=daoName%> dao = (<%=daoName%>) + getContext().getDAO(<%=usageSimpleType%>.class); + tmp = dao.<%=methodName%>(entity); + result.addAll(tmp); + } +}*/ + ); + } + } + + buffer.append("" +/*{ + return (List<U>) result; + }*/ + ); + setOperationBody(operation, buffer.toString()); + + operation = addOperation(result, + "findAllUsages", + "Map<Class<? extends TopiaEntity>, List<? extends TopiaEntity>>", + ObjectModelJavaModifier.PUBLIC); + + addParameter(operation, "E", "entity"); + addException(operation, TopiaException.class); + addAnnotation(result, operation, "Override"); + + buffer = new StringBuilder(300); + buffer.append("" +/*{ + Map<Class<? extends TopiaEntity>,List<? extends TopiaEntity>> result; + result = new HashMap<Class<? extends TopiaEntity>, List<? extends TopiaEntity>>(<%=usagesForclass.size()%>); + + List<? extends TopiaEntity> list; +}*/ + ); + for (ObjectModelClass usageClass : usagesForclass) { + + String fqn = usageClass.getName(); + buffer.append("" +/*{ + list = findUsages(<%=fqn%>.class, entity); + if (!list.isEmpty()) { + result.put(<%=fqn%>.class, list); + } +}*/ + ); + + } + buffer.append("" +/*{ + return result; + }*/ + ); + + setOperationBody(operation, buffer.toString()); + } + + /** + * Generation of DAO operations signatures from class. These operations are + * abstract and identified by <<dao>> stereotype in the model. The + * developper must defined these methods in the DAOImpl associated to this + * DAOAbstract. + * + * @param result clazz where to add operations + * @param operations operations to generate + */ + private void generateDAOOperations(ObjectModelClass result, + Collection<ObjectModelOperation> + operations) { + if (CollectionUtils.isEmpty(operations)) { + + // no extra operations to generate + return; + } + + for (ObjectModelOperation op : operations) { + + Set<String> exceptions = op.getExceptions(); + exceptions.add(TopiaException.class.getName()); + cloneOperation(op, + result, + true, + ObjectModelJavaModifier.ABSTRACT, + ObjectModelJavaModifier.fromVisibility(op.getVisibility()) + ); + } + } + + + + + private String generateSecurity(ObjectModelClass result, + ObjectModelClass clazz, + String tagValue) { + StringBuilder buffer = new StringBuilder(); + + if (StringUtils.isNotEmpty(tagValue)) { + String security = tagValue; + Pattern propertiesPattern = Pattern + .compile("((?:[_a-zA-Z0-9]+\\.)+(?:_?[A-Z][_a-zA-Z0-9]*\\.)+)attribute\\.(?:([_a-z0-9][_a-zA-Z0-9]*))#(?:(create|load|update|delete))"); + String[] valuesSecurity = security.split(":"); + + for (String valueSecurity : valuesSecurity) { + Matcher matcher = propertiesPattern.matcher(valueSecurity); + matcher.find(); + // className is fully qualified name of class + String className = matcher.group(1); + className = StringUtil.substring(className, 0, -1); // remove ended + // . + // target is class, attribute or operation + String attributeName = matcher.group(2); + String actions = matcher.group(3).toUpperCase(); + + String query = ""; + String daoClass = ""; + if (className.equals(clazz.getQualifiedName())) { + query = "select " + attributeName + ".topiaId from " + clazz.getQualifiedName() + " where topiaId = :id"; + daoClass = clazz.getAttribute(attributeName).getClassifier().getQualifiedName(); + } else { + query = "select at.topiaId from " + className + " at inner join at." + attributeName + " cl where cl.topiaId = :id"; + daoClass = className; + } + buffer.append("" +/*{ + resultPermissions.addAll(getRequestPermission(topiaId, + <%=actions%>, + "<%=query%>", + <%=daoClass%>.class)); +}*/ + ); + } + } else { + buffer.append("" +/*{ return null; + }*/ + ); + } + return buffer.toString(); + } + + protected void generateNoNMultiplicity(String clazzName, + ObjectModelClass result, + ObjectModelAttribute attr, + boolean isAssoc) { + String attrName = attr.getName(); + String attrType = attr.getType(); + String propertyName = clazzName + "." + getConstantName(attrName); + if (!isAssoc && attr.hasAssociationClass()) { + String assocClassName = attr.getAssociationClass().getName(); + String assocAttrName = TopiaGeneratorUtil.getAssocAttrName(attr); + // It is about transitivity : use the property to access the + // associationClass + '.' + the property to access the expected + // attribute + // <class>.<attrAssoc> + '.' + <assocClass>.<attr> + propertyName = + clazzName + '.' + getConstantName(assocAttrName) + + " + '.' + " + + assocClassName + '.' + getConstantName(attrName); + } + + ObjectModelOperation op; + op = addOperation(result, + getJavaBeanMethodName("findBy", attrName), + "E", + ObjectModelJavaModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, attrType, "v"); + setOperationBody(op, "" +/*{ + E result = findByProperty(<%=propertyName%>, v); + return result; + }*/ + ); + + op = addOperation(result, + getJavaBeanMethodName("findAllBy", attrName), + "List<E>", + ObjectModelJavaModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, attrType, "v"); + setOperationBody(op, "" +/*{ + List<E> result = findAllByProperty(<%=propertyName%>, v); + return result; + }*/ + ); + + if (!isAssoc && attr.hasAssociationClass()) { + String assocClassName = attr.getAssociationClass().getName(); + String assocClassFQN = attr.getAssociationClass().getQualifiedName(); + String assocAttrName = TopiaGeneratorUtil.getAssocAttrName(attr); + String assocPropertyConstantName = getConstantName(assocAttrName); + op = addOperation(result, + getJavaBeanMethodName("findBy", assocClassName), + "E", + ObjectModelJavaModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, assocClassFQN, "value"); + setOperationBody(op, "" +/*{ + E result = findByProperty(<%=clazzName + "." + assocPropertyConstantName%>, value); + return result; + }*/ + ); + + op = addOperation(result, + getJavaBeanMethodName("findAllBy", assocClassName), + "List<E>", + ObjectModelJavaModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, assocClassFQN, "value"); + setOperationBody(op, "" +/*{ + List<E> result = findAllByProperty(<%=clazzName + "." + assocPropertyConstantName%>, value); + return result; + }*/ + ); + } + } + + protected void generateNMultiplicity(String clazzName, + ObjectModelClass result, + ObjectModelAttribute attr) { + String attrName = attr.getName(); + String attrType = attr.getType(); + if (attr.hasAssociationClass()) { + // do nothing for association class, too complex... + return; + } + ObjectModelOperation op; + // Since 2.4 do nothing, findContains and findAllContains are not generated anymore + op = addOperation(result, + getJavaBeanMethodName("findContains", attrName), + "E", + ObjectModelJavaModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, attrType, "v"); + setOperationBody(op, "" +/*{ + E result = findContains(<%=clazzName + "." + getConstantName(attrName)%>, v); + return result; + }*/ + ); + + op = addOperation(result, + getJavaBeanMethodName("findAllContains", attrName), + "List<E>", + ObjectModelJavaModifier.PUBLIC); + addException(op, TopiaException.class); + addParameter(op, attrType, "v"); + setOperationBody(op, "" +/*{ + List<E> result = findAllContains(<%=clazzName + "." + getConstantName(attrName)%>, v); + return result; + }*/ + ); + } + + + /** + * Obtain business operations of the DAO. + * + * This operations can not be generated, but must be written by developper. + * + * @param clazz the clazz to test. + * @return collections of extra operations, or empty collection if none found. + */ + public Collection<ObjectModelOperation> getDAOOperations( + ObjectModelClass clazz) { + +// // Note : this collection will contains extra operations for DAO. +// // Overriding existing generated methods is not managed yet +// Collection<ObjectModelOperation> results = +// new ArrayList<ObjectModelOperation>(); + +// // This code will be deprecated +// for (ObjectModelOperation op : clazz.getOperations()) { +// if (TopiaGeneratorUtil.hasDaoStereotype(op)) { +// results.add(op); +// } +// } + Collection<ObjectModelOperation> extra = + extraOperations.get(clazz.getQualifiedName()); + return extra; +// if (extra != null) { +// for (ObjectModelOperation op : extra) { +// results.add(op); +// } +// } + +// return results; + } + + private void generateNaturalId(ObjectModelClass result, + ObjectModelClass clazz) { + Set<ObjectModelAttribute> props = + TopiaGeneratorUtil.getNaturalIdAttributes(clazz); + + if (!props.isEmpty()) { + + if (log.isDebugEnabled()) { + log.debug("generateNaturalId for " + props); + } + ObjectModelOperation findByNaturalId = addOperation(result, + "findByNaturalId", "E", ObjectModelJavaModifier.PUBLIC); + addException(findByNaturalId, TopiaException.class); + + ObjectModelOperation existByNaturalId = addOperation(result, + "existByNaturalId", "boolean", ObjectModelJavaModifier.PUBLIC); + addException(existByNaturalId, TopiaException.class); + + // TODO sletellier 20120406 : remove method on 3.0 + ObjectModelOperation create = addOperation(result, + "create", "E", ObjectModelJavaModifier.PUBLIC); + + // sletellier : mark as Deprecated (http://nuiton.org/issues/2051) + setDocumentation(create, "@deprecated since 2.6.10, prefer use {@link #createByNaturalId}\n"); + addAnnotation(result, create, "Deprecated"); + addException(create, TopiaException.class); + + ObjectModelOperation createByNaturalId = addOperation(result, + "createByNaturalId", "E", ObjectModelJavaModifier.PUBLIC); + + addException(createByNaturalId, TopiaException.class); + + // used for calling findByProperties in findByNaturalId + String searchProperties = ""; + // used for calling findByNaturalId in existByNaturalId +// String params = ""; + String clazzName = clazz.getName(); + for (ObjectModelAttribute attr : props) { + String propName = attr.getName(); + // add property as param in both methods + addParameter(findByNaturalId, attr.getType(), propName); + addParameter(existByNaturalId, attr.getType(), propName); + addParameter(create, attr.getType(), propName); + addParameter(createByNaturalId, attr.getType(), propName); + + searchProperties += + ", " + clazzName + '.' + getConstantName(propName) + + ", " + propName; + //params += ", " + propName; + } + searchProperties = searchProperties.substring(2); + //params = params.substring(2); + + setOperationBody(findByNaturalId, "" +/*{ + return findByProperties(<%=searchProperties%>); + }*/ + ); + + setOperationBody(existByNaturalId, "" +/*{ + return existByProperties(<%=searchProperties%>); + }*/ + ); + + setOperationBody(create, "" +/*{ + return create(<%=searchProperties%>); + }*/ + ); + + setOperationBody(createByNaturalId, "" +/*{ + return create(<%=searchProperties%>); + }*/ + ); + } + } + + private void generateNotNull(ObjectModelClass result, + ObjectModelClass clazz) { + + Set<ObjectModelAttribute> props = + TopiaGeneratorUtil.getNotNullAttributes(clazz); + + if (!props.isEmpty()) { + + if (log.isDebugEnabled()) { + log.debug("generateNotNull for " + props); + } + + ObjectModelOperation createByNotNull = addOperation(result, + "createByNotNull", "E", ObjectModelJavaModifier.PUBLIC); + + addException(createByNotNull, TopiaException.class); + + String searchProperties = ""; +// String params = ""; + String clazzName = clazz.getName(); + for (ObjectModelAttribute attr : props) { + String propName = attr.getName(); + // add property as param in both methods + addParameter(createByNotNull, attr.getType(), propName); + + searchProperties += + ", " + clazzName + '.' + getConstantName(propName) + + ", " + propName; + //params += ", " + propName; + } + searchProperties = searchProperties.substring(2); + //params = params.substring(2); + + setOperationBody(createByNotNull, "" +/*{ + return create(<%=searchProperties%>); + }*/ + ); + } + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/generator/EntityDTOTransformer.java b/topia-persistence/src/main/java/org/nuiton/topia/generator/EntityDTOTransformer.java new file mode 100644 index 0000000..ee55514 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/generator/EntityDTOTransformer.java @@ -0,0 +1,363 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.generator; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.nuiton.eugene.GeneratorUtil; +import org.nuiton.eugene.java.ObjectModelTransformerToJava; +import org.nuiton.eugene.models.object.ObjectModelAssociationClass; +import org.nuiton.eugene.models.object.ObjectModelAttribute; +import org.nuiton.eugene.models.object.ObjectModelClass; +import org.nuiton.eugene.models.object.ObjectModelClassifier; +import org.nuiton.eugene.models.object.ObjectModelJavaModifier; +import org.nuiton.eugene.models.object.ObjectModelModifier; +import org.nuiton.eugene.models.object.ObjectModelOperation; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.io.Serializable; + +import static org.nuiton.topia.generator.TopiaGeneratorUtil.hasUnidirectionalRelationOnAbstractType; +import static org.nuiton.topia.generator.TopiaGeneratorUtil.shouldGenerateDTOTopiaIdTagValue; + + +/*{generator option: parentheses = false}*/ + +/*{generator option: writeString = +}*/ + +/** + * Created: 14 déc. 2009 + * + * @author tchemit <chemit@codelutin.com> + * @version $Id$ + * @since 2.3.0 + * @plexus.component role="org.nuiton.eugene.Template" role-hint="org.nuiton.topia.generator.EntityDTOTransformer" + */ +public class EntityDTOTransformer extends ObjectModelTransformerToJava { + + public boolean isEntity(String type) { + ObjectModelClassifier clazz = model.getClassifier(type); + return clazz != null && ! clazz.isEnum() + && TopiaGeneratorUtil.isEntity(clazz); + } + + @Override + public void transformFromClass(ObjectModelClass clazz) { + if (!TopiaGeneratorUtil.isEntity(clazz)) { + return; + } + String clazzName = clazz.getName(); + ObjectModelClass result; + result = createClass(clazzName + "DTO", clazz.getPackageName()); + addImport(result, ToStringBuilder.class); + addImport(result, PropertyChangeListener.class); + + setDocumentation(result, "Implantation DTO pour l'entité " + StringUtils.capitalize(clazzName) + "."); + String extendClass = ""; + for (ObjectModelClass parent : clazz.getSuperclasses()) { + extendClass = parent.getQualifiedName() + "DTO"; + // no multi-inheritance in java + break; + } + if (extendClass.length() > 0) { + setSuperClass(result, extendClass); + } + addInterface(result, Serializable.class); + + + addAttributes(result,clazz); + + addOperations(result,clazz); + + } + + protected void addAttributes(ObjectModelClass result, ObjectModelClass clazz) { + + String svUID = TopiaGeneratorUtil.findTagValue("dto-serialVersionUID", clazz, model); + if (svUID != null) { + addAttribute(result, "serialVersionUID", "long", svUID, ObjectModelJavaModifier.FINAL, ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC); + } + + boolean generateDTOId = shouldGenerateDTOTopiaIdTagValue(clazz, model); + if (generateDTOId) { + addAttribute(result, "topiaId", "String"); + } + + ObjectModelAttribute attr2; + for (ObjectModelAttribute attr : clazz.getAttributes()) { + ObjectModelAttribute reverse = attr.getReverseAttribute(); + + // pour les asso quoi qu'il arrive il faut les lier des 2 cotes + // pour pouvoir supprimer en cascade l'asso lors de la suppression + // d'un des cotes + if (!(attr.isNavigable() + || hasUnidirectionalRelationOnAbstractType(reverse, model) + || attr.hasAssociationClass())) { + continue; + } + + String attrVisibility = attr.getVisibility(); + ObjectModelModifier modifier = ObjectModelJavaModifier.fromVisibility(attrVisibility); + if (!attr.hasAssociationClass()) { + String attrType = attr.getType(); + String attrName = attr.getName(); + if (isEntity(attrType)) { + attrType += "DTO"; + } + if (!GeneratorUtil.isNMultiplicity(attr)) { + attr2 = addAttribute(result, attrName, attrType, null, modifier); + } else { + attr2 = addAttribute(result, attrName, attrType + "[]", null, modifier); + } + } else { + String assocAttrName = TopiaGeneratorUtil.getAssocAttrName(attr); + String assocClassFQN = attr.getAssociationClass().getQualifiedName(); + if (!GeneratorUtil.isNMultiplicity(attr)) { + attr2 = addAttribute(result, GeneratorUtil.toLowerCaseFirstLetter(assocAttrName), assocClassFQN + "DTO", null, modifier); + } else { + attr2 = addAttribute(result, GeneratorUtil.toLowerCaseFirstLetter(assocAttrName), assocClassFQN + "DTO[]", null, modifier); + } + } + if (attr2 != null) { + if (TopiaGeneratorUtil.hasDocumentation(attr)) { + setDocumentation(attr2, attr.getDocumentation()); + } + + String annotation = TopiaGeneratorUtil.getAnnotationTagValue(attr); + if (!StringUtils.isEmpty(annotation)) { + addAnnotation(result, attr2, annotation); + } + } + } + + //Déclaration des attributs d'une classe d'associations + if (clazz instanceof ObjectModelAssociationClass) { + ObjectModelAssociationClass assoc = (ObjectModelAssociationClass) clazz; + for (ObjectModelAttribute attr : assoc.getParticipantsAttributes()) { + if (attr != null) { + String attrVisibility = attr.getVisibility(); + ObjectModelModifier modifier = ObjectModelJavaModifier.fromVisibility(attrVisibility); + String attrType = attr.getType(); + String attrName = attr.getName(); + if (isEntity(attrType)) { + attrType += "DTO"; + } + addAttribute(result, GeneratorUtil.toLowerCaseFirstLetter(attrName), attrType, null, modifier); + } + } + } + + addAttribute(result,"p", PropertyChangeSupport.class,"new PropertyChangeSupport(this)",ObjectModelJavaModifier.PROTECTED,ObjectModelJavaModifier.FINAL); + } + + protected void addOperations(ObjectModelClass result,ObjectModelClass clazz) { + + boolean generateDTOId = shouldGenerateDTOTopiaIdTagValue(clazz, model); + ObjectModelOperation op; + if (generateDTOId) { + op = addOperation(result, "setTopiaId", "void", ObjectModelJavaModifier.PUBLIC); + addParameter(op, "String", "topiaId"); + setOperationBody(op, "" +/*{ + this.topiaId = topiaId; + }*/ + ); + + op = addOperation(result, "getTopiaId", "String", ObjectModelJavaModifier.PUBLIC); + setOperationBody(op, "" +/*{ + return topiaId; + }*/ + ); + } + + op = addOperation(result, "addPropertyChangeListener", "void"); + addParameter(op,PropertyChangeListener.class,"listener"); + setOperationBody(op,"" +/*{ + p.addPropertyChangeListener(listener); + }*/ + ); + + op = addOperation(result, "addPropertyChangeListener", "void"); + addParameter(op, String.class, "propertyName"); + addParameter(op, PropertyChangeListener.class, "listener"); + setOperationBody(op, "" +/*{ + p.addPropertyChangeListener(propertyName, listener); + }*/ + ); + + op = addOperation(result, "removePropertyChangeListener", "void"); + addParameter(op, PropertyChangeListener.class, "listener"); + setOperationBody(op, "" +/*{ + p.removePropertyChangeListener(listener); + }*/ + ); + + op = addOperation(result, "removePropertyChangeListener", "void"); + addParameter(op, String.class, "propertyName"); + addParameter(op, PropertyChangeListener.class, "listener"); + setOperationBody(op, "" +/*{ + p.removePropertyChangeListener(propertyName, listener); + }*/ + ); + + for (ObjectModelAttribute attr : clazz.getAttributes()) { + + ObjectModelAttribute reverse = attr.getReverseAttribute(); + + if (!(attr.isNavigable() || hasUnidirectionalRelationOnAbstractType(reverse, model))) { + continue; + } + + String attrName = attr.getName(); + + if (!attr.hasAssociationClass()) { + String attrType = attr.getType(); + if (isEntity(attrType)) { + attrType += "DTO"; + } + String setterName = getJavaBeanMethodName("set", attrName); + String getterName = getJavaBeanMethodName("get", attrName); + if (!GeneratorUtil.isNMultiplicity(attr)) { + op = addOperation(result, setterName, "void", ObjectModelJavaModifier.PUBLIC); + addParameter(op, attrType, "value"); + setOperationBody(op, "" +/*{ + <%=attrType%> oldValue = this.<%=attrName%>; + this.<%=attrName%> = value; + p.firePropertyChange("<%=attrName%>", oldValue, value); + }*/ + ); + + op = addOperation(result, getterName, attrType, ObjectModelJavaModifier.PUBLIC); + setOperationBody(op, "" +/*{ + return <%=attrName%>; + }*/ + ); + + } else { + + op = addOperation(result, setterName, "void", ObjectModelJavaModifier.PUBLIC); + addParameter(op, attrType+"[]", "values"); + setOperationBody(op, "" +/*{ + <%=attrType%>[] oldValues = this.<%=attrName%>; + this.<%=attrName%> = values; + p.firePropertyChange("<%=attrName%>", oldValues, values); + }*/ + ); + + op = addOperation(result, getterName, attrType+"[]", ObjectModelJavaModifier.PUBLIC); + setOperationBody(op, "" +/*{ + return <%=attrName%>; + }*/ + ); + } + } else { + String assocAttrName = TopiaGeneratorUtil.getAssocAttrName(attr); + String propertyName = GeneratorUtil.toLowerCaseFirstLetter(assocAttrName); + String assocClassFQN = attr.getAssociationClass().getQualifiedName(); + String setterName = getJavaBeanMethodName("set", assocAttrName); + String getterName = getJavaBeanMethodName("get", assocAttrName); + if (!GeneratorUtil.isNMultiplicity(attr)) { + op = addOperation(result, setterName, "void", ObjectModelJavaModifier.PUBLIC); + addParameter(op, assocClassFQN + "DTO", "association"); + setOperationBody(op, "" +/*{ + <%=assocClassFQN%>DTO oldAssocation= this.<%=propertyName%>; + this.<%=propertyName%> = association; + p.firePropertyChange("<%=attrName%>", oldAssocation, assocation); + }*/ + ); + + op = addOperation(result, getterName, assocClassFQN + "DTO", ObjectModelJavaModifier.PUBLIC); + setOperationBody(op, "" +/*{ + return <%=propertyName%>; + }*/ + ); + + } else { + op = addOperation(result, setterName, "void", ObjectModelJavaModifier.PUBLIC); + addParameter(op, assocClassFQN + "DTO[]", "values"); + setOperationBody(op, "" +/*{ + <%=assocClassFQN%>DTO[] oldValues = this.<%=propertyName%>; + this.<%=propertyName%> = values; + p.firePropertyChange("<%=attrName%>", oldValues, values); + }*/ + ); + + op = addOperation(result, getterName, assocClassFQN + "DTO[]", ObjectModelJavaModifier.PUBLIC); + setOperationBody(op, "" +/*{ + return this.<%=propertyName%>; + }*/ + ); + } + } + } + + op = addOperation(result,"toString",String.class, ObjectModelJavaModifier.PUBLIC); + StringBuilder buffer = new StringBuilder(); + + buffer.append("" +/*{ + String result = new ToStringBuilder(this). +}*/ + ); + + for (Object o : clazz.getAttributes()) { + ObjectModelAttribute attr = (ObjectModelAttribute) o; + //FIXME possibilité de boucles (non directes) + ObjectModelClass attrEntity = null; + if (model.hasClass(attr.getType())) { + attrEntity = model.getClass(attr.getType()); + } + boolean isEntity = attrEntity != null && TopiaGeneratorUtil.isEntity(attrEntity); + ObjectModelAttribute reverse = attr.getReverseAttribute(); + if (isEntity && (reverse == null || !reverse.isNavigable()) && !attr.hasAssociationClass() || !isEntity) { + String attrName = attr.getName(); + buffer.append("" +/*{ append("<%=attrName%>", this.<%=attrName%>). +}*/ + ); + } + } + buffer.append("" +/*{ toString(); + return result; +}*/ + ); + setOperationBody(op, buffer.toString()); + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/generator/EntityHibernateMappingGenerator.java b/topia-persistence/src/main/java/org/nuiton/topia/generator/EntityHibernateMappingGenerator.java new file mode 100644 index 0000000..006f385 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/generator/EntityHibernateMappingGenerator.java @@ -0,0 +1,764 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/*{generator option: parentheses = true}*/ +/*{generator option: writeString = output.write}*/ + +/* * +* EntityHibernateMappingGenerator.java +* +* Created: 12 déc. 2005 +* +* @author Arnaud Thimel <thimel@codelutin.com> +* @version $Revision$ +* +*/ + +package org.nuiton.topia.generator; + +import org.apache.commons.lang3.BooleanUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.eugene.models.object.ObjectModelAssociationClass; +import org.nuiton.eugene.models.object.ObjectModelAttribute; +import org.nuiton.eugene.models.object.ObjectModelClass; +import org.nuiton.eugene.models.object.ObjectModelGenerator; + +import java.beans.Introspector; +import java.io.File; +import java.io.IOException; +import java.io.Writer; +import java.sql.Types; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +import static org.nuiton.topia.generator.TopiaGeneratorUtil.hasUnidirectionalRelationOnAbstractType; + +/** + * FIXME-poussin-20060102 mettre les attributs node="..." sur tous les attributs + * + * @author poussin <poussin@codelutin.com> + * @version $Id$ + * @plexus.component role="org.nuiton.eugene.Template" role-hint="org.nuiton.topia.generator.EntityHibernateMappingGenerator" + */ +public class EntityHibernateMappingGenerator extends ObjectModelGenerator { + + /** + * Logger. + */ + private static final Log log = LogFactory + .getLog(EntityHibernateMappingGenerator.class); + + private static final String HIBERNATE_ATTRIBUTE_DEFAULT = "default"; + + private static final String HIBERNATE_ATTRIBUTE_SQL_TYPE = "sql-type"; + + private static final String HIBERNATE_ATTRIBUTE_NAME = "name"; + + private Map<String, String[]> columnNamesMap = new HashMap<String, String[]>(); + + public static final String HIBERNATE_ATTRIBUTE_LAZY = "lazy"; + + public static final String HIBERNATE_ATTRIBUTE_FETCH = "fetch"; + + public static final String HIBERNATE_ATTRIBUTE_NOT_NULL = "not-null"; + + public static final String HIBERNATE_ATTRIBUTE_SCHEMA = "schema"; + + public static final String HIBERNATE_ATTRIBUTE_INDEX = "index"; + + public static final String HIBERNATE_ATTRIBUTE_UNIQUE = "unique"; + + public static final String HIBERNATE_ATTRIBUTE_LENGTH = "length"; + + public static final String HIBERNATE_ATTRIBUTE_ORDER_BY = "order-by"; + + @Override + public String getFilenameForClass(ObjectModelClass clazz) { + String DOName = TopiaGeneratorUtil.getDOType(clazz, model); + return DOName.replace('.', File.separatorChar) + ".hbm.xml"; + } + + @Override + public void generateFromClass(Writer output, + ObjectModelClass input) throws IOException { + String persistenceType = TopiaGeneratorUtil.getPersistenceType(input); + if (!TopiaGeneratorUtil.isEntity(input) && + TopiaGeneratorUtil.PERSISTENCE_TYPE_HIBERNATE.equals(persistenceType)) { + return; + } +/*{<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "classpath://org/hibernate/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="<%=input.getPackageName()%>"> +}*/ + boolean haveSuper = input.getSuperclasses().size() > 0; + // la liste des attributs faisant parti de la clef metier + List<ObjectModelAttribute> naturalAttributes = new ArrayList<ObjectModelAttribute>(); + // la liste des autres attributs + List<ObjectModelAttribute> noneNaturalAttributes = new ArrayList<ObjectModelAttribute>(); + + String clazzDOType = TopiaGeneratorUtil.getDOType(input, model); + String tableName = TopiaGeneratorUtil.getDbName(input); + String isAbstract = BooleanUtils.toStringTrueFalse(input.isAbstract()); + String clazzFQN = input.getQualifiedName(); + + String optionalAttributes = ""; + String schema = TopiaGeneratorUtil.getDbSchemaNameTagValue(input, model); + if (schema != null) { + optionalAttributes += "schema=\"" + schema + "\" "; + } + + //On précise au proxy de quelle interface hérite l'objet + String proxyTagValue = TopiaGeneratorUtil.getProxyInterfaceTagValue(input, model); + if (StringUtils.isEmpty(proxyTagValue) || !proxyTagValue.equals("none")) { + optionalAttributes += "proxy=\"" + clazzFQN + "\" "; + } + + if (!optionalAttributes.isEmpty()) { + optionalAttributes = " " + optionalAttributes.trim(); + } + if (haveSuper) { + ObjectModelClass superClass = input.getSuperclasses().iterator().next(); + String superClassname = superClass.getQualifiedName(); + if (log.isDebugEnabled()) { + log.debug("superClass for " + input.getQualifiedName() + " is " + superClassname); + } + String superClassDOType = TopiaGeneratorUtil.getDOType(superClassname, model); + +/*{ <union-subclass name="<%=clazzDOType%>" extends="<%=superClassDOType%>" table="<%=tableName%>" node="<%=clazzDOType%>" abstract="<%=isAbstract%>"<%=optionalAttributes%>> +}*/ + // FIXME mieux gerer le cas haveSuper + noneNaturalAttributes.addAll(input.getAttributes()); + } else { +/*{ <class name="<%=clazzDOType%>" table="<%=tableName%>" node="<%=clazzDOType%>" abstract="<%=isAbstract%>" <%=optionalAttributes%>> + <id name="topiaId" type="string" length="255" node="@topiaId"/> +}*/ + // on detecte les attributs des clef metiers + for (ObjectModelAttribute attr : input.getAttributes()) { + if (TopiaGeneratorUtil.isNaturalId(attr)) { + // attribut metier + naturalAttributes.add(attr); + } else { + // attribut normal + noneNaturalAttributes.add(attr); + } + } + if (!naturalAttributes.isEmpty()) { + // generation de la clef metier + boolean mutable = TopiaGeneratorUtil.isNaturalIdMutable(input); + String mutableStr = mutable ? " mutable=\"true\"" : ""; + if (log.isDebugEnabled()) { + log.debug("natural-id detected for class " + input.getName() + " (" + mutableStr + ") attributes : " + naturalAttributes); + } +/*{ <natural-id<%=mutableStr%>> +}*/ + generateAttributes(output, input, naturalAttributes, " "); +/*{ </natural-id> +}*/ + } +/*{ <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="timestamp" node="@topiaCreateDate"/> +}*/ + } + + generateAttributes(output, input, noneNaturalAttributes, ""); + + if (haveSuper) { +/*{ </union-subclass> +}*/ + } else { +/*{ </class> +}*/ + } + + generateDatabaseObjects(output, input, naturalAttributes); + generateDatabaseObjects(output, input, noneNaturalAttributes); + +/*{</hibernate-mapping> +}*/ + } + + protected void generateDatabaseObjects(Writer output, + ObjectModelClass clazz, + List<ObjectModelAttribute> attributes) throws IOException { + + for (ObjectModelAttribute attribute : attributes) { + if (!attribute.isNavigable() || + attribute.hasAssociationClass() || + !TopiaGeneratorUtil.isNMultiplicity(attribute) || + attribute.getClassifier() == null || + !TopiaGeneratorUtil.isEntity(attribute.getClassifier()) + ) { + + // skip for this case (not a nm-multiplicity attribute) + continue; + } + + String indexForeignKeys = + TopiaGeneratorUtil.getIndexForeignKeys(attribute, model); + + if (StringUtils.isEmpty(indexForeignKeys) || !Boolean.valueOf(indexForeignKeys)) { + + // no index to put of the attribute. + continue; + } + + // add database-object to create and drop index + + String tableName; + String indexName = "idx_" + clazz.getName() + "_" + attribute.getName(); + String propertyName; + + + if (TopiaGeneratorUtil.isNMultiplicity(attribute.getReverseMaxMultiplicity())) { + + // many to many + tableName = TopiaGeneratorUtil.getManyToManyTableName(attribute); + propertyName = TopiaGeneratorUtil.getDbName(attribute.getReverseAttribute()); + } else { + + // one to many + tableName =TopiaGeneratorUtil.getDbName(attribute.getClassifier()); + propertyName = TopiaGeneratorUtil.getDbName(attribute.getReverseAttribute()); + } + + // add schema if exist (http://nuiton.org/issues/2052) + String schema = TopiaGeneratorUtil.getDbSchemaNameTagValue(clazz, model); + if (StringUtils.isNotEmpty(schema)) { + tableName = schema + "." + tableName; + } +/*{ <database-object> + <create>CREATE INDEX <%=indexName%> ON <%=tableName%>(<%=propertyName%>)</create> + <drop>DROP INDEX <%=indexName%></drop> + </database-object> +}*/ + + } + } + protected void generateAttributes(Writer output, + ObjectModelClass clazz, + List<ObjectModelAttribute> attributes, + String prefix) throws IOException { + for (ObjectModelAttribute attr : attributes) { + ObjectModelAttribute reverse = attr.getReverseAttribute(); + + // pour les asso quoi qu'il arrive il faut les lier des 2 cotes + // pour pouvoir supprimer en cascade l'asso lors de la suppression + // d'un des cotes + if (attr.isNavigable() + || hasUnidirectionalRelationOnAbstractType(reverse, model) + || attr.hasAssociationClass()) { + if (!TopiaGeneratorUtil.isNMultiplicity(attr)) { + if (attr.getClassifier() != null && TopiaGeneratorUtil.isEntity(attr.getClassifier())) { + if (TopiaGeneratorUtil.isNMultiplicity(attr.getReverseMaxMultiplicity()) && !attr.hasAssociationClass()) { + generateHibernateManyToOne(output, attr, prefix); + } else { + generateHibernateOneToOne(output, attr, prefix); + } + } else { + generateHibernateProperty(output, clazz, attr, prefix); + } + } else { + if (attr.getClassifier() != null && TopiaGeneratorUtil.isEntity(attr.getClassifier())) { + if (TopiaGeneratorUtil.isNMultiplicity(attr.getReverseMaxMultiplicity()) && !attr.hasAssociationClass()) { + generateHibernateManyToMany(output, clazz, attr, prefix); + } else { + generateHibernateOneToMany(output, attr, prefix); + } + } else { + generateHibernateMany(output, attr, prefix); + } + } + } + } + + //Attributs pour les classes d'association + if (clazz instanceof ObjectModelAssociationClass) { + ObjectModelAssociationClass assoc = (ObjectModelAssociationClass)clazz; + for (ObjectModelAttribute attr : assoc.getParticipantsAttributes()) { + if (attr != null) { + +// Note(poussin) pour moi quoi qu'il arrive sur la classe d'association il faut +// un many-to-one, sinon on a des problemes. +// if ((!attr.getReverseAttribute().isNavigable()) || !Util.isNMultiplicity(attr.getReverseAttribute())) { +// / *{ <one-to-one name="<%=getName(attr, true)%>" class="<%=getType(attr, true)%>"<%=(TopiaGeneratorUtil.notEmpty(attr.getTagValue(TopiaGeneratorUtil.TAG_LENGTH))?(" length=\"" + attr.getTagValue(TopiaGeneratorUtil.TAG_LENGTH) + "\""):"")%><%=(attr.isComposite()?" cascade=\"delete\"":"")%>/> +// } */ +// } else { + String notNull = " " + generateFromTagValue(HIBERNATE_ATTRIBUTE_NOT_NULL, TopiaGeneratorUtil.getNotNullTagValue(attr)); + String attrName = getName(attr, true); + String attrType = getType(attr, true); + String lazy = generateFromTagValue(HIBERNATE_ATTRIBUTE_LAZY, TopiaGeneratorUtil.getLazyTagValue(attr)); + String attrColumn = TopiaGeneratorUtil.getDbName(attr); +/*{<%=prefix%> <many-to-one name="<%=attrName%>" class="<%=attrType%>" <%=lazy%>column="<%=attrColumn%>" node="<%=attrName%>/@topiaId" <%=notNull%>/> +}*/ +// } + //Ne sert plus grâce à l'utilisation de la navigabilité +// if (!attr.getReverseAttribute().isNavigable()) { +// String type = TopiaGeneratorUtil.getDOType(((ObjectModelClassifier)attr.getDeclaringElement()).getQualifiedName(), model); +// String name = Util.toLowerCaseFirstLetter(attr.getDeclaringElement().getName()); +// if (log.isTraceEnabled()) {log.trace("reverse: " + type + " " + name);} +// if (!Util.isNMultiplicity(attr)) { +//{<!-- <one-to-one name="<%=name%>" class="<%=type%>"/> +//} +// } else { +//{ <many-to-one name="<%=name%>" class="<%=type%>" column="<%=name.toLowerCase()%>"/> --> +//} +// } +// } + } + } + } + } + + protected String getName(ObjectModelAttribute attr) { + return getName(attr, false); + } + + protected String getName(ObjectModelAttribute attr, boolean isAssoc) { + String result = Introspector.decapitalize(attr.getName()); + if (attr.hasAssociationClass() && !isAssoc) { + result = TopiaGeneratorUtil.getAssocAttrName(attr); + } + return result; + } + + protected String getType(ObjectModelAttribute attr) { + return getType(attr, false); + } + + protected String getType(ObjectModelAttribute attr, boolean isAssoc) { + String type = attr.getType(); + String attrType = TopiaGeneratorUtil.getTypeTagValue(attr); + if (StringUtils.isNotEmpty(attrType)) { + + // tag value detected of the attribute + type = attrType; + } else { + + String modelType = model.getTagValue(type); + if (StringUtils.isNotEmpty(modelType)) { + + // tag value detected of the model + + //TODO tchemit 20100507 Explain What todes it do ? Dont understand the story of columnNamesMap + int bracketIndex = modelType.indexOf('('); + if (bracketIndex != -1) { + type = modelType.substring(0, bracketIndex); + int bracketEndIndex = modelType.indexOf(')', bracketIndex + 1); + String colmunList; + if (bracketEndIndex != -1) { + colmunList = modelType.substring(bracketIndex + 1, bracketEndIndex); + } else { + colmunList = modelType.substring(bracketIndex); + } + columnNamesMap.put(type, colmunList.split(",")); + } else { + type = modelType; + } + } + } + if (attr.hasAssociationClass() && !isAssoc) { + type = attr.getAssociationClass().getQualifiedName(); + } + return TopiaGeneratorUtil.getDOType(type, model); + } + + protected void generateHibernateProperty(Writer output, + ObjectModelClass clazz, + ObjectModelAttribute attr, + String prefix) throws IOException { + String attrType = getType(attr); + + String accessField = "field"; + String tagValue = TopiaGeneratorUtil.getAccessTagValue(attr); + if (StringUtils.isNotEmpty(tagValue)) { + accessField = tagValue; + } + String attrName = attr.getName(); + String declaringElementDBName = TopiaGeneratorUtil.getDbName(attr.getDeclaringElement()); + String tableName = declaringElementDBName + "_" + attrName; + + boolean attrIsEnumeration = attr.getClassifier() != null + && attr.getClassifier().isEnum(); + + if (attrType.trim().endsWith("[]")) { + attrType = attrType.trim().substring(0, attrType.trim().length()-2); + + String optionalAttributes = ""; + //String schema = TopiaGeneratorUtil.getDbSchemaNameTagValue(attr.getClassifier(), model); + String schema = TopiaGeneratorUtil.getDbSchemaNameTagValue(clazz, model); + if (schema != null) { + optionalAttributes += generateFromTagValue(HIBERNATE_ATTRIBUTE_SCHEMA, schema); + } + + if (TopiaGeneratorUtil.hasIndexedStereotype(attr)) { + String indexName = tableName + "_idx"; + optionalAttributes += generateFromTagValue(HIBERNATE_ATTRIBUTE_INDEX, indexName); + } + if (!optionalAttributes.isEmpty()) { + optionalAttributes = " " + optionalAttributes.trim(); + } + +/*{<%=prefix%> <primitive-array name="<%=attrName%>" table="<%=tableName%>" access="<%=accessField%>"<%=optionalAttributes%>> +<%=prefix%> <key column="<%=declaringElementDBName%>"/> +<%=prefix%> <list-index column="<%=attrName%>_idx"/> +<%=prefix%> <element type="<%=attrType%>"/> +<%=prefix%> </primitive-array> +}*/ + } else { + String optionalAttributes = ""; + if (TopiaGeneratorUtil.hasIndexedStereotype(attr)) { + String indexName = tableName + "_idx"; + optionalAttributes += generateFromTagValue(HIBERNATE_ATTRIBUTE_INDEX, indexName); +// optionalAttributes += "index=\"" + indexName + "\""; + } + + if (TopiaGeneratorUtil.hasUniqueStereotype(attr)) { + // the trim method is called on optionalAttributes after this set to suppress unusual space if no index is set on this attribute + optionalAttributes += generateFromTagValue(HIBERNATE_ATTRIBUTE_UNIQUE, "true"); +// optionalAttributes += " unique=\"true\""; + } + optionalAttributes += generateFromTagValue(HIBERNATE_ATTRIBUTE_NOT_NULL, TopiaGeneratorUtil.getNotNullTagValue(attr)); +/*{<%=prefix%> <property name="<%=attrName%>" access="<%=accessField%>"}*/ + if ( ! attrIsEnumeration) { +/*{ type="<%=attrType%>"}*/ + } + optionalAttributes = optionalAttributes.trim(); + String[] columnNames = columnNamesMap.get(attrType); + + // contains all required attributes for a column node + Map<String,String> columnAttributes = new TreeMap<String, String>(); + if (StringUtils.isNotEmpty(attr.getDefaultValue())) { + //TC-20100129 with a default value we must use the column child tag + + String defaultValue = attr.getDefaultValue().trim(); + columnAttributes.put(HIBERNATE_ATTRIBUTE_DEFAULT, defaultValue); + } + String sqlType = TopiaGeneratorUtil.getSqlTypeTagValue(attr); + if (!StringUtils.isEmpty(sqlType)) { + + // an specific sql type was specified for the attribute, use it + columnAttributes.put(HIBERNATE_ATTRIBUTE_SQL_TYPE, sqlType); + } + + // add length attribute if required + String lengthTagValue = TopiaGeneratorUtil.getLengthTagValue(attr); + if (!StringUtils.isEmpty(lengthTagValue)) { + + optionalAttributes += generateFromTagValue(HIBERNATE_ATTRIBUTE_LENGTH, lengthTagValue); + } + + optionalAttributes = optionalAttributes.trim(); + if (StringUtils.isNotEmpty(optionalAttributes)) { + optionalAttributes = " " + optionalAttributes; + } + + // to know if specific column name mapping is given + boolean noSpecifiedColumn = columnNames == null || columnNames.length == 0; + + if (noSpecifiedColumn) { + + String attrColumn = TopiaGeneratorUtil.getDbName(attr); + + if (columnAttributes.isEmpty()) { + + // simple case with no column node to generate + +/*{ column="<%=attrColumn%>" node="<%=attrName%>"<%=optionalAttributes%>}*/ + if (attrIsEnumeration) { +/*{> +<%=prefix%> <type name="org.hibernate.type.EnumType"> +<%=prefix%> <param name="<%=org.hibernate.type.EnumType.ENUM%>"><%=attrType%></param>}*/ + + // if the user tuned the model to use name instead of + // ordinal to store the values, we must add a clause + boolean useEnumerationName = TopiaGeneratorUtil.hasUseEnumerationNameTagValue(attr, model); + if (useEnumerationName) { + String enumSQLType = String.valueOf(Types.VARCHAR); +/*{ +<%=prefix%> <!-- using name instead of ordinal to store enumeration value --> +<%=prefix%> <param name="<%=org.hibernate.type.EnumType.TYPE%>"><%=enumSQLType%></param>}*/ + } + +/*{ +<%=prefix%> </type> +<%=prefix%> </property> +}*/ + } else { +/*{/> +}*/ + } + } else { + + // there is some attributes to write for the column node + + columnAttributes.put(HIBERNATE_ATTRIBUTE_NAME, attrColumn); + + String columnAttributesAsString =""; + for (Map.Entry<String, String> entry : + columnAttributes.entrySet()) { + String name = entry.getKey(); + String value = entry.getValue(); + columnAttributesAsString += generateFromTagValue(name, value, null); + } + columnAttributesAsString = " " + columnAttributesAsString.trim(); +/*{<%=optionalAttributes%>> +<%=prefix%> <column<%=columnAttributesAsString%>/> +<%=prefix%> </property> +}*/ + } + } else { + + // there is a colum name mapping specified, must use it + //FIXME tchemit 2010-12-29 Really don't know how to apply columnAttributes for multi-columns... +/*{<%=optionalAttributes%>> +}*/ + for (String columnName : columnNames) { + columnName = attrName + "_" + columnName.trim(); +/*{<%=prefix%> <column name="<%=columnName%>"/> +}*/ + } +/*{<%=prefix%> </property> +}*/ + } + } + } + + protected void generateHibernateOneToOne(Writer output, + ObjectModelAttribute attr, + String prefix) throws IOException { +// boolean accessField = hasUnidirectionalRelationOnAbstractType(attr.getReverseAttribute(), model); +/// *{ <one-to-one name="<%=getName(attr)%>" class="<%=getType(attr)%>"<%=(TopiaGeneratorUtil.notEmpty(attr.getTagValue(TopiaGeneratorUtil.TAG_LENGTH))?(" length=\"" + attr.getTagValue(TopiaGeneratorUtil.TAG_LENGTH) + "\""):"")%><%=((attr.isComposite() || attr.hasAssociationClass())?" cascade=\"delete\"":"")%><%=((accessField)?" access=\"field\"":"")%> node="<%=getName(attr)%>/@topiaId" /> +//} */ + + // for hibernate many-to-one with unique="true" => one-to-one + // but if it is one-to-zero-or-one unique contraints is violated + // with null values + boolean unique = TopiaGeneratorUtil.isOneMultiplicity(attr); + generateHibernateManyToOne(output, attr, unique, prefix); + + } + + protected void generateHibernateOneToMany(Writer output, + ObjectModelAttribute attr, + String prefix) throws IOException { + boolean needsIndex = TopiaGeneratorUtil.hasIndexedStereotype(attr); + boolean isInverse = attr.getReverseAttribute().isNavigable(); + isInverse |= hasUnidirectionalRelationOnAbstractType(attr, model); + + String attrName = getName(attr); // ??? + String attrType = getType(attr); + String reverseAttrDBName = TopiaGeneratorUtil.getReverseDbName(attr); + String orderBy = generateFromTagValue(HIBERNATE_ATTRIBUTE_ORDER_BY, TopiaGeneratorUtil.getOrderByTagValue(attr)); + + String cascade = ""; + if (attr.isComposite() || attr.hasAssociationClass()) { + cascade += "cascade=\"all,delete-orphan\" "; + } + + String lazy = generateFromTagValue(HIBERNATE_ATTRIBUTE_LAZY, TopiaGeneratorUtil.getLazyTagValue(attr), "true"); + + String fetch = generateFromTagValue(HIBERNATE_ATTRIBUTE_FETCH, TopiaGeneratorUtil.getFetchTagValue(attr)); + + String collType = TopiaGeneratorUtil.getNMultiplicityHibernateType(attr); + String inverse = ""; + if (isInverse) { + inverse = "inverse=\"true\" "; + } + if (needsIndex) { +/*{<%=prefix%> <<%=collType%> name="<%=attrName%>" <%=inverse%><%=lazy%><%=cascade%>node="<%=attrName%>"> +<%=prefix%> <key column="<%=reverseAttrDBName%>"/> +<%=prefix%> <list-index column="<%=reverseAttrDBName%>_idx"/> +<%=prefix%> <one-to-many class="<%=attrType%>" node="topiaId"/> +<%=prefix%> </<%=collType%>> +}*/ + }else { +/*{<%=prefix%> <<%=collType%> name="<%=attrName%>" <%=inverse%><%=orderBy%><%=fetch%><%=lazy%><%=cascade%>node="<%=attrName%>"> +<%=prefix%> <key column="<%=reverseAttrDBName%>"/> +<%=prefix%> <one-to-many class="<%=attrType%>" node="topiaId"/> +<%=prefix%> </<%=collType%>> +}*/ + } + } + + private String generateFromTagValue(String attributeName, String tagValue) { + return generateFromTagValue(attributeName, tagValue, null); + } + + /** + * Generate hibernate xml attribute with a final space. + * @param attributeName + * @param tagValue + * @param defaultValue + * @return + */ + private String generateFromTagValue(String attributeName, String tagValue, String defaultValue) { + String result = ""; + if (StringUtils.isNotEmpty(tagValue)) { + result+= attributeName + "=\"" + tagValue+"\" "; + } else if (defaultValue != null) { + result+= attributeName + "=\"" + defaultValue +"\" "; + } +// if (attr.hasTagValue(tagName) || defaultValue != null) { +// result+= attributeName + "=\""; +// if (attr.hasTagValue(tagName)) { +// result += attr.getTagValue(tagName); +// } else { +// result += defaultValue; +// } +// result += "\" "; +// } + return result; + } + + protected void generateHibernateMany(Writer output, + ObjectModelAttribute attr, + String prefix) throws IOException { + boolean needsIndex = TopiaGeneratorUtil.hasIndexedStereotype(attr); + String attrName = getName(attr); + String attrType = getType(attr); + String collType = TopiaGeneratorUtil.getNMultiplicityHibernateType(attr); + String lazy = generateFromTagValue(HIBERNATE_ATTRIBUTE_LAZY, TopiaGeneratorUtil.getLazyTagValue(attr)); + String attrColumn = TopiaGeneratorUtil.getDbName(attr); + +/*{<%=prefix%> <<%=collType%> name="<%=attrName%>" <%=lazy%>node="<%=attrName%>"> +<%=prefix%> <key column="OWNER"/> +}*/ + if (needsIndex) { +/*{<%=prefix%> <list-index/> +}*/ + } +/*{<%=prefix%> <element type="<%=attrType%>" column="<%=attrColumn%>" node="id"/> +<%=prefix%> </<%=collType%>> +}*/ + } + + protected void generateHibernateManyToOne(Writer output, + ObjectModelAttribute attr, + String prefix) throws IOException { + generateHibernateManyToOne(output, attr, false, prefix); + } + + protected void generateHibernateManyToOne(Writer output, + ObjectModelAttribute attr, + boolean isUnique, + String prefix) throws IOException { + String attrName = getName(attr); + String attrType = getType(attr); + String attrColumn = TopiaGeneratorUtil.getDbName(attr); +/*{<%=prefix%> <many-to-one name="<%=attrName%>" class="<%=attrType%>" column="<%=attrColumn%>" }*/ + if (attr.isComposite() || attr.hasAssociationClass()) { +/*{cascade="delete" }*/ + } + // Pour le test suivant, on verifie d'abord que l'attribut a un reverse. + // S'il n'en a pas, cela signifie qu'il ne s'agit pas d'un entite + // (au sens stereotype entity), donc a donc pas besoin de faire un access=field. + if (attr.getReverseAttribute() != null && hasUnidirectionalRelationOnAbstractType(attr.getReverseAttribute(), model)) { +/*{access="field" }*/ + } + // vérifier si le tag lazy est defini par defaut dans le fichier de proprietes + String lazy = generateFromTagValue(HIBERNATE_ATTRIBUTE_LAZY, TopiaGeneratorUtil.getLazyTagValue(attr)); +/*{<%=lazy%>}*/ + String notNull = generateFromTagValue(HIBERNATE_ATTRIBUTE_NOT_NULL, TopiaGeneratorUtil.getNotNullTagValue(attr)); +/*{<%=notNull%>}*/ + if (isUnique) { +/*{unique="true" }*/ + } +/*{node="<%=attrName%>/@topiaId"}*/ + +/*{/> +}*/ + } + + protected void generateHibernateManyToMany(Writer output, + ObjectModelClass clazz, + ObjectModelAttribute attr, + String prefix) throws IOException { + // On ne met le inverse="true" uniquement pour un seul coté de la relation. + // Dans le cas contraire, les modifications dans la relation ne seront + // pas sauvegardées. Ceci n'est vrai que si les deux coté sont navigable + boolean isInverse = attr.isNavigable() && attr.getReverseAttribute().isNavigable(); + //isInverse |= !Util.isFirstAttribute(attr); + //isInverse = false; // 20070117 poussin: pour du many, jamais de inverse + + // Modification FD-2010-04-01 : + // Le tagvalue "inverse" permet de spécifier qui possède le + // inverse="true". Il est impératif de l'utiliser sur les deux + // extrémités pour ne pas avoir de surprise. + String inverseValue = TopiaGeneratorUtil.getInverseTagValue(attr); + if (StringUtils.isNotEmpty(inverseValue)) { + isInverse &= Boolean.parseBoolean(inverseValue); + // Si aucun tagvalue n'est défini, le choix est arbitraire : le + // premier attribut dans l'ordre alphabétique sera choisi pour porter le + // inverse="true" + } else { + isInverse &= TopiaGeneratorUtil.isFirstAttribute(attr); + } + + boolean needsIndex = TopiaGeneratorUtil.hasIndexedStereotype(attr); + String cascade = ""; + if (attr.isComposite() || attr.hasAssociationClass()) { + cascade = " cascade=\"delete,delete-orphan\""; + } + + String attrType = getType(attr); + String attrName = getName(attr); + String attrColumn = TopiaGeneratorUtil.getDbName(attr); + String lazy = generateFromTagValue(HIBERNATE_ATTRIBUTE_LAZY, TopiaGeneratorUtil.getLazyTagValue(attr), "true"); + String orderBy = generateFromTagValue(HIBERNATE_ATTRIBUTE_ORDER_BY, TopiaGeneratorUtil.getOrderByTagValue(attr)); + String collType = TopiaGeneratorUtil.getNMultiplicityHibernateType(attr); + String tableName = TopiaGeneratorUtil.getManyToManyTableName(attr); + String inverse = ""; + if (isInverse) { + inverse = "inverse=\"true\" "; + } + String reverseAttrDBName = TopiaGeneratorUtil.getReverseDbName(attr); + String optionalAttributes=""; + + //String schema = TopiaGeneratorUtil.getDbSchemaNameTagValue(attr.getClassifier(), model); + String schema = TopiaGeneratorUtil.getDbSchemaNameTagValue(clazz, model); + if (schema != null) { + optionalAttributes += generateFromTagValue(HIBERNATE_ATTRIBUTE_SCHEMA, schema); + } + if (!optionalAttributes.isEmpty()) { + optionalAttributes = " " + optionalAttributes.trim(); + } + +/*{<%=prefix%> <<%=collType%> name="<%=attrName%>" table="<%=tableName%>" <%=inverse%><%=lazy%><%=cascade%> node="<%=attrName%>"<%=optionalAttributes%>> +<%=prefix%> <key column="<%=reverseAttrDBName%>"/> +}*/ + if (needsIndex) { +/*{<%=prefix%> <list-index column="<%=reverseAttrDBName%>_idx"/> +}*/ + } +/*{<%=prefix%> <many-to-many class="<%=attrType%>" column="<%=attrColumn%>" <%=orderBy%>node="topiaId"/> +<%=prefix%> </<%=collType%>> +}*/ + } + +} //EntityHibernateMappingGenerator diff --git a/topia-persistence/src/main/java/org/nuiton/topia/generator/EntityTransformer.java b/topia-persistence/src/main/java/org/nuiton/topia/generator/EntityTransformer.java new file mode 100644 index 0000000..3adfe76 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/generator/EntityTransformer.java @@ -0,0 +1,1634 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.generator; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.eugene.EugeneTagValues; +import org.nuiton.eugene.java.ObjectModelTransformerToJava; +import org.nuiton.eugene.models.object.ObjectModelAssociationClass; +import org.nuiton.eugene.models.object.ObjectModelAttribute; +import org.nuiton.eugene.models.object.ObjectModelClass; +import org.nuiton.eugene.models.object.ObjectModelClassifier; +import org.nuiton.eugene.models.object.ObjectModelInterface; +import org.nuiton.eugene.models.object.ObjectModelJavaModifier; +import org.nuiton.eugene.models.object.ObjectModelModifier; +import org.nuiton.eugene.models.object.ObjectModelOperation; +import org.nuiton.eugene.models.object.ObjectModelParameter; +import org.nuiton.eugene.models.object.ObjectModelUMLModifier; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.EntityVisitor; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityAbstract; +import org.nuiton.topia.persistence.TopiaEntityContextable; +import org.nuiton.topia.persistence.util.TopiaEntityHelper; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +import static org.nuiton.topia.generator.TopiaGeneratorUtil.hasUnidirectionalRelationOnAbstractType; + +/*{generator option: parentheses = false}*/ +/*{generator option: writeString = +}*/ + +/** + * A template to generate all the {@link TopiaEntity} api for all classifier + * with a {@code entity} stereotype. + * + * For example, given a {@code House} entity, it will generates : + * <ul> + * <li>{@code House} : contract of entity</li> + * <li>{@code AbstractHouse} : default abstract implementation of entity</li> + * <li>{@code HouseImpl} : default impl of abstract entity</li> + * </ul> + * + * <b>Note: </b> The impl will ony be generated in these cases : + * <ul> + * <li>There is no abstract method</li> + * <li>There is no already defined such class in class-path</li> + * </ul> + * + * @author tchemit <chemit@codelutin.com> + * @since 2.3.4 + * @plexus.component role="org.nuiton.eugene.Template" role-hint="org.nuiton.topia.generator.EntityTransformer" + */ +public class EntityTransformer extends ObjectModelTransformerToJava { + + /** Logger */ + private static final Log log = LogFactory.getLog(EntityTransformer.class); + + protected ObjectModelInterface outputInterface; + + protected ObjectModelClass outputAbstract; + + protected ObjectModelClass outputImpl; + + private boolean associationClass; + + protected boolean generateInterface; + + protected boolean generateAbstract; + + protected boolean generateImpl; + + protected boolean generateBooleanGetMethods; + + protected void clean() { + outputInterface = null; + outputAbstract = null; + outputImpl = null; + } + + @Override + public void transformFromClass(ObjectModelClass input) { + + if (!TopiaGeneratorUtil.isEntity(input)) { + + // not an entity, skip class. + return; + } + + if (log.isDebugEnabled()) { + log.debug("for entity : " + input.getQualifiedName()); + log.debug("Will use classLoader " + getClassLoader()); + } + + // fix once for all the constant prefix to use + String prefix = getConstantPrefix(input); + if (StringUtils.isEmpty(prefix)) { + + // no specific prefix, so no prefix + if (log.isWarnEnabled()) { + log.warn("[" + input.getName() + "] Will generate constants with NO prefix, not a good idea... \n" + + "Use '" + EugeneTagValues.TAG_CONSTANT_PREFIX + + "' tagvalue in your xmi properties. For example " + + "for all the model : model.tagvalue." + EugeneTagValues.TAG_CONSTANT_PREFIX + "=PROPERTY_"); + } + } + setConstantPrefix(prefix); + + generateInterface = isGenerateInterface(input); + generateAbstract = isGenerateAbstract(input); + generateImpl = isGenerateImpl(input); + + generateBooleanGetMethods = getEugeneTagValues().isGenerateBooleanGetMethods(input, null, model); + + if (generateInterface) { + + // Create Entity Interface and its header + createEntityInterface(input); + } + + if (generateAbstract) { + + // Create Entity Abstract class and its header + createEntityAbstractClass(input); + } + + // Generate i18n block + String i18nPrefix = getEugeneTagValues().getI18nPrefixTagValue(input, null, model); + if (!StringUtils.isEmpty(i18nPrefix)) { + generateI18nBlock(input, outputAbstract, i18nPrefix); + } + + // Create accept operation, will be updated during property generation + createAcceptOperation(); + createAcceptInternalOperation(input); + + // Add constant, attribute and operations for each property + generateProperties(input.getAttributes()); + + // Case of association class : properties from participants/extremities + // of the association class. + if (input instanceof ObjectModelAssociationClass) { + ObjectModelAssociationClass association = + (ObjectModelAssociationClass)input; + associationClass = true; + generateProperties(association.getParticipantsAttributes()); + associationClass = false; + } + + closeAcceptInternalOperation(); + + // Add extra constants (from uml dependency) + generateExtraConstants(input); + + // Add extra operations (defined on the entity) + generateExtraOperations(input); + + // Implement aggregate and composite operations + generateAggregateOperation(input); + generateCompositeOperation(input); + + // Implement toString operation + if (TopiaGeneratorUtil.generateToString(input, model)) { + generateToStringOperation(input); + } + + // Generate serialVersionUID on abstract class + generateSerialVersionUID(input, outputAbstract); + + // Generate Entity Implementation class + if (generateImpl) { + generateImpl(input); + generateSerialVersionUID(input, outputImpl); + } + + + + // Clean data output after transformation + clean(); + } + + protected void generateSerialVersionUID(ObjectModelClass input, + ObjectModelClass ouput) { + + // serialVersionUID + String svUID = TopiaGeneratorUtil.findTagValue(TopiaGeneratorUtil.SERIAL_VERSION_UID, + input, + model + ); + if (svUID == null) { + + // use a default one + svUID = TopiaGeneratorUtil.generateSerialVersionUID(ouput) + "L"; + } + addConstant(ouput, TopiaGeneratorUtil.SERIAL_VERSION_UID, long.class, svUID, + ObjectModelJavaModifier.PRIVATE); + } + + protected void createEntityInterface(ObjectModelClass input) { + + outputInterface = createInterface(input.getName(), + input.getPackageName()); + + // Documentation + if (TopiaGeneratorUtil.hasDocumentation(input)) { + setDocumentation(outputInterface, input.getDocumentation()); + } + + if (log.isTraceEnabled()) { + log.trace("Will add interfaces on " + + outputInterface.getQualifiedName()); + } + + List<String> interfaceAlreadyDone = new LinkedList<String> (); + // Extends + for (ObjectModelClassifier parent : input.getInterfaces()) { + addInterface(interfaceAlreadyDone, outputInterface, parent); + } + + // Extends from inheritance + boolean needTopiaEntity = true; + for (ObjectModelClassifier parent : input.getSuperclasses()) { + if (TopiaGeneratorUtil.isEntity(parent)) { + addInterface(interfaceAlreadyDone, outputInterface, parent); + needTopiaEntity = false; + } + } + + // Extends TopiaEntity (only if hasn't parent entity) + if (needTopiaEntity) { + + Class<?> interfaze = TopiaEntity.class; + + if (TopiaGeneratorUtil.isContextable(input)) { + interfaze = TopiaEntityContextable.class; + } + + addInterface(interfaceAlreadyDone, + outputInterface, + interfaze); + } + } + + protected void createEntityAbstractClass(ObjectModelClass input) { + + outputAbstract = createAbstractClass(input.getName() + "Abstract", + input.getPackageName()); + + // Documentation + StringBuilder doc = new StringBuilder(); + doc.append("Implantation POJO pour l'entité {@link "); + doc.append(StringUtils.capitalize(outputInterface.getName())); + doc.append("}\n"); + + String dbName = TopiaGeneratorUtil.getDbName(input); + if (dbName != null) { + doc.append("<p>Nom de l'entité en BD : "); + doc.append(dbName); + doc.append(".</p>"); + } + + setDocumentation(outputAbstract, doc.toString()); + + // Implements + addInterface(outputAbstract, outputInterface.getName()); + + // Extends + for (ObjectModelClass parent : input.getSuperclasses()) { + //tchemit-2011-09-12 What ever abstract or not, we alwyas use an Impl, moreover use the util method instead + String extendClass = TopiaGeneratorUtil.getDOType(parent, model); +// String extendClass = parent.getQualifiedName(); +// //Si une des classes parentes définies des méthodes abstraites, son +// // impl ne sera pas créé +// boolean abstractParent = TopiaGeneratorUtil.shouldBeAbstract(parent); +// if (TopiaGeneratorUtil.isEntity(parent)) { +// if (abstractParent) { +// extendClass += "Abstract"; +// } else { +// extendClass += "Impl"; +// } +// } + setSuperClass(outputAbstract, extendClass); + } + + // Extends TopiaEntityAbstract (only if hasn't parent entity) + if (outputAbstract.getSuperclasses().isEmpty()) { + setSuperClass(outputAbstract, TopiaEntityAbstract.class); + } + + addContextableMethods(input, outputAbstract); + } + + /** + * Ajout les methodes necessaire à l'interface {@link TopiaEntityContextable} + * si le tagValue {@link TopiaTagValues#TAG_CONTEXTABLE} est renseigné. + * @param input + * @param outputAbstract + */ + protected void addContextableMethods(ObjectModelClass input, + ObjectModelClass outputAbstract) { + + if (TopiaGeneratorUtil.isContextable(input)) { + + addImport(outputAbstract, TopiaContextImplementor.class); + + ObjectModelOperation op = addOperation(outputAbstract, "update", "void", + ObjectModelJavaModifier.PUBLIC); + addException(op, TopiaException.class); +// setDocumentation(op,"@since 2.5.3"); + addAnnotation(outputAbstract, op, Override.class.getSimpleName()); + setOperationBody(op, "" +/*{ + ((TopiaContextImplementor)getTopiaContext()).getDAO(<%=input.getName()%>.class).update(this); +}*/ + ); + + op = addOperation(outputAbstract, "delete", "void", ObjectModelJavaModifier.PUBLIC); + addException(op, TopiaException.class); +// setDocumentation(op,"@since 2.5.3"); + addAnnotation(outputAbstract, op, Override.class.getSimpleName()); + setOperationBody(op, "" +/*{ + ((TopiaContextImplementor)getTopiaContext()).getDAO(<%=input.getName()%>.class).delete(this); +}*/ + ); + } + } + + protected boolean isGenerateInterface(ObjectModelClass input) { + + boolean alreadyInClassPath = !isInClassPath(input); + return alreadyInClassPath; + } + + protected boolean isGenerateAbstract(ObjectModelClass input) { + + String fqn = input.getQualifiedName() + " Abstract"; + boolean alreadyInClassPath = !isInClassPath(fqn); + return alreadyInClassPath; + } + + protected boolean isGenerateImpl(ObjectModelClass input) { + + Collection<ObjectModelOperation> operations = input.getOperations(); + String fqn = input.getQualifiedName() + "Impl"; + + boolean alreadyInClassPath = isInClassPath(fqn); + if (alreadyInClassPath) { + + return false; + } + + // On ne génère pas le impl si l'entité a des opérations + if (!operations.isEmpty()) { + + log.info("Will not generate [" + fqn + "], there is some operations to manually implement"); + return false; + } + + //De même, on ne génère pas le impl si il y a des opérations venant des + // superclasses non implémentées + for (ObjectModelOperation otherOp : input.getAllOtherOperations(false)) { + if (otherOp.isAbstract()) { + log.info("Will not generate [" + fqn + "], there is an abstract operation [" + otherOp.getName() + "] in allOtherOperations."); + return false; + } + } + + return true; + } + + protected void generateImpl(ObjectModelClass input) { + + String implName = input.getName() + "Impl"; + String packageName = input.getPackageName(); + if (isVerbose()) { + log.info("Will generate [" + implName + "]"); + } + + if (isAbstract(input)) { + outputImpl = createAbstractClass(implName, packageName); + } else { + outputImpl = createClass(implName, packageName); + } + + setDocumentation(outputImpl, "Implantation des operations pour l'entité " + + input.getName() + "."); + setSuperClass(outputImpl, input.getQualifiedName() + "Abstract"); + } + + /** + * Generate extra constants if {@code input} has dependencies on + * enum used as constant injector. + * + * @param input Entity class to treate + */ + protected void generateExtraConstants(ObjectModelClass input) { + Set<String> constants = addConstantsFromDependency(input, outputInterface); + + if (log.isDebugEnabled()) { + log.debug("Add constants from dependency : " + constants); + } + } + + protected void generateExtraOperations(ObjectModelClass input) { + for (ObjectModelOperation operation : input.getOperations()) { + + String opName = operation.getName(); + String opType = operation.getReturnType(); + ObjectModelModifier visibility = + ObjectModelJavaModifier.fromVisibility(operation.getVisibility()); + + if (log.isDebugEnabled()) { + log.debug("Extra operation for : " + input.getQualifiedName() + + " - method : " + opName + + " - returnType : " + opType + + " - visibility : " + visibility); + } + + // Deprecated from 2.3.4 + // Pas de génération des signatures de méthodes pour celles à intégrer au DAO de l'entité + if (TopiaGeneratorUtil.hasDaoStereotype(operation)) { + return; + + // Generate entity methods which have not a public visibility. + // Only in abstract entity class as abstract operation. + } else if (!visibility.equals(ObjectModelJavaModifier.PUBLIC)) { + addOperation(outputAbstract, opName, opType, visibility, + ObjectModelJavaModifier.ABSTRACT); + + // Other operations, only in entity interface, implementations + // need to be done in implementation class created by developper + } else { + cloneOperationSignature(operation, outputInterface, true); + } + } + } + + /** + * Generate properties from {@code attributes}. Generate + * constant, attribute and operations for each property. + * + * @param attributes Input attributes + */ + protected void generateProperties(Collection<ObjectModelAttribute> attributes) { + for (ObjectModelAttribute attribute : attributes) { + + if (!associationClass) { + + // FIXME-fdesbois-2010-06-25 : Strange behavior to keep those links, will break hibernate, may be a problem in mapping + if (!attribute.isNavigable() && attribute.hasAssociationClass()) { + generatePropertyConstant(attribute); + + generatePropertyAttribute(attribute); + + updateAcceptOperation(attribute); + } + + if (!attribute.isNavigable() && + !TopiaGeneratorUtil.hasUnidirectionalRelationOnAbstractType( + attribute.getReverseAttribute(), model)) { + continue; + } + } + + // constant + generatePropertyConstant(attribute); + + // attribute + generatePropertyAttribute(attribute); + + // operations + generatePropertyOperations(attribute); + + // update accept body + updateAcceptOperation(attribute); + } + } + + // ------------------------------------------------------------------------- + // Generate for property + // ------------------------------------------------------------------------- + + /** + * Generate constant in interface for {@code attribute}. + * + * @param attribute Input attribute to treate + * @see #getPropertyName(ObjectModelAttribute) + */ + protected void generatePropertyConstant(ObjectModelAttribute attribute) { + String attrName = getPropertyName(attribute); + + if (log.isDebugEnabled()) { + log.debug("Generate constant for property : " + attrName); + } + + addAttribute(outputInterface, getConstantName(attrName), String.class, + "\"" + attrName + "\""); + } + + protected void generatePropertyAttribute(ObjectModelAttribute attribute) { + + String attrName = getPropertyName(attribute); + String attrType = getPropertyType(attribute); + String collectionType = getCollectionType(attribute); + + if (collectionType != null) { + attrType = collectionType + "<" + attrType + ">"; + } + + //String attrVisibility = attr.getVisibility(); + + // Declaration + ObjectModelAttribute property = + addAttribute(outputAbstract, attrName, attrType, null, + //ObjectModelJavaModifier.toValue(attrVisibility), + ObjectModelJavaModifier.PROTECTED + ); + + // Documentation + StringBuilder buffer = new StringBuilder(); + if (TopiaGeneratorUtil.hasDocumentation(attribute)) { + String attrDocumentation = attribute.getDocumentation(); + buffer.append(attrDocumentation).append('\n'); + } + + String dbName = TopiaGeneratorUtil.getDbName(attribute); + if (!StringUtils.isEmpty(dbName)) { + buffer.append("Nom de l'attribut en BD : ").append(dbName).append('\n'); + } + setDocumentation(property, buffer.toString()); + + // Annotation + String annotation = TopiaGeneratorUtil.getAnnotationTagValue(attribute); + if (!StringUtils.isEmpty(annotation)) { + //FIXME Make annotation works... + //TODO tchemit 20100513 Test it still works + addAnnotation(outputAbstract, property, annotation); + } + } + + /** + * Generation operations for {@code attributes}. + * One method exists for each operation to generate. Methods starting + * with 'addSingle' is for maxMultiplicity attribute = 1 and for collection + * case, methods start with 'addMultiple'. Other case are take care in each + * method (association class, reverse, entity reference, ...). + * + * @param attribute Input attribute to treate + * @see #addSingleGetOperation(ObjectModelAttribute, String, String) + * @see #addSingleSetOperation(ObjectModelAttribute) + * @see #addMultipleAddOperation(ObjectModelAttribute, String) + * @see #addMultipleAddAllOperation(ObjectModelAttribute, String) + * @see #addMultipleSetOperation(ObjectModelAttribute, String, String) + * @see #addMultipleRemoveOperation(ObjectModelAttribute) + * @see #addMultipleClearOperation(ObjectModelAttribute, String, String) + * @see #addMultipleGetOperation(ObjectModelAttribute, String) + * @see #addMultipleGetTopiaIdOperation(ObjectModelAttribute) + * @see #addMultipleGetOperationFromEntity(ObjectModelAttribute) + * @see #addMultipleSizeOperation(ObjectModelAttribute) + * @see #addMultipleIsEmptyOperation(ObjectModelAttribute) + */ + protected void generatePropertyOperations(ObjectModelAttribute attribute) { + + if (attribute.getMaxMultiplicity() == 1 || associationClass) { + + // setXXX + addSingleSetOperation(attribute); + + boolean booleanProperty = + TopiaGeneratorUtil.isBooleanPrimitive(attribute); + + String attrType = getPropertyType(attribute); + + if (booleanProperty) { + + // isXXX + addSingleGetOperation( + attribute, + attrType, + TopiaGeneratorUtil.OPERATION_GETTER_BOOLEAN_PREFIX); + } + + if (!booleanProperty || generateBooleanGetMethods) { + + // getXXX + addSingleGetOperation( + attribute, + attrType, + TopiaGeneratorUtil.OPERATION_GETTER_DEFAULT_PREFIX); + } + + } else { + + // List, Set or Collection ? + String collectionInterface = + TopiaGeneratorUtil.getNMultiplicityInterfaceType(attribute); + String collectionImpl = + TopiaGeneratorUtil.getNMultiplicityObjectType(attribute); + + addImport(outputInterface, collectionInterface); + addImport(outputAbstract, collectionInterface); + addImport(outputAbstract, collectionImpl); + + collectionInterface = + TopiaGeneratorUtil.getSimpleName(collectionInterface); + collectionImpl = + TopiaGeneratorUtil.getSimpleName(collectionImpl); + + // addXXX + addMultipleAddOperation(attribute, collectionImpl); + + // addAllXXX + addMultipleAddAllOperation(attribute, collectionInterface); + + // setXXX + addMultipleSetOperation(attribute, collectionInterface, collectionImpl); + + // removeXXX + addMultipleRemoveOperation(attribute); + + // clearXXX + addMultipleClearOperation(attribute, collectionInterface, collectionImpl); + + // getXXX + addMultipleGetOperation(attribute, collectionInterface); + + if (TopiaGeneratorUtil.isEntity(attribute, model)) { + + // getXXXByTopiaId + addMultipleGetTopiaIdOperation(attribute); + } + + if (attribute.hasAssociationClass()) { + // getXXX with entity parameter + addMultipleGetOperationFromEntity(attribute); + } + + // sizeXXX + addMultipleSizeOperation(attribute); + + // isXXXEmpty + addMultipleIsEmptyOperation(attribute); + } + } + + protected void addSingleSetOperation(ObjectModelAttribute attribute) { + + String attrName = getPropertyName(attribute); + String attrType = getPropertyType(attribute); + + if (log.isDebugEnabled()) { + log.debug("Generate single 'set' operation for property : " + attrName + + " [" + attrType + "]"); + } + + // Interface operation + ObjectModelOperation interfaceOperation = + createPropertySetterSignature(outputInterface, attrType, attrName, + ""); + + // Implementation + ObjectModelOperation implOperation = createImplOperation(interfaceOperation); + + attrType = TopiaGeneratorUtil.getSimpleName(attrType); + + String constantName = getConstantName(attrName); + + setOperationBody(implOperation, "" +/*{ + <%=attrType%> oldValue = this.<%=attrName%>; + fireOnPreWrite(<%=constantName%>, oldValue, <%=attrName%>); + this.<%=attrName%> = <%=attrName%>; + fireOnPostWrite(<%=constantName%>, oldValue, <%=attrName%>); + }*/ + ); + } + + /** + * Add getter for simple property (neither association nor multiple). + * Will add two different operations for boolean case ('is' method and + * 'get' method). This method add the operation in both {@code + * outputAbstract} and {@code outputInterface}. + * + * @param attribute ObjectModelAttribute for getter operation + * @param attrType type of the attribute + * @param operationPrefix Operation prefix : 'get' by default, if prefix + * is null + */ + protected void addSingleGetOperation(ObjectModelAttribute attribute, + String attrType, + String operationPrefix) { + + String attrName = getPropertyName(attribute); + + if (log.isDebugEnabled()) { + log.debug("Generate single '" + operationPrefix + "' operation for property : " + + attrName + " [" + attrType + "]"); + } + + String constantName = getConstantName(attrName); + + // Interface operation + ObjectModelOperation interfaceOperation = + addOperation(outputInterface, getJavaBeanMethodName(operationPrefix, attrName), + attrType, ObjectModelJavaModifier.PACKAGE); + + boolean generateReadListeners = !TopiaGeneratorUtil.isDoNotGenerateReadListeners(attribute, model); + + // Implementation + ObjectModelOperation implOperation = + createImplOperation(interfaceOperation); + + attrType = TopiaGeneratorUtil.getSimpleName(attrType); + + StringBuilder body = new StringBuilder(); + if (generateReadListeners) { + body.append("" +/*{ + fireOnPreRead(<%=constantName%>, <%=attrName%>); +}*/); + } + + body.append("" +/*{ + <%=attrType%> result = this.<%=attrName%>; +}*/); + + if (generateReadListeners) { + body.append("" +/*{ + fireOnPostRead(<%=constantName%>, <%=attrName%>); +}*/); + } + + body.append("" +/*{ + return result; + }*/); + setOperationBody(implOperation, body.toString()); + } + + protected void addMultipleAddOperation(ObjectModelAttribute attribute, + String collectionImpl) { + + String attrName = getPropertyName(attribute); + String attrType = getPropertyType(attribute); + ObjectModelAttribute reverse = attribute.getReverseAttribute(); + + if (log.isDebugEnabled()) { + log.debug("Generate multiple 'add' operation for property : " + attrName + + " [" + attrType + "]"); + } + + String constantName = getConstantName(attrName); + + // Interface operation + ObjectModelOperation interfaceOperation = + addOperation(outputInterface, getJavaBeanMethodName("add", attrName), + void.class, ObjectModelJavaModifier.PACKAGE); + ObjectModelParameter param = + addParameter(interfaceOperation, attrType, attrName); + + // Implementation + ObjectModelOperation implOperation = createImplOperation(interfaceOperation); + + attrType = TopiaGeneratorUtil.getSimpleName(attrType); + + StringBuilder body = new StringBuilder(); + + body.append("" +/*{ + fireOnPreWrite(<%=constantName%>, null, <%=attrName%>); + if (this.<%=attrName%> == null) { + this.<%=attrName%> = new <%=collectionImpl%><<%=attrType%>>(); + } +}*/ + ); + + if (reverse != null && (reverse.isNavigable() || + hasUnidirectionalRelationOnAbstractType(attribute, model))) { + String getterName = getJavaBeanMethodName("get", reverse.getName()); + String setterName = getJavaBeanMethodName("set", reverse.getName()); + + String reverseAttrType = TopiaGeneratorUtil.getSimpleName(reverse.getType()); + + if (!TopiaGeneratorUtil.isNMultiplicity(reverse)) { + body.append("" +/*{ + <%=attrName%>.<%=setterName%>(this); +}*/ + ); + // Don't manage reverse attribute add if attribute has associationClass + } else if (!attribute.hasAssociationClass()) { + body.append("" +/*{ + if (<%=attrName%>.<%=getterName%>() == null) { + <%=attrName%>.<%=setterName%>(new <%=collectionImpl%><<%=reverseAttrType%>>()); + } + <%=attrName%>.<%=getterName%>().add(this); +}*/ + ); + } + } + body.append("" +/*{ + this.<%=attrName%>.add(<%=attrName%>); + fireOnPostWrite(<%=constantName%>, this.<%=attrName%>.size(), null, <%=attrName%>); + }*/ + ); + setOperationBody(implOperation, body.toString()); + } + + protected void addMultipleAddAllOperation(ObjectModelAttribute attribute, + String collectionInterface) { + + String attrName = getPropertyName(attribute); + String attrType = getPropertyType(attribute); + + if (log.isDebugEnabled()) { + log.debug("Generate multiple 'addAll' operation for property : " + attrName + + " [" + attrType + "]"); + } + + // Interface operation + ObjectModelOperation interfaceOperation = + addOperation(outputInterface, getJavaBeanMethodName("addAll", attrName), + void.class, ObjectModelJavaModifier.PACKAGE); + ObjectModelParameter param = + addParameter(interfaceOperation, collectionInterface + "<" + attrType + ">", attrName); + + // Implementation + ObjectModelOperation implOperation = + createImplOperation(interfaceOperation); + + attrType = TopiaGeneratorUtil.getSimpleName(attrType); + String addMethodName = getJavaBeanMethodName("add", attrName); + setOperationBody(implOperation, "" +/*{ + if (<%=attrName%> == null) { + return; + } + for (<%=attrType%> item : <%=attrName%>) { + <%=addMethodName%>(item); + } + }*/ + ); + } + + protected void addMultipleSetOperation(ObjectModelAttribute attribute, + String collectionInterface, + String collectionImpl) { + + String attrName = getPropertyName(attribute); + String referenceType = getPropertyType(attribute); + String attrType = collectionInterface + "<" + referenceType + ">"; + String constantName = getConstantName(attrName); + + if (log.isDebugEnabled()) { + log.debug("Generate multiple 'set' operation for property : " + attrName + + " [" + attrType + "]"); + } + + // Interface operation + ObjectModelOperation interfaceOperation = + createPropertySetterSignature(outputInterface, attrType, attrName, + ""); + + ObjectModelOperation implOperation = createImplOperation(interfaceOperation); + + attrType = TopiaGeneratorUtil.getSimpleName(attrType); + referenceType = TopiaGeneratorUtil.getSimpleName(referenceType); + + // Force fire for collection + setOperationBody(implOperation, "" +/*{ + // Copy elements to keep data for fire with new reference + <%=attrType%> oldValue = this.<%=attrName%> != null ? new <%=collectionImpl%><<%=referenceType%>>(this.<%=attrName%>) : null; + fireOnPreWrite(<%=constantName%>, oldValue, <%=attrName%>); + this.<%=attrName%> = <%=attrName%>; + fireOnPostWrite(<%=constantName%>, oldValue, <%=attrName%>); + }*/ + ); + } + + protected void addMultipleRemoveOperation(ObjectModelAttribute attribute) { + + String attrName = getPropertyName(attribute); + String attrType = getPropertyType(attribute); + ObjectModelAttribute reverse = attribute.getReverseAttribute(); + String constantName = getConstantName(attrName); + + if (log.isDebugEnabled()) { + log.debug("Generate 'remove' operation for property : " + attrName + + " [" + attrType + "]"); + } + + // Interface operation + ObjectModelOperation interfaceOperation = + addOperation(outputInterface, getJavaBeanMethodName("remove" , attrName), + void.class, ObjectModelJavaModifier.PACKAGE); + ObjectModelParameter param = + addParameter(interfaceOperation, attrType, attrName); + + // Implementation + ObjectModelOperation implOperation = createImplOperation(interfaceOperation); + + attrType = TopiaGeneratorUtil.getSimpleName(attrType); + + StringBuilder body = new StringBuilder(); + + body.append("" +/*{ + fireOnPreWrite(<%=constantName%>, <%=attrName%>, null); + if (this.<%=attrName%> == null || !this.<%=attrName%>.remove(<%=attrName%>)) { + throw new IllegalArgumentException("List does not contain given element"); + } +}*/ + ); + + if (reverse != null && (reverse.isNavigable() || + hasUnidirectionalRelationOnAbstractType(attribute, model))) { + String getterName = getJavaBeanMethodName("get", reverse.getName()); + String setterName = getJavaBeanMethodName("set", reverse.getName()); + if (!TopiaGeneratorUtil.isNMultiplicity(reverse)) { + body.append("" +/*{ + <%=attrName%>.<%=setterName%>(null); +}*/ + ); + // Don't manage reverse attribute remove if attribute has associationClass + } else if (!attribute.hasAssociationClass()) { + body.append("" +/*{ + <%=attrName%>.<%=getterName%>().remove(this); +}*/ + ); + } + } + body.append("" +/*{ + fireOnPostWrite(<%=constantName%>, this.<%=attrName%>.size() + 1, <%=attrName%>, null); + }*/ + ); + setOperationBody(implOperation, body.toString()); + } + + protected void addMultipleClearOperation(ObjectModelAttribute attribute, + String collectionInterface, + String collectionImpl) { + + String attrName = getPropertyName(attribute); + String attrType = getPropertyType(attribute); + ObjectModelAttribute reverse = attribute.getReverseAttribute(); + String constantName = getConstantName(attrName); + + if (log.isDebugEnabled()) { + log.debug("Generate multiple 'clear' operation for property : " + attrName + + " [" + attrType + "]"); + } + + // Interface operation + ObjectModelOperation interfaceOperation = + addOperation(outputInterface, getJavaBeanMethodName("clear" , attrName), + void.class, ObjectModelJavaModifier.PACKAGE); + + // Implementation + ObjectModelOperation implOperation = createImplOperation(interfaceOperation); + + attrType = TopiaGeneratorUtil.getSimpleName(attrType); + + StringBuilder body = new StringBuilder("" +/*{ + if (this.<%=attrName%> == null) { + return; + } +}*/ + ); + + if (reverse != null && (reverse.isNavigable() || + hasUnidirectionalRelationOnAbstractType(attribute, model))) { + String getterName = getJavaBeanMethodName("get", reverse.getName()); + String setterName = getJavaBeanMethodName("set", reverse.getName()); + body.append("" +/*{ for (<%=attrType%> item : this.<%=attrName%>) { +}*/ + ); + if (!TopiaGeneratorUtil.isNMultiplicity(reverse)) { + body.append("" +/*{ item.<%=setterName%>(null); +}*/ + ); + // Don't manage reverse attribute remove if attribute has associationClass + } else if (!attribute.hasAssociationClass()) { + body.append("" +/*{ item.<%=getterName%>().remove(this); +}*/ + ); + } + body.append("" +/*{ } +}*/ + ); + } + body.append("" +/*{ <%=collectionInterface%><<%=attrType%>> oldValue = new <%=collectionImpl%><<%=attrType%>>(this.<%=attrName%>); + fireOnPreWrite(<%=constantName%>, oldValue, this.<%=attrName%>); + this.<%=attrName%>.clear(); + fireOnPostWrite(<%=constantName%>, oldValue, this.<%=attrName%>); + }*/ + ); + setOperationBody(implOperation, body.toString()); + } + + protected void addMultipleGetOperation(ObjectModelAttribute attribute, + String collectionInterface) { + + String attrName = getPropertyName(attribute); + String attrType = collectionInterface + "<" + getPropertyType(attribute) + ">"; + + if (log.isDebugEnabled()) { + log.debug("Generate multiple 'get' operation for property : " + attrName + + " [" + attrType + "]"); + } + + // Interface operation + ObjectModelOperation interfaceOperation = + addOperation(outputInterface, getJavaBeanMethodName("get" , attrName), + attrType, ObjectModelJavaModifier.PACKAGE); + + // Implementation + ObjectModelOperation implOperation = createImplOperation(interfaceOperation); + + setOperationBody(implOperation, "" +/*{ + return <%=attrName%>; + }*/ + ); + } + + protected void addMultipleGetTopiaIdOperation(ObjectModelAttribute attribute) { + + String attrName = getPropertyName(attribute); + String attrType = getPropertyType(attribute); + + if (log.isDebugEnabled()) { + log.debug("Generate multiple 'getByTopiaId' operation for property : " + attrName + + " [" + attrType + "]"); + } + + // Interface operation + ObjectModelOperation interfaceOperation = + addOperation(outputInterface, getJavaBeanMethodName("get", attrName) + "ByTopiaId", + attrType, ObjectModelJavaModifier.PACKAGE); + ObjectModelParameter param = + addParameter(interfaceOperation, String.class, "topiaId"); + + // Implementation + ObjectModelOperation implOperation = + createImplOperation(interfaceOperation); + + addImport(outputAbstract, TopiaEntityHelper.class); + + setOperationBody(implOperation, "" +/*{ + return TopiaEntityHelper.getEntityByTopiaId(<%=attrName%>, topiaId); + }*/ + ); + } + + protected void addMultipleGetOperationFromEntity(ObjectModelAttribute attribute) { + + // reference to the real attribute name + String referenceName = attribute.getName(); + String referenceType = attribute.getType(); + + String referenceGetterName = getJavaBeanMethodName("get", referenceName); + // association attribute name + String attrName = getPropertyName(attribute); + String attrType = getPropertyType(attribute); + + if (log.isDebugEnabled()) { + log.debug("Generate multiple 'getFromEntity' operation for property : " + attrName + + " [" + attrType + "]"); + } + + // Interface operation + ObjectModelOperation interfaceOperation = + addOperation(outputInterface, + getJavaBeanMethodName("get", attrName), attrType); + + addParameter(interfaceOperation, referenceType, referenceName); + + // Implementation + ObjectModelOperation implOperation = createImplOperation(interfaceOperation); + + attrType = TopiaGeneratorUtil.getSimpleName(attrType); + + setOperationBody(implOperation, "" +/*{ + if (<%=referenceName%> == null || this.<%=attrName%> == null) { + return null; + } + for (<%=attrType%> item : this.<%=attrName%>) { + if (<%=referenceName%>.equals(item.<%=referenceGetterName%>())) { + return item; + } + } + return null; + }*/ + ); + } + + protected void addMultipleSizeOperation(ObjectModelAttribute attribute) { + + String attrName = getPropertyName(attribute); + + if (log.isDebugEnabled()) { + log.debug("Generate multiple 'size' operation for property : " + attrName); + } + + // Interface operation + ObjectModelOperation interfaceOperation = + addOperation(outputInterface, getJavaBeanMethodName("size", attrName), + int.class, ObjectModelJavaModifier.PACKAGE); + + // Implementation + ObjectModelOperation implOperation = createImplOperation(interfaceOperation); + + setOperationBody(implOperation, "" +/*{ + if (<%=attrName%> == null) { + return 0; + } + return <%=attrName%>.size(); + }*/ + ); + } + + protected void addMultipleIsEmptyOperation(ObjectModelAttribute attribute) { + + String attrName = getPropertyName(attribute); + + if (log.isDebugEnabled()) { + log.debug("Generate multiple 'isEmpty' operation for property : " + attrName); + } + + String sizeMethodName = getJavaBeanMethodName("size", attrName); + // Interface operation + ObjectModelOperation interfaceOperation = + addOperation(outputInterface, getJavaBeanMethodName("is", attrName)+ "Empty", + boolean.class, ObjectModelJavaModifier.PACKAGE); + + // Implementation + ObjectModelOperation implOperation = createImplOperation(interfaceOperation); + + setOperationBody(implOperation, "" +/*{ + int size = <%=sizeMethodName%>(); + return size == 0; + }*/ + ); + } + + // ------------------------------------------------------------------------- + // Generate util operations + // ------------------------------------------------------------------------- + + private ObjectModelOperation internalAcceptOperation; + + private StringBuilder internalAcceptOperationBody; + + protected void createAcceptOperation() { + + ObjectModelOperation acceptOperation = addOperation(outputAbstract, "accept", void.class); + addAnnotation(outputAbstract, acceptOperation , Override.class); + addParameter(acceptOperation, EntityVisitor.class, "visitor"); + + addException(acceptOperation, TopiaException.class); + + setOperationBody(acceptOperation,"" +/*{ + visitor.start(this); + accept0(visitor); + visitor.end(this); + }*/ + ); + } + + protected void createAcceptInternalOperation(ObjectModelClass input) { + + boolean withSuperEntity = false; + for (ObjectModelClassifier parent : input.getSuperclasses()) { + if (TopiaGeneratorUtil.isEntity(parent)) { + withSuperEntity= true; + break; + } + } + + internalAcceptOperation = addOperation(outputAbstract, "accept0", void.class, ObjectModelJavaModifier.PROTECTED); + addParameter(internalAcceptOperation, EntityVisitor.class, "visitor"); + addException(internalAcceptOperation, TopiaException.class); + + if (withSuperEntity) { + addAnnotation(outputAbstract, internalAcceptOperation, Override.class); + internalAcceptOperationBody = new StringBuilder("" +/*{ + super.accept0(visitor); +}*/ + ); + } else { + internalAcceptOperationBody= new StringBuilder("" +/*{ +}*/ + ); + } + } + + protected void updateAcceptOperation(ObjectModelAttribute attribute) { + String attrName = + TopiaGeneratorUtil.getSimpleName(getPropertyName(attribute)); + String attrType = + TopiaGeneratorUtil.getSimpleName(getPropertyType(attribute)); + String collectionType = getCollectionType(attribute); + String constantName = getConstantName(attrName); + if (collectionType != null) { + collectionType = TopiaGeneratorUtil.getSimpleName(collectionType); + internalAcceptOperationBody.append("" +/*{ visitor.visit(this, <%=constantName%>, <%=collectionType%>.class, <%=attrType%>.class, <%=attrName%>); +}*/ + ); + } else { + internalAcceptOperationBody.append("" +/*{ visitor.visit(this, <%=constantName%>, <%=attrType%>.class, <%=attrName%>); +}*/ + ); + } + } + + protected void closeAcceptInternalOperation() { + + setOperationBody(internalAcceptOperation, internalAcceptOperationBody.toString() + "" +/*{ }*/ + ); + } + + protected void generateToStringOperation(ObjectModelClass input) { + + if (log.isDebugEnabled()) { + log.debug("generate toString method for entity " + + outputInterface.getQualifiedName()); + } + ObjectModelOperation operation = + addOperation(outputAbstract, "toString", String.class); + + addAnnotation(outputAbstract, operation, Override.class.getSimpleName()); + + addImport(outputAbstract, ToStringBuilder.class); + + StringBuilder body = new StringBuilder("" +/*{ + String result = new ToStringBuilder(this). +}*/ + ); + for (ObjectModelAttribute attr : input.getAttributes()) { + + //FIXME possibilité de boucles (non directes) + + ObjectModelClass attrEntity = null; + + if (model.hasClass(attr.getType())) { + attrEntity = model.getClass(attr.getType()); + } + + boolean isEntity = attrEntity != null && + TopiaGeneratorUtil.isEntity(attrEntity); + + ObjectModelAttribute reverse = attr.getReverseAttribute(); + if (isEntity && (reverse == null || !reverse.isNavigable()) + && !attr.hasAssociationClass() || !isEntity) { + String attrName = attr.getName(); + String constantName = getConstantName(attrName); + body.append("" +/*{ append(<%=constantName%>, this.<%=attrName%>). +}*/ + ); + } + } + body.append("" +/*{ toString(); + return result; + }*/ + ); + setOperationBody(operation, body.length() == 0 ? " " : body.toString()); + + } + + protected void generateCompositeOperation(ObjectModelClass input) { + + ObjectModelOperation operation = + addOperation(outputAbstract, "getComposite", + List.class.getName() + '<' + TopiaEntity.class.getName() + '>'); + + addException(operation, TopiaException.class); + addAnnotation(outputAbstract, operation, Override.class.getSimpleName()); + + addImport(outputAbstract, ArrayList.class); + addImport(outputAbstract, List.class); + + StringBuilder body = new StringBuilder("" +/*{ + List<TopiaEntity> tmp = new ArrayList<TopiaEntity>(); + + // pour tous les attributs rechecher les composites et les class d'asso + // on les ajoute dans tmp +}*/ + ); + for (ObjectModelAttribute attr : input.getAttributes()) { + + if (attr.referenceClassifier() && + TopiaGeneratorUtil.isEntity(attr.getClassifier())) { + + if (attr.isComposite()) { + String attrName = attr.getName(); + String getterName = getJavaBeanMethodName("get", attrName); + if (TopiaGeneratorUtil.isNMultiplicity(attr)) { + body.append("" +/*{ if (<%=getterName%>() != null) { + tmp.addAll(<%=getterName%>()); + } +}*/ + ); + } else { + body.append("" +/*{ tmp.add(<%=getterName%>()); +}*/ + ); + } + } else if (attr.hasAssociationClass()) { + String assocAttrName = TopiaGeneratorUtil.getAssocAttrName( + attr); + String assocClassFQN = TopiaGeneratorUtil.getSimpleName( + attr.getAssociationClass().getQualifiedName()); + String ref = "this." + TopiaGeneratorUtil.toLowerCaseFirstLetter( + assocAttrName); + if (!TopiaGeneratorUtil.isNMultiplicity(attr)) { + body.append("" +/*{ + if (<%=ref%> != null) { + tmp.add(<%=ref%>); + } +}*/ + ); + } else { + ObjectModelAttribute reverse = attr.getReverseAttribute(); + String reverseAttrName = reverse.getName(); + // On utilise pas l'attribut car il est potentiellement + // pas a jour, car pour les asso avec cardinalité + // personne ne fait de add. Ce qui est normal, mais + // pour pouvoir faire tout de meme des delete en cascade + // sur les asso, le champs est dans le mapping + // hibernate et donc il le faut aussi dans la classe + // sinon hibernate rale lorsqu'il charge l'objet +// if (<%=ref%> != null) { +// tmp.addAll(<%=ref%>); +// } + + addImport(outputAbstract, TopiaContextImplementor.class); + body.append("" +/*{ + { + org.nuiton.topia.persistence.TopiaDAO<<%=assocClassFQN%>> dao = ((TopiaContextImplementor) getTopiaContext()).getDAO(<%=assocClassFQN%>.class); + List<<%=assocClassFQN%>> findAllByProperties = dao.findAllByProperties("<%=reverseAttrName%>", this); + if (findAllByProperties != null) { + tmp.addAll(findAllByProperties); + } + } +}*/ + ); + } + } + } + } + body.append("" +/*{ + // on refait un tour sur chaque entity de tmp pour recuperer leur + // composite + List<TopiaEntity> result = new ArrayList<TopiaEntity>(); + for (TopiaEntity entity : tmp) { + if (entity != null) { + result.add(entity); + result.addAll(entity.getComposite()); + } + } + return result; + }*/ + ); + + setOperationBody(operation, body.length() == 0 ? " " : body.toString()); + } + + protected void generateAggregateOperation(ObjectModelClass input) { + + ObjectModelOperation operation = + addOperation(outputAbstract, "getAggregate", + List.class.getName() + '<' + TopiaEntity.class.getName() + '>'); + + addException(operation, TopiaException.class); + addAnnotation(outputAbstract, operation, Override.class.getSimpleName()); + + addImport(outputAbstract, ArrayList.class); + addImport(outputAbstract, List.class); + + StringBuilder body = new StringBuilder("" +/*{ + List<TopiaEntity> tmp = new ArrayList<TopiaEntity>(); + + // pour tous les attributs rechecher les composites et les class d'asso + // on les ajoute dans tmp +}*/ + ); + for (ObjectModelAttribute attr : input.getAttributes()) { + + if (attr.referenceClassifier() && + TopiaGeneratorUtil.isEntity(attr.getClassifier()) && + attr.isAggregate()) { + + String attrName = attr.getName(); + String getterName = getJavaBeanMethodName("get", attrName); + if (TopiaGeneratorUtil.isNMultiplicity(attr)) { + body.append("" +/*{ tmp.addAll(<%=getterName%>()); +}*/ + ); + } else { + body.append("" +/*{ tmp.add(<%=getterName%>()); +}*/ + ); + } + } + } + body.append("" +/*{ + // on refait un tour sur chaque entity de tmp pour recuperer leur + // composite + List<TopiaEntity> result = new ArrayList<TopiaEntity>(); + for (TopiaEntity entity : tmp) { + result.add(entity); + result.addAll(entity.getAggregate()); + } + return result; + }*/ + ); + + setOperationBody(operation, body.length() == 0 ? " " : body.toString()); + } + + // ------------------------------------------------------------------------- + // Helpers + // ------------------------------------------------------------------------- + + protected boolean isAbstract(ObjectModelClass clazz) { + if (clazz.isAbstract()) { + return true; + } + + //Une classe peut être abstraite si elle a des méthodes définies dans + // ses superinterface et non implantées dans ses superclasses + Collection<ObjectModelOperation> allInterfaceOperations = + clazz.getAllInterfaceOperations(true); + allInterfaceOperations.removeAll(clazz.getAllOtherOperations(true)); + for (ObjectModelOperation op : allInterfaceOperations) { + boolean implementationFound = false; + for (ObjectModelClass superClazz : clazz.getSuperclasses()) { + for (ObjectModelOperation matchingOp : + superClazz.getOperations(op.getName())) { + implementationFound = op.equals(matchingOp) && + !matchingOp.isAbstract(); + if (implementationFound) { + break; + } + } + if (implementationFound) { + break; + } + } + if (!implementationFound) { + if (log.isDebugEnabled()) { + log.debug(clazz.getName() + " : abstract operation " + op); + } + return true; + } + } + return false; + } + + protected String getCollectionType(ObjectModelAttribute attribute) { + String result = null; + if (!associationClass && TopiaGeneratorUtil.isNMultiplicity(attribute)) { + result = TopiaGeneratorUtil.getNMultiplicityInterfaceType(attribute); + } + return result; + } + + protected String getPropertyName(ObjectModelAttribute attribute) { + String propertyName = attribute.getName(); + if (!associationClass && attribute.hasAssociationClass()) { + propertyName = TopiaGeneratorUtil.getAssocAttrName(attribute); + } + return propertyName; + } + + protected String getPropertyType(ObjectModelAttribute attribute) { + String propertyType = attribute.getType(); + if (!associationClass && attribute.hasAssociationClass()) { + propertyType = attribute.getAssociationClass().getQualifiedName(); + } + return propertyType; + } + + protected ObjectModelOperation createImplOperation(ObjectModelOperation interfaceOperation) { + ObjectModelOperation implOperation = + cloneOperationSignature(interfaceOperation, outputAbstract, false); + addAnnotation(outputAbstract, implOperation, Override.class.getSimpleName()); + return implOperation; + } + + /** + * TODO-fdesbois-2010-06-25 : This method can be put in JavaBuilder or ObjectModelTransformerToJava + * + * This method create an set operation in {@code classifier} with + * {@code propertyType} as return type and {@code propertyName} used for + * operation name ('set[propertyName]'). {@code operationDocument} can + * also be added to the operation created. Only signature with default + * visibility will be added. + * + * @param classifier Classifier where the operation will be added + * @param propertyType Type of the property (better if qualified name) + * @param propertyName Name of the property to set + * @param operationDocumentation Documentation for the operation + * @return the created operation + */ + protected ObjectModelOperation createPropertySetterSignature(ObjectModelClassifier classifier, + String propertyType, + String propertyName, + String operationDocumentation) { + // Operation + ObjectModelOperation operation = + addOperation(classifier, + getJavaBeanMethodName("set", propertyName), void.class); + + ObjectModelParameter param = + addParameter(operation, propertyType, propertyName); + + // Documentation + if (StringUtils.isNotEmpty(operationDocumentation)) { + setDocumentation(operation, operationDocumentation); + setDocumentation(param, "La valeur de l'attribut à positionner."); + } + + return operation; + } + + protected void addInterface(List<String> interfaceAlreadyDone, + ObjectModelClassifier output, + ObjectModelClassifier interfaze) { + String qualifiedName = interfaze.getQualifiedName(); + if (!interfaceAlreadyDone.contains(qualifiedName)) { + + interfaceAlreadyDone.add(qualifiedName); + + if (output != null) { + + // add it to output + addInterface(output, qualifiedName); + + if (log.isTraceEnabled()) { + log.trace("Add interface " + qualifiedName + " on " + + output.getQualifiedName()); + } + } else { + if (log.isTraceEnabled()) { + log.trace("Skip included interface " + qualifiedName); + } + } + + // scan also all interfaces or super-classes of it + for (ObjectModelClassifier parent : interfaze.getInterfaces()) { + addInterface(interfaceAlreadyDone, null, parent); + } + } + } + + protected void addInterface(List<String> interfaceAlreadyDone, + ObjectModelClassifier output, + Class<?> clazz) { + String qualifiedName = clazz.getName(); + if (!interfaceAlreadyDone.contains(qualifiedName)) { + + // add it to output + addInterface(output, qualifiedName); + + } + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/generator/QueryHelperTransformer.java b/topia-persistence/src/main/java/org/nuiton/topia/generator/QueryHelperTransformer.java new file mode 100644 index 0000000..183fe39 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/generator/QueryHelperTransformer.java @@ -0,0 +1,601 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.generator; + +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.eugene.GeneratorUtil; +import org.nuiton.eugene.java.JavaBuilder; +import org.nuiton.eugene.java.ObjectModelTransformerToJava; +import org.nuiton.eugene.models.object.ObjectModel; +import org.nuiton.eugene.models.object.ObjectModelAssociationClass; +import org.nuiton.eugene.models.object.ObjectModelAttribute; +import org.nuiton.eugene.models.object.ObjectModelClass; +import org.nuiton.eugene.models.object.ObjectModelClassifier; +import org.nuiton.eugene.models.object.ObjectModelJavaModifier; +import org.nuiton.eugene.models.object.ObjectModelModifier; +import org.nuiton.eugene.models.object.ObjectModelOperation; +import org.nuiton.eugene.models.object.ObjectModelType; +import org.nuiton.topia.framework.TopiaQuery; +import org.nuiton.topia.persistence.TopiaEntity; + +import java.util.HashMap; +import java.util.Map; + +/** + * Created: 23 juin 2010 + * + * @author fdesbois <fdesbois@codelutin.com> + * @version $Id$ + * @since 2.4 + * @plexus.component role="org.nuiton.eugene.Template" role-hint="org.nuiton.topia.generator.QueryHelperTransformer" + */ +public class QueryHelperTransformer extends ObjectModelTransformerToJava { + + private static final Log log = LogFactory.getLog(QueryHelperTransformer.class); + + protected ObjectModelClass helperClass; + + protected ObjectModelClass abstractEntityPropertyClass; + + protected static final String ENTITY_PROPERTY_CLASS_NAME = "EntityProperty"; + + protected static final String ENTITY_PROPERTY_SUFFIX = "Property"; + + protected static final String ENTITY_PROPERTY_GENERIC_TYPE = "<E>"; + + protected String mainException; + + protected Map<String, String> aliases; + + public static final String CONSTANT_PREFIX = "ALIAS_"; + + /*************************** MAIN PART OF THE HELPER **********************/ + + @Override + public void transformFromModel(ObjectModel model) { + + aliases = new HashMap<String, String>(); + + String modelName = StringUtils.capitalize(model.getName()); + String packageName = getDefaultPackageName(); + helperClass = createClass(modelName + "QueryHelper", packageName); + + addImport(helperClass, TopiaQuery.class); + addImport(helperClass, TopiaEntity.class); + + String exception = TopiaGeneratorUtil.getExceptionClassTagValue(model); + if (exception != null) { + addImport(helperClass, exception); + mainException = TopiaGeneratorUtil.getSimpleName(exception); + } + + initConstantPrefixFromModel(); + + createInnerAbstractEntityPropertyClass(); + createUtilOperations(); + } + + protected void createInnerAbstractEntityPropertyClass() { + + abstractEntityPropertyClass = (ObjectModelClass)addInnerClassifier(helperClass, + ObjectModelType.OBJECT_MODEL_CLASS, + ENTITY_PROPERTY_CLASS_NAME + ENTITY_PROPERTY_GENERIC_TYPE, + ObjectModelJavaModifier.ABSTRACT, + ObjectModelJavaModifier.STATIC); + + addImport(helperClass, HashMap.class); + addImport(helperClass, Map.class); + + addAttribute(abstractEntityPropertyClass, "alias", + String.class, null, + ObjectModelJavaModifier.PROTECTED); + + addAttribute(abstractEntityPropertyClass, "propertiesCache", + "Map<String, String>", null, + ObjectModelJavaModifier.PROTECTED); + + // Constructor + // FIXME-fdesbois-2010-06-23 : need to take care of generic case in JavaBuilder in EUGene +// ObjectModelOperation constructor = +// addConstructor(abstractEntityPropertyClass, ObjectModelJavaModifier.PUBLIC); + ObjectModelOperation constructor = + builder.addOperation(abstractEntityPropertyClass, ENTITY_PROPERTY_CLASS_NAME, null, ObjectModelJavaModifier.PUBLIC); + + setOperationBody(constructor, "" + /*{ + propertiesCache = new HashMap<String, String>(); + }*/ + ); + + // Getter and setter for alias + ObjectModelOperation setAlias = + addOperation(abstractEntityPropertyClass, "setAlias", "void", + ObjectModelJavaModifier.PROTECTED); + addParameter(setAlias, String.class, "alias"); + + + setOperationBody(setAlias, "" + /*{ + this.alias = alias; + }*/ + ); + + ObjectModelOperation getAlias = + addOperation(abstractEntityPropertyClass, "$alias", String.class, + ObjectModelJavaModifier.PUBLIC); + + + setOperationBody(getAlias, "" + /*{ + return alias; + }*/ + ); + + // Getter for properties + ObjectModelOperation getProperty = + addOperation(abstractEntityPropertyClass, "$property", String.class, + ObjectModelJavaModifier.PUBLIC); + addParameter(getProperty, String.class, "propertyName"); + + + setOperationBody(getProperty, "" + /*{ + String result = propertiesCache.get(propertyName); + if (result == null) { + result = TopiaQuery.getProperty(alias, propertyName); + propertiesCache.put(propertyName, result); + } + return result; + }*/ + ); + + ObjectModelOperation topiaCreateDate = + addOperation(abstractEntityPropertyClass, "topiaCreateDate", String.class, + ObjectModelJavaModifier.PUBLIC); + + setOperationBody(topiaCreateDate, "" + /*{ + return $property(TopiaEntity.TOPIA_CREATE_DATE); + }*/ + ); + + ObjectModelOperation topiaId = + addOperation(abstractEntityPropertyClass, "topiaId", String.class, + ObjectModelJavaModifier.PUBLIC); + + setOperationBody(topiaId, "" + /*{ + return $property(TopiaEntity.TOPIA_ID); + }*/ + ); + + ObjectModelOperation topiaVersion = + addOperation(abstractEntityPropertyClass, "topiaVersion", String.class, + ObjectModelJavaModifier.PUBLIC); + + setOperationBody(topiaVersion, "" + /*{ + return $property(TopiaEntity.TOPIA_VERSION); + }*/ + ); + + // Abstract methods + addOperation(abstractEntityPropertyClass, "getEntityClass", + "Class" + ENTITY_PROPERTY_GENERIC_TYPE, + ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.ABSTRACT); + + addOperation(abstractEntityPropertyClass, "defaultAlias", String.class, + ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.ABSTRACT); + } + + protected void createUtilOperations() { + + // createQuery method with EntityProperty in argument + ObjectModelOperation createQuery = + addOperation(helperClass, "createQuery", TopiaQuery.class, + ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC); + addParameter(createQuery, ENTITY_PROPERTY_CLASS_NAME, "property"); + + setOperationBody(createQuery, "" + /*{ + return new TopiaQuery((Class<? extends TopiaEntity>)property.getEntityClass(), property.$alias()); + }*/ + ); + + // format method to format statement using $1, $2 corresponding to property names + ObjectModelOperation format = + addOperation(helperClass, "format", String.class, + ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC); + addParameter(format, String.class, "statement"); + addParameter(format, "String...", "propertyNames"); + + setOperationBody(format, "" + /*{ + for (int i = 1; i <= propertyNames.length; i++) { + statement = statement.replace("$" + i, propertyNames[i-1]); + } + return statement; + }*/ + ); + + // Methods to instantiate EntityProperty + String genericType = "<P extends " + ENTITY_PROPERTY_CLASS_NAME + "> P"; + ObjectModelOperation newEntityProperty1 = + addOperation(helperClass, "newEntityProperty", genericType, + ObjectModelJavaModifier.PRIVATE, ObjectModelJavaModifier.STATIC); + addParameter(newEntityProperty1, "Class<P>", "propertyClass"); + + setOperationBody(newEntityProperty1, "" + /*{ + return newEntityProperty(propertyClass, null); + }*/ + ); + ObjectModelOperation newEntityProperty2 = + addOperation(helperClass, "newEntityProperty", genericType, + ObjectModelJavaModifier.PRIVATE, ObjectModelJavaModifier.STATIC); + addParameter(newEntityProperty2, "Class<P>", "propertyClass"); + addParameter(newEntityProperty2, String.class, "alias"); + + StringBuilder buffer = new StringBuilder("" + /*{ + try { + P property = propertyClass.newInstance(); + if (alias == null) { + alias = property.defaultAlias(); + } + property.setAlias(alias); + return property; + } catch (Exception eee) { + }*/ + ); + + if (mainException != null) { + addException(newEntityProperty1, mainException); + addException(newEntityProperty2, mainException); + buffer.append("" + /*{ + throw new <%=mainException%>("Error instantiate " + propertyClass.getName(), eee); + }*/ + ); + } else { + buffer.append("" + /*{ + throw new Error("Error instantiate " + propertyClass.getName(), eee); + }*/ + ); + } + buffer.append("" + /*{ + } + }*/ + ); + + setOperationBody(newEntityProperty2, buffer.toString()); + } + + /*************************** INNER PROPERTY CLASSES ***********************/ + + @Override +// public void transformFromClass(ObjectModelClass clazz) { + public void transformFromClassifier(ObjectModelClassifier clazz) { + if (!TopiaGeneratorUtil.hasEntityStereotype(clazz)) { + return; + } + + // Create default alias for this entity + String aliasConstant = createAliasConstant(clazz.getName()); + + // Create inner class for this entity + ObjectModelClass entityPropertyClass = createInnerClass(clazz, aliasConstant); + + // Create methods to instantiate the inner class + createNewOperations(entityPropertyClass); + + addExtraForSubEntity(clazz); + } + + protected String createAliasConstant(String entityName) { + + String constantName = + TopiaGeneratorUtil.convertVariableNameToConstantName(entityName); + + String[] words = constantName.split("_"); + String alias = ""; + // Use first letter of each word as alias + for (String word : words) { + alias += word.substring(0, 1); + } + + // Case of existing alias, check other letters in lastWord + String lastWord = words[words.length - 1]; + while(aliases.containsKey(alias)) { + // Remove first letter + lastWord = lastWord.substring(1, lastWord.length()); + if (!lastWord.isEmpty()) { + // Use first letter of new lastWord to concat the alias + alias += lastWord.charAt(0); + } else { + // Generate an alea char to concat + alias += StringUtils.upperCase(RandomStringUtils.randomAlphabetic(1)); + } + } + + String aliasPropertyName = CONSTANT_PREFIX + constantName; + + if (log.isDebugEnabled()) { + log.debug("Add alias '" + alias + "' named " + aliasPropertyName); + } + + aliases.put(alias, aliasPropertyName); + + addAttribute(helperClass, aliasPropertyName, String.class, "\"" + alias + "\"", + ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC, ObjectModelJavaModifier.FINAL); + + return aliasPropertyName; + } + + protected ObjectModelClass createInnerClass(ObjectModelClassifier input, String aliasConstant) { + String className = getPropertyClassName(input); + + ObjectModelClass propertyClass = (ObjectModelClass) + addInnerClassifier(helperClass, + ObjectModelType.OBJECT_MODEL_CLASS, + className, + ObjectModelJavaModifier.STATIC); + + if (log.isDebugEnabled()) { + log.debug("Generate for entity : " + input.getQualifiedName()); + } + + // FIXME-fdesbois-2010-06-23 : need to manage imports for inner classes : EUGene ImportsManager + addImport(helperClass, input.getQualifiedName()); + + // Important to keep qualifiedName for setSuperClass + String superClassQualifiedName = abstractEntityPropertyClass.getQualifiedName(). + replace(ENTITY_PROPERTY_GENERIC_TYPE, "<" + input.getName() + ">"); + + setSuperClass(propertyClass, superClassQualifiedName); + + ObjectModelOperation constructor = + addConstructor(propertyClass, ObjectModelJavaModifier.PROTECTED); + + setOperationBody(constructor, "" + /*{ + }*/ + ); + + ObjectModelOperation getEntityClass = + addOperation(propertyClass, "getEntityClass", "Class<" + input.getName() + ">", + ObjectModelJavaModifier.PUBLIC); + + addAnnotation(propertyClass, getEntityClass, "Override"); + + setOperationBody(getEntityClass, "" + /*{ + return <%=input.getName()%>.class; + }*/ + ); + + ObjectModelOperation defaultAlias = + addOperation(propertyClass, "defaultAlias", String.class, + ObjectModelJavaModifier.PUBLIC); + + addAnnotation(propertyClass, defaultAlias, "Override"); + + setOperationBody(defaultAlias, "" + /*{ + return <%=aliasConstant%>; + }*/ + ); + + createGetterOperations(input, propertyClass); + + return propertyClass; + } + + protected void createGetterOperations(ObjectModelClassifier input, ObjectModelClass propertyClass) { + + // Generate for all attributes + for (ObjectModelAttribute attr : input.getAttributes()) { + + // Case we don't want generation for + if (!attr.isNavigable()) { + continue; + } + + String attrName = getReferenceAttributeName(attr); + + if (log.isDebugEnabled()) { + log.debug("Entity property : name=" + attrName + + " _ navigable=" + attr.isNavigable() + + " _ maxMultiplicity=" + attr.getMaxMultiplicity() + + " _ associationClass=" + attr.hasAssociationClass() + + " _ referenceClassifier=" + attr.referenceClassifier()); + } + + ObjectModelOperation propertyNameOperation = + createGetPropertyNameOperation(propertyClass, attrName, input.getName()); + + createGetPropertyObjectOperation(propertyClass, attr, propertyNameOperation); + } + + // Case of Association class : generate also for participant properties + if (input instanceof ObjectModelAssociationClass) { + + ObjectModelAssociationClass assoc = (ObjectModelAssociationClass)input; + + for (ObjectModelAttribute attr : assoc.getParticipantsAttributes()) { + ObjectModelOperation propertyNameOperation = + createGetPropertyNameOperation(propertyClass, attr.getName(), input.getName()); + + createGetPropertyObjectOperation(propertyClass, attr, propertyNameOperation); + } + } + } + + protected ObjectModelOperation createGetPropertyNameOperation(ObjectModelClass output, String attrName, String entityClassName) { + ObjectModelOperation result = + addOperation(output, attrName, String.class, ObjectModelJavaModifier.PUBLIC); + + String constantName = + entityClassName + "." + getConstantName(attrName); + + if (log.isDebugEnabled()) { + log.debug("Add getter for property : " + attrName + + " _ constantName = " + constantName + + " _ constantPrefix = " + getConstantPrefix()); + } + + setOperationBody(result, "" + /*{ + return $property(<%=constantName%>); + }*/ + ); + + return result; + } + + protected ObjectModelOperation createGetPropertyObjectOperation(ObjectModelClass output, + ObjectModelAttribute attrReference, + ObjectModelOperation propertyNameOperation) { + + ObjectModelClassifier referenceClass = getReferenceAttributeClassifier(attrReference); + + // No reference, can't add method to getPropertyObject + // ANO-#1648: Enum are ignored here, they will be used as simple property + if (referenceClass == null || referenceClass.isEnum()) { + return null; + } + + String operationName = getReferenceAttributeName(attrReference) + + ENTITY_PROPERTY_SUFFIX; + + String referencePropertyClassName = getPropertyClassName(referenceClass); + + ObjectModelOperation result = + addOperation(output, operationName, referencePropertyClassName); + + if (log.isDebugEnabled()) { + log.debug("Extra operation : " + operationName + + " _ className = " + referencePropertyClassName); + } + + setOperationBody(result, "" + /*{ + return new<%=referencePropertyClassName%>(<%=propertyNameOperation.getName()%>()); + }*/ + ); + + return result; + } + + protected void createNewOperations(ObjectModelClass entityProperty) { + + String className = entityProperty.getName(); + + String methodName = "new" + className; + + ObjectModelOperation newEntityProperty1 = + addOperation(helperClass, "new" + className, className, + ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC); + + setOperationBody(newEntityProperty1, "" + /*{ + return <%=methodName%>(null); + }*/ + ); + + ObjectModelOperation newEntityProperty2 = + addOperation(helperClass, "new" + className, className, + ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC); + addParameter(newEntityProperty2, String.class, "alias"); + + setOperationBody(newEntityProperty2, "" + /*{ + return newEntityProperty(<%=className%>.class, alias); + }*/ + ); + } + + protected void addExtraForSubEntity(ObjectModelClassifier entityClass) { + for (ObjectModelAttribute attr : entityClass.getAttributes()) { + + if (attr.isNavigable() && attr.referenceClassifier() && + attr.getClassifier().getName().equals(entityClass.getName())) { + // Same entity + + String propertyClassName = getPropertyClassName(entityClass); + + String subEntityName = entityClass.getName() + StringUtils.capitalize(attr.getName()); + String aliasConstant = createAliasConstant(subEntityName); + + ObjectModelOperation newEntityProperty = + addOperation(helperClass, "new" + subEntityName + "Property", propertyClassName, + ObjectModelJavaModifier.PUBLIC, ObjectModelJavaModifier.STATIC); + + String callMethodName = "new" + propertyClassName; + + setOperationBody(newEntityProperty, "" + /*{ + return <%=callMethodName%>(<%=aliasConstant%>); + }*/ + ); + + } + } + } + + // Helpers + protected String getPropertyClassName(ObjectModelClassifier entityClass) { + return entityClass.getName() + ENTITY_PROPERTY_SUFFIX; + } + + protected String getReferenceAttributeName(ObjectModelAttribute attrReference) { + String attrName = attrReference.getName(); + if(attrReference.hasAssociationClass()) { + attrName = GeneratorUtil.getAssocAttrName(attrReference); + } + return attrName; + } + + protected ObjectModelClassifier getReferenceAttributeClassifier(ObjectModelAttribute attrReference) { + ObjectModelClassifier referenceClass = null; + // case for attribute classifier, only for maxMultiplicity = 1 + if (attrReference.referenceClassifier() && attrReference.getMaxMultiplicity() == 1) { + referenceClass = attrReference.getClassifier(); + // case for association attribute + } else if (attrReference.hasAssociationClass()) { + referenceClass = attrReference.getAssociationClass(); + } + return referenceClass; + } + + // For tests + protected void setBuilder(JavaBuilder builder) { + this.builder = builder; + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/generator/ServiceTransformer.java b/topia-persistence/src/main/java/org/nuiton/topia/generator/ServiceTransformer.java new file mode 100644 index 0000000..ff8007c --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/generator/ServiceTransformer.java @@ -0,0 +1,773 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.generator; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.eugene.GeneratorUtil; +import org.nuiton.eugene.java.ObjectModelTransformerToJava; +import org.nuiton.eugene.models.object.ObjectModel; +import org.nuiton.eugene.models.object.ObjectModelClass; +import org.nuiton.eugene.models.object.ObjectModelInterface; +import org.nuiton.eugene.models.object.ObjectModelJavaModifier; +import org.nuiton.eugene.models.object.ObjectModelModifier; +import org.nuiton.eugene.models.object.ObjectModelOperation; +import org.nuiton.eugene.models.object.ObjectModelParameter; +import org.nuiton.i18n.I18n; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.Map; + +/*{generator option: parentheses = false}*/ + +/*{generator option: writeString = +}*/ + +/** + * This Template is used to create the skeleton of services for a final + * application which using Topia. + * <div> + * Generation from interfaces with stereotype <<service>> : + * <ul> + * <li>Service : interface of the service defined in model.</li> + * <li><p>ServiceAbstract : abstract class which contains :</p> + * <p>* treateError : abstract method used to catch all exception from a + * service method.</p> + * <p>* closeTransaction : abstract method used to finally the try/catch + * of a service method</p> + * <p>* beginTransaction : abstract method used to start the transaction + * using rootContext.</p> + * <p>* constructor with AppContextImplementor in argument</p> + * <p>* for each method : the implementation of the method (skeleton with + * try/catch and beginTransaction call to open a new TopiaContext from + * AppContextImplementor). Usage of i18n keys for error messages in + * exception.</p> + * <p>* for each method : an abstract method used to execute the business + * code of the method : need to be implemented in subclass.</p> + * </li> + * </ul> + * </div> + * <div> + * Exemple of ServiceImpl utils method implementation. (The AppException + * is considered if defined in model tagvalue "exceptionClass") : <br /> + * <pre> + * public class ServiceImpl implements ServiceAbstract { + * + * // properties for Topia configuration + * protected Properties properties; + * ... + * + * @Override + * public void treateError(TopiaContext transaction, Exception eee, + * String message, Object... args) throws AppException { + * + * // Note that the message from service doesn't directly use _() for + * // i18 messages but n_(). In this log, the _() is used to translate + * // correctly the message. But the message must be translate when + * // catching the AppException in UI. + * if (log.isErrorEnabled()) { + * log.error(_(message, args), eee); + * } + * + * // rollback of current transaction + * if (transaction != null) { + * try { + * transaction.rollbackTransaction(); + * } catch (TopiaException ex) { + * if (log.isErrorEnabled()) { + * log.error(_("app.error.context.rollback"), ex); + * } + * } + * } + * // wrapping the exception in a AppException with message and + * // arguments for i18n translation + * throw new AppException(eee, message, args); + * } + * + * @Override + * public void closeTransaction(TopiaContext transaction) { + * if (transaction != null) { + * try { + * transaction.closeContext(); + * } catch (TopiaException eee) { + * if (log.isErrorEnabled()) { + * log.error(_("app.error.context.close"), eee); + * } + * } + * } + * } + * + * @Override + * public TopiaContext beginTransaction() throws TopiaException { + * TopiaContext rootContext = null; + * try { + * // You have to manage the properties using ApplicationConfig + * // or other lib to have configuration for Topia + * rootContext = TopiaContextFactory.getContext(properties); + * + * return getTopiaRootContext().beginTransaction(); + * + * // only catch exception for rootContext + * } catch (TopiaNotFoundException eee) { + * treateError(eee, n_("app.error.context.getTopiaRootContext")); + * } + * return null; + * } + * + * // Implementation of abstract method, the interface method is + * // called 'createMyEntity(MyEntity entity)' in this case. + * @Override + * public void executeCreateMyEntity(TopiaContext transaction, + * MyEntity entity) throws TopiaException { + * + * MyEntityDAO dao = AppDAOHelper.getMyEntityDAO(transaction); + * dao.create(entity); + * // That's it, no need to manage errors or transaction, the abstract + * // service will do this job. + * } + * } + * </pre> + * <div> + * <h2>TAG_TRANSACTION</h2> + * <p>Default value : true</p> + * <p>You can use the tagValue 'transaction=false' to specify that a method + * doesn't need any TopiaContext, so no need to instantiate a new one. + * This tagValue can only be put directly in the model and not in properties + * file (because of multiple methods with same name problem).</p> + * </div> + * <div> + * <h2>TAG_ERROR_ARGS</h2> + * <p>Default value : false</p> + * <p>You can use the tagValue 'errorArgs=true' to specify that a method + * need arguments for error message. This tagValue can only be put directly + * in the model and not in properties file.</p> + * </div> + * <div> + * <h2>TAG_EXCEPTION_CLASS</h2> + * <p>Default value : null</p> + * <p>You can use the tagValue 'exceptionClass=my.exception.full.qualified.Name' + * to specify that all contract methods will throw this exception.</p> + * </div> + * <p>It is smooth, isn't it :p ?</p> + * <p> + * + * Created: 23 mars 2010 + * + * @author fdesbois <fdesbois@codelutin.com> + * @version $Id$ + * @since 2.3.1 + * @plexus.component role="org.nuiton.eugene.Template" role-hint="org.nuiton.topia.generator.ServiceTransformer" + */ +// TODO : may be refactor to integrate JTA or webservice or may be not in this transformer. +public class ServiceTransformer extends ObjectModelTransformerToJava { + + + private static final Log log = LogFactory.getLog(ServiceTransformer.class); + protected String modelName; + + protected String defaultPackageName; + + protected String exceptionName; + + private static final String OP_NAME_BEGIN_TRANSACTION = "beginTransaction"; + + private static final String OP_NAME_COMMIT_TRANSACTION = "commitTransaction"; + + private static final String OP_NAME_CLOSE_TRANSACTION = "closeTransaction"; + + private static final String OP_NAME_TREATE_ERROR = "treateError"; + + public static final String PARAMETER_TRANSACTION = "transaction"; + + protected String getServiceAbstractClassName(String serviceName) { + return serviceName + "Abstract"; + } + + @Override + public void transformFromModel(ObjectModel model) { + exceptionName = TopiaGeneratorUtil.getExceptionClassTagValue(model); + modelName = model.getName(); + } + + @Override + public void transformFromInterface(ObjectModelInterface input) { + if (!TopiaGeneratorUtil.hasServiceStereotype(input)) { + return; + } + + // global transaction needed (if set to false then never use transaction) + boolean needTransaction = isTransactionNeeded(input); + + ObjectModelInterface serviceContract = createServiceContract(input); + + createServiceAbstract(input, + serviceContract, + needTransaction); + } + + /** + * Create the service contract using {@code source} interface defined + * in model. + * + * @param source interface from model + * @return the ObjectModelInterface created + */ + protected ObjectModelInterface createServiceContract( + ObjectModelInterface source) { + + ObjectModelInterface serviceContract = + createInterface(source.getName(), source.getPackageName()); + + setDocumentation(serviceContract, source.getDocumentation()); + for (ObjectModelOperation op : source.getOperations()) { + ObjectModelOperation newOp = addOperation(serviceContract, + op.getName(), + op.getReturnType() + ); + setDocumentation(newOp.getReturnParameter(), + op.getReturnParameter().getDocumentation() + ); + for (ObjectModelParameter param : op.getParameters()) { + ObjectModelParameter newParam = addParameter(newOp, + param.getType(), + param.getName() + ); + setDocumentation(newParam, param.getDocumentation()); + } + for (String ex : op.getExceptions()) { + addException(newOp, ex); + } + if (exceptionName != null) { + addException(newOp, exceptionName); + } + setDocumentation(newOp, op.getDocumentation()); + } + return serviceContract; + } + + protected void createBeginTransactionMethod(ObjectModelInterface source, + ObjectModelInterface serviceContract, + ObjectModelClass serviceAbstract) { + ObjectModelOperation operation = + addOperation(serviceAbstract, OP_NAME_BEGIN_TRANSACTION, + TopiaContext.class, + ObjectModelJavaModifier.ABSTRACT, + ObjectModelJavaModifier.PROTECTED); + addException(operation, TopiaException.class); + } + + protected void createCommitTransactionMethod(ObjectModelClass serviceAbstract) { + ObjectModelOperation operation = + addOperation(serviceAbstract, + OP_NAME_COMMIT_TRANSACTION, + "void", + ObjectModelJavaModifier.PROTECTED); + addParameter(operation, TopiaContext.class, PARAMETER_TRANSACTION); + addException(operation, TopiaException.class); + setOperationBody(operation,"" +/*{ + transaction.commitTransaction(); +}*/ + ); + + } + + protected void createCloseTransactionMethod(ObjectModelInterface source, + ObjectModelInterface serviceContract, + ObjectModelClass serviceAbstract) { + ObjectModelOperation operation = + addOperation(serviceAbstract, + OP_NAME_CLOSE_TRANSACTION, + "void", + ObjectModelJavaModifier.ABSTRACT, + ObjectModelJavaModifier.PROTECTED); + addParameter(operation, TopiaContext.class, PARAMETER_TRANSACTION); + addException(operation, TopiaException.class); + } + + protected void createTreateErrorMethod(ObjectModelInterface source, + ObjectModelInterface serviceContract, + ObjectModelClass serviceAbstract, + boolean needTransaction) { + + ObjectModelOperation treateError1 = + addOperation(serviceAbstract, + OP_NAME_TREATE_ERROR, + "void", + ObjectModelJavaModifier.ABSTRACT, + ObjectModelJavaModifier.PROTECTED); + if (needTransaction) { + addParameter(treateError1, TopiaContext.class, PARAMETER_TRANSACTION); + } + addParameter(treateError1, Exception.class, "eee"); + addParameter(treateError1, String.class, "message"); + addParameter(treateError1, "Object...", "args"); + if (exceptionName != null) { + addException(treateError1, exceptionName); + } + + if (needTransaction) { + ObjectModelOperation treateError2 = + addOperation(serviceAbstract, OP_NAME_TREATE_ERROR, "void", + ObjectModelJavaModifier.PROTECTED); + addParameter(treateError2, Exception.class, "eee"); + addParameter(treateError2, String.class, "message"); + addParameter(treateError2, "Object...", "args"); + if (exceptionName != null) { + addException(treateError2, exceptionName); + } + + setOperationBody(treateError2, "" + /*{ + treateError(null, eee, message, args); + }*/ + ); + } + } + /** + * Create the service abstract for {@code serviceContract} + * using {@code source} interface defined + * in model. + * + * @param source interface from model + * @param serviceContract to implement + * @param needTransaction flag to know if service globally use transaction + */ + protected void createServiceAbstract(ObjectModelInterface source, + ObjectModelInterface serviceContract, + boolean needTransaction) { + + ObjectModelClass serviceAbstract = createAbstractClass( + getServiceAbstractClassName(serviceContract.getName()), + serviceContract.getPackageName()); + + // Imports for implementations + if (needTransaction) { + addImport(serviceAbstract, TopiaContext.class); + } + addImport(serviceAbstract, I18n.class); + + // Implements contract interface + addInterface(serviceAbstract, serviceContract.getQualifiedName()); + + // Create abstract methods + + if (needTransaction) { + createBeginTransactionMethod(source, + serviceContract, + serviceAbstract); + + createCommitTransactionMethod(serviceAbstract); + + createCloseTransactionMethod(source, + serviceContract, + serviceAbstract); + } + + createTreateErrorMethod(source, + serviceContract, + serviceAbstract, + needTransaction + ); + + // keep execute methods (we want to generate them at the top of the + // class since they are all abstract) + // Note: using a LinkedHashMap permits to keep incoming order + Map<ObjectModelOperation, ObjectModelOperation> abstractExecuteMethods = + new LinkedHashMap<ObjectModelOperation, ObjectModelOperation>(); + + // first generate the abstract execute methods + for (ObjectModelOperation operation : source.getOperations()) { + + ObjectModelOperation executeOp = createOperationExecuteAbstract( + serviceAbstract, + operation, + needTransaction + ); + + abstractExecuteMethods.put(operation , executeOp); + } + + // Then generates the real operation which boxes the execute methods + for (Map.Entry<ObjectModelOperation, ObjectModelOperation> entry : + abstractExecuteMethods.entrySet()) { + ObjectModelOperation operation = entry.getKey(); + ObjectModelOperation executeOperation = entry.getValue(); + createOperationImplementation( + serviceAbstract, + executeOperation, + operation, + source.getName(), + needTransaction + ); + } + } + + /** + * Create an operation abstract to execute in contract implementation. + * You can use tagvalues "errorArgs" (default = false) and "transaction" + * (default = true) to generate appropriate parameters. This abstract + * method will throw all exceptions (Exception.class). This is the method + * which will be implemented by the developper in service implementation + * class. + * + * @param serviceAbstract where the operation will be created + * @param source ObjectModelOperation from model + * @param needTransaction flag to know if service globally use transaction + * @return the abstract operation created + * @see #isErrorArgsNeeded(ObjectModelOperation) + * @see #isTransactionNeeded(ObjectModelOperation) + * @see #isTransactionNeeded(ObjectModelInterface) + */ + protected ObjectModelOperation createOperationExecuteAbstract( + ObjectModelClass serviceAbstract, + ObjectModelOperation source, + boolean needTransaction) { + String opName = StringUtils.capitalize(source.getName()); + + // Abstract operation to execute method content + ObjectModelOperation executeOperation = + addOperation(serviceAbstract, "execute" + opName, + source.getReturnType(), + ObjectModelJavaModifier.ABSTRACT, + ObjectModelJavaModifier.PROTECTED); + + // Throw all exception from abstract method + // They will be catched by interface method to use treateError + addException(executeOperation, Exception.class); + + if (needTransaction && isTransactionNeeded(source)) { + addParameter(executeOperation, TopiaContext.class, PARAMETER_TRANSACTION); + } + + if (isErrorArgsNeeded(source)) { + // Add errorArgs to abstract operation + addParameter(executeOperation, "java.util.List<Object>", "errorArgs"); + } + + // Copy other operation parameters + for (ObjectModelParameter param : source.getParameters()) { + addParameter(executeOperation, param.getType(), param.getName()); + } + return executeOperation; + } + + /** + * Create an operation implementation. This is the skeleton of the operation + * defined from model. This will put a try/catch block over an abstract + * method {@code abstOp}. You can use tagvalues "errorArgs" and + * "transaction" for abstract method parameters to call. If the transaction + * is needed, this will use the beginTransaction() and closeTransaction() + * methods defined in {@code serviceAbstract} class. + * + * @param serviceAbstract where the operation will be created + * @param abstOp to execute into the implementation body + * @param source ObjectModelOperation from model + * @param serviceContractName where the signature method is defined + * @param needTransaction flag to know if service globally use transaction + * @see #isErrorArgsNeeded(ObjectModelOperation) + * @see #isTransactionNeeded(ObjectModelInterface) + */ + protected void createOperationImplementation( + ObjectModelClass serviceAbstract, + ObjectModelOperation abstOp, + ObjectModelOperation source, + String serviceContractName, + boolean needTransaction) { + + // boolean to specify if the method need a transaction or not + // Default set to true but can be override by a tagvalue on the + // method + needTransaction &= isTransactionNeeded(source); + + // boolean to specify if the method need error arguments or not + // Default set to true but can be override by a tagvalue on the + // method + boolean needErrorArgs = isErrorArgsNeeded(source); + + // Implementation of interface operation + ObjectModelOperation implOp = + addOperation(serviceAbstract, + source.getName(), + source.getReturnType(), + ObjectModelJavaModifier.PUBLIC); + + addAnnotation(serviceAbstract, implOp, Override.class.getSimpleName()); + + String toStringAppend = ""; + String separatorLog = " : "; + // Copy operation parameters + for (ObjectModelParameter param : source.getParameters()) { + String paramName = param.getName(); + addParameter(implOp, param.getType(), paramName); + } + + // Use buffer for operation body + StringBuilder buffer = new StringBuilder(); + + // Abstract operation parameters + String abstName = abstOp.getName(); + String abstParams = + GeneratorUtil.getOperationParametersListName(abstOp); + + // Abstract operation return managment + String abstReturnType = ""; + String abstReturn = ""; + String finalReturn = ""; + String returnType = GeneratorUtil.getSimpleName(abstOp.getReturnType(), + true + ); + if (!returnType.equals("void")) { + abstReturnType = returnType + " result = "; + abstReturn = "return result;"; + finalReturn = "return " + + getReturnValue(abstOp.getReturnType()) + ";"; + } + + // Error key for i18n + String contract = + GeneratorUtil.toLowerCaseFirstLetter(serviceContractName); + String errorKey = StringUtils.lowerCase(modelName) + ".error." + + contract + "." + source.getName(); + + String treateErrorParams = "eee, I18n.n_(\"" + errorKey + "\")"; + + if (needErrorArgs) { + addImport(serviceAbstract, ArrayList.class); + // Init errorArgs + buffer.append("" + /*{ + List<Object> errorArgs = new ArrayList<Object>(); + }*/ ); + treateErrorParams += ", errorArgs.toArray()"; + } + + if (needTransaction) { + // Open the transaction + buffer.append("" + /*{ + TopiaContext transaction = null; + try { + transaction = beginTransaction(); + + try {}*/ + ); + // Add transaction in treateError parameters + treateErrorParams = "transaction, " + treateErrorParams; + } else { + buffer.append("" + /*{ + try { + }*/ + ); + } + String implName = StringUtils.capitalize(implOp.getName()); + String first = modelName.substring(0, 1); + + buffer.append("" + /*{ + <%=abstReturnType%><%=abstName%>(<%=abstParams%>);}*/); + + if (needTransaction && isCommit(source, model)) { + + // add the commit instruction + buffer.append("" + /*{ + commitTransaction(transaction);}*/); + + } + buffer.append("" + /*{ + <%=abstReturn%>}*/); + + if (needTransaction) { + // Finally block to close transaction + buffer.append("" + /*{ + } finally { + closeTransaction(transaction); + } + }*/ + ); + + } + // Copy exceptions + for (String ex : source.getExceptions()) { + addException(implOp, ex); + // Add catch block for known exceptions we want to throw + String exName = GeneratorUtil.getSimpleName(ex); + buffer.append("" + /*{ + } catch (<%=exName%> eee) { + throw eee; }*/); + } + if (exceptionName != null) { + addException(implOp, exceptionName); + } + + buffer.append("" + /*{ + } catch (Exception eee) { + treateError(<%=treateErrorParams%>); }*/); + + + buffer.append("" + /*{ + } + <%=finalReturn%> + }*/ + ); + + setOperationBody(implOp, buffer.toString()); + } + + /** + * boolean to specify if the method need a transaction or not. + * Default set to true but can be override using a tagvalue "transaction" + * on the method from model. + * + * @param op where the tagvalue is set + * @return {@code true} if transaction is needed + */ + protected boolean isTransactionNeeded(ObjectModelInterface op) { + boolean needTransaction = true; + + String transactionTag = TopiaGeneratorUtil.getTransactionTagValue(op); + + if (transactionTag != null) { + needTransaction = Boolean.parseBoolean(transactionTag); + } + return needTransaction; + } + + /** + * boolean to specify if the method need a transaction or not. + * Default set to true but can be override using a tagvalue "transaction" + * on the method from model. + * + * @param op where the tagvalue is set + * @return {@code true} if transaction is needed + */ + protected boolean isTransactionNeeded(ObjectModelOperation op) { + boolean needTransaction = true; + + String transactionTag = TopiaGeneratorUtil.getTransactionTagValue(op); + + if (transactionTag != null) { + needTransaction = Boolean.parseBoolean(transactionTag); + } + return needTransaction; + } + + /** + * boolean to specify if method needs a commit after the executeXXX code invoked. + * + * @param op model element where the tagvalue is set + * @param model model where to tagvalue can be also set + * @return {@code true} if a commit must be generated after the executeXXX invocation + * @see TopiaTagValues#TAG_DO_COMMIT + * @since 2.5 + */ + protected boolean isCommit(ObjectModelOperation op, ObjectModel model) { + boolean needCommit = false; + + String tagValue = TopiaGeneratorUtil.getDoCommitTagValue( + op, + model + ); + if (tagValue != null) { + needCommit = Boolean.parseBoolean(tagValue); + } + if (isVerbose()) { + log.info("commit needed for op [" + op.getName() + "] : " + needCommit); + } + return needCommit; + } + + /** + * boolean to specify if the method need error arguments or not + * Default set to false but can be override using a tagvalue "errorArgs" on + * the method from model. + * + * @param op where the tagvalue is set + * @return true if errorArgs are needed + */ + protected boolean isErrorArgsNeeded(ObjectModelOperation op) { + // + boolean needErrorArgs = false; + + String errorArgsTag = TopiaGeneratorUtil.getErrorArgsTagValue(op); + + if (errorArgsTag != null) { + needErrorArgs = Boolean.parseBoolean(errorArgsTag); + } + return needErrorArgs; + } + + /** + * This method give the return string for an operation {@code returnType}. + * This use {@link Primitive} enum to provide default values for primitive + * type. For all other object type, this method will return null. + * + * @param returnType + * @return the defaultValue of the returnType + */ + protected String getReturnValue(String returnType) { + try { + //FIXME-TC20100423 : can not deal with Object types (float != Float) + Primitive prim = + Primitive.valueOf(StringUtils.upperCase(returnType)); + return prim.getValue(); + // If not defined in Primitive enum, return null + } catch (IllegalArgumentException eee) { + return null; + } + } + + //FIXME-TC20100423 : REMOVE THIS! + protected enum Primitive { + BYTE("0"), + SHORT("0"), + INT("0"), + LONG("0"), + FLOAT("0."), + DOUBLE("0."), + CHAR("''"), + BOOLEAN("false"); + + private String value; + + Primitive(String value) { + this.value = value; + } + + public String getValue() { + return value; + } + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaGeneratorUtil.java b/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaGeneratorUtil.java new file mode 100644 index 0000000..4a75ea2 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaGeneratorUtil.java @@ -0,0 +1,1928 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.generator; + +import java.util.LinkedHashSet; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.eugene.AbstractGenerator; +import org.nuiton.eugene.EugeneStereoTypes; +import org.nuiton.eugene.GeneratorUtil; +import org.nuiton.eugene.java.JavaGeneratorUtil; +import org.nuiton.eugene.models.Model; +import org.nuiton.eugene.models.object.ObjectModel; +import org.nuiton.eugene.models.object.ObjectModelAssociationClass; +import org.nuiton.eugene.models.object.ObjectModelAttribute; +import org.nuiton.eugene.models.object.ObjectModelClass; +import org.nuiton.eugene.models.object.ObjectModelClassifier; +import org.nuiton.eugene.models.object.ObjectModelElement; +import org.nuiton.eugene.models.object.ObjectModelInterface; +import org.nuiton.eugene.models.object.ObjectModelOperation; +import org.nuiton.eugene.models.object.ObjectModelPackage; +import org.nuiton.eugene.models.object.ObjectModelParameter; +import org.nuiton.eugene.models.tagvalue.TagValues; +import org.nuiton.topia.persistence.TopiaDAOImpl; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +/** + * Classe regroupant divers méthodes utiles pour la génération des entités + * <p/> + * Created: 13 déc. 2005 + * + * @author Arnaud Thimel <thimel@codelutin.com> + * @author tchemit <tchemit@codelutin.com> + * @author fdesbois <fdesbois@codelutin.com> + * @author chatellier <chatellier@codelutin.com> + * @version $Id$ + */ +public class TopiaGeneratorUtil extends JavaGeneratorUtil { + + /** Logger */ + private static final Log log = LogFactory.getLog(TopiaGeneratorUtil.class); + + /** + * dependency to add extra operations for entity dao. + * + * @since 2.3.4 + */ + public static final String DEPENDENCIES_DAO = "dao"; + + /** Type de persistence Hibernate */ + public static final String PERSISTENCE_TYPE_HIBERNATE = "hibernate"; + + /** Type de persistence LDAP */ + public static final String PERSISTENCE_TYPE_LDAP = "ldap"; + + /** Type de persistence par défaut (si aucun précisé) */ + public static final String PERSISTENCE_TYPE_DEFAULT = PERSISTENCE_TYPE_HIBERNATE; + + /** Propriété des générateurs indiquant le package par défaut */ + public static final String PROPERTY_DEFAULT_PACKAGE = "defaultPackage"; + + /** Le package par défaut si aucun n'est spécifié */ + public static final String DEFAULT_PACKAGE = "org.codelutin.malo"; + + /** + * Renvoie le package par défaut pour le générateur donné + * + * @param generator le générateur donné + * @return le package par défaut du générator donné + */ + public static String getDefaultPackage(AbstractGenerator<?> generator) { + String packageName = generator.getProperty(PROPERTY_DEFAULT_PACKAGE); + if (packageName == null || "".equals(packageName)) { + packageName = DEFAULT_PACKAGE; + } + return packageName; + } + + /** + * Renvoie l'interface DAO associée à la classe passée en paramètre + * + * @param clazz la classe à tester + * @param model le modele utilisé + * @return l'interface trouvée ou null sinon + */ + public static ObjectModelInterface getDAOInterface(ObjectModelClass clazz, + ObjectModel model) { + for (Object o : model.getInterfaces()) { + ObjectModelInterface daoInterface = (ObjectModelInterface) o; + if (daoInterface.getName().equals(clazz.getName() + "DAO")) { + if (hasDaoStereotype(daoInterface)) { + return daoInterface; + } + } + } + return null; + } + + /** + * Renvoie le type de persistence pour l'élément donné. Si aucun n'est + * trouvé, le type par défaut est utilisé + * + * @param element l'élément à tester + * @return le type de persitence pour l'élément donné. + * @deprecated since 2.5, prefer use the method {@link #getPersistenceType(ObjectModelClassifier)} + */ + @Deprecated + public static String getPersistenceType(ObjectModelElement element) { + String tag = element.getTagValue(TopiaTagValues.TAG_PERSISTENCE_TYPE); + if (tag == null) { + tag = PERSISTENCE_TYPE_DEFAULT; + } + return tag; + } + + /** + * Renvoie le type de persistence pour le classifier donné. Si aucun n'est + * trouvé, le type par défaut est utilisé + * + * @param classifier l'élément à tester + * @return le type de persitence pour l'élément donné. + * @since 2.5 + */ + public static String getPersistenceType(ObjectModelClassifier classifier) { + String tag = getPersistenceTypeTagValue(classifier); + if (StringUtils.isEmpty(tag)) { + tag = PERSISTENCE_TYPE_DEFAULT; + } + return tag; + } + + /** + * @param attr the attribute to inspece + * @return the name of the name in db of the reverse attribute + * @deprecated since 2.5, prefer use the methode {@link #getReverseDbName(ObjectModelAttribute)} + */ + @Deprecated + public static String getReverseDBName(ObjectModelAttribute attr) { + return getReverseDbName(attr); + } + + /** + * Obtain the reverse db name of an attribute. + * <p/> + * Try first to get the reverse db Name from the ReverseDbname tag-vlaue, then + * If attribute has a specific reverse attribute, use his db name, otherwise + * suffix the db name of the attribute by {@code _id}. + * + * @param attr the attribute to seek + * @return the value of the reverse name + * @since 2.5 + */ + public static String getReverseDbName(ObjectModelAttribute attr) { + + String result = getReverseDbNameTagValue(attr); + if (StringUtils.isEmpty(result)) { + if (attr.getReverseAttribute() != null) { + result = getDbName(attr.getReverseAttribute()); + } else { + result = getDbName(attr) + "_id"; + } + } + return result; + + } + + /** + * Renvoie le nom BD de l'élement passé en paramètre. Elle se base sur le + * tag associé si il existe, sinon sur le nom de l'élément + * + * @param element l'élément à tester + * @return le nom de table + * @deprecated since 2.5, prefer use the method {@link #getDbName(ObjectModelElement)} + */ + @Deprecated + public static String getDBName(ObjectModelElement element) { + return getDbName(element); + } + + /** + * Renvoie le nom BD de l'élement passé en paramètre. Elle se base sur le + * tag associé si il existe, sinon sur le nom de l'élément + * + * @param element l'élément à tester + * @return le nom de table + */ + public static String getDbName(ObjectModelElement element) { + if (element == null) { + return null; + } + String value = getDbNameTagValue(element); + if (!StringUtils.isEmpty(value)) { + return value; + } + return toLowerCaseFirstLetter(element.getName()); + } + + /** + * Cherche et renvoie le schema a utiliser sur cet element, sinon sur le + * model. + * + * @param element l'élément à tester + * @param model le modele utilisé + * @return le nom du schema ou null + * @deprecated since 2.5, prefer use the method {@link #getDbSchemaNameTagValue(ObjectModelClassifier, ObjectModel)} + */ + @Deprecated + public static String getSchemaName(ObjectModelElement element, + ObjectModel model) { + return findTagValue(TopiaTagValues.TAG_SCHEMA_NAME, element, model); + } + + /** + * Cherche et renvoie le prefixe i18n à utiliser sur cet element, sinon sur + * le model. + * + * @param element l'élément à tester + * @param model le modele utilisé + * @return le prefix i18n ou <code>null</code> si non spécifié + * @deprecated since 2.5, prefer use the {@link JavaGeneratorUtil#getI18nPrefixTagValue(ObjectModelElement, ObjectModelPackage, ObjectModel)} + */ + @Deprecated + public static String getI18nPrefix(ObjectModelElement element, + ObjectModel model) { + return JavaGeneratorUtil.getI18nPrefixTagValue(element, null, model); + } + + /** + * Cherche si le tagvalue {@link TopiaTagValues#TAG_GENERATE_OPERATOR_FOR_DAO_HELPER} a été + * activé dans le model. + * + * @param element l'élément à tester + * @param model le modele utilisé + * @return {@code true} si le tag value trouvé dans le modèle, {@code false} + * sinon. + * @deprecated since 2.5, use now the method {@link #shouldGenerateOperatorForDAOHelper(ObjectModel)} + */ + @Deprecated + public static boolean shouldgenerateOperatorForDAOHelper( + ObjectModelElement element, ObjectModel model) { + return shouldGenerateOperatorForDAOHelper(model); +// String tagValue = GeneratorUtil.findTagValue( +// TopiaTagValues.TAG_GENERATE_OPERATOR_FOR_DAO_HELPER, element, model); +// boolean generate = StringUtils.isNotEmpty(tagValue) && +// Boolean.valueOf(tagValue); +// return generate; + } + + /** + * Cherche si le tagvalue {@link TopiaTagValues#TAG_GENERATE_OPERATOR_FOR_DAO_HELPER} a été + * activé dans le model. + * + * @param model le modele utilisé + * @return {@code true} si le tag value trouvé dans le modèle, {@code false} + * sinon. + * @since 2.5 + */ + public static boolean shouldGenerateOperatorForDAOHelper(ObjectModel model) { + String tagValue = getGenerateOperatorForDAOHelperTagValue(model); + boolean generate = StringUtils.isNotEmpty(tagValue) && + Boolean.valueOf(tagValue); + return generate; + } + + /** + * Cherche si le tagvalue {@link TopiaTagValues#TAG_GENERATE_OPERATOR_FOR_DAO_HELPER} a été + * activé dans le model. + * + * @param element l'élément à tester + * @param model le modele utilisé + * @return {@code true} si le tag value trouvé dans le modèle, {@code false} + * sinon. + * @since 2.4.1 + * @deprecated since 2.5, prefer use the method {@link #shouldGenerateStandaloneEnumForDAOHelper(ObjectModel)} + */ + @Deprecated + public static boolean shouldGnerateStandaloneEnumForDAOHelper( + ObjectModelElement element, + ObjectModel model) { + return shouldGenerateStandaloneEnumForDAOHelper(model); +// String tagValue = GeneratorUtil.findTagValue( +// TopiaTagValues.TAG_GENERATE_STANDALONE_ENUM_FOR_DAO_HELPER, element, model); +// boolean generate = GeneratorUtil.notEmpty(tagValue) && +// Boolean.valueOf(tagValue); +// return generate; + } + + /** + * Cherche si le tagvalue {@link TopiaTagValues#TAG_GENERATE_OPERATOR_FOR_DAO_HELPER} a été + * activé dans le model. + * + * @param model le modele utilisé + * @return {@code true} si le tag value trouvé dans le modèle, {@code false} + * sinon. + * @since 2.5 + */ + public static boolean shouldGenerateStandaloneEnumForDAOHelper(ObjectModel model) { + String tagValue = getGenerateStandaloneEnumForDAOHelperTagValue(model); + boolean generate = StringUtils.isNotEmpty(tagValue) && + Boolean.valueOf(tagValue); + return generate; + } + + /** + * Cherche et renvoie la liste des attributs constituant la clef metier + * d'une classe. + * + * @param clazz la classe à tester + * @return la liste des attributs de la clef métier + */ + public static Set<ObjectModelAttribute> getNaturalIdAttributes( + ObjectModelClass clazz) { + + // use {@link LinkedHashSet} to keep order and prevent duplicate natural ids found + Set<ObjectModelAttribute> results = + new LinkedHashSet<ObjectModelAttribute>(); + for (ObjectModelAttribute attr : clazz.getAttributes()) { + if (isNaturalId(attr)) { + results.add(attr); + } + } + + // sletellier : #2050 Natural id and not null attributes are not propagated on generalized entities (http://nuiton.org/issues/2050) + Collection<ObjectModelClass> superclasses = clazz.getSuperclasses(); + for (ObjectModelClass superClass : superclasses) { + Set<ObjectModelAttribute> naturalIdsOfSuperClass = getNaturalIdAttributes(superClass); + results.addAll(naturalIdsOfSuperClass); + } + return results; + } + + /** + * Cherche et renvoie la liste des attributs qui ne doivent pas etre null dans + * une classe. + * + * @param clazz la classe à tester + * @return la liste des attributs qui ne doivent pas etre null + */ + public static Set<ObjectModelAttribute> getNotNullAttributes( + ObjectModelClass clazz) { + + // use {@link LinkedHashSet} to keep order and prevent duplicate not null found + Set<ObjectModelAttribute> results = + new LinkedHashSet<ObjectModelAttribute>(); + for (ObjectModelAttribute attr : clazz.getAttributes()) { + if (isNotNull(attr)) { + results.add(attr); + } + } + + Collection<ObjectModelClass> superclasses = clazz.getSuperclasses(); + for (ObjectModelClass superClass : superclasses) { + Set<ObjectModelAttribute> notNullOfSuperClass = getNotNullAttributes(superClass); + results.addAll(notNullOfSuperClass); + } + return results; + } + + /** + * Test if we need to generate {@code toString} method for the given class. + * + * @param clazz class to test + * @param model model + * @return {@code true} if {@code toString} should be generated. + * clef métier. + */ + public static boolean generateToString(ObjectModelClass clazz, + ObjectModel model) { + String value = getNotGenerateToStringTagValue(clazz, model); + return StringUtils.isEmpty(value); +// String value; +// value = model.getTagValue(TAG_NOT_GENERATE_TO_STRING); +// if (value != null && !value.trim().isEmpty()) { +// return false; +// } +// value = clazz.getTagValue(TAG_NOT_GENERATE_TO_STRING); +// return value == null || value.trim().isEmpty(); + } + + + /** + * Cherche et renvoie la liste des attributs constituant la clef metier + * d'une classe. + * + * @param clazz la classe à tester + * @param model le modele + * @return la liste des attributs de la clef métier ou null si pas de + * clef métier. + */ + public static boolean sortAttribute(ObjectModelClass clazz, + ObjectModel model) { + String value = getSortAttributeTagValue(clazz, model); + return "true".equals(value); +// String value; +// value = clazz.getTagValue(TopiaTagValues.TAG_SORT_ATTRIBUTE); +// if (value == null || value.trim().isEmpty() || +// "false".equals(value.trim())) { +// return false; +// } +// if ("true".equals(value.trim())) { +// return true; +// } +// +// value = model.getTagValue(TopiaTagValues.TAG_SORT_ATTRIBUTE); +// if (value == null || value.trim().isEmpty() || +// "false".equals(value.trim())) { +// return false; +// } +// if ("true".equals(value.trim())) { +// return true; +// } +// return true; + } + + /** + * Detecte si un attribut fait partie d'une clef metier. + * + * @param attribute l'attribut à tester + * @return {@code true} si l'attribut fait partie d'une clef metier, + * <code>false</cdoe> sinon. + */ + public static boolean isNaturalId(ObjectModelAttribute attribute) { + String value = getNaturalIdTagValue(attribute); + if (StringUtils.isEmpty(value)) { + // valeur null, donc pas positionnee + return false; + } + try { + return Boolean.valueOf(value.trim()); + } catch (Exception e) { + // on a pas reussi a convertir en boolean. + //todo peut-être declancher une exception ? + return false; + } + } + + /** + * Detecte si un attribut est marqué comme non null. + * Les naturalId {@link #isNaturalId} sont not null par défaut + * + * @param attribute l'attribut à tester + * @return {@code true} si l'attribut doit être non null, + * par défaut pour les naturalId, {@code false} sinon.. + * @since 2.6.9 + */ + public static boolean isNotNull(ObjectModelAttribute attribute) { + String value = getNotNullTagValue(attribute); + if (StringUtils.isEmpty(value)) { + // valeur null, donc pas positionnee + return isNaturalId(attribute); + } + try { + return Boolean.valueOf(value.trim()); + } catch (Exception e) { + // on a pas reussi a convertir en boolean. + //todo peut-être declancher une exception ? + return false; + } + } + + /** + * Cherches et renvoie le copyright a utiliser sur le model. + * + * @param model le modele utilisé + * @return le texte du copyright ou null$ + * @deprecated since 2.5 never use anywhere + */ + @Deprecated + public static String getCopyright(Model model) { + return findTagValue(TopiaTagValues.TAG_COPYRIGHT, null, model); + } + + public static <Type extends ObjectModelElement> Collection<Type> getElementsWithStereotype( + Collection<Type> elements, String... stereotypes) { + Collection<Type> result = new ArrayList<Type>(); + for (Type element : elements) { + if (hasStereotypes(element, stereotypes)) { + result.add(element); + } + } + return result; + } + + public static boolean hasStereotypes(ObjectModelElement element, + String... stereotypes) { + for (String stereotype : stereotypes) { + if (!element.hasStereotype(stereotype)) { + return false; + } + } + return true; + } + + public static String getPrimaryKeyAttributesListDeclaration( + ObjectModelClass clazz, boolean includeName) { + String attributes = ""; + Collection<ObjectModelAttribute> attributeCollection; + attributeCollection = getElementsWithStereotype(clazz.getAttributes(), + TopiaStereoTypes.STEREOTYPE_PRIMARYKAY); + for (ObjectModelAttribute attr : attributeCollection) { + attributes += attr.getType(); + if (includeName) { + attributes += ' ' + attr.getName(); + } + attributes += ", "; + } + if (attributes.length() > 0) { + attributes = attributes.substring(0, attributes.length() - 2); + } + return attributes; + } + +// public static String capitalize(String s) { +// return StringUtils.capitalize(s); +// } + + public static boolean isAssociationClassDoublon(ObjectModelAttribute attr) { + return attr.getReverseAttribute() != null && + attr.getDeclaringElement().equals( + attr.getReverseAttribute().getDeclaringElement()) && + !GeneratorUtil.isFirstAttribute(attr); + } + + public static String getDOType(ObjectModelElement elem, ObjectModel model) { + String type = elem.getName(); + if (elem instanceof ObjectModelAttribute) { + type = ((ObjectModelAttribute) elem).getType(); + } + if (elem instanceof ObjectModelClass) { + type = ((ObjectModelClass) elem).getQualifiedName(); + } + return getDOType(type, model); + } + + public static String getDOType(String type, ObjectModel model) { + if (!model.hasClass(type)) { + return type; + } + ObjectModelClass clazz = model.getClass(type); + if (isEntity(clazz)) { + //tchemit-2011-09-12 What ever abstract or not, we alwyas use an Impl + type += "Impl"; +// if (shouldBeAbstract(clazz)) { +// type += "Abstract"; +// } else { +// type += "Impl"; +// } + } + return type; + } + + private static final Set<String> numberTypes = new HashSet<String>(); + + private static final Set<String> textTypes = new HashSet<String>(); + + private static final Set<String> booleanTypes = new HashSet<String>(); + + private static final Set<String> primitiveTypes = new HashSet<String>(); + + private static final String VOID_TYPE = "void"; + + static { + numberTypes.add("byte"); + numberTypes.add("java.lang.Byte"); + numberTypes.add("Byte"); + numberTypes.add("short"); + numberTypes.add("java.lang.Short"); + numberTypes.add("Short"); + numberTypes.add("int"); + numberTypes.add("java.lang.Integer"); + numberTypes.add("Integer"); + numberTypes.add("long"); + numberTypes.add("java.lang.Long"); + numberTypes.add("Long"); + numberTypes.add("float"); + numberTypes.add("java.lang.Float"); + numberTypes.add("Float"); + numberTypes.add("double"); + numberTypes.add("java.lang.Double"); + numberTypes.add("Double"); + + textTypes.add("char"); + textTypes.add("java.lang.Char"); + textTypes.add("Char"); + textTypes.add("java.lang.String"); + textTypes.add("String"); + + booleanTypes.add("boolean"); + booleanTypes.add("java.lang.Boolean"); + booleanTypes.add("Boolean"); + + primitiveTypes.addAll(numberTypes); + primitiveTypes.addAll(textTypes); + primitiveTypes.addAll(booleanTypes); + } + + public static boolean isNumericType(ObjectModelAttribute attr) { + return numberTypes.contains(attr.getType()); + } + + public static boolean isTextType(ObjectModelAttribute attr) { + return textTypes.contains(attr.getType()); + } + + public static boolean isDateType(ObjectModelAttribute attr) { + return "java.util.Date".equals(attr.getType()); + } + + public static boolean isBooleanType(ObjectModelAttribute attr) { + return booleanTypes.contains(attr.getType()); + } + + public static boolean isPrimitiveType(ObjectModelAttribute attr) { + return primitiveTypes.contains(attr.getType()); + } + + /** + * Indique si la classe specifiee n'a aucune ou que des methodes abstraites + * + * @param clazz l'instance de ObjectModelClass + * @return true si la classe n'a que des operations abstraite ou aucune + * operation + */ + public static boolean hasNothingOrAbstractMethods(ObjectModelClass clazz) { + boolean result = true; + Iterator<?> operations = clazz.getOperations().iterator(); + while (result && operations.hasNext()) { + ObjectModelOperation op = (ObjectModelOperation) operations.next(); + result = op.isAbstract(); + } + return result; + } + + /** + * Indique si la classe specifiee devrait etre abstraite + * + * @param clazz l'instance de ObjectModelClass + * @return true dans ce cas, false sinon + */ + public static boolean shouldBeAbstract(ObjectModelClass clazz) { + return clazz != null && clazz.isAbstract() && + hasNothingOrAbstractMethods(clazz); + } + + /** + * <p> + * Cette méthode permet de détecter si + * - l'attribut représente une relation 1-n + * - cette relation est unidirectionnelle + * - le type de l'attribut représente un entité + * - cette entité a des sous-classes dans le modèle + * <p/> + * Ce cas correspond à une incompatibilité d'Hibernate qui nous oblige a + * adopter un comportement particulier. + * </p> + * + * @param attr l'attribut a tester + * @param model le model + * @return true si et seulement si il s'agit bien de ce type de relation + */ + public static boolean hasUnidirectionalRelationOnAbstractType( + ObjectModelAttribute attr, ObjectModel model) { + ObjectModelAttribute reverse = attr.getReverseAttribute(); + //relation 1-n + if (reverse != null && isNMultiplicity(attr) && + !isNMultiplicity(reverse)) { + //Pas de navigabilité + if (!reverse.isNavigable()) { + //Il s'agit d'une entity + ObjectModelClass clazz = model.getClass(attr.getType()); + if (clazz != null && isEntity(clazz)) { + //Cette classe a des sous-classes dans le modèle + for (ObjectModelClass subClass : model.getClasses()) { + if (subClass.getSuperclasses().contains(clazz)) { + return true; + } + } + } + } + } + return false; + } + + /** + * Renvoie le nom unique de table pour une relation ManyToMany en fonction + * de l'attribut <code>attr</code> + * <p/> + * Plusieurs cas de figure: + * <li> + * + * @param attr l'attribut servant de base au calcul du nom + * @return le nom de la table + */ + public static String getManyToManyTableName(ObjectModelAttribute attr) { + String result; + + if (attr.hasAssociationClass()) { + result = getDbName(attr.getAssociationClass()); + } else { + result = getManytoManyTableNameTagValue(attr); + if (StringUtils.isEmpty(result)) { + String name = attr.getName(); + String revers = attr.getReverseAttributeName(); + + if (name.compareToIgnoreCase(revers) < 0) { + result = name + '_' + revers; + } else { + result = revers + '_' + name; + } + } + } + return result.toLowerCase(); + } + + /** + * Renvoie le type d'interface à utiliser en fonction de l'attribut + * + * @param attr l'attribut a traiter + * @return String + */ + public static String getNMultiplicityInterfaceType( + ObjectModelAttribute attr) { + if (hasUniqueStereotype(attr)) { + return Set.class.getName(); + } else if (EugeneStereoTypes.hasIndexedStereotype(attr) || attr.isOrdered()) { + return List.class.getName(); + } + return Collection.class.getName(); + } + + /** + * Renvoie le type d'objet (instance) à utiliser en fonction de l'attribut + * + * @param attr l'attribut a traiter + * @return String + */ + public static String getNMultiplicityObjectType(ObjectModelAttribute attr) { + if (hasUniqueStereotype(attr)) { + return HashSet.class.getName(); + } else if (EugeneStereoTypes.hasIndexedStereotype(attr) || attr.isOrdered()) { + //On considère qu'on ne sait pas traiter vraiment l'attribut "ordered" + // puisqu'on va conserver l'ordre d'insertion, et non un ordre en + // fonction d'un élément donné. Donc on renvoi une ArrayList + return ArrayList.class.getName(); + } + LinkedList.class.getName(); + return ArrayList.class.getName(); + } + + @Deprecated + public static boolean hasIndexedStereotype(ObjectModelAttribute attr) { + + return EugeneStereoTypes.hasIndexedStereotype(attr); + } + + /** + * Renvoie le type d'interface à utiliser en fonction de l'attribut + * + * @param attr l'attribut a traiter + * @return String + */ + public static String getNMultiplicityHibernateType( + ObjectModelAttribute attr) { + if (hasUniqueStereotype(attr)) { + return "set"; + } else if (EugeneStereoTypes.hasIndexedStereotype(attr)) { + return "list"; + } + //attr.isOrdered() - On génère le ordered en bag + return "bag"; + } + + /** + * Obtain the list of entities classes with the possibility to sort the + * result. + * + * @param model the current model to scan + * @param sort flag to allow sort the result + * @return the list of filtred classes by their stereotype + */ + public static List<ObjectModelClass> getEntityClasses(ObjectModel model, + boolean sort) { + return getClassesByStereotype(TopiaStereoTypes.STEREOTYPE_ENTITY, model, sort); + } + + /** + * Obtain the list of classes for a given stereotype with the possibility + * to sort the result. + * + * @param stereotype filter stereotype + * @param model the current model to scan + * @param sort flag to allow sort the result + * @return the list of filtred classes by their stereotype + */ + public static List<ObjectModelClass> getClassesByStereotype( + String stereotype, ObjectModel model, boolean sort) { + List<ObjectModelClass> classes = new ArrayList<ObjectModelClass>(); + for (ObjectModelClass clazz : model.getClasses()) { + if (clazz.hasStereotype(stereotype)) { + classes.add(clazz); + } + } + if (sort && !classes.isEmpty()) { + + Collections.sort(classes, OBJECT_MODEL_CLASS_COMPARATOR); + } + return classes; + } + + static public final Comparator<ObjectModelClass> + OBJECT_MODEL_CLASS_COMPARATOR = + new Comparator<ObjectModelClass>() { + + @Override + public int compare(ObjectModelClass o1, + ObjectModelClass o2) { + return o1.getQualifiedName().compareTo( + o2.getQualifiedName()); + } + }; + + /** + * Detecte si la clef metier d'une classe est mutable ou pas. + * <p/> + * On respecte la valeur par defaut d'hibernate, à savoir que par default + * une clef metier est non mutable. + * + * @param clazz la classe a tester + * @return {@code true} si le tag value a ete positionne sur la classe + * via le tag {@link TopiaTagValues#TAG_NATURAL_ID_MUTABLE}, {@code false} + * sinon. + */ + public static boolean isNaturalIdMutable(ObjectModelClass clazz) { + String value = getNaturalIdMutableTagValue(clazz); + if (StringUtils.isEmpty(value)) { + // valeur null, donc par default positionnee + return false; + } + try { + return Boolean.valueOf(value.trim()); + } catch (Exception e) { + // on a pas reussi a convertir en boolean. + //todo peut-être declancher une exception ? + return false; + } + } + + /** + * Retourne true si le tagValue {@link TopiaTagValues#TAG_CONTEXTABLE} + * à la valeur {@code true}. + * + * @param classifier classifier to test + * @return {@code true} si {@link TopiaTagValues#TAG_CONTEXTABLE} == {@code true} + */ + public static boolean isContextable(ObjectModelClassifier classifier) { + boolean result = false; + String value = classifier.getTagValue(TopiaTagValues.TAG_CONTEXTABLE); + if (StringUtils.equalsIgnoreCase(value, "true")) { + result = true; + } + return result; + } + + /** + * Obtain the list of fqn of object involed in the given class. + * + * @param aClass the clazz to inspect + * @param incomingFqns incoming fqns + * @return the list of fqn of attributes + */ + public static List<String> getImports(ObjectModelClass aClass, + String... incomingFqns) { + Set<String> tmp = new HashSet<String>(); + tmp.addAll(Arrays.asList(incomingFqns)); + getImports(aClass, tmp); + List<String> result = cleanImports(aClass.getPackageName(), tmp); + return result; + } + + /** + * Obtain the list of fqn of object involed in the given interface. + * + * @param anInterface the interface to inspect + * @param incomingFqns incoming fqns + * @return the list of fqn of attributes + */ + public static List<String> getImports(ObjectModelInterface anInterface, + String... incomingFqns) { + Set<String> tmp = new HashSet<String>(); + tmp.addAll(Arrays.asList(incomingFqns)); + getImports(anInterface, tmp); + List<String> result = cleanImports(anInterface.getPackageName(), tmp); + return result; + } + + /** + * Obtain the list of fqn of object involed in the given class. + * + * @param aClass the class to inspect + * @param fqns where to store found fqns + */ + protected static void getImports(ObjectModelClass aClass, + Set<String> fqns) { + // scan attributes + for (ObjectModelAttribute attr : aClass.getAttributes()) { + fqns.add(attr.getType()); + if (isNMultiplicity(attr)) { + String collectionType = getNMultiplicityInterfaceType(attr); + fqns.add(collectionType); + String collectionObject = getNMultiplicityObjectType(attr); + fqns.add(collectionObject); + } + } + for (ObjectModelAttribute attribute : aClass.getAllOtherAttributes()) { + fqns.add(attribute.getType()); + } + // scan associations + if (aClass instanceof ObjectModelAssociationClass) { + ObjectModelAssociationClass assoc = + (ObjectModelAssociationClass) aClass; + for (ObjectModelAttribute attr : + assoc.getParticipantsAttributes()) { + if (attr == null) { + continue; + } + fqns.add(attr.getType()); + if (isNMultiplicity(attr)) { + String collectionType = getNMultiplicityInterfaceType(attr); + fqns.add(collectionType); + String collectionObject = getNMultiplicityObjectType(attr); + fqns.add(collectionObject); + } + } + } + // scan operations + for (ObjectModelOperation operation : aClass.getOperations()) { + getImports(operation, fqns); + } + // scan super interfaces + for (ObjectModelInterface modelInterface : aClass.getInterfaces()) { + fqns.add(modelInterface.getQualifiedName()); + getImports(modelInterface, fqns); + } + // scan super classes + for (ObjectModelClass modelClass : aClass.getSuperclasses()) { + fqns.add(modelClass.getQualifiedName()); + getImports(modelClass); + } + } + + /** + * Obtain the list of fqn of object involed in the given interface. + * + * @param anInterface the interface to inspect + * @param fqns where to store found fqns + */ + protected static void getImports(ObjectModelInterface anInterface, + Set<String> fqns) { + // scan operations + for (ObjectModelOperation operation : anInterface.getOperations()) { + getImports(operation, fqns); + } + // scan super interfaces + for (ObjectModelInterface modelInterface : anInterface.getInterfaces()) { + fqns.add(modelInterface.getQualifiedName()); + getImports(modelInterface, fqns); + } + } + + /** + * Obtain the fqn's list of all involed type in a givne operation. + * + * @param operation operation to inspect + * @param fqns where to store found fqns + */ + protected static void getImports(ObjectModelOperation operation, + Set<String> fqns) { + String fqn = operation.getReturnType(); + fqns.add(fqn); + for (ObjectModelParameter parameter : operation.getParameters()) { + fqns.add(parameter.getType()); + } + } + + /** + * Clean a set of fqns, transform it into a {@link List} and sort it. + * + * @param packageName the current package name + * @param fqns the dirty set of fqns + * @return the sorted cleaned list of fqns. + */ + protected static List<String> cleanImports(String packageName, + Set<String> fqns) { + fqns.removeAll(primitiveTypes); + fqns.remove(VOID_TYPE); + int packageLength = packageName.length(); + List<String> genericType = new ArrayList<String>(); + for (Iterator<String> it = fqns.iterator(); it.hasNext(); ) { + String fqn = it.next(); + int lastIndex = fqn.lastIndexOf("."); + if (lastIndex == packageLength && fqn.startsWith(packageName)) { + // same package + it.remove(); + continue; + } + int genericIndex = fqn.indexOf('<'); + if (genericIndex != -1) { + genericType.add(fqn.substring(0, genericIndex)); + it.remove(); + } + } + fqns.addAll(genericType); + + ArrayList<String> result = new ArrayList<String>(fqns); + Collections.sort(result); + return result; + } + + /** + * Obtain the class to use as abstract dao. + * <p/> + * It will look after a tag value {@link TopiaTagValues#TAG_DAO_IMPLEMENTATION} in model + * and if not found will use the default value which is {@link TopiaDAOImpl}. + * + * @param model the model which could contains + * @return the type of the abstract dao to use + * @since 2.5 + */ + public static Class<?> getDAOImplementation(ObjectModel model) { + String daoImpl = getDaoImplementationTagValue(model); + Class<?> result; + if (StringUtils.isEmpty(daoImpl)) { + + // use the default dao implementation of topia + result = TopiaDAOImpl.class; + } else { + try { + result = Class.forName(daoImpl); + } catch (ClassNotFoundException e) { + String message = "Could not find dao implementation named " + daoImpl; + log.error(message); + throw new IllegalStateException(message, e); + } + } + return result; + } + + public static Map<ObjectModelClass, Set<ObjectModelClass>> + searchDirectUsages(ObjectModel model) { + List<ObjectModelClass> allEntities; + Map<String, ObjectModelClass> allEntitiesByFQN; + Map<ObjectModelClass, Set<ObjectModelClass>> usages; + + allEntities = getEntityClasses(model, true); + + allEntitiesByFQN = new TreeMap<String, ObjectModelClass>(); + usages = new LinkedHashMap<ObjectModelClass, Set<ObjectModelClass>>(); + + // prepare usages map and fill allEntitiesByFQN map + for (ObjectModelClass klass : allEntities) { + usages.put(klass, new HashSet<ObjectModelClass>()); + allEntitiesByFQN.put(klass.getQualifiedName(), klass); + } + + // first pass to detect direct usages + for (ObjectModelClass klass : allEntities) { + searchDirectUsages(klass, allEntitiesByFQN, usages); + } + allEntities.clear(); + allEntitiesByFQN.clear(); + return usages; + + } + + public static void searchDirectUsages( + ObjectModelClass klass, + Map<String, ObjectModelClass> allEntitiesByFQN, + Map<ObjectModelClass, Set<ObjectModelClass>> usages) { + + if (log.isDebugEnabled()) { + log.debug("for entity " + klass.getQualifiedName()); + } + for (ObjectModelAttribute attr : klass.getAttributes()) { + if (!attr.isNavigable()) { + // skip this case + continue; + } + String type; + if (attr.hasAssociationClass()) { + type = attr.getAssociationClass().getQualifiedName(); + } else { + type = attr.getType(); + } + if (!allEntitiesByFQN.containsKey(type)) { + // not a entity, can skip for this attribute + continue; + } + if (log.isDebugEnabled()) { + log.debug(" uses " + type); + } + // register the klass as using the targetEntity + ObjectModelClass targetEntity = allEntitiesByFQN.get(type); + Set<ObjectModelClass> classes = usages.get(targetEntity); + classes.add(klass); + } + } + + public static boolean isImportNeeded(Collection<ObjectModelOperation> operations, + String importName) { + if (CollectionUtils.isNotEmpty(operations)) { + for (ObjectModelOperation op : operations) { + if (op.getReturnType().contains(importName)) { + return true; + } + for (ObjectModelParameter param : op.getParameters()) { + if (param.getType().contains(importName)) { + return true; + } + } + } + } + return false; + } + + public static boolean isCollectionNeeded( + Collection<ObjectModelOperation> operations) { + return isImportNeeded(operations, + Collection.class.getSimpleName()); + } + + public static boolean isSetNeeded(Collection<ObjectModelOperation> operations) { + return isImportNeeded(operations, + Set.class.getSimpleName()); + } + + /** + * Check if the given classifier has the + * {@link TopiaStereoTypes#STEREOTYPE_FACADE} stereotype. + * + * @param classifier classifier to test + * @return {@code true} if stereotype was found, {@code false otherwise} + * @see TopiaStereoTypes#STEREOTYPE_FACADE + * @since 2.5 + */ + public static boolean hasFacadeStereotype(ObjectModelClassifier classifier) { + return classifier.hasStereotype(TopiaStereoTypes.STEREOTYPE_FACADE); + } + + /** + * Check if the given classifier has the + * {@link TopiaStereoTypes#STEREOTYPE_ENTITY} stereotype. + * + * @param classifier classifier to test + * @return {@code true} if stereotype was found, {@code false} otherwise + * @see TopiaStereoTypes#STEREOTYPE_ENTITY + * @since 2.5 + */ + public static boolean hasEntityStereotype(ObjectModelClassifier classifier) { + return classifier.hasStereotype(TopiaStereoTypes.STEREOTYPE_ENTITY); + } + + /** + * Check if the given attribute type is an entity. + * + * @param attribute attribute to test + * @param model model containing the attribute + * @return {@code true} if type of attribute is an entity, + * {@code false} otherwise + * @see TopiaStereoTypes#STEREOTYPE_ENTITY + * @since 2.7 + */ + public static boolean isEntity(ObjectModelAttribute attribute, + ObjectModel model) { + if (isPrimitiveType(attribute)) { + return false; + } + String attributeType = attribute.getType(); + ObjectModelClassifier typeclassifier = + model.getClassifier(attributeType); + return typeclassifier != null && isEntity(typeclassifier); + } + + /** + * Check if the given classifier has the + * {@link TopiaStereoTypes#STEREOTYPE_ENTITY} and is not an enumeration + * + * @param classifier classifier to test + * @return {@code true} if stereotype was found and classifier is not + * enumeration, {@code false} otherwise + * @see TopiaStereoTypes#STEREOTYPE_ENTITY + * @since 2.5 + */ + public static boolean isEntity(ObjectModelClassifier classifier) { + return hasEntityStereotype(classifier) && !classifier.isEnum(); + } + + + /** + * Check if the given attribute has the + * {@link TopiaStereoTypes#STEREOTYPE_ENTITY} stereotype. + * + * @param attribute attribute to test + * @return {@code true} if stereotype was found, {@code false otherwise} + * @see TopiaStereoTypes#STEREOTYPE_ENTITY + * @since 2.5 + */ + public static boolean hasEntityStereotype(ObjectModelAttribute attribute) { + return attribute.hasStereotype(TopiaStereoTypes.STEREOTYPE_ENTITY); + } + + /** + * Check if the given classifier has the + * {@link TopiaStereoTypes#STEREOTYPE_DTO} stereotype. + * + * @param classifier classifier to test + * @return {@code true} if stereotype was found, {@code false otherwise} + * @see TopiaStereoTypes#STEREOTYPE_DTO + * @since 2.5 + */ + public static boolean hasDtoStereotype(ObjectModelClassifier classifier) { + return classifier.hasStereotype(TopiaStereoTypes.STEREOTYPE_DTO); + } + + /** + * Check if the given classifier has the + * {@link TopiaStereoTypes#STEREOTYPE_SERVICE} stereotype. + * + * @param classifier classifier to test + * @return {@code true} if stereotype was found, {@code false otherwise} + * @see TopiaStereoTypes#STEREOTYPE_SERVICE + * @since 2.5 + */ + public static boolean hasServiceStereotype(ObjectModelClassifier classifier) { + return classifier.hasStereotype(TopiaStereoTypes.STEREOTYPE_SERVICE); + } + + /** + * Check if the given classifier has the + * {@link TopiaStereoTypes#STEREOTYPE_DAO} stereotype. + * + * @param classifier classifier to test + * @return {@code true} if stereotype was found, {@code false otherwise} + * @see TopiaStereoTypes#STEREOTYPE_DAO + * @since 2.5 + */ + public static boolean hasDaoStereotype(ObjectModelClassifier classifier) { + return classifier.hasStereotype(TopiaStereoTypes.STEREOTYPE_DAO); + } + + /** + * Check if the given operation has the + * {@link TopiaStereoTypes#STEREOTYPE_DAO} stereotype. + * + * @param operation operation to test + * @return {@code true} if stereotype was found, {@code false otherwise} + * @see TopiaStereoTypes#STEREOTYPE_DAO + * @since 2.5 + */ + public static boolean hasDaoStereotype(ObjectModelOperation operation) { + return operation.hasStereotype(TopiaStereoTypes.STEREOTYPE_DAO); + } + + /** + * Check if the given attribute has the + * {@link TopiaStereoTypes#STEREOTYPE_UNIQUE} stereotype. + * + * @param attribute attribute to test + * @return {@code true} if stereotype was found, {@code false otherwise} + * @see TopiaStereoTypes#STEREOTYPE_UNIQUE + * @since 2.5 + */ + public static boolean hasUniqueStereotype(ObjectModelAttribute attribute) { + return attribute.hasStereotype(TopiaStereoTypes.STEREOTYPE_UNIQUE); + } + + /** + * Check if the given attribute has the + * {@link TopiaStereoTypes#STEREOTYPE_PRIMARYKAY} stereotype. + * + * @param attribute attribute to test + * @return {@code true} if stereotype was found, {@code false otherwise} + * @see TopiaStereoTypes#STEREOTYPE_PRIMARYKAY + * @since 2.5 + */ + public static boolean hasPrimaryKeyStereotype(ObjectModelAttribute attribute) { + return attribute.hasStereotype(TopiaStereoTypes.STEREOTYPE_PRIMARYKAY); + } + + /** + * Check if the given attribute has the + * {@link TopiaStereoTypes#STEREOTYPE_ARRAY} stereotype. + * + * @param attribute attribute to test + * @return {@code true} if stereotype was found, {@code false otherwise} + * @see TopiaStereoTypes#STEREOTYPE_ARRAY + * @since 2.5 + * @deprecated since 2.5 , only BeanTransformer use it and it is a deprecated transformer, will be remove in version 3.0 + */ + @Deprecated + public static boolean hasArrayStereotype(ObjectModelAttribute attribute) { + return attribute.hasStereotype(TopiaStereoTypes.STEREOTYPE_ARRAY); + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_PERSISTENCE_TYPE} + * tag value on the given classifier. + * <p/> + * + * @param classifier classifier to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_PERSISTENCE_TYPE + * @since 2.5 + */ + public static String getPersistenceTypeTagValue(ObjectModelClassifier classifier) { + String value = findTagValue(TopiaTagValues.TAG_PERSISTENCE_TYPE, classifier, null); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_DB_NAME} + * tag value on the given element. + * <p/> + * + * Note that it won't and search on declaring element or anywhere else than on the given element. + * See https://forge.nuiton.org/issues/2342 + * + * @param element element to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_DB_NAME + * @since 2.5 + */ + public static String getDbNameTagValue(ObjectModelElement element) { + String value = element.getTagValue(TopiaTagValues.TAG_DB_NAME); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_REVERSE_DB_NAME} + * tag value on the given element. + * <p/> + * + * Note that it won't and search on declaring element or anywhere else than on the given element. + * See https://forge.nuiton.org/issues/2342 + * + * @param element element to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_REVERSE_DB_NAME + * @since 2.5 + */ + public static String getReverseDbNameTagValue(ObjectModelAttribute element) { + String value = element.getTagValue(TopiaTagValues.TAG_REVERSE_DB_NAME); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_MANY_TO_MANY_TABLE_NAME} + * tag value on the given attribute. + * <p/> + * + * Note that it won't and search on declaring element or anywhere else than on the given element. + * See https://forge.nuiton.org/issues/2342 + * + * @param attribute attribute to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_MANY_TO_MANY_TABLE_NAME + * @since 2.9.2 + */ + public static String getManytoManyTableNameTagValue(ObjectModelAttribute attribute) { + String value = attribute.getTagValue(TopiaTagValues.TAG_MANY_TO_MANY_TABLE_NAME); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_SCHEMA_NAME} + * tag value on the given classifier. + * <p/> + * + * @param classifier classifier to seek + * @param model model to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_SCHEMA_NAME + * @since 2.5 + */ + public static String getDbSchemaNameTagValue(ObjectModelClassifier classifier, ObjectModel model) { + ObjectModelPackage aPackage = null; + if (classifier!=null) { + aPackage = model.getPackage(classifier); + } + String value = TagValues.findTagValue(TopiaTagValues.TAG_SCHEMA_NAME, model, aPackage , classifier); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_LENGTH} + * tag value on the given attribute. + * <p/> + * + * @param attribute attribute to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_LENGTH + * @since 2.5 + */ + public static String getLengthTagValue(ObjectModelAttribute attribute) { + String value = findTagValue(TopiaTagValues.TAG_LENGTH, attribute, null); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_ANNOTATION} + * tag value on the given attribute. + * <p/> + * + * @param attribute attribute to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_ANNOTATION + * @since 2.5 + */ + public static String getAnnotationTagValue(ObjectModelAttribute attribute) { + String value = findTagValue(TopiaTagValues.TAG_ANNOTATION, attribute, null); + return value; + } + + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_ACCESS} + * tag value on the given attribute. + * <p/> + * + * @param attribute attribute to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_ACCESS + * @since 2.5 + */ + public static String getAccessTagValue(ObjectModelAttribute attribute) { + String value = findTagValue(TopiaTagValues.TAG_ACCESS, attribute, null); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_NATURAL_ID} + * tag value on the given attribute. + * <p/> + * + * @param attribute attribute to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_NATURAL_ID + * @since 2.5 + */ + public static String getNaturalIdTagValue(ObjectModelAttribute attribute) { + String value = findTagValue(TopiaTagValues.TAG_NATURAL_ID, attribute, null); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_NATURAL_ID_MUTABLE} + * tag value on the given classifier. + * <p/> + * + * @param classifier classifier to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_NATURAL_ID_MUTABLE + * @since 2.5 + */ + public static String getNaturalIdMutableTagValue(ObjectModelClassifier classifier) { + String value = findTagValue(TopiaTagValues.TAG_NATURAL_ID_MUTABLE, classifier, null); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_INVERSE} + * tag value on the given attribute. + * <p/> + * + * @param attribute attribute to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_INVERSE + * @since 2.5 + */ + public static String getInverseTagValue(ObjectModelAttribute attribute) { + String value = findTagValue(TopiaTagValues.TAG_INVERSE, attribute, null); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_LAZY} + * tag value on the given attribute. + * <p/> + * + * @param attribute attribute to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_LAZY + * @since 2.5 + */ + public static String getLazyTagValue(ObjectModelAttribute attribute) { + String value = findTagValue(TopiaTagValues.TAG_LAZY, attribute, null); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_FETCH} + * tag value on the given attribute. + * <p/> + * + * @param attribute attribute to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_FETCH + * @since 2.5 + */ + public static String getFetchTagValue(ObjectModelAttribute attribute) { + String value = findTagValue(TopiaTagValues.TAG_FETCH, attribute, null); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_ORDER_BY} + * tag value on the given attribute. + * <p/> + * + * @param attribute attribute to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_ORDER_BY + * @since 2.5 + */ + public static String getOrderByTagValue(ObjectModelAttribute attribute) { + String value = findTagValue(TopiaTagValues.TAG_ORDER_BY, attribute, null); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_NOT_NULL} + * tag value on the given attribute. + * <p/> + * + * @param attribute attribute to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_NOT_NULL + * @since 2.5 + */ + public static String getNotNullTagValue(ObjectModelAttribute attribute) { + String value = findTagValue(TopiaTagValues.TAG_NOT_NULL, attribute, null); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_PROXY_INTERFACE} + * tag value on the given classifier. + * <p/> + * + * @param classifier classifier to seek + * @param model model to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_PROXY_INTERFACE + * @since 2.5 + */ + public static String getProxyInterfaceTagValue(ObjectModelClassifier classifier, ObjectModel model) { + String value = findTagValue(TopiaTagValues.TAG_PROXY_INTERFACE, classifier, model); + return value; + } + + /** + * Tests if the given classifier own at least one security tag value. + * + * @param classifier the classifier to test + * @return {@code true} if there is at least one security tag value on the given class + * @since 2.5 + */ + public static boolean isClassWithSecurity(ObjectModelClassifier classifier) { + return StringUtils.isNotEmpty(getSecurityCreateTagValue(classifier)) || + StringUtils.isNotEmpty(getSecurityLoadTagValue(classifier)) || + StringUtils.isNotEmpty(getSecurityUpdateTagValue(classifier)) || + StringUtils.isNotEmpty(getSecurityDeleteTagValue(classifier)); + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_SECURITY_CREATE} + * tag value on the given classifier. + * <p/> + * + * @param classifier classifier to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_SECURITY_CREATE + * @since 2.5 + */ + public static String getSecurityCreateTagValue(ObjectModelClassifier classifier) { + String value = findTagValue(TopiaTagValues.TAG_SECURITY_CREATE, classifier, null); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_SECURITY_DELETE} + * tag value on the given classifier. + * <p/> + * + * @param classifier classifier to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_SECURITY_DELETE + * @since 2.5 + */ + public static String getSecurityDeleteTagValue(ObjectModelClassifier classifier) { + String value = findTagValue(TopiaTagValues.TAG_SECURITY_DELETE, classifier, null); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_SECURITY_LOAD} + * tag value on the given classifier. + * <p/> + * + * @param classifier classifier to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_SECURITY_LOAD + * @since 2.5 + */ + public static String getSecurityLoadTagValue(ObjectModelClassifier classifier) { + String value = findTagValue(TopiaTagValues.TAG_SECURITY_LOAD, classifier, null); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_SECURITY_UPDATE} + * tag value on the given classifier. + * <p/> + * + * @param classifier classifier to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_SECURITY_UPDATE + * @since 2.5 + */ + public static String getSecurityUpdateTagValue(ObjectModelClassifier classifier) { + String value = findTagValue(TopiaTagValues.TAG_SECURITY_UPDATE, classifier, null); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_NOT_GENERATE_TO_STRING} + * tag value on the given class. + * <p/> + * + * @param clazz class to seek + * @param model model to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_NOT_GENERATE_TO_STRING + * @since 2.5 + */ + public static String getNotGenerateToStringTagValue(ObjectModelClassifier clazz, ObjectModel model) { + String value = findTagValue(TopiaTagValues.TAG_NOT_GENERATE_TO_STRING, clazz, model); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_SORT_ATTRIBUTE} + * tag value on the given classifier. + * <p/> + * + * @param classifier classifier to seek + * @param model model to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_SORT_ATTRIBUTE + * @since 2.5 + */ + public static String getSortAttributeTagValue(ObjectModelClassifier classifier, ObjectModel model) { + String value = findTagValue(TopiaTagValues.TAG_SORT_ATTRIBUTE, classifier, model); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_GENERATE_STANDALONE_ENUM_FOR_DAO_HELPER} + * tag value on the given model. + * <p/> + * + * @param model model to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_GENERATE_STANDALONE_ENUM_FOR_DAO_HELPER + * @since 2.5 + */ + public static String getGenerateStandaloneEnumForDAOHelperTagValue(ObjectModel model) { + String value = findTagValue(TopiaTagValues.TAG_GENERATE_STANDALONE_ENUM_FOR_DAO_HELPER, null, model); + return value; + } + + /* Obtain the value of the {@link TopiaTagValues#TAG_GENERATE_OPERATOR_FOR_DAO_HELPER} + * tag value on the given model. + * <p/> + * + * @param model model to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_GENERATE_OPERATOR_FOR_DAO_HELPER + * @since 2.5 + */ + + public static String getGenerateOperatorForDAOHelperTagValue(ObjectModel model) { + String value = findTagValue(TopiaTagValues.TAG_GENERATE_OPERATOR_FOR_DAO_HELPER, null, model); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_TYPE} + * tag value on the given attribute. + * <p/> + * + * @param attribute attribute to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_TYPE + * @since 2.5 + */ + public static String getTypeTagValue(ObjectModelAttribute attribute) { + String value = findTagValue(TopiaTagValues.TAG_TYPE, attribute, null); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_SQL_TYPE} + * tag value on the given attribute. + * <p/> + * + * @param attribute attribute to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_TYPE + * @since 2.5 + */ + public static String getSqlTypeTagValue(ObjectModelAttribute attribute) { + String value = findTagValue(TopiaTagValues.TAG_SQL_TYPE, attribute, null); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_EXCEPTION_CLASS} + * tag value on the given interface. + * <p/> + * + * @param model model to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_EXCEPTION_CLASS + * @since 2.5 + */ + public static String getExceptionClassTagValue(ObjectModel model) { + String value = findTagValue(TopiaTagValues.TAG_EXCEPTION_CLASS, null, model); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_TRANSACTION} + * tag value on the given operation. + * <p/> + * + * @param operation operation to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_TRANSACTION + * @since 2.5 + */ + public static String getTransactionTagValue(ObjectModelOperation operation) { + String value = findTagValue(TopiaTagValues.TAG_TRANSACTION, operation, null); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_TRANSACTION} + * tag value on the given classifier. + * <p/> + * + * @param classifier classifier to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_TRANSACTION + * @since 2.5 + */ + public static String getTransactionTagValue(ObjectModelClassifier classifier) { + String value = findTagValue(TopiaTagValues.TAG_TRANSACTION, classifier, null); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_DO_COMMIT} + * tag value on the given operation. + * <p/> + * + * @param operation operation to seek + * @param model model to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_DO_COMMIT + * @since 2.5 + */ + public static String getDoCommitTagValue(ObjectModelOperation operation, ObjectModel model) { + String value = findTagValue(TopiaTagValues.TAG_DO_COMMIT, operation, model); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_ERROR_ARGS} + * tag value on the given operation. + * <p/> + * + * @param operation operation to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_ERROR_ARGS + * @since 2.5 + */ + public static String getErrorArgsTagValue(ObjectModelOperation operation) { + String value = findTagValue(TopiaTagValues.TAG_ERROR_ARGS, operation, null); + return value; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_NO_LOG_IN_SERVICE} + * tag value on the given classifier. + * <p/> + * + * @param classifier classifier to seek + * @param model model to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_NO_LOG_IN_SERVICE + * @since 2.5 + * @deprecated since 2.5.4, no more use will be remove soon. + */ + @Deprecated + public static String getNoLogInServiceTagValue(ObjectModelClassifier classifier, ObjectModel model) { + String value = findTagValue(TopiaTagValues.TAG_NO_LOG_IN_SERVICE, classifier, model); + return value; + } + + /** + * Obtains the value of the {@link TopiaTagValues#TAG_DAO_IMPLEMENTATION} + * tag value on the given model. + * <p/> + * + * @param model model to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_DAO_IMPLEMENTATION + * @since 2.5 + */ + public static String getDaoImplementationTagValue(ObjectModel model) { + String value = findTagValue(TopiaTagValues.TAG_DAO_IMPLEMENTATION, null, model); + return value; + } + + /** + * Obtains the value of the tag value + * {@link TopiaTagValues#TAG_INDEX_FOREIGN_KEYS} on the model or on the + * given attribute. + * + * @param attribute attribute to test + * @param model model to test + * @return none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_INDEX_FOREIGN_KEYS + * @since 2.6.5 + */ + public static String getIndexForeignKeys(ObjectModelAttribute attribute, ObjectModel model) { + String value = findTagValue(TopiaTagValues.TAG_INDEX_FOREIGN_KEYS, attribute, model); + return value; + } + + public static boolean hasUseEnumerationNameTagValue(ObjectModelAttribute attr, ObjectModel model) { + String value = findTagValue(TopiaTagValues.TAG_USE_ENUMERATION_NAME, attr, model); + return Boolean.parseBoolean(value); + } + + /** + * Search if the TagValue {@link TopiaTagValues#TAG_GENERATE_TOPIA_ID_IN_DTO} has been + * activated in the model. + * + * @param classifier classifier to seek + * @param model model to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_GENERATE_TOPIA_ID_IN_DTO + * @since 2.6.7 + */ + public static boolean shouldGenerateDTOTopiaIdTagValue(ObjectModelClassifier classifier, ObjectModel model) { + String tagValue = findTagValue(TopiaTagValues.TAG_GENERATE_TOPIA_ID_IN_DTO, classifier, model); + boolean generate = StringUtils.isNotEmpty(tagValue) && Boolean.valueOf(tagValue); + return generate; + } + + /** + * Obtain the value of the {@link TopiaTagValues#TAG_DO_NOT_GENERATE_READ_LISTENERS} + * tag value on the given model or classifier. + * + * It will first look on the model, and then in the given classifier. + * + * @param model model to seek + * @param attribute attribute to seek + * @return the none empty value of the found tag value or {@code null} if not found nor empty. + * @see TopiaTagValues#TAG_DO_NOT_GENERATE_READ_LISTENERS + * @since 2.9 + */ + public static boolean isDoNotGenerateReadListeners(ObjectModelAttribute attribute, ObjectModel model) { + String value = findTagValue(TopiaTagValues.TAG_DO_NOT_GENERATE_READ_LISTENERS, attribute, model); + return value != null && "true".equals(value); + } + +} // TopiaGeneratorUtil + diff --git a/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaJavaValidator.java b/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaJavaValidator.java new file mode 100644 index 0000000..b348688 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaJavaValidator.java @@ -0,0 +1,156 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.generator; + +import org.apache.commons.lang3.StringUtils; +import org.nuiton.eugene.models.object.ObjectModel; +import org.nuiton.eugene.models.object.ObjectModelAttribute; +import org.nuiton.eugene.models.object.ObjectModelClass; +import org.nuiton.eugene.models.object.ObjectModelOperation; +import org.nuiton.eugene.models.object.validator.ObjectModelValidator; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Validateur qui valide : + * - les types des attributs + * - les nom des attributs + * - les duplication d'attibuts + * + * @author chatellier <chatellier@codelutin.com> + * @version $Id$ + */ +public class TopiaJavaValidator extends ObjectModelValidator { + + /** + * Constructor. + * + * @param model model to validate + */ + public TopiaJavaValidator(ObjectModel model) { + super(model); + } + + @Override + protected boolean validateAttribute(ObjectModelAttribute attr) { + + boolean isValid = super.validateAttribute(attr); + + // type null ou vide + if (attr.getType() == null || attr.getType().isEmpty()) { + isValid = false; + + addError(attr, "Invalid type \"" + attr.getType() + "\""); + } + + // name = java reserved keywords + if (!isJavaIdentifier(attr.getName())) { + isValid = false; + + addError(attr, "Attribute name " + attr.getName() + + " is not valid java identifier"); + } + + // test sur les mots réservés ? + + return isValid; + + } + + @Override + protected boolean validateClass(ObjectModelClass clazz) { + + boolean isValid = super.validateClass(clazz); + + // test attribute names duplication + Set<String> attributesName = new HashSet<String>(); + for (ObjectModelAttribute attr : clazz.getAttributes()) { + if (!attr.isNavigable()) { + + // not navigable, so will not use it... + continue; + } + String attrName = attr.getName(); + if (!attributesName.add(attrName)) { + addError(attr, "Attribute name " + attrName + + " already exists"); + + isValid = false; + } + } + + if (TopiaGeneratorUtil.isEntity(clazz)) { + + Set<String> methodsName = new HashSet<String>(); + for (ObjectModelAttribute attr : clazz.getAttributes()) { + + String capitalizeAttrName = StringUtils.capitalize(attr.getName()); + methodsName.add(TopiaGeneratorUtil.OPERATION_GETTER_BOOLEAN_PREFIX + capitalizeAttrName); + methodsName.add(TopiaGeneratorUtil.OPERATION_GETTER_DEFAULT_PREFIX + capitalizeAttrName); + methodsName.add("set" + capitalizeAttrName); + } + + // test if there is a method an already reserved name + for (ObjectModelOperation operation : clazz.getOperations()) { + String operationName = operation.getName(); + if (methodsName.contains(operationName)) { + addError(operation, "Operation name " + operationName + + " is already reserved for a getter/setter of an entity attribute"); + + isValid = false; + } + } + } + + return isValid; + } + + @Override + protected boolean validateModel(ObjectModel model) { + + return super.validateModel(model); + } + + /** + * Returns true if s is a legal Java identifier. + * + * @param s string to test + * @return true if s is a legal Java identifier + */ + public static boolean isJavaIdentifier(String s) { + if (s.length() == 0 || !Character.isJavaIdentifierStart(s.charAt(0))) { + return false; + } + for (int i = 1; i < s.length(); i++) { + if (!Character.isJavaIdentifierPart(s.charAt(i))) { + return false; + } + } + return true; + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaMetaTransformer.java b/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaMetaTransformer.java new file mode 100644 index 0000000..8b6cd73 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaMetaTransformer.java @@ -0,0 +1,127 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.generator; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.eugene.AbstractMetaTransformer; +import org.nuiton.eugene.models.object.ObjectModel; +import org.nuiton.eugene.models.object.ObjectModelClass; +import org.nuiton.eugene.models.object.validator.AttributeNamesValidator; +import org.nuiton.eugene.models.object.validator.ClassNamesValidator; +import org.nuiton.eugene.models.object.validator.ObjectModelValidator; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created: 20 déc. 2009 + * + * @author tchemit <chemit@codelutin.com> + * @version $Id$ + * @plexus.component role="org.nuiton.eugene.Template" role-hint="org.nuiton.topia.generator.TopiaMetaTransformer" + * @since 2.3.0 + */ +public class TopiaMetaTransformer extends AbstractMetaTransformer<ObjectModel> { + + /** Logger */ + private static final Log log = LogFactory.getLog(TopiaMetaTransformer.class); + + public TopiaMetaTransformer() { +// super( +// EntityTransformer.class, +// DAOTransformer.class, +// DAOImplTransformer.class, +// DAOAbstractTransformer.class, +// DAOHelperTransformer.class, +// EntityHibernateMappingGenerator.class +// ); + + setTemplateTypes( + EntityTransformer.class, + EntityDAOTransformer.class, +// DAOTransformer.class, +// DAOImplTransformer.class, +// DAOAbstractTransformer.class, + DAOHelperTransformer.class, + EntityHibernateMappingGenerator.class + ); + } + + protected boolean validateModel(ObjectModel model) { + List<ObjectModelValidator> validators = new ArrayList<ObjectModelValidator>(); + + AttributeNamesValidator attrValidator = new AttributeNamesValidator( + model); + attrValidator.addNameAndReason("next", + "Le nom d'attribut \"next\" est incompatible avec HSQL"); + attrValidator.addNameAndReason("value", + "Le nom d'attribut \"value\" est incompatible avec certains SGBD"); + attrValidator.addNameAndReason("values", + "Le nom d'attribut \"values\" est incompatible avec certains SGBD"); + attrValidator.addNameAndReason("begin", + "Le nom d'attribut \"begin\" est incompatible avec certains SGBD"); + attrValidator.addNameAndReason("end", + "Le nom d'attribut \"end\" est incompatible avec certains SGBD"); + attrValidator.addNameAndReason("authorization", + "Le nom d'attribut \"authorization\" est incompatible avec certains SGBD"); + attrValidator.addNameAndReason("order", + "Le nom d'attribut \"order\" est incompatible avec certains SGBD"); + validators.add(attrValidator); + + ClassNamesValidator classValidator = new ClassNamesValidator(model); + classValidator.addNameAndReason("constraint", "Nom de classe incompatible avec certains SGBD"); + classValidator.addNameAndReason("user", "Nom de classe incompatible avec certains SGBD"); + validators.add(classValidator); + + validators.add(new TopiaJavaValidator(model)); + validators.add(new TopiaRelationValidator(model)); + + for (ObjectModelValidator validator : validators) { + if (!validator.validate()) { + for (String error : validator.getErrors()) { + if (log.isWarnEnabled()) { + log.warn("[VALIDATION] " + error); + } + } + } + } + + // test before all if there is some entities to generate + List<ObjectModelClass> classes = TopiaGeneratorUtil.getEntityClasses(model, true); + + if (classes.isEmpty()) { + // no entity to generate, can stop safely + if (log.isWarnEnabled()) { + log.warn("No entity to generate, " + getClass().getName() + + " is skipped"); + } + return false; + } + //FIXME-TC20091220 seems we still generate if there is some validation errors ? not very normal + return true; + } + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaRelationValidator.java b/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaRelationValidator.java new file mode 100644 index 0000000..dc27f06 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaRelationValidator.java @@ -0,0 +1,120 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + + +/* * +* TopiaRelationValidator.java +* +* Created: 31 mars 2006 +* +* @author Arnaud Thimel <thimel@codelutin.com> +* @version $Revision$ +* +* Mise a jour: $Date$ +* par : $Author$ +*/ + +package org.nuiton.topia.generator; + +import org.nuiton.eugene.models.object.ObjectModel; +import org.nuiton.eugene.models.object.ObjectModelAttribute; +import org.nuiton.eugene.models.object.validator.ObjectModelValidator; + +/** + * Validateur pour les relations du modèle. + * Vérifie que : + * <ul> + * <li>Toutes les relations ont au moins une navigabilité</li> + * <li>Une relation 1-n unidirectionnelle pointant sur une classe ayant des + * sous-classes dans le modèle est incompatibe avec Hibernate</li> + * <li>Toutes les relations ont des reverseAttribute</li> + * </ul> + * + * @version $Id$ + */ +public class TopiaRelationValidator extends ObjectModelValidator { + + /** + * Constructeur de TopiaRelationValidator. + * + * @param model le modèle à valider + */ + public TopiaRelationValidator(ObjectModel model) { + super(model); + } + + /* (non-Javadoc) + * @see org.nuiton.eugene.models.object.validator.ObjectModelValidator#validateAttribute(org.nuiton.eugene.models.object.ObjectModelAttribute) + */ + @Override + protected boolean validateAttribute(ObjectModelAttribute attr) { + boolean isValid = true; + ObjectModelAttribute reverse = attr.getReverseAttribute(); + + /* Relation navigabilité */ + //Pour ne pas avoir de doublons, on ne vérifie que sur le premier + //attribut par ordre alphabétique + if (TopiaGeneratorUtil.isFirstAttribute(attr)) { + if (!attr.isNavigable() && !reverse.isNavigable()) { + addError(attr, "La relation entre " + "\"" + reverse.getType() + + "\"[" + attr.getName() + "] et " + "\"" + + attr.getType() + "\"[" + reverse.getName() + "] " + + "n'est navigable dans aucun sens"); + isValid = false; + } + } + + /* Relation héritage */ + if (TopiaGeneratorUtil.hasUnidirectionalRelationOnAbstractType(attr, model)) { + isValid = false; + addError( + attr, + "La relation entre " + + "\"" + + reverse.getType() + + "\"[" + + attr.getName() + + "] et " + + "\"" + + attr.getType() + + "\"[" + + reverse.getName() + + "] " + + "n'est navigable que dans un sens et " + + "la classe \"" + + attr.getType() + + "\" a des sous-classes. " + + "Des accesseurs doivent donc etre generes pour Hibernate."); + } + + /* Pas d'inverse */ + if (reverse == null && model.hasClass(attr.getType())) { + isValid = false; + addError(attr, "Cet attribut n'a pas d'inverse."); + } + + return isValid; + } + +} //TopiaRelationValidator diff --git a/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaStereoTypes.java b/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaStereoTypes.java new file mode 100644 index 0000000..243dd76 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaStereoTypes.java @@ -0,0 +1,117 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.generator; + +import org.nuiton.eugene.models.object.ObjectModelAttribute; +import org.nuiton.eugene.models.object.ObjectModelClassifier; +import org.nuiton.eugene.models.object.ObjectModelOperation; +import org.nuiton.eugene.models.stereotype.StereotypeDefinition; +import org.nuiton.eugene.models.stereotype.StereotypeDefinitionProvider; + +/** + * All extra stereotypes usable in topia generators. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5 + * @plexus.component role="org.nuiton.eugene.models.stereotype.StereotypeDefinitionProvider" role-hint="topia-templates" + */ +public class TopiaStereoTypes extends StereotypeDefinitionProvider { + + /** + * Stéréotype pour les attributs considérés comme des tableaux. + * + * @see TopiaGeneratorUtil#hasArrayStereotype(ObjectModelAttribute) + * @deprecated since 2.5 : only BeanTransformer use it and it is a deprecated transformer, will be remove in version 3.0 + */ + @Deprecated + @StereotypeDefinition(target = ObjectModelAttribute.class, + documentation = "Deprecated! To specify that an attribute is an array") + public static final String STEREOTYPE_ARRAY = "array"; + + /** + * Stéréotype pour les interfaces devant être générées sous forme de facades. + * + * @deprecated since 2.5 : nobydy use it, will be remove in version 3.0 + */ + @Deprecated + public static final String STEREOTYPE_FACADE = "facade"; + + /** + * Stéréotype pour les objets devant être générées sous forme d'entités + * + * @see TopiaGeneratorUtil#isEntity(ObjectModelClassifier) + * @see TopiaGeneratorUtil#hasEntityStereotype(ObjectModelAttribute) + */ + @StereotypeDefinition(target = {ObjectModelClassifier.class, ObjectModelAttribute.class}, + documentation = "To specify that a class is an Entity") + public static final String STEREOTYPE_ENTITY = "entity"; + + /** + * Stéréotype pour les objets devant être générées sous forme de DTO. + * + * @see TopiaGeneratorUtil#hasDtoStereotype(ObjectModelClassifier) + */ + @StereotypeDefinition(target = ObjectModelClassifier.class, + documentation = "to specify that a class is a DTO") + public static final String STEREOTYPE_DTO = "dto"; + + /** + * Stéréotype pour les interfaces devant être générées sous forme de + * services. + * + * @see ServiceTransformer + * @see TopiaGeneratorUtil#hasServiceStereotype(ObjectModelClassifier) + */ + @StereotypeDefinition(target = ObjectModelClassifier.class, + documentation = "To specify that a class is a Service") + public static final String STEREOTYPE_SERVICE = "service"; + + /** + * Stéréotype pour les interfaces devant être générées sous forme de DAO. + * + * @see TopiaGeneratorUtil#hasDaoStereotype(ObjectModelClassifier) + * @see TopiaGeneratorUtil#hasDaoStereotype(ObjectModelOperation) + */ + @StereotypeDefinition(target = {ObjectModelClassifier.class, ObjectModelOperation.class}, + documentation = "To Specify that a classifier or an operation should be generated as (or in) a DAO") + public static final String STEREOTYPE_DAO = "dao"; + + /** + * Stéréotype pour les collections avec unicité. + * + * @see TopiaGeneratorUtil#hasUniqueStereotype(ObjectModelAttribute) + */ + @StereotypeDefinition(target = ObjectModelAttribute.class, + documentation = "To specify that an attribute is unique (Hibernate mapping)") + public static final String STEREOTYPE_UNIQUE = "unique"; + + /** + * Stéréotype pour les attributs étant des clés primaires. + * + * @see TopiaGeneratorUtil#hasPrimaryKeyStereotype(ObjectModelAttribute) + */ + @StereotypeDefinition(target = ObjectModelAttribute.class, + documentation = "To specify that an attribute is part of a primary key (Hibernate mapping)") + public static final String STEREOTYPE_PRIMARYKAY = "primaryKey"; +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaTagValues.java b/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaTagValues.java new file mode 100644 index 0000000..e3ebc6c --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/generator/TopiaTagValues.java @@ -0,0 +1,501 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.generator; + +import org.nuiton.eugene.models.Model; +import org.nuiton.eugene.models.object.ObjectModel; +import org.nuiton.eugene.models.object.ObjectModelAttribute; +import org.nuiton.eugene.models.object.ObjectModelClass; +import org.nuiton.eugene.models.object.ObjectModelClassifier; +import org.nuiton.eugene.models.object.ObjectModelElement; +import org.nuiton.eugene.models.object.ObjectModelOperation; +import org.nuiton.eugene.models.object.ObjectModelPackage; +import org.nuiton.eugene.models.tagvalue.TagValueDefinition; +import org.nuiton.eugene.models.tagvalue.TagValueDefinitionProvider; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.persistence.TopiaEntityContextable; +import org.nuiton.topia.persistence.TopiaEntityEnum; + +/** + * All extra tag values usable in topia generators. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5 + * @plexus.component role="org.nuiton.eugene.models.tagvalue.TagValueDefinitionProvider" role-hint="topia-templates" + */ +public class TopiaTagValues extends TagValueDefinitionProvider { + + /** + * Tag pour ajouter specifier le copyright d'un fichier. + * + * @since 2.5 + * @deprecated since 2.5 only use in a deprecated method {@link TopiaGeneratorUtil#getCopyright(Model)} + */ + @Deprecated + public static final String TAG_COPYRIGHT = "copyright"; + + /** + * Tag pour le type de persistence. + * + * @see TopiaGeneratorUtil#getPersistenceType(ObjectModelClassifier) + * @see TopiaGeneratorUtil#getPersistenceTypeTagValue(ObjectModelClassifier) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModelClassifier.class}, + documentation = "") + public static final String TAG_PERSISTENCE_TYPE = "persistenceType"; + + /** + * Tag pour que les entités etendent {@link TopiaEntityContextable} et + * se fasse injecter le {@link TopiaContext} par rapport aux autres + * entités qui ne l'ont pas. + * + * @since 2.5.3 + */ + @TagValueDefinition(target = {ObjectModelClassifier.class}, documentation = "") + public static final String TAG_CONTEXTABLE = "contextable"; + + /** + * Tag pour le nom du champ / entité en BD. + * + * @see TopiaGeneratorUtil#getDbNameTagValue(ObjectModelElement) + * @see TopiaGeneratorUtil#getDbName(ObjectModelElement) + * @see TopiaGeneratorUtil#getReverseDbName(ObjectModelAttribute) + */ + @TagValueDefinition(target = {ObjectModelElement.class}, + documentation = "Sets the database name of an element of the model (a table or a column)") + public static final String TAG_DB_NAME = "dbName"; + + /** + * Tag to specify the reverse db name of an attribute in database. + * + * @see TopiaGeneratorUtil#getReverseDbNameTagValue(ObjectModelAttribute) + * @see TopiaGeneratorUtil#getReverseDbName(ObjectModelAttribute) + */ + @TagValueDefinition(target = {ObjectModelAttribute.class}, + documentation = "Sets the database name of the reverse db name attribute of the model (a column)") + public static final String TAG_REVERSE_DB_NAME = "reverseDbName"; + + /** + * Tag to specify the reverse db name of an attribute in database. + * + * @see TopiaGeneratorUtil#getManytoManyTableNameTagValue(ObjectModelAttribute) + * @see TopiaGeneratorUtil#getManyToManyTableName(ObjectModelAttribute) + * @since 2.9.2 + */ + @TagValueDefinition(target = {ObjectModelAttribute.class}, + documentation = "Sets the database table name of a many to many relation for a attribute of the model") + public static final String TAG_MANY_TO_MANY_TABLE_NAME = "manyToManyTableName"; + + /** + * Tag pour le nom du schema en BD. + * + * @see TopiaGeneratorUtil#getDbSchemaNameTagValue(ObjectModelClassifier, ObjectModel) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModel.class, ObjectModelPackage.class, ObjectModelClassifier.class}, + documentation = "Sets the database schema name") + public static final String TAG_SCHEMA_NAME = "dbSchema"; + + /** + * Tag pour spécifier la caractère embed-xml d'une association. + * + * @see TopiaGeneratorUtil#getPersistenceTypeTagValue(ObjectModelClassifier) + * @deprecated @since 2.5, use nowhere, will be remove soon + */ + @Deprecated + public static final String TAG_EMBED_XML = "embedXml"; + + /** + * Tag pour la taille du champ en BD. + * + * @see TopiaGeneratorUtil#getLengthTagValue(ObjectModelAttribute) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModelAttribute.class}, + documentation = "Sets the length of an attribute in database") + public static final String TAG_LENGTH = "length"; + + /** + * Tag pour ajouter une annotation à un champ. + * + * @see TopiaGeneratorUtil#getAnnotationTagValue(ObjectModelAttribute) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModelAttribute.class}, + documentation = "Sets an annotation of an attribute") + public static final String TAG_ANNOTATION = "annotation"; + + /** + * Tag pour specfier le type d'acces a un champ. + * + * @see TopiaGeneratorUtil#getAccessTagValue(ObjectModelAttribute) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModelAttribute.class}, + documentation = "Sets the access type of an attribute (Hibernate mapping)") + public static final String TAG_ACCESS = "access"; + + /** + * Tag pour ajouter un attribut dans une clef métier. + * + * @see TopiaGeneratorUtil#getNaturalIdTagValue(ObjectModelAttribute) + * @see TopiaGeneratorUtil#isNaturalId(ObjectModelAttribute) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModelAttribute.class}, + documentation = "Sets an attribute as part of a natural id (Hibernate Mapping)") + public static final String TAG_NATURAL_ID = "naturalId"; + + /** + * Tag pour specifier si une clef metier est mutable. + * + * @see TopiaGeneratorUtil#getNaturalIdMutableTagValue(ObjectModelClassifier) + * @see TopiaGeneratorUtil#isNaturalIdMutable(ObjectModelClass) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModelClassifier.class}, + documentation = "Sets if natural id of a entity is mutable (hibernate mapping)") + public static final String TAG_NATURAL_ID_MUTABLE = "naturalIdMutable"; + + /** + * Tag pour permettre de choisir qui contrôle la relation N-N + * bidirectionnelle. A utiliser sur les deux extremités de l'association. + * Mettre inverse=false sur le rôle fils et inverse=true sur le rôle père. + * Par défaut le inverse=true est placé sur le premier rôle trouvé dans + * l'ordre alphabétique. + * + * @see TopiaGeneratorUtil#getInverseTagValue(ObjectModelAttribute) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModelAttribute.class}, + documentation = "Sets which part of a N-N relation is master (inverse=true) and slave (inverse=false) (must be put on each side on a such relation) (Hibernate mapping)") + public static final String TAG_INVERSE = "inverse"; + + /** + * Tag pour spécifier la caractère lazy d'une association multiple. + * + * @see TopiaGeneratorUtil#getLazyTagValue(ObjectModelAttribute) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModelAttribute.class}, + documentation = "Sets if an association should be lazy (Hibernate mapping)") + public static final String TAG_LAZY = "lazy"; + + /** + * Tag pour spécifier la caractère fetch d'une association multiple. + * + * @see TopiaGeneratorUtil#getFetchTagValue(ObjectModelAttribute) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModelAttribute.class}, + documentation = "Sets the fetch caracteristic of an attribute (Hibernate mapping)") + public static final String TAG_FETCH = "fetch"; + + /** + * Tag pour spécifier la caractère order-by d'une association multiple. + * + * @see TopiaGeneratorUtil#getOrderByTagValue(ObjectModelAttribute) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModelAttribute.class}, + documentation = "Sets the order by propertie of an multiple association (Hibernate mapping)") + public static final String TAG_ORDER_BY = "orderBy"; + + /** + * Tag pour spécifier la caractère not-null d'un attribut. + * + * @see TopiaGeneratorUtil#getNotNullTagValue(ObjectModelAttribute) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModelAttribute.class}, + documentation = "Sets if an attribute must be not null (Hibernate mapping)") + public static final String TAG_NOT_NULL = "notNull"; + + /** + * Tag à placer sur un l'attribut d'une entité. Cet attribut est de type + * énumération : l'ajout de la tagValue indique qu'il faut utiliser le + * {@code name} de l'énumération et non l'ordinal pour stocker la valeur en + * base + */ + @TagValueDefinition(target = {ObjectModelAttribute.class}, + documentation = "'true' if the value of this attribute of type Enumeration should be stored with its name (instead of using ordinal)") + public static final String TAG_USE_ENUMERATION_NAME = "useEnumerationName"; + + /** + * Tag pour configurer l'interface du proxy sur autre chose que l'implementation par defaut. + * <p/> + * Par defaut : + * null > generere le proxy sur l'interface de l'implementation + * Autre valeur : + * "none" > laisse la configuration par defaut d'hibernate + * + * @see TopiaGeneratorUtil#getPersistenceTypeTagValue(ObjectModelClassifier) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModel.class, ObjectModelClassifier.class}, + documentation = "Configure the proxy interface on something else than the default implementation (null to use our default implementation, none to let hibernate deal it) (Hibernate mapping)") + public static final String TAG_PROXY_INTERFACE = "hibernateProxyInterface"; + + /** + * Tag pour spécifier le permissions à la création. + * + * @see TopiaGeneratorUtil#getSecurityCreateTagValue(ObjectModelClassifier) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModelClassifier.class}, + documentation = "Sets the create permission on an entity") + public static final String TAG_SECURITY_CREATE = "securityCreate"; + + /** + * Tag pour spécifier le permissions au chargement. + * + * @see TopiaGeneratorUtil#getSecurityLoadTagValue(ObjectModelClassifier) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModelClassifier.class}, + documentation = "Sets the load permission on an entity") + public static final String TAG_SECURITY_LOAD = "securityLoad"; + + /** + * Tag pour spécifier le permissions à la mise à jour. + * + * @see TopiaGeneratorUtil#getSecurityUpdateTagValue(ObjectModelClassifier) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModelClassifier.class}, + documentation = "Sets the update permission on an entity") + public static final String TAG_SECURITY_UPDATE = "securityUpdate"; + + /** + * Tag pour spécifier le permissions à la suppression. + * + * @see TopiaGeneratorUtil#getSecurityDeleteTagValue(ObjectModelClassifier) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModelClassifier.class}, + documentation = "sets the delete permission on an entity") + public static final String TAG_SECURITY_DELETE = "securityDelete"; + + /** + * Tag pour specifier de ne pas generer la methode toString. + * + * @see TopiaGeneratorUtil#getNotGenerateToStringTagValue(ObjectModelClassifier, ObjectModel) + * @see TopiaGeneratorUtil#generateToString(ObjectModelClass, ObjectModel) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModel.class, ObjectModelClassifier.class}, + documentation = "To not generate the toString method on entities") + public static final String TAG_NOT_GENERATE_TO_STRING = "notGenerateToString"; + + /** + * Tag pour specifier de trier les attributs par nom lors de la generation. + * + * @see TopiaGeneratorUtil#getSortAttributeTagValue(ObjectModelClassifier, ObjectModel) + * @see TopiaGeneratorUtil#sortAttribute(ObjectModelClass, ObjectModel) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModel.class, ObjectModelClassifier.class}, + documentation = "To sort attribute while generation") + public static final String TAG_SORT_ATTRIBUTE = "sortAttribute"; + + /** + * Tag pour specfier si on doit générer la methode getOperator dans les daohelpers. + * + * @see TopiaGeneratorUtil#getGenerateOperatorForDAOHelperTagValue(ObjectModel) + * @see TopiaGeneratorUtil#shouldGenerateOperatorForDAOHelper(ObjectModel) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModel.class}, + documentation = "To generate EntityOperation on generated DAOHelper") + public static final String TAG_GENERATE_OPERATOR_FOR_DAO_HELPER = "generateOperatorForDAOHelper"; + + /** + * Tag pour spécifier si on doit générer le {@link TopiaEntityEnum} en tant qu'inner classe + * du dao helper ou pas. + * <p/> + * <b>Note:</b> Par défaut, on génère en tant qu'inner classe. + * + * @see TopiaGeneratorUtil#getGenerateStandaloneEnumForDAOHelperTagValue(ObjectModel) + * @see TopiaGeneratorUtil#shouldGenerateStandaloneEnumForDAOHelper(ObjectModel) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModel.class}, + documentation = "To generate a standalon EntityEnum outside the DAOHelper") + public static final String TAG_GENERATE_STANDALONE_ENUM_FOR_DAO_HELPER = "generateStandaloneEnumForDAOHelper"; + + /** + * Tag pour spécifier le type d'une propriété dans le mapping hibernate. + * + * @see TopiaGeneratorUtil#getTypeTagValue(ObjectModelAttribute) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModelAttribute.class}, + documentation = "Sets the hibernate type of an attribute (Hibernate mapping)") + public static final String TAG_TYPE = "type"; + + /** + * Tag pour spécifier le type sql d'une propriété dans le mapping hibernate. + * + * @see TopiaGeneratorUtil#getSqlTypeTagValue(ObjectModelAttribute) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModelAttribute.class}, + documentation = "Sets the sql type of an attribute (Hibernate mapping)") + public static final String TAG_SQL_TYPE = "sqlType"; + + /** + * To use the legacy DAO generation. + * + * @see TopiaGeneratorUtil#getTypeTagValue(ObjectModelAttribute) + * @since 2.5 + * @deprecated since 2.5, prefer use the tag value + */ + @Deprecated + @TagValueDefinition(target = {ObjectModel.class}, + documentation = "Deprecated! To use the previous DAO implementation in generated DAO (use Criteria api)") + public static final String TAG_USE_LEGACY_DAO = "useLegacyDAO"; + + /** + * To specify the abstract dao to use. + * <p/> + * If none given, will use the {@code org.nuiton.topia.persistence.TopiaDAOImpl}. + * <p/> + * Other value possible is {@code org.nuiton.topia.persistence.TopiaDAOLegacy} + * + * @see TopiaGeneratorUtil#getDaoImplementationTagValue(ObjectModel) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModel.class}, + documentation = "Sets the fully qualified name of the DAO implementation to use in generated DAO (default is DAOImpl (base on TopiaQuery))") + public static final String TAG_DAO_IMPLEMENTATION = "daoImplementation"; + + + /** + * Tag pour specifier l'exception principale de l'application. + * Utiliser dans le ServiceTransformer ou QueryHelperTransformer pour etre + * automatiquement jeter + * depuis les methodes des services. + * + * @see ServiceTransformer + * @see QueryHelperTransformer + * @see TopiaGeneratorUtil#getExceptionClassTagValue(ObjectModel) + * @since 2.3.2 + */ + @TagValueDefinition(target = {ObjectModel.class}, + documentation = "Sets the fully qualified name of the exception to generate in Services and QueryHelper") + public static final String TAG_EXCEPTION_CLASS = "exceptionClass"; + + // ------------------------------------------------------------------------- + // ServiceTransformer specific tag values + // ------------------------------------------------------------------------- + + /** + * Tag pour specifier si une methode a besoin d'une transaction + * (TopiaContext) ou non + * + * @see ServiceTransformer + * @see TopiaGeneratorUtil#getTransactionTagValue(ObjectModelClassifier) + * @see TopiaGeneratorUtil#getTransactionTagValue(ObjectModelOperation) + * @since 2.3.1 + */ + @TagValueDefinition(target = {ObjectModelClassifier.class, ObjectModelOperation.class}, + documentation = "Sets if an operation or a complete service required transaction (if set to true then a TopiaContext parameter will be added to methods)") + public static final String TAG_TRANSACTION = "transaction"; + + /** + * Tag pour specifier si une methode a besoin d'un commit après son + * exécution. + * + * @see ServiceTransformer + * @see TopiaGeneratorUtil#getDoCommitTagValue(ObjectModelOperation, ObjectModel) + * @since 2.5 + */ + @TagValueDefinition(target = {ObjectModelOperation.class}, + documentation = "Sets if an operation needs a commit") + public static final String TAG_DO_COMMIT = "doCommit"; + + /** + * Tag pour specifier si une methode de service a besoin d'arguments pour + * le message d'erreur ou non + * + * @see ServiceTransformer + * @see TopiaGeneratorUtil#getErrorArgsTagValue(ObjectModelOperation) + * @since 2.3.1 + */ + @TagValueDefinition(target = {ObjectModelOperation.class}, + documentation = "Sets if an operation required errors arguments ?") + public static final String TAG_ERROR_ARGS = "errorArgs"; + + /** + * Tag to specify if we want to add logs in any method of service + * generated by {@link ServiceTransformer}. + * <p/> + * <b>Note:</b> To have no log just use this tag on services or gloabaly + * on model (for all services). + * + * @see ServiceTransformer + * @see TopiaGeneratorUtil#getNoLogInServiceTagValue(ObjectModelClassifier, ObjectModel) + * @since 2.5 + * @deprecated since 2.5.4, will not be replaced (no log are any longer + * generated in {@link ServiceTransformer} + */ + @Deprecated + @TagValueDefinition(target = {ObjectModel.class, ObjectModelClassifier.class}, + documentation = "This is deprecated since 2.5.4, no effect : no logs are generated in services.") + public static final String TAG_NO_LOG_IN_SERVICE = "noLogInService"; + + /** + * Stéréotype pour les attributs avec multiplicité nécessitant la création d'un index. + * + * @see TopiaGeneratorUtil#getIndexForeignKeys(ObjectModelAttribute, ObjectModel) + * @since 2.6.5 + */ + @TagValueDefinition(target = {ObjectModel.class, ObjectModelAttribute.class}, + documentation = "Specifies if an nm-multiplicity attribute (or all nm-multiplicity attributes of a given model) needs an index in db (Hibernate mapping)") + public static final String TAG_INDEX_FOREIGN_KEYS = "indexForeignKeys"; + + /** + * Tag to specify if we want to add an "id" property in DTO generated by + * {@link EntityDTOTransformer}. + * <p/> + * + * @see EntityDTOTransformer + * @see TopiaGeneratorUtil#shouldGenerateDTOTopiaIdTagValue(ObjectModelClassifier, ObjectModel) + * @since 2.6.7 + */ + @TagValueDefinition(target = {ObjectModel.class, ObjectModelClassifier.class}, + documentation = "Add a \"id\" property with its getter/setter on a DTO.") + public static final String TAG_GENERATE_TOPIA_ID_IN_DTO = "generateDTOTopiaId"; + + /** + * Tag to specify if we want to generate fireOnPreRead and fireOnPostRead method into entity getters. + * + * @see TopiaGeneratorUtil#isDoNotGenerateReadListeners(ObjectModelAttribute, ObjectModel) + * @since 2.9 + */ + @TagValueDefinition(target = {ObjectModel.class, ObjectModelClassifier.class}, + documentation = "Add a \"id\" property with its getter/setter on a DTO.") + public static final String TAG_DO_NOT_GENERATE_READ_LISTENERS = "notGenerateReadListeners"; +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/generator/package-info.java b/topia-persistence/src/main/java/org/nuiton/topia/generator/package-info.java new file mode 100644 index 0000000..dce0573 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/generator/package-info.java @@ -0,0 +1,117 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +/** + * TODO-FD20100507 : Need update this javadoc for ToPIA 2.4 + * + * <h1>Les Générateurs</h1> + * + * <h2>TopiaMetaGenerator</h2> + * + * <p>TopiaMetaGenerator permet d'enchainer les différents + * générateurs.</p> + * + * <h2>Les DAO</h2> + * + * <p>Il il y a trois générateurs pour les DAO</p> + * + * <ul> + * <li>DAOHelperGenerator</li> + * + * <li>DAOAbstractGenerator</li> + * + * <li>DAOGenerator</li> + * </ul> + * + * <p><b><i>DAOHelperGenerator</i></b> permet de récupérer les DAOs + * générés spécifiquement pour l'application sans avoir besoin de passer + * le type de l'entité en paramètre. Cette classe contient donc une + * methode get par type d'entity qui permet de récupérer le DAO associé. + * Ces méthodes sont des méthodes statiques et prennent en paramètre un + * TopiaContext.</p> + * + * <p><b><i>DAOAbstractGenerator</i></b> est une classe abstraite même si + * elle peut implanter toutes les méthodes de l'interface TopiaDAO. De + * cette façon on oblige l'existance d'une classe concrète qui en hérite + * soit développé par le développeur soit généré par DAOGenerator. + * DAOAbstractGenerator contient toutes les méthodes findBy, findAllBy, + * ... associées aux attributs existants. La classe généré hérite + * directement ou indirectement de TopiaDAODelegator.</p> + * + * <p><b><i>DAOGenerator</i></b> génère une classe vide qui permet au + * programme de compiler, si le développeur à besoin de méthode find + * supplémentaire sur son DAO, il lui suffit de d'implanter cette classe + * dans ses sources, le processus de génération écrasera alors la classe + * généré par la classe développée spécifiquement.</p> + * + * <h2>Les entités</h2> + * + * <p>Les entités sont de pure POJO et ne contiennent pas de référence en + * interne sur le TopiaContext ou le DAO qui les à créée, elle peuvent + * donc facilement migrer, être utilisé dans différents context, ... le + * but etant qu'elle reste des classes complètement déconnecté à + * l'exécution du framework.</p> + * + * <p>La seul contrainte est qu'elles implante TopiaEntity</p> + * + * <p>Il il y a trois générateurs pour les entités, plus un pour le + * mapping hibernate</p> + * + * <ul> + * <li>EntityInterfaceGenerator</li> + * + * <li>EntityAbstractGenerator</li> + * + * <li>EntityImplGenerator</li> + * + * <li>EntityHibernateMappingGenerator</li> + * </ul> + * + * <p><b><i>EntityInterfaceGenerator</i></b> génère l'interface de + * l'entité avec les méthodes d'accès aux attributs et les opérations + * définis par l'utilisateur dans son diagrammme de classe. Elle implante + * TopiaEntity</p> + * + * <p><b><i>EntityAbstractGenerator</i></b> génère une classe qui + * implante l'interface de l'entité et étend TopiaEntityAbstract qui + * implante les méthodes du framework, méthode d'accès aux attributs + * topiaId, topiaVersion et topiaCreateDate.</p> + * + * <p><b><i>EntityImplGenerator</i></b> génère une classe vide qui permet + * au programme de compiler si l'entité n'a pas d'opération spécifique. + * Si elle a des opérations spécifiques le développeur doit implanter + * cette classe dans ses sources et y mettre le code pour les opérations + * spécifiques, le processus de génération écrasera alors la classe + * généré par la classe développée spécifiquement.</p> + * + * <p><b><i>EntityHibernateMappingGenerator</i></b> génère le fichier de + * mapping pour entité. L'interface est déclaré dans le mapping et est + * mappé sur une table préfixé par I. Ensuite le Impl est déclaré en + * union-subclass de cette interface. On a besoin de l'interface car les + * méthodes l'utilise dans les signatures de méthode lorsqu'il y a un + * lien entre deux entités.</p> + * + * @author poussin <poussin@codelutin.com> + * @version $Id$ + */ +package org.nuiton.topia.generator; diff --git a/topia-persistence/src/main/java/org/nuiton/topia/package-info.java b/topia-persistence/src/main/java/org/nuiton/topia/package-info.java new file mode 100644 index 0000000..6608cd5 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/package-info.java @@ -0,0 +1,136 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +/** + * TODO-FD20100507 : Need update this javadoc for ToPIA 2.4 + * + * <p>ToPIA est un framework de persistence basé sur Hibernate. Il + * contient un point d'entré le TopiaContext qui permet d'ouvrir des + * transactions qui retourne des TopiaContext fils sur lequel on peut + * récupéré des DAO pour accèder aux entités.</p> + * + * <p>ToPIA offre en plus la possibilité de générer à partir d'une + * fichier XMI toutes les classes utiles pour la persistence. Ce qui + * permet d'évité un travail fastidieux d'ecriture de classe, d'être sur + * que le jour on l'on veut ajouter une méthode technique (getXML, ...) à + * toutes ces entités cela sera fait de façon simple et automatique + * (modification du générateur puis regénération). Et surtout d'avoir des + * classes générés qui permettent un typage fort de l'application (pas de + * cast, pas de générique)</p> + * + * <p>Bien sur il est possible d'utilisé ToPIA sans générateur, il est + * d'ailleurs utilisé ainsi pour les tests</p> + * + * <p>ToPIA contient aussi des classes techniques réutilisables dans ses + * applications pour tout ce qui touche à l'authentification et + * l'autorisation (TopiaUser, TopiaGroup, TopiaPermission) (partie non + * encore développé)</p> + * + * <h2>La persistance</h2> + * + * <p>La persistence se base complètement sur hibernate, mais il est + * aussi possible d'indiquer que certaine classe doivent être sauvé dans + * des fichiers textes plutot qu'une base de données, cette persistence + * est appelée FlatFile.</p> + * + * <p>Le TopiaContext doit être configuré avec un objet Properties. + * Celui-ci peut contenir différentes entrées. Vu que la persistence est + * complètement basé sur hibernate, même si vous n'avez que des entités + * sauvé en FlatFile il vous faudra fournir à hibernate un accès à une + * base de données. Celle-ci peu très bien être une base embarqué comme + * hsql, mckoi ou derby.</p> + * + * <dl> + * <dt>topia.persistence.properties.file</dt> + * + * <dd>le fichier de propriété a utiliser pour configurer + * hibernate</dd> + * + * <dt>topia.persistence.directories</dt> + * + * <dd>la liste des repertoires contenant les mappings hibernates + * (.hbm.xml) la liste de repertoire est separer par des virgules + * ','</dd> + * + * <dt>topia.persistence.classes</dt> + * + * <dd>la liste des classes que doit géré ToPIA. On peut tres bien + * utiliser topia.persistence.directories pour un ensemble d'entié du + * meme repertoire et topia.persistence.classes pour d'autres + * classes</dd> + * </dl> + * + * <dl> + * <dt>topia.dao.flatfile.properties.file</dt> + * + * <dd>indique le fichier de configuration a utiliser en plus de la + * configuration du context</dd> + * + * <dt>topia.dao.flatfile.directory</dt> + * + * <dd>indique le répertoire au sauver les entités</dd> + * + * <dt>topia.dao.flatfile.directory.[fqn-entity]</dt> + * + * <dd>permet de spécifier un répertoire différent pour une entity + * spécifique</dd> + * + * <dt>topia.dao.flatfile.mapping</dt> + * + * <dd>permet d'indique le mapping a utiliser pour les entités</dd> + * + * <dt>topia.dao.flatfile.mapping.[fqn-entity]</dt> + * + * <dd>permet d'indique un mapping différent pour entité</dd> + * </dl> + * + * <p>à la place flatfile il est possible de mettre le FQN du DAO utilisé + * par exemple + * org.nuiton.topia.persistence.flatfile.TopiaDAOFlatFile</p> + * + * <p>Si directory est absent alors "." est utilisé</p> + * + * <p>les mappings s'écrivent de la façon suivant:</p> + * + * <ul> + * <li>ext=extension a ajouter au fichier contenant l'entity</li> + * + * <li>key=attribute</li> + * + * <li>body=attribute</li> + * </ul> + * + * <p>Si key est absent alors on utilise le topiaId, key est utilisé + * comme nom de fichier de sauvegarde</p> + * + * <p>Si body est absent alors on utilise un fichier de propriété pour + * sauver l'entity. Si body est présent seul cet attribut sera sauvé.</p> + * + * <pre> + * topia.dao.flatfile.mapping.key=topiaId + * + * topia.dao.flatfile.mapping.fr.ifremer.isisfish.entities.Script.key=name + * topia.dao.flatfile.mapping.fr.ifremer.isisfish.entities.Script.body=script + * </pre> + */ +package org.nuiton.topia; diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/DepthEntityVisitor.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/DepthEntityVisitor.java new file mode 100644 index 0000000..f554701 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/DepthEntityVisitor.java @@ -0,0 +1,148 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaException; + +import java.util.ArrayList; +import java.util.Collection; + +/** + * Parcourt du graphe d'entité en profondeur. + * + * @author echatellier <chatellier@codelutin.com> + * @author tchemit <tchemit@codelutin.com> + * @version $Id$ + */ +public class DepthEntityVisitor implements EntityVisitor { + + /** Class logger. */ + private static Log log = LogFactory.getLog(DepthEntityVisitor.class); + + /** Cache of already explored entities. */ + protected Collection<TopiaEntity> alreadyExplored; + + /** + * Le visiteur metier (optionel). + */ + protected EntityVisitor delegateVisitor; + + public DepthEntityVisitor() { + this(null); + } + + public DepthEntityVisitor(EntityVisitor delegateVisitor) { + + alreadyExplored = new ArrayList<TopiaEntity>(); + + this.delegateVisitor = delegateVisitor; + } + + @Override + public void start(TopiaEntity e) { + if (delegateVisitor != null) { + delegateVisitor.start(e); + } + if (!alreadyExplored.contains(e)) { + alreadyExplored.add(e); + } + } + + @Override + public void visit(TopiaEntity e, String propertyName, Class<?> type, + Object value) { + // si c'est une entité + if (value instanceof TopiaEntity) { + TopiaEntity entity = (TopiaEntity) value; + try { + if (!alreadyExplored.contains(entity)) { + entity.accept(this); + } + } catch (TopiaException e1) { + if (log.isErrorEnabled()) { + log.error("Error on depth exploration", e1); + } + } + } else { + if (delegateVisitor != null) { + delegateVisitor.visit(e, propertyName, type, value); + } + } + } + + @Override + public void visit(TopiaEntity e, String propertyName, + Class<?> collectionType, Class<?> type,Object value) { + + Collection<?> cValue = (Collection<?>) value; + if (cValue != null && !cValue.isEmpty()) { + int i = 0; + for (Object currentValue : cValue) { + visit(e, propertyName, type, collectionType, i++, currentValue); + } + } + } + + @Override + public void visit(TopiaEntity e, String propertyName, + Class<?> collectionType, Class<?> type, int index, + Object value) { + // si c'est une entité + if (value instanceof TopiaEntity) { + TopiaEntity entity = (TopiaEntity) value; + try { + if (!alreadyExplored.contains(entity)) { + entity.accept(this); + } + } catch (TopiaException e1) { + if (log.isErrorEnabled()) { + log.error("Error on depth exploration", e1); + } + } + } else { + if (delegateVisitor != null) { + delegateVisitor.visit(e, propertyName, collectionType, type, + index, value); + } + } + } + + @Override + public void end(TopiaEntity e) { + if (delegateVisitor != null) { + delegateVisitor.end(e); + } + } + + @Override + public void clear() { + alreadyExplored.clear(); + if (delegateVisitor != null) { + delegateVisitor.clear(); + } + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/EntityVisitor.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/EntityVisitor.java new file mode 100644 index 0000000..0fd5f3a --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/EntityVisitor.java @@ -0,0 +1,103 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence; + +/** + * The contract of a visitor of any {@link TopiaEntity}. + * <p/> + * Created: 28 janv. 2009 18:10:34 + * + * @author bpoussin <poussin@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @version $Id$ + */ +public interface EntityVisitor { + + /** + * Start the visit of the given entity. + * + * @param entity the visited entity + */ + void start(TopiaEntity entity); + + /** + * Ends the visit of the given entity. + * + * @param entity the visited entity + */ + void end(TopiaEntity entity); + + /** + * Visit a none indexed property for the given entity. + * <p/> + * The property visited is defined by the other parameters. + * + * @param entity the visited entity + * @param propertyName the name of the visited property + * @param type the type of the visited property + * @param value the value of the visited property + */ + void visit(TopiaEntity entity, String propertyName, Class<?> type, + Object value); + + /** + * Visit a collection property for the given entity. + * <p/> + * The property visited is defined by the other parameters. + * + * @param entity the visited entity + * @param propertyName the name of the visited property + * @param collectionType the type of the visited collection + * @param type the type of the visited property + * @param value the value of the visited property + */ + void visit(TopiaEntity entity, String propertyName, + Class<?> collectionType, Class<?> type, Object value); + + /** + * Visit a indexed value from a collection property for the given entity. + * <p/> + * The property visited is defined by the other parameters. + * + * @param entity the visited entity + * @param propertyName the name of the visited property + * @param collectionType the type of the container of the visited property + * @param type the type of the visited property + * @param index the index of the visited property in his container + * @param value the value of the visited property + */ + void visit(TopiaEntity entity, String propertyName, + Class<?> collectionType, Class<?> type, int index, Object value); + + /** + * Reset all states of the visitor. + * <p/> + * If you use internal states inside the visitor, this method should clean + * all of them. + * <p/> + * This method should be invoked after usage of the visitor. + */ + void clear(); +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/HorizontalEntityVisitor.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/HorizontalEntityVisitor.java new file mode 100644 index 0000000..76b4433 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/HorizontalEntityVisitor.java @@ -0,0 +1,143 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaException; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * Parcourt en largeur du modele et délegation à un autre visiteur. + * + * @author echatellier <chatellier@codelutin.com> + * @author tchemit <tchemit@codelutin.com> + * @version $Id$ + */ +public class HorizontalEntityVisitor implements EntityVisitor { + + /** Class logger. */ + private static Log log = LogFactory.getLog(HorizontalEntityVisitor.class); + + /** Delegate visitor. */ + protected EntityVisitor delegateVisitor; + + /** Cache used to remember entity during exploration. */ + protected List<TopiaEntity> alreadyExplored; + + /** Entity to be visited later. */ + protected List<TopiaEntity> toVisitEntities; + + /** + * Constructor. + * + * @param delegateVisitor delegate visitor + */ + public HorizontalEntityVisitor(EntityVisitor delegateVisitor) { + this.delegateVisitor = delegateVisitor; + alreadyExplored = new ArrayList<TopiaEntity>(); + toVisitEntities = new ArrayList<TopiaEntity>(); + } + + @Override + public void start(TopiaEntity entity) { + delegateVisitor.start(entity); + if (!alreadyExplored.contains(entity)) { + alreadyExplored.add(entity); + } + } + + @Override + public void visit(TopiaEntity entity, String propertyName, Class<?> type, + Object value) { + // si c'est une entité + if (value instanceof TopiaEntity) { + TopiaEntity entityValue = (TopiaEntity) value; + toVisitEntities.add(entityValue); + } else { + delegateVisitor.visit(entity, propertyName, type, value); + } + } + + @Override + public void visit(TopiaEntity entity, String propertyName, + Class<?> collectionType,Class<?> type, Object value) { + Collection<?> values = (Collection<?>) value; + if (values != null && !values.isEmpty()) { + int i = 0; + for (Object currentValue : values) { + visit(entity, propertyName, type, collectionType, i++, + currentValue); + } + } + } + + @Override + public void visit(TopiaEntity entity, String propertyName, + Class<?> collectionType, Class<?> type, int index, + Object value) { + // si c'est une entité + if (value instanceof TopiaEntity) { + TopiaEntity entityValue = (TopiaEntity) value; + toVisitEntities.add(entityValue); + } else { + delegateVisitor.visit(entity, propertyName, collectionType, type, + index, value); + } + } + + @Override + public void end(TopiaEntity entity) { + delegateVisitor.end(entity); + + // here, must revisit all remembered entities + List<TopiaEntity> currentEntities = + new ArrayList<TopiaEntity>(toVisitEntities); + // TODO-chatellier-20091221 : verify if clearing here is enough + toVisitEntities.clear(); + for (TopiaEntity currentEntity : currentEntities) { + try { + if (!alreadyExplored.contains(currentEntity)) { + currentEntity.accept(this); + log.debug("Post process " + currentEntity); + } + } catch (TopiaException ex) { + if (log.isErrorEnabled()) { + log.error("Error on horizontal exploration", ex); + } + } + } + } + + @Override + public void clear() { + alreadyExplored.clear(); + toVisitEntities.clear(); + delegateVisitor.clear(); + } +} // HorizontallyEntityVisitor diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/SearchFields.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/SearchFields.java new file mode 100644 index 0000000..794e5c7 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/SearchFields.java @@ -0,0 +1,76 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * +* SearchFields.java +* +* Created: 7 juin 2006 +* +* @author Arnaud Thimel <thimel@codelutin.com> +* @version $Revision$ +* +* Mise a jour: $Date$ +* par : $Author$ +*/ + +package org.nuiton.topia.persistence; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; +import static java.lang.annotation.ElementType.TYPE; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +/** + * + * TODO-fdesbois-20100508 : javadoc : where is it used for ? which service use it ? + * + * Ces annotations permettent de savoir quels sont les champs sur lesquels + * la recherche pourra s'effectuer. + */ +@Retention(RUNTIME) +@Target(TYPE) +public @interface SearchFields { + + /** + * @return la liste des champs textes + */ + String[] txtFields() default {}; + + /** + * @return la liste des champs numeriques + */ + String[] numFields() default {}; + + /** + * @return la liste des champs booleens + */ + String[] boolFields() default {}; + + /** + * @return la liste des champs date + */ + String[] dateFields() default {}; + +} //SearchFields diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAO.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAO.java new file mode 100644 index 0000000..afa342a --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAO.java @@ -0,0 +1,688 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * + * TopiaDAO.java + * + * Created: 30 déc. 2005 03:00:57 + * + * @author poussin <poussin@codelutin.com> + * @version $Revision$ + * + * Last update: $Date$ + * by : $Author$ + */ +package org.nuiton.topia.persistence; + +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.event.TopiaEntityListener; +import org.nuiton.topia.event.TopiaEntityVetoable; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.generator.EntityDAOTransformer; +import org.nuiton.topia.persistence.pager.TopiaPagerBean; + +import java.util.List; +import java.util.Map; + +/** + * TopiaDAO is used to manipulate entities corresponding to {@code E} type : + * create, delete, update or find entities. + * <p/> + * This interface is implemented by {@link TopiaDAOImpl} overridden by generation + * from {@link EntityDAOTransformer}. + * <p/> + * + * @param <E> the entity type managed by the dao + * @author bpoussin <poussin@codelutin.com> + * @author fdesbois <fdesbois@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @version $Id$ + */ +public interface TopiaDAO<E extends TopiaEntity> extends TopiaDAODeprecated<E>, Iterable<E> { + + //------------------------------------------------------------------------// + //-- Create - update - delete methods ------------------------------------// + //------------------------------------------------------------------------// + + /** + * Create a new instance of managed entity <strong>not persisted</strong>. + * + * @return new entity instance + * @throws TopiaException if any pb while creating the entity + * @since 2.3.1 + */ + E newInstance() throws TopiaException; + + /** + * Construit une nouvelle instance de l'objet géré par ce DAO + * + * @param properties la liste des propriétés que doit avoir l'objet créé les + * arguments vont par paire (propertyName, value) + * @return un nouvel objet + * @throws TopiaException si un problème est rencontré durant + * l'instanciation + * @throws IllegalArgumentException Si le nombre on le type des arguments + * n'est pas bon ou que le type ou le nom + * d'une propriété est fausse + */ + E create(Object... properties) throws TopiaException; + + /** + * Construit une nouvelle instance de l'objet géré par ce DAO + * + * @param properties la liste des propriétés que doit avoir l'objet créé + * @return un nouvel objet + * @throws TopiaException si un problème est rencontré durant + * l'instanciation + * @throws IllegalArgumentException Si le nombre on le type des arguments + * n'est pas bon ou que le type ou le nom + * d'une propriété est fausse + */ + E create(Map<String, Object> properties) throws TopiaException; + + /** + * Permet de sauver un object instancié sans le DAO. + * Utilisé notement dans le cas ou le DAO est situé derriere une couche + * de webservice et que l'appel à la methode {@link #create(Object...)} + * serait trop couteux. + * + * @param e l'entité instanciée à sauver + * @return l'entité avec son topiaID valorisé + * @throws TopiaException if any pb while creating datas + * @since 2.3.1 + */ + E create(E e) throws TopiaException; + + /** + * Permet d'ajouter ou de mettre a jour un objet. Cela permet d'ajouter par + * exemple un objet provenant d'un autre context mais du meme type de DAO. + * + * @param e l'entite a ajouter ou mettre a jour + * @return l'entity passé en paramètre. + * @throws TopiaException if any pb while updating datas + */ + E update(E e) throws TopiaException; + + /** + * Permet de supprimer une entite. + * + * @param e l'entite a supprimer + * @throws TopiaException if any pb while deleting datas + */ + void delete(E e) throws TopiaException; + + /** + * Permet de supprimer des entités. + * + * @param entities les entités à supprimer + * @throws TopiaException if any pb while deleting datas + */ + void deleteAll(Iterable<E> entities) throws TopiaException; + + //------------------------------------------------------------------------// + //-- findByXXX methods ---------------------------------------------------// + //------------------------------------------------------------------------// + + /** + * Find an entity corresponding to the {@code id}. If the {@code id} is + * null, nothing will be searched. + * + * @param id topiaId of the entity to found + * @return the entity found or null if not + * @throws TopiaException for Topia errors on query + */ + E findByTopiaId(String id) throws TopiaException; + + /** + * Find an entity matching {@code value} for the given {@code propertyName}. + * + * @param propertyName property name to filter + * @param value value of the property to math + * @return the first entity matching the request + * @throws TopiaException + */ + E findByProperty(String propertyName, Object value) throws TopiaException; + + /** + * @param propertyName le nom de la propriété + * @param value la valeur à tester + * @param others les autres proprietes doivent aller par 2 + * propertyName, value + * @return l'entité trouvé + * @throws TopiaException if any pb while getting datas + */ + E findByProperties(String propertyName, Object value, Object... others) throws TopiaException; + + /** + * Find an entity matching {@code properties}. + * + * @param properties les propriétés à matcher + * @return l'entité trouvé + * @throws TopiaException if any pb while getting datas + */ + E findByProperties(Map<String, Object> properties) throws TopiaException; + + /** + * Execute une requête basé sur l'entité du DAO. Permet de récupérer une + * entité correspondant à la requête. + * + * @param hql la requête hql à executer + * @param params les paramètres de la requète + * @return l'entité correspondant à la recherche ou null si aucune entité + * n'a été trouvée + * @throws TopiaException if any pb while getting datas + * @since 2.6.12 + */ + E findByQuery(String hql, Object... params) throws TopiaException; + + /** + * Execute une requête basé sur le {@code type} donné. + * <p/> + * Permet de récupérer une entité correspondant à la requête. + * + * @param hql la requête hql à executer + * @param params les paramètres de la requète + * @return l'entité correspondant à la recherche ou null si aucune entité + * n'a été trouvée + * @throws TopiaException if any pb while getting datas + * @since 2.6.12 + */ + <R> R findByQuery(Class<R> type, String hql, Object... params) throws TopiaException; + + /** + * Recherche la classe en utilisant la cle naturelle, chaque champs de la + * cle naturelle est une entre de la map passe en argument. + * + * @param keys la liste des champs de la cle naturelle avec leur valeur + * @return l'entite trouvé + * @throws TopiaException if any pb while getting datas + */ + E findByPrimaryKey(Map<String, Object> keys) throws TopiaException; + + /** + * Recherche la classe en utilisant la cle naturelle, si la cle naturelle + * est composé de plusieurs champs alors les arguments passés doivent être + * dans l'ordre de declaration dans le fichier de mapping + * + * @param k l'objet cle naturelle de la classe + * @return l'entité trouvé + * @throws TopiaException if any pb while getting datas + */ + E findByPrimaryKey(Object... k) throws TopiaException; + + /** + * Récupère la première entité (du type géré par ce dao) dont la + * collection nommée {@code propertyName} contient la {@code property} + * donnée. + * + * @param propertyName le nom de la propriété + * @param property la propriété recherchée + * @return la première entité recherchée + * @throws TopiaException pour tout erreur lors de la recherche + * @since 2.5.4 + */ + E findContains(String propertyName, Object property) throws TopiaException; + + //------------------------------------------------------------------------// + //-- findAllXXX methods --------------------------------------------------// + //------------------------------------------------------------------------// + + /** + * Gets all entitys of the dao type in db. + * + * @return all entities of the dao type in db + * @throws TopiaException + */ + List<E> findAll() throws TopiaException; + + /** + * Recuperation de tous les ids en base pour le type d'entite du dao. + * + * @return la liste de tous les ids + * @throws TopiaException si pb en base + */ + List<String> findAllIds() throws TopiaException; + + /** + * Gets all entities of the dao type matching the {@code value} for the + * given {@code propertyName}. + * + * @param propertyName property name to filter + * @param value value to match + * @return matching entities + * @throws TopiaException if any pb while getting datas + */ + List<E> findAllByProperty(String propertyName, Object value) + throws TopiaException; + + /** + * Gets all entities of the dao type matching the {@code value} for the + * given {@code propertyName} and {@code others}. + * + * @param propertyName le nom de la propriété + * @param value la valeur à tester + * @param others les autres proprietes doivent aller par 2 + * propertyName, value + * @return matching entities + * @throws TopiaException if any pb while getting datas + */ + List<E> findAllByProperties(String propertyName, Object value, + Object... others) throws TopiaException; + + /** + * Gets all entities of the dao type matching all the {@code properties}. + * + * @param properties properties to match + * @return matching entities + * @throws TopiaException if any pb while getting datas + */ + List<E> findAllByProperties(Map<String, Object> properties) + throws TopiaException; + + /** + * Gets all entities when executing the given select query for the dao + * entity type. + * + * @param hql hql query + * @param params query params + * @return entites of the query result + * @throws TopiaException if any pb while getting datas + * @since 2.6.12 + */ + List<E> findAllByQuery(String hql, Object... params) throws TopiaException; + + /** + * Gets all entities when executing the given select query for the given + * {@code type} which may not be a entity type (int, long, map,...). + * + * @param hql hql query + * @param params query params + * @return entites of the query result + * @throws TopiaException if any pb while getting datas + * @since 2.6.12 + */ + <R> List<R> findAllByQuery(Class<R> type, + String hql, + Object... params) throws TopiaException; + + /** + * Gets all entities in lazy mode when executing the given select query + * for the dao entity type. + * <p/> + * <strong>Important note:</strong> + * + * @param hql hql query + * @param params query params + * @return entites of the query result + * @throws TopiaException if any pb while getting datas + * @since 2.6.14 + */ + Iterable<E> findAllLazyByQuery(String hql, + Object... params) throws TopiaException; + + /** + * Gets all entities in lazy mode when executing the given select query + * for the given {@code type} which may not be a entity type (int, long, map,...). + * <p/> + * <strong>Important note:</strong> + * + * @param type type of data to return + * @param hql hql query + * @param params query params + * @return entites of the query result + * @throws TopiaException if any pb while getting datas + * @since 2.6.14 + */ + <R> Iterable<R> findAllLazyByQuery(Class<R> type, + String hql, + Object... params) throws TopiaException; + + /** + * Gets all entities in lazy mode when executing the given select query + * for the dao entity type. + * <p/> + * <strong>Important note:</strong> + * + * @param batchSize batch size + * @param hql hql query + * @param params query params + * @return entites of the query result + * @throws TopiaException if any pb while getting datas + * @since 2.6.14 + */ + Iterable<E> findAllLazyByQuery(int batchSize, + String hql, + Object... params) throws TopiaException; + + /** + * Gets all entities in lazy mode when executing the given select query + * for the given {@code type} which may not be a entity type (int, long, map,...). + * <p/> + * <strong>Important note:</strong> + * + * @param type type of data to return + * @param batchSize batch size + * @param hql hql query + * @param params query params + * @return entites of the query result + * @throws TopiaException if any pb while getting datas + * @since 2.6.14 + */ + <R> Iterable<R> findAllLazyByQuery(Class<R> type, + int batchSize, + String hql, + Object... params) throws TopiaException; + + /** + * Gets a page of entities when executing the given select query for the dao + * entity type (will only return the window of {@code startIndex - + * endIndex} entities). + * + * @param hql hql query to execute + * @param startIndex first index of entity to return + * @param endIndex last index of entity to return + * @param params query params + * @return entites of the paginated query result + * @throws TopiaException if any pb while getting datas + * @since 2.6.12 + */ + <R> List<R> findAllByQueryWithBound(Class<R> type, + String hql, + int startIndex, + int endIndex, + Object... params) throws TopiaException; + + /** + * Gets a page of entities when executing the given select query for the dao + * entity type (will only return the window of {@code startIndex - + * endIndex} entities). + * + * @param hql hql query to execute + * @param startIndex first index of entity to return + * @param endIndex last index of entity to return + * @param params query params + * @return entites of the paginated query result + * @throws TopiaException if any pb while getting datas + * @since 2.6.12 + */ + List<E> findAllByQueryWithBound(String hql, + int startIndex, + int endIndex, + Object... params) throws TopiaException; + + /** + * Gets a page of entities of the given select {@code hql} query using the + * {@code pager} to obtain the window of entities to return. + * + * @param type type of data to return + * @param hql hql query to execute + * @param pager pager to obtan the correct window of data + * @param params params of the query + * @return entities of the paginated query result + * @throws TopiaException if any pb while getting datas + * @see TopiaPagerBean + * @since 2.6.12 + */ + <R> List<R> findAllByQueryAndPager(Class<R> type, + String hql, + TopiaPagerBean pager, + Object... params) throws TopiaException; + + /** + * Gets a page of entities of the given select {@code hql} query using the + * {@code pager} to obtain the window of entities to return. + * + * @param hql hql query to execute + * @param pager pager to obtan the correct window of data + * @param params params of the query + * @return entities of the paginated query result + * @throws TopiaException if any pb while getting datas + * @see TopiaPagerBean + * @since 2.6.12 + */ + List<E> findAllByQueryAndPager(String hql, + TopiaPagerBean pager, + Object... params) throws TopiaException; + + /** + * Gets all entites for the dao entity type order by given {@code propertyNames}. + * <p/> + * You can add on each {@code property} {@code ASC} ou {@code DESC} to + * fix order way (by default is {@code ASC}). + * + * @param propertyNames property names of order to apply + * @return all entities of the dao entity type with given order + * @throws TopiaException if any pb while getting datas + */ + List<E> findAllWithOrder(String... propertyNames) + throws TopiaException; + + /** + * Récupère toutes les entités (du type géré par ce dao) dont la + * collection nommée {@code propertyName} contient la {@code property} + * donnée. + * + * @param propertyName le nom de la propriété + * @param property la propriété recherchée + * @return toutes les entités recherchées + * @throws TopiaException pour tout erreur lors de la recherche + * @since 2.5.4 + */ + List<E> findAllContains(String propertyName, + Object property) throws TopiaException; + + //------------------------------------------------------------------------// + //-- existsByXXX methods -------------------------------------------------// + //------------------------------------------------------------------------// + + /** + * Check the existence of an entity with technical {@code id}. + * + * @param id unique id of the entity to test existence. + * @return true if entity exists, false otherwise + * @throws TopiaException for Topia errors + * @since 2.3.4 + */ + boolean existByTopiaId(String id) throws TopiaException; + + /** + * Check the existence of an entity with {@code propertyName} with {@code + * propertyValue}. {@code others} properties can be added to test + * existence. + * + * @param propertyName first property name to test existence + * @param propertyValue first property value to test existence + * @param others altern propertyName and propertyValue + * @return true if entity exists, false otherwise + * @throws TopiaException for Topia errors + * @since 2.3.4 + */ + boolean existByProperties(String propertyName, Object propertyValue, + Object... others) throws TopiaException; + + /** + * Check the existence of an entity using a {@code hql} query. + * + * @param hql query used to test existence + * @param params params used by the query + * @return true if entity exists, false otherwise + * @throws TopiaException + * @since 2.6.12 + */ + boolean existsByQuery(String hql, Object... params) throws TopiaException; + + //------------------------------------------------------------------------// + //-- countXXX methods ----------------------------------------------------// + //------------------------------------------------------------------------// + + /** + * Count the number of existing entities. + * + * @return number of total entities + * @throws TopiaException if any pb while getting datas + * @since 2.3.4 + */ + long count() throws TopiaException; + + /** + * Count the number of entities based on a {@code hql}. + * + * @param hql hql query to use + * @return number of entities filtered by the query + * @throws TopiaException if any pb while getting datas + * @since 2.6.12 + */ + long countByQuery(String hql, Object... params) throws TopiaException; + + //------------------------------------------------------------------------// + //-- other request methods -----------------------------------------------// + //------------------------------------------------------------------------// + + /** + * Find usages of the given {@code entity} in the entities of the given + * {@code type}. + * + * @param type the type of entity to search + * @param entity the entity on which search is done + * @param <R> type of entity to search + * @return the list of entities of the given type which uses the given + * entity + * @throws TopiaException if any problem while getting data + * @since 2.3.0 + */ + <R extends TopiaEntity> List<R> findUsages(Class<R> type, E entity) + throws TopiaException; + + /** + * Find all usages of the given {@code entity}. + * + * @param entity the entity + * @return the dictionnary of usages of the given entities (keys are entity + * usage container, values are the list of this type of entity to + * use the given entity). + * @throws TopiaException if any pb while getting data + * @since 2.3.0 + */ + + Map<Class<? extends TopiaEntity>, List<? extends TopiaEntity>> findAllUsages(E entity) + throws TopiaException; + + /** + * Execute the count {@code hql} query and then synch the pager to this + * result (says fill the + * {@link TopiaPagerBean#records} field and then adapt + * the number of pages available and the current number page). + * + * @param hql the count hql to execute + * @param pager the page to synch + * @param params params of the count query + * @throws TopiaException if any pb while getting datas + * @see TopiaPagerBean + * @since 2.6.12 + */ + void computeAndAddRecordsToPager(String hql, + TopiaPagerBean pager, + Object... params) throws TopiaException; + + //------------------------------------------------------------------------// + //-- Misc methods --------------------------------------------------------// + //------------------------------------------------------------------------// + + /** + * Get the entityEnum of the type of entity managed by this DAO. + * + * @return entity type enum managed by this DAO + */ + TopiaEntityEnum getTopiaEntityEnum(); + + /** + * Get the type of entity managed by this DAO. + * + * @return entity type managed by this DAO + */ + Class<E> getEntityClass(); + + /** + * Obtains the batch size used to load data. + * <p/> + * Default value if 1000. + * + * @return the batch size. + * @since 2.6.14 + */ + int getBatchSize(); + + /** + * Set a new default batch size. + * + * @param batchSize new batch size to use when iterating. + * @since 2.6.14 + */ + void setBatchSize(int batchSize); + + /** + * When TopiaContextImpl create the TopiaDAOHibernate, it must call this + * method just after. + * + * @param context context + * @param entityClass entity class + * @throws TopiaException if any pb while init + */ + void init(TopiaContextImplementor context, Class<E> entityClass) + throws TopiaException; + + /** + * Return context used by this DAO. + * + * @return the context. + */ + TopiaContextImplementor getContext(); + + /** + * Create the simple hql query for the entity managed by the dao. + * <p/> + * A optional alias can be passed: + * <p/> + * <pre>FROM MyEntityImpl myAlias</pre> + * + * @param alias optional alias to use in query + * @return the hql query + * @since 2.6.14 + */ + String createSimpleQuery(String alias); + + //------------------------------------------------------------------------// + //-- Listener methods ----------------------------------------------------// + //------------------------------------------------------------------------// + + void addTopiaEntityListener(TopiaEntityListener listener); + + void addTopiaEntityVetoable(TopiaEntityVetoable vetoable); + + void removeTopiaEntityListener(TopiaEntityListener listener); + + void removeTopiaEntityVetoable(TopiaEntityVetoable vetoable); + +} //TopiaDAO + diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAODeprecated.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAODeprecated.java new file mode 100644 index 0000000..41e2fea --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAODeprecated.java @@ -0,0 +1,169 @@ +package org.nuiton.topia.persistence; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaQuery; + +import java.security.Permission; +import java.util.List; +import java.util.Map; + +/** + * Contains all method that are deprecated from {@link TopiaDAO} in version + * {@code 2.6.12} and then will be removed in next major version ({@code 3.0}). + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +public interface TopiaDAODeprecated<E extends TopiaEntity> { + + /** + * Crée une requete basé sur l'entité lié au DAO. Résultat attendu : "FROM + * E" + * + * @return une nouvelle TopiaQuery vide. (uniquement avec le From sur le + * type d'entité) + * @since 2.3 + * @deprecated since 2.6.12, {@link TopiaQuery} will be removed in version 3.0 + */ + @Deprecated + TopiaQuery createQuery(); + + /** + * Crée une requête basé sur l'entité lié au DAO et lui assigne un alias + * valable dans la requête.. + * <p/> + * Résultat attendu : "FROM E AS entityAlias" + * + * @param entityAlias alias permettant de manipuler l'entité dans la + * requête + * @return une nouvelle TopiaQuery + * @since 2.3 + * @deprecated since 2.6.12, {@link TopiaQuery} will be removed in version 3.0 + */ + @Deprecated + TopiaQuery createQuery(String entityAlias); + + /** + * Execute une requête basé sur l'entité du DAO. Permet de récupérer une + * entité correspondant à la requête. + * + * @param query la requête + * @return l'entité correspondant à la recherche ou null si aucune entité + * n'a été trouvée + * @throws TopiaException if any pb while getting datas + * @see TopiaQuery#executeToEntity(TopiaContext, Class) + * @since 2.3 + * @deprecated since 2.6.12, {@link TopiaQuery} will be removed in version 3.0 + */ + @Deprecated + E findByQuery(TopiaQuery query) throws TopiaException; + + /** + * Execute une requête basé sur l'entité du DAO. Permet de récupérer une + * liste d'entités correspondant à la requête. + * + * @param query la requête + * @return la liste d'entités correspondant à la recherche + * @throws TopiaException if any pb while getting datas + * @see TopiaQuery#executeToEntityList(TopiaContext, Class) + * @since 2.3 + * @deprecated since 2.6.12, {@link TopiaQuery} will be removed in version 3.0 + */ + @Deprecated + List<E> findAllByQuery(TopiaQuery query) throws TopiaException; + + /** + * Execute une requête basé sur l'entité du DAO. Permet de récupérer une map + * d'entités correspondant à la requête. La clé de la map étant le topiaId + * de l'entité. + * + * @param query la requête + * @return la map d'entités correspondant à la recherche + * @throws TopiaException if any pb while getting datas + * @see TopiaQuery#executeToEntityMap(TopiaContext, Class) + * @since 2.3 + * @deprecated since 2.6.12, {@link TopiaQuery} will be removed in version 3.0 + */ + @Deprecated + Map<String, E> findAllMappedByQuery(TopiaQuery query) throws TopiaException; + + /** + * Execute une requête basé sur l'entité du DAO. Permet de récupérer une map + * d'entités correspondant à la requête. Le type et le nom de la propriété + * utilisé comme clé de la map doit être passé en argument. + * + * @param <K> type de la clé de la map + * @param query la requête + * @param keyName nom de la propriété de l'entité utilisée comme clé + * @param keyClass type de la propriété de l'entité utilisée comme clé + * @return la map d'entités correspondant à la recherche + * @throws TopiaException if any pb while getting datas + * @see TopiaQuery#executeToEntityMap(TopiaContext, Class) + * @since 2.3 + * @deprecated since 2.6.12, {@link TopiaQuery} will be removed in version 3.0 + */ + @Deprecated + <K> Map<K, E> findAllMappedByQuery(TopiaQuery query, + String keyName, Class<K> keyClass) throws TopiaException; + + /** + * Check the existence of an entity using a {@code query}. + * + * @param query query used to test existence + * @return true if entity exists, false otherwise + * @throws TopiaException + * @since 2.3.4 + * @deprecated since 2.6.12, {@link TopiaQuery} will be removed in version 3.0 + */ + @Deprecated + boolean existByQuery(TopiaQuery query) throws TopiaException; + + /** + * Count the number of entities based on {@code query}. + * + * @param query query + * @return number of entities filtered by the query + * @throws TopiaException if any pb while getting datas + * @since 2.3.4 + * @deprecated since 2.6.12, {@link TopiaQuery} will be removed in version 3.0 + */ + @Deprecated + int countByQuery(TopiaQuery query) throws TopiaException; + + /** + * Retourne les permissions a verifier pour l'acces a l'entite pour le + * service Taas. + * + * @param topiaId topiaId d'une entite + * @param actions encoded actions + * @return la liste des permissions + * @throws TopiaException if any pb while getting datas + * @deprecated since 2.6.14, {@link TopiaQuery} will be removed in version 3.0 + */ + List<Permission> getRequestPermission(String topiaId, int actions) + throws TopiaException; +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAOImpl.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAOImpl.java new file mode 100644 index 0000000..b12338d --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAOImpl.java @@ -0,0 +1,925 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * + * TopiaDAOAbstract.java + * + * Created: 31 déc. 2005 13:10:34 + * + * @author poussin <poussin@codelutin.com> + * @version $Revision$ + * + * Last update: $Date$ + * by : $Author$ + */ + +package org.nuiton.topia.persistence; + +import com.google.common.base.Preconditions; +import com.google.common.collect.Iterators; +import org.apache.commons.beanutils.PropertyUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.HibernateException; +import org.hibernate.Session; +import org.hibernate.metadata.ClassMetadata; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaRuntimeException; +import org.nuiton.topia.event.TopiaEntityListener; +import org.nuiton.topia.event.TopiaEntityVetoable; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.framework.TopiaQuery; +import org.nuiton.topia.persistence.pager.TopiaPagerBean; +import org.nuiton.util.PagerBeanUtil; + +import java.io.Serializable; +import java.lang.reflect.InvocationTargetException; +import java.security.Permission; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; + +/** + * Cette classe permet d'avoir un ensemble de méthode implantée de façon + * standard et plus spécifiquement pour Hibernate. + * <p/> + * Certains accès à Hibernate sont tout de même fait ici, car on a pris le choix + * de se baser entièrement sur hibernate pour la persistence, et il est ainsi + * possible d'accèder au meta information hibernate sur les classes lorque l'on + * en a besoin. + * + * @param <E> le type de l'entite + * @author bpoussin <poussin@codelutin.com> + * @version $Id$ + */ + +public class TopiaDAOImpl<E extends TopiaEntity> implements + TopiaDAO<E> { // TopiaDAOImpl + + /** to use log facility, just put in your code: log.info(\"...\"); */ + private static Log log = LogFactory.getLog(TopiaDAOImpl.class); + + /** + * Type of entity managed by this dao. + * + * @since ever + */ + protected Class<E> entityClass; + + /** + * Underlying context used by this dao to do actions on db. + * + * @since ever + */ + protected TopiaContextImplementor context; + + /** + * Default batch size used to iterate on data. + * + * @since 2.6.14 + */ + private int batchSize = 1000; + + @Override + public TopiaEntityEnum getTopiaEntityEnum() { + throw new UnsupportedOperationException( + "This method must be overided in generated DAO"); + } + + @Override + public Class<E> getEntityClass() { + throw new UnsupportedOperationException( + "This method must be overided in generated DAO"); + } + + @Override + public int getBatchSize() { + return batchSize; + } + + @Override + public void setBatchSize(int batchSize) { + this.batchSize = batchSize; + } + + @Override + public Iterator<E> iterator() { + + Iterator<E> iterator = new FindAllIterator<E, E>( + this, + getEntityClass(), + batchSize, + "FROM " + getTopiaEntityEnum().getImplementationFQN() + " ORDER BY id"); + + return iterator; + } + + /** + * Retourne l'id de l'entity + * + * @param e l'entity + * @return l'id de l'entity ou null si pas trouvé + * @throws TopiaException Si une erreur survient durant la recherche + */ + protected Serializable getId(E e) throws TopiaException { + ClassMetadata meta = getClassMetadata(); + String idPropName = meta.getIdentifierPropertyName(); + + try { + Serializable result; + result = (Serializable) PropertyUtils.getSimpleProperty(e, + idPropName); + return result; + } catch (Exception eee) { + throw new TopiaException("Impossible de récuperer l'identifiant " + + idPropName + " de l'entite: " + e); + } + } + + /** + * Retourne l'id de l'entity representer comme une map + * + * @param map l'entity en representation map + * @return l'id de l'entity ou null si pas trouvé + * @throws TopiaException Si une erreur survient durant la recherche + */ + protected Serializable getId(Map map) throws TopiaException { + try { + ClassMetadata meta = getClassMetadata(); + String idPropName = meta.getIdentifierPropertyName(); + + Serializable id = (Serializable) map.get(idPropName); + return id; + } catch (HibernateException eee) { + throw new TopiaException(eee); + } + } + + /** + * When TopiaContextImpl create the TopiaDAOHibernate, it must call this + * method just after. + * + * @param entityClass + */ + @Override + public void init(TopiaContextImplementor context, Class<E> entityClass) + throws TopiaException { + log.debug("init dao for " + entityClass.getName()); + this.context = context; + this.entityClass = entityClass; + } + + @Override + public TopiaContextImplementor getContext() { + return context; + } + + @Override + public String createSimpleQuery(String alias) { + String hql = "FROM " + getTopiaEntityEnum().getImplementationFQN(); + if (StringUtils.isNotBlank(alias)) { + hql += " " + alias; + } + return hql; + } + + @SuppressWarnings("unchecked") + @Override + public E newInstance() throws TopiaException { + if (log.isDebugEnabled()) { + log.debug("entityClass = " + entityClass); + } + Class<E> implementation = (Class<E>) + getTopiaEntityEnum().getImplementation(); + + try { + E result = implementation.newInstance(); + return result; + } catch (Exception e) { + throw new TopiaException( + "Impossible de trouver ou d'instancier la classe " + + implementation); + } + } + + @Override + public <U extends TopiaEntity> List<U> findUsages(Class<U> type, E e) + throws TopiaException { + // must be implemented by specialized dao + throw new UnsupportedOperationException(); + } + + @Override + public Map<Class<? extends TopiaEntity>, List<? extends TopiaEntity>> findAllUsages(E e) + throws TopiaException { + // must be implemented by specialized dao + throw new UnsupportedOperationException(); + } + + @Override + public List<Permission> getRequestPermission(String topiaId, int actions) + throws TopiaException { + return null; + } + + @Override + public void addTopiaEntityListener(TopiaEntityListener listener) { + getContext().addTopiaEntityListener(entityClass, listener); + } + + @Override + public void addTopiaEntityVetoable(TopiaEntityVetoable vetoable) { + getContext().addTopiaEntityVetoable(entityClass, vetoable); + } + + @Override + public void removeTopiaEntityListener(TopiaEntityListener listener) { + getContext().removeTopiaEntityListener(entityClass, listener); + } + + @Override + public void removeTopiaEntityVetoable(TopiaEntityVetoable vetoable) { + getContext().removeTopiaEntityVetoable(entityClass, vetoable); + } + + @Override + public E create(E entity) throws TopiaException { + + // TODO EC-20100322 this code may be merged with other create() methods + try { + // first set topiaId + String topiaId = TopiaId.create(entityClass); + TopiaEntityAbstract entityAbstract = (TopiaEntityAbstract) entity; + entityAbstract.setTopiaId(topiaId); + entityAbstract.setTopiaContext(getContext()); + + // save entity + getSession().save(entity); + getContext().getFiresSupport().warnOnUpdateEntity(entity); + return entity; + } catch (HibernateException eee) { + throw new TopiaException(eee); + } + } + + @Override + public E create(Object... properties) throws TopiaException { + Map<String, Object> map = new HashMap<String, Object>(); + Object propertyName = null; + Object value; + try { + for (int i = 0; i < properties.length; ) { + propertyName = properties[i++]; + value = properties[i++]; + map.put((String) propertyName, value); + } + } catch (ArrayIndexOutOfBoundsException eee) { + throw new IllegalArgumentException("Wrong number of argument " + + properties.length + + ", you must have even number. Last property name read: " + + propertyName); + } catch (ClassCastException eee) { + throw new IllegalArgumentException( + "Wrong argument type, wait property name as String and " + + "have " + propertyName.getClass().getName()); + } + + E result = create(map); + return result; + } + + /** + * Cette methode appelle fireVetoableCreate et fireOnCreated Si vous la + * surchargé, faites attention a appeler le super ou a appeler vous aussi + * ces deux methodes. + */ + @Override + public E create(Map<String, Object> properties) throws TopiaException { + E result = newInstance(); + + // TODO reflechir s'il ne faudrait pas creer l'id avant l'event precedent + // reflechir toujours dans un context on les E pourrait ne pas + // etre des TopiaEntity + if (result instanceof TopiaEntity) { + String topiaId = TopiaId.create(entityClass); + TopiaEntityAbstract entity = (TopiaEntityAbstract) result; + entity.setTopiaId(topiaId); + entity.setTopiaContext(getContext()); + } + try { + for (Map.Entry<String, Object> e : properties.entrySet()) { + String propertyName = e.getKey(); + Object value = e.getValue(); + PropertyUtils.setProperty(result, propertyName, value); + } + } catch (IllegalAccessException eee) { + throw new IllegalArgumentException( + "Can't put properties on new Object", eee); + } catch (InvocationTargetException eee) { + throw new IllegalArgumentException( + "Can't put properties on new Object", eee); + } catch (NoSuchMethodException eee) { + throw new IllegalArgumentException( + "Can't put properties on new Object", eee); + } + + // on fait un save maintenant, car puisqu'on a creer l'entity au + // travers du DAO, on s'attend a l'avoir a disposition tout de + // suite pour les requetes sans avoir a faire un update dessus + getSession().save(result); + getContext().getFiresSupport().warnOnCreateEntity(result); + return result; + } + + @Override + public E update(E e) throws TopiaException { + try { + getSession().saveOrUpdate(e); + getContext().getFiresSupport().warnOnUpdateEntity(e); + return e; + } catch (HibernateException eee) { + throw new TopiaException(eee); + } + } + + @Override + public void delete(E e) throws TopiaException { + try { + getSession().delete(e); + getContext().getFiresSupport().warnOnDeleteEntity(e); + } catch (HibernateException eee) { + throw new TopiaException(eee); + } + } + + @Override + public void deleteAll(Iterable<E> entities) throws TopiaException { + for (E entity : entities) { + delete(entity); + } + } + + @Override + public TopiaQuery createQuery() { + return new TopiaQuery(getEntityClass()); + } + + @Override + public TopiaQuery createQuery(String entityAlias) { + return new TopiaQuery(getEntityClass(), entityAlias); + } + + @Override + public E findByTopiaId(String id) throws TopiaException { + // Nothing to do if id is null, the result will still be null. + E result = null; + if (id != null) { + TopiaQuery query = createQuery().addEquals(TopiaEntity.TOPIA_ID, id); + result = findByQuery(query); + } + return result; + } + + @Override + public E findByProperty(String propertyName, Object value) + throws TopiaException { + Map<String, Object> properties = new HashMap<String, Object>(); + properties.put(propertyName, value); + E result = findByProperties(properties); + return result; + } + + @Override + public E findByProperties(String propertyName, Object value, + Object... others) throws TopiaException { + Map<String, Object> properties = + convertPropertiesArrayToMap(propertyName, value, others); + E result = findByProperties(properties); + return result; + } + + @Override + public E findByProperties(Map<String, Object> properties) + throws TopiaException { + TopiaQuery query = createQuery().addEquals(properties); + E result = findByQuery(query); + return result; + } + + @Override + public E findByQuery(TopiaQuery query) throws TopiaException { + E result = query.executeToEntity(getContext(), getEntityClass()); + return result; + } + + @Override + public List<E> findAll() throws TopiaException { + List<E> results = findAllByQuery(createQuery()); + return results; + } + + @Override + public List<String> findAllIds() throws TopiaException { + TopiaQuery query = createQuery().setSelect(TopiaEntity.TOPIA_ID); + List<String> results = getContext().findByQuery(query); + return results; + } + + @Override + public List<E> findAllByProperty(String propertyName, Object value) + throws TopiaException { + Map<String, Object> properties = + convertPropertiesArrayToMap(propertyName, value); + List<E> result = findAllByProperties(properties); + return result; + } + + @Override + public List<E> findAllByProperties(String propertyName, Object value, + Object... others) throws TopiaException { + Map<String, Object> properties = + convertPropertiesArrayToMap(propertyName, value, others); + List<E> result = findAllByProperties(properties); + return result; + } + + @Override + public List<E> findAllByProperties(Map<String, Object> properties) + throws TopiaException { + TopiaQuery query = createQuery().addEquals(properties); + List<E> results = findAllByQuery(query); + return results; + } + + @Override + public List<E> findAllByQuery(TopiaQuery query) throws TopiaException { + List<E> results = query.executeToEntityList(getContext(), getEntityClass()); + return results; + } + + @Override + public Map<String, E> findAllMappedByQuery(TopiaQuery query) + throws TopiaException { + Map<String, E> results = + query.executeToEntityMap(getContext(), getEntityClass()); + return results; + } + + @Override + public <K> Map<K, E> findAllMappedByQuery(TopiaQuery query, String keyName, + Class<K> keyClass) + throws TopiaException { + Map<K, E> results = + query.executeToEntityMap(getContext(), getEntityClass(), + keyName, keyClass); + return results; + } + + @Override + public List<E> findAllWithOrder(String... propertyNames) + throws TopiaException { + TopiaQuery query = createQuery().addOrder(propertyNames); + List<E> results = findAllByQuery(query); + return results; + } + + @Override + public E findContains(String propertyName, + Object property) throws TopiaException { + TopiaQuery k = createQuery(). + addInElements(":K", propertyName). + addParam("K", property); + return findByQuery(k); + } + + @Override + public List<E> findAllContains(String propertyName, + Object property) throws TopiaException { + TopiaQuery k = createQuery(). + addInElements(":K", propertyName). + addParam("K", property); + return findAllByQuery(k); + } + + @Override + public boolean existByTopiaId(String id) throws TopiaException { + boolean result = existByProperties(TopiaEntity.TOPIA_ID, id); + return result; + } + + @Override + public boolean existByProperties(String propertyName, Object propertyValue, + Object... others) throws TopiaException { + Map<String, Object> properties = + convertPropertiesArrayToMap(propertyName, propertyValue, others); + TopiaQuery query = createQuery().addEquals(properties); + boolean result = existByQuery(query); + return result; + } + + @Override + public boolean existByQuery(TopiaQuery query) throws TopiaException { + int count = countByQuery(query); + boolean result = count > 0; + return result; + } + + @Override + public long count() throws TopiaException { + int result = countByQuery(createQuery()); + return result; + } + + @Override + public int countByQuery(TopiaQuery query) throws TopiaException { + int result = query.executeCount(getContext()); + return result; + } + + /** + * Convert a properties array to a proper map used to find entities in + * methods {@link #findByProperties(String, Object, Object...)} and {@link + * #findAllByProperties(String, Object, Object...)}. + * + * @param propertyName first property name to test existence + * @param propertyValue first property value to test existence + * @param others altern propertyName and propertyValue + * @return a Map with properties, propertyName as key. + * @throws IllegalArgumentException for ClassCast or ArrayIndexOutOfBounds + * errors + */ + private Map<String, Object> convertPropertiesArrayToMap(String propertyName, + Object propertyValue, + Object... others) + throws IllegalArgumentException { + Map<String, Object> properties = new HashMap<String, Object>(); + properties.put(propertyName, propertyValue); + Object name = null; + for (int i = 0; i < others.length; ) { + try { + name = others[i++]; + propertyValue = others[i++]; + properties.put((String) name, propertyValue); + } catch (ClassCastException eee) { + throw new IllegalArgumentException( + "Les noms des propriétés doivent être des chaines et " + + "non pas " + propertyName.getClass().getName(), + eee); + } catch (ArrayIndexOutOfBoundsException eee) { + throw new IllegalArgumentException( + "Le nombre d'argument n'est pas un nombre pair: " + + (others.length + 2) + + " La dernière propriété était: " + name, eee); + } + } + return properties; + } + + @Override + public E findByPrimaryKey(Map<String, Object> keys) + throws TopiaException { + try { + // we used hibernate meta information for all persistence type + // it's more easy than create different for all persistence + ClassMetadata meta = getClassMetadata(); + if (meta.hasNaturalIdentifier()) { + E result = findByProperties(keys); + return result; + } + } catch (HibernateException eee) { + throw new TopiaException(eee); + } + throw new TopiaException("La classe " + entityClass.getName() + + " n'a pas de cle primaire naturelle"); + + } + + @Override + public E findByPrimaryKey(Object... k) throws TopiaException { + // TODO pour une meilleur gestion des problemes a la compilation + // mettre un premier couple (propName, value) en argument ca evitera + // de pouvoir appeler cette methode sans argument + try { + ClassMetadata meta = getClassMetadata(); + if (meta.hasNaturalIdentifier()) { + int[] ikeys = meta.getNaturalIdentifierProperties(); + String[] pnames = meta.getPropertyNames(); + + Map<String, Object> keys = new HashMap<String, Object>(); + for (int ikey : ikeys) { + keys.put(pnames[ikey], k[ikey]); + } + + E result = findByProperties(keys); + return result; + } + } catch (HibernateException eee) { + throw new TopiaException(eee); + } + throw new TopiaException("La classe " + entityClass.getName() + + " n'a pas de cle primaire naturelle"); + } + + @Override + public boolean existsByQuery(String hql, + Object... params) throws TopiaException { + long count = countByQuery(hql, params); + return count > 0; + } + + @Override + public long countByQuery(String hql, + Object... params) throws TopiaException { + + Preconditions.checkNotNull(StringUtils.isNotBlank(hql)); + Preconditions.checkArgument(hql.toUpperCase().trim().startsWith("SELECT COUNT(")); + + return findByQuery(Long.class, hql, params); + } + + @Override + public E findByQuery(String hql, + Object... params) throws TopiaException { + return findByQuery(getEntityClass(), hql, params); + } + + @Override + public <R> R findByQuery(Class<R> type, + String hql, + Object... params) throws TopiaException { + + Preconditions.checkNotNull(type); + Preconditions.checkNotNull(hql); + + Object unique = getContext().findUnique(hql, params); + Preconditions.checkState(unique == null || + type.isAssignableFrom(unique.getClass())); + return (R) unique; + } + + @Override + public List<E> findAllByQuery(String hql, + Object... params) throws TopiaException { + + return findAllByQuery(getEntityClass(), hql, params); + } + + @Override + public <R> List<R> findAllByQuery(Class<R> type, + String hql, + Object... params) throws TopiaException { + + Preconditions.checkNotNull(type); + Preconditions.checkNotNull(hql); + + List<R> result = getContext().findAll(hql, params); + return result; + } + + @Override + public Iterable<E> findAllLazyByQuery(String hql, + Object... params) throws TopiaException { + return findAllLazyByQuery(batchSize, hql, params); + } + + @Override + public <R> Iterable<R> findAllLazyByQuery(Class<R> type, + String hql, + Object... params) throws TopiaException { + return findAllLazyByQuery(type, batchSize, hql, params); + } + + @Override + public Iterable<E> findAllLazyByQuery(int batchSize, + String hql, + Object... params) throws TopiaException { + return findAllLazyByQuery(getEntityClass(), batchSize, hql, params); + } + + @Override + public <R> Iterable<R> findAllLazyByQuery(Class<R> type, + int batchSize, + String hql, + Object... params) throws TopiaException { + + final Iterator<R> iterator = new FindAllIterator<E, R>(this, + type, + batchSize, + hql, + params); + return new Iterable<R>() { + @Override + public Iterator<R> iterator() { + return iterator; + } + }; + } + + @Override + public <R> List<R> findAllByQueryWithBound(Class<R> type, + String hql, + int startIndex, + int endIndex, + Object... params) throws TopiaException { + Preconditions.checkNotNull(type); + Preconditions.checkNotNull(hql); + + List<R> result = getContext().find(hql, startIndex, endIndex, params); + return result; + } + + @Override + public List<E> findAllByQueryWithBound(String hql, + int startIndex, + int endIndex, + Object... params) throws TopiaException { + return findAllByQueryWithBound(getEntityClass(), + hql, + startIndex, + endIndex, + params); + } + + @Override + public <R> List<R> findAllByQueryAndPager(Class<R> type, + String hql, + TopiaPagerBean pager, + Object... params) throws TopiaException { + Preconditions.checkNotNull(pager); + Preconditions.checkNotNull(hql); + + if (StringUtils.isNotBlank(pager.getSortColumn())) { + hql += " ORDER BY " + pager.getSortColumn(); + if (!pager.isSortAscendant()) { + hql += " DESC"; + } + } + List<R> result = findAllByQueryWithBound(type, hql, + (int) pager.getRecordStartIndex(), + (int) pager.getRecordEndIndex() - 1, + params); + return result; + } + + @Override + public List<E> findAllByQueryAndPager(String hql, + TopiaPagerBean pager, + Object... params) throws TopiaException { + + return findAllByQueryAndPager(getEntityClass(), + hql, + pager, + params); + } + + @Override + public void computeAndAddRecordsToPager(String hql, + TopiaPagerBean pager, + Object... params) throws TopiaException { + + long records = countByQuery(hql, params); + + pager.setRecords(records); + PagerBeanUtil.computeRecordIndexesAndPagesNumber(pager); + } + + /** + * Renvoie la Session contenue dans le contexte + * + * @return hibernate session + * @throws TopiaException if any pb + */ + Session getSession() throws TopiaException { + return getContext().getHibernate(); + } + + /** + * package locale method because this is hibernate specific method and + * we don't want expose it. + * + * @return the meta-data of the entity + * @throws TopiaException if any pb + */ + ClassMetadata getClassMetadata() throws TopiaException { + ClassMetadata meta = getContext().getHibernateFactory() + .getClassMetadata(entityClass); + if (meta == null) { + meta = getContext().getHibernateFactory().getClassMetadata( + getTopiaEntityEnum().getImplementationFQN()); + } + return meta; + } + + public static class FindAllIterator<E extends TopiaEntity, R> implements Iterator<R> { + + protected Iterator<R> data; + + protected final TopiaDAO<E> dao; + + protected final Class<R> type; + + protected final String hql; + + protected final Object[] params; + + protected TopiaPagerBean pager; + + public FindAllIterator(TopiaDAO<E> dao, + Class<R> type, + int batchSize, + String hql, + Object... params) { + this.dao = dao; + this.type = type; + this.hql = hql; + this.params = params; + try { + String hql2 = hql.toLowerCase(); + int i = hql2.indexOf("order by"); + if (i == -1) { + throw new IllegalStateException( + "must have a *order by* in hql, " + + "but did not find it in " + hql); + } + + // get the count (removing the order-by) + long count2 = dao.countByQuery("SELECT COUNT(*) " + + hql.substring(0, i), params); + pager = new TopiaPagerBean(); + pager.setRecords(count2); + pager.setPageSize(batchSize); + PagerBeanUtil.computeRecordIndexesAndPagesNumber(pager); + } catch (TopiaException e) { + throw new TopiaRuntimeException(e); + } + + // empty iterator (will be changed at first next call) + data = Iterators.emptyIterator(); + } + + @Override + public boolean hasNext() { + return data.hasNext() || // no more data + pager.getPageIndex() < pager.getPagesNumber(); + } + + @Override + public R next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + + if (!data.hasNext()) { + + // must load iterator + + // increments page index + pager.setPageIndex(pager.getPageIndex() + 1); + PagerBeanUtil.computeRecordIndexesAndPagesNumber(pager); + + // load new window of data + try { + data = dao.findAllByQueryAndPager(type, + hql, + pager, + params).iterator(); + } catch (TopiaException e) { + throw new TopiaRuntimeException(e); + } + } + + R next = data.next(); + return next; + } + + @Override + public void remove() { + throw new UnsupportedOperationException( + "This iterator does not support remove operation."); + } + } +} //TopiaDAOImpl diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAOLegacy.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAOLegacy.java new file mode 100644 index 0000000..0d90f88 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaDAOLegacy.java @@ -0,0 +1,625 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +/* *##% + * ToPIA :: Persistence + * Copyright (C) 2004 - 2009 CodeLutin + * + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * ##%*/ + +package org.nuiton.topia.persistence; + +import org.apache.commons.beanutils.PropertyUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.Criteria; +import org.hibernate.FlushMode; +import org.hibernate.HibernateException; +import org.hibernate.criterion.Criterion; +import org.hibernate.criterion.Order; +import org.hibernate.criterion.Restrictions; +import org.hibernate.metadata.ClassMetadata; +import org.nuiton.topia.TopiaException; + +import java.lang.reflect.InvocationTargetException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Surcharge du {@link TopiaDAOImpl} pour utiliser l'api criteria au lieu du hql + * pour tout ce qui est requétage. + * <p/> + * Created: 31 déc. 2005 13:10:34 + * + * @param <E> le type de l'entite + * @author bpoussin <poussin@codelutin.com> + * @author tchemit <chemit@codelutin.com> + * @deprecated since 2.6.12 Using the hibernate Criteria api is not a good idea as we wants to use in ToPIA next generation (version 3.0) jpa api. + */ +@Deprecated +public class TopiaDAOLegacy<E extends TopiaEntity> extends TopiaDAOImpl<E> { // TopiaDAOLegacy + + /** Logger. */ + private static Log log = LogFactory.getLog(TopiaDAOLegacy.class); + + @SuppressWarnings("unchecked") + protected E instanciateNew() throws TopiaException { + E result = newInstance(); + return result; + } + + @Override + public E create(Object... properties) throws TopiaException { + Map<String, Object> map = new HashMap<String, Object>(); + Object propertyName = null; + Object value = null; + try { + for (int i = 0; i < properties.length; ) { + propertyName = properties[i++]; + value = properties[i++]; + map.put((String) propertyName, value); + } + } catch (ArrayIndexOutOfBoundsException eee) { + throw new IllegalArgumentException("Wrong number of argument " + + properties.length + + ", you must have even number. Last property name read: " + + propertyName); + } catch (ClassCastException eee) { + throw new IllegalArgumentException( + "Wrong argument type, wait property name as String and have " + + propertyName.getClass().getName()); + } + + E result = create(map); + return result; + } + + @Override + public E findByPrimaryKey(Map<String, Object> keys) + throws TopiaException { + try { + // we used hibernate meta information for all persistence type + // it's more easy than create different for all persistence + ClassMetadata meta = getClassMetadata(); + if (meta.hasNaturalIdentifier()) { + E result = findByProperties(keys); + return result; + } + } catch (HibernateException eee) { + throw new TopiaException(eee); + } + throw new TopiaException("La classe " + entityClass.getName() + + " n'a pas de cle primaire naturelle"); + + } + + @Override + public E findByPrimaryKey(Object... k) throws TopiaException { + // TODO pour une meilleur gestion des problemes a la compilation + // mettre un premier couple (propName, value) en argument ca evitera + // de pouvoir appeler cette methode sans argument + try { + ClassMetadata meta = getClassMetadata(); + if (meta.hasNaturalIdentifier()) { + int[] ikeys = meta.getNaturalIdentifierProperties(); + String[] pnames = meta.getPropertyNames(); + + Map<String, Object> keys = new HashMap<String, Object>(); + for (int ikey : ikeys) { + keys.put(pnames[ikey], k[ikey]); + } + + E result = findByProperties(keys); + return result; + } + } catch (HibernateException eee) { + throw new TopiaException(eee); + } + throw new TopiaException("La classe " + entityClass.getName() + + " n'a pas de cle primaire naturelle"); + + } + + @Override + public E findByProperties(String propertyName, Object value, + Object... others) throws TopiaException { + Map<String, Object> properties = new HashMap<String, Object>(); + properties.put(propertyName, value); + Object name = null; + for (int i = 0; i < others.length; ) { + try { + name = others[i++]; + value = others[i++]; + properties.put((String) name, value); + } catch (ClassCastException eee) { + throw new IllegalArgumentException( + "Les noms des propriétés doivent être des chaines et non pas " + + propertyName.getClass().getName(), eee); + } catch (ArrayIndexOutOfBoundsException eee) { + throw new IllegalArgumentException( + "Le nombre d'argument n'est pas un nombre pair: " + + (others.length + 2) + + " La dernière propriété était: " + name, eee); + } + } + E result = findByProperties(properties); + return result; + } + + @Override + public List<E> findAllByProperties(String propertyName, Object value, + Object... others) throws TopiaException { + Map<String, Object> properties = new HashMap<String, Object>(); + properties.put(propertyName, value); + Object name = null; + for (int i = 0; i < others.length; ) { + try { + name = others[i++]; + value = others[i++]; + properties.put((String) name, value); + } catch (ClassCastException eee) { + throw new IllegalArgumentException( + "Les noms des propriétés doivent être des chaines et non pas " + + propertyName.getClass().getName(), eee); + } catch (ArrayIndexOutOfBoundsException eee) { + throw new IllegalArgumentException( + "Le nombre d'argument n'est pas un nombre pair: " + + (others.length + 2) + + " La dernière propriété était: " + name, eee); + } + } + List<E> result = findAllByProperties(properties); + return result; + } + +// @Override +// public E findContainsProperties(Map<String, Collection<?>> properties) +// throws TopiaException { +// List<E> results = findAllContainsProperties(properties); +// E result = null; +// if (results.size() > 0) { +// result = results.get(0); +// } +// return result; +// } +// +// @Override +// public E findContainsProperties(String propertyName, +// Collection values, Object... others) throws TopiaException { +// Map<String, Collection<?>> properties = new HashMap<String, Collection<?>>(); +// properties.put(propertyName, values); +// Object name = null; +// for (int i = 0; i < others.length;) { +// try { +// name = others[i++]; +// values = (Collection) others[i++]; +// properties.put((String) name, values); +// } catch (ClassCastException eee) { +// throw new IllegalArgumentException( +// "Les noms des propriétés doivent être des chaines et non pas " +// + propertyName.getClass().getName(), eee); +// } catch (ArrayIndexOutOfBoundsException eee) { +// throw new IllegalArgumentException( +// "Le nombre d'argument n'est pas un nombre pair: " +// + (others.length + 2) +// + " La dernière propriété était: " + name, eee); +// } +// } +// E result = findContainsProperties(properties); +// return result; +// } +// +// /** +// * Find all entities with a specific rule : +// * When the entity have a Collection type property, you want to find all entites where some values are +// * contained in the collection type property. +// * Example entity parameter : private Collection<Date> historicalDates; +// * You want some dates to be contained in historicalDates. +// * Collection<Date> myDates... +// * myDates.add(date1) ... +// * Map<String, Collection> properties = new HashMap<String,Collection>(); +// * properties.put("historicalDates",myDates); +// * findAllContainsProperties(properties); +// * @param properties +// * @return the list of entities corresponding to the request +// * @throws org.nuiton.topia.TopiaException if any pb +// */ +// @Override +// public List<E> findAllContainsProperties(Map<String, Collection<?>> properties) throws TopiaException { +// List<E> all = findAll(); +// List<E> result = new ArrayList<E>(); +// for (E e : all) { +// boolean ok = true; +// try { +// for (Entry<String, Collection<?>> kv : properties.entrySet()) { +// Collection entityValues = (Collection) PropertyUtils +// .getProperty(e, kv.getKey()); +// Collection values = kv.getValue(); +// if (!entityValues.containsAll(values)) { +// ok = false; +// break; +// } +// } +// } catch (IllegalAccessException eee) { +// ok = false; +// if (log.isWarnEnabled()) { +// log.warn( +// "Impossible d'acceder a la methode demandé pour l'obbjet " +// + e, eee); +// } +// } catch (InvocationTargetException eee) { +// ok = false; +// if (log.isWarnEnabled()) { +// log.warn( +// "Impossible d'acceder a la methode demandé pour l'obbjet " +// + e, eee); +// } +// } catch (NoSuchMethodException eee) { +// ok = false; +// if (log.isWarnEnabled()) { +// log.warn( +// "Impossible d'acceder a la methode demandé pour l'obbjet " +// + e, eee); +// } +// } +// if (ok) { +// result.add(e); +// } +// } +// return result; +// } +// +// @Override +// public List<E> findAllContainsProperties(String propertyName, +// Collection values, Object... others) throws TopiaException { +// Map<String, Collection<?>> properties = new HashMap<String, Collection<?>>(); +// properties.put(propertyName, values); +// Object name = null; +// for (int i = 0; i < others.length;) { +// try { +// name = others[i++]; +// values = (Collection) others[i++]; +// properties.put((String) name, values); +// } catch (ClassCastException eee) { +// throw new IllegalArgumentException( +// "Les noms des propriétés doivent être des chaines et non pas " +// + propertyName.getClass().getName(), eee); +// } catch (ArrayIndexOutOfBoundsException eee) { +// throw new IllegalArgumentException( +// "Le nombre d'argument n'est pas un nombre pair: " +// + (others.length + 2) +// + " La dernière propriété était: " + name, eee); +// } +// } +// List<E> result = findAllContainsProperties(properties); +// return result; +// } + + @Override + public List<E> findAllByProperty(String propertyName, Object value) + throws TopiaException { + Map<String, Object> properties = new HashMap<String, Object>(); + properties.put(propertyName, value); + List<E> result = findAllByProperties(properties); + return result; + } + + @Override + public E findByProperty(String propertyName, Object value) + throws TopiaException { + Map<String, Object> properties = new HashMap<String, Object>(); + properties.put(propertyName, value); + E result = findByProperties(properties); + return result; + } + + @Deprecated + private Criterion computeCriterions(Object... values) { + if (values == null) { + return null; + } + Criterion criterion = null; + for (Object value : values) { + criterion = or(criterion, computeCriterion(value)); + } + return criterion; + } + + @Deprecated + private Criterion computeCriterion(Object value) { + Criterion criterion = null; + SearchFields fields = entityClass.getAnnotation(SearchFields.class); + String textValue = "%" + value + "%"; + //textFields + String[] textFields = fields.txtFields(); + for (String propName : textFields) { + criterion = or(criterion, Restrictions.like(propName, textValue)); + } + //numFields + boolean isNumber = value instanceof Number; + if (value instanceof String) { + try { + Double.parseDouble((String) value); + isNumber = true; + } catch (NumberFormatException nfe) { + isNumber = false; + } + + } + if (isNumber) { + String[] numFields = fields.numFields(); + for (String propName : numFields) { + criterion = or(criterion, Restrictions.sqlRestriction(propName + + " like '" + textValue + "'")); + } + } + //boolFields + boolean isBoolean = value instanceof Boolean; + if (value instanceof String) { + isBoolean |= "true".equalsIgnoreCase((String) value) || "false" + .equalsIgnoreCase((String) value); + } + if (isBoolean) { + Boolean booleanValue; + if (value instanceof String) { + booleanValue = Boolean.valueOf((String) value); + } else { + booleanValue = (Boolean) value; + } + String[] boolFields = fields.numFields(); + for (String propName : boolFields) { + criterion = or(criterion, Restrictions.eq(propName, + booleanValue)); + } + } + //timeFields + String[] timeFields = fields.dateFields(); + for (String propName : timeFields) { + criterion = or(criterion, Restrictions.sqlRestriction(propName + + " like '" + textValue + "'")); + } + return criterion; + } + + @Deprecated + private Criterion or(Criterion crit1, Criterion crit2) { + if (crit1 == null) { + return crit2; + } + if (crit2 == null) { + return crit1; + } + return Restrictions.or(crit1, crit2); + } + + /** + * Cette methode appelle fireVetoableCreate et fireOnCreated + * Si vous la surchargé, faites attention a appeler le super + * ou a appeler vous aussi ces deux methodes. + */ + @Override + public E create(Map<String, Object> properties) throws TopiaException { + E result = instanciateNew(); + + // TODO reflechir s'il ne faudrait pas creer l'id avant l'event precedent + // reflechir toujours dans un context on les E pourrait ne pas + // etre des TopiaEntity + if (result instanceof TopiaEntity) { + String topiaId = TopiaId.create(entityClass); + TopiaEntityAbstract entity = (TopiaEntityAbstract) result; + entity.setTopiaId(topiaId); + entity.setTopiaContext(getContext()); + } + try { + for (Map.Entry<String, Object> e : properties.entrySet()) { + String propertyName = e.getKey(); + Object value = e.getValue(); + PropertyUtils.setProperty(result, propertyName, value); + } + } catch (IllegalAccessException eee) { + throw new IllegalArgumentException( + "Can't put properties on new Object", eee); + } catch (InvocationTargetException eee) { + throw new IllegalArgumentException( + "Can't put properties on new Object", eee); + } catch (NoSuchMethodException eee) { + throw new IllegalArgumentException( + "Can't put properties on new Object", eee); + } + + // on fait un save maintenant, car puisqu'on a creer l'entity au + // travers du DAO, on s'attend a l'avoir a disposition tout de + // suite pour les requetes sans avoir a faire un update dessus + getSession().save(result); + getContext().getFiresSupport().warnOnCreateEntity(result); + return result; + } + + @Override + public E findByTopiaId(String k) throws TopiaException { + return query(Restrictions.idEq(k)); + } + + @Override + public List<E> findAll() throws TopiaException { + try { + Criteria criteria = createCriteria(FlushMode.AUTO); + List<E> result = (List<E>) criteria.list(); + result = getContext().getFiresSupport().fireEntitiesLoad(context, + result); + return result; + } catch (HibernateException eee) { + throw new TopiaException(eee); + } + } + + @Override + public List<String> findAllIds() throws TopiaException { +// List<String> findAll = context.findAll("select src.topiaId from " + entityClass.getSimpleName() + "Impl src"); + List<String> find = context.findAll("select src.topiaId from " + getEntityClass() + " src"); + return find; + } + + + @Override + public List<E> findAllWithOrder(String... propertyNames) + throws TopiaException { + try { + Criteria criteria = createCriteria(FlushMode.AUTO); + for (String propertyName : propertyNames) { + criteria.addOrder(Order.asc(propertyName)); + } + List<E> result = (List<E>) criteria.list(); + result = getContext().getFiresSupport().fireEntitiesLoad(context, + result); + return result; + } catch (HibernateException eee) { + throw new TopiaException(eee); + } + } + + @Override + public E findByProperties(Map<String, Object> properties) + throws TopiaException { + return query(Restrictions.allEq(properties)); + } + + @Override + public List<E> findAllByProperties(Map<String, Object> properties) + throws TopiaException { + return queryAll(Restrictions.allEq(properties)); + } + + private List<E> queryAll(Criterion criterion) throws TopiaException { + try { + Criteria criteria = createCriteria(FlushMode.AUTO); + criteria.add(criterion); + List<E> result = (List<E>) criteria.list(); + result = getContext().getFiresSupport().fireEntitiesLoad(context, + result); + return result; + } catch (HibernateException eee) { + throw new TopiaException(eee); + } + } + + private E query(Criterion criterion) throws TopiaException { + try { + Criteria criteria = createCriteria(FlushMode.AUTO); + criteria.add(criterion); + criteria.setMaxResults(1); + List<E> result = (List<E>) criteria.list(); + int sizeBefore = result != null ? result.size() : 0; + result = getContext().getFiresSupport().fireEntitiesLoad(context, + result); + int sizeAfter = result != null ? result.size() : 0; + if (sizeAfter < sizeBefore) { + if (log.isDebugEnabled()) { + log.debug((sizeBefore - sizeAfter) + + " element(s) removed. Filter entity: " + + entityClass.getName() + " - criterion: " + + criterion); + } + } + if (result != null && result.size() > 0) { + E elem = result.get(0); + return elem; + } + return null; + } catch (HibernateException eee) { + throw new TopiaException(eee); + } + } + + //FIXME : Commenté car impossible de trouver le bon Criterion + // ATTENTION ancienne methode du TopiaDAOAbstract deja presente dans le fichier + + // /* (non-Javadoc) + // * @see org.nuiton.topia.persistence.TopiaDAO#findContainsProperties(java.util.Map) + // */ + // public E findContainsProperties(Map<String, Collection> properties) throws TopiaException { + // try { + // Criteria criteria = createCriteria(FlushMode.AUTO); + // for (Entry<String, Collection> entry : properties.entrySet()) { + // for (Object value : entry.getValue()) { + // criteria.add(Restrictions.eq(entry.getKey(), value)); + // } + // } + // criteria.setMaxResults(1); + // List<E> results = (List<E>)criteria.list(); + // if (results.size() > 0) { + // return (E)results.get(0); + // } else { + // return null; + // } + // } catch (HibernateException eee) { + // throw new TopiaException(eee); + // } + // } + // + // /* (non-Javadoc) + // * @see org.nuiton.topia.persistence.TopiaDAO#findAllContainsProperties(java.util.Map) + // */ + // public List<E> findAllContainsProperties(Map<String, Collection> properties) throws TopiaException { + // try { + // Criteria criteria = createCriteria(FlushMode.AUTO); + // for (Entry<String, Collection> entry : properties.entrySet()) { + // for (Object value : entry.getValue()) { + // criteria.add(Restrictions.eq(entry.getKey(), value)); + // } + // } + // List<E> result = (List<E>)criteria.list(); + // return result; + // } catch (HibernateException eee) { + // throw new TopiaException(eee); + // } + // } + + /** + * Renvoie un Criteria créé avec l'entityClass + * + * @param mode le FlushMode du Criteria + * @return le Criteria nouvellement créé + * @throws TopiaException if any pb + */ + private Criteria createCriteria(FlushMode mode) throws TopiaException { + Criteria criteria = getSession().createCriteria(entityClass); + criteria.setFlushMode(mode); + return criteria; + } + +} //TopiaDAOLegacy diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaEntity.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaEntity.java new file mode 100644 index 0000000..32535ea --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaEntity.java @@ -0,0 +1,215 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin, Chemit Tony + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * + * TopiaEntity.java + * + * Created: 28 déc. 2005 22:48:10 + * + * @author poussin <poussin@codelutin.com> + * @version $Revision$ + * + * Last update: $Date$ + * by : $Author$ + */ + +package org.nuiton.topia.persistence; + +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.generator.EntityHibernateMappingGenerator; +import org.nuiton.topia.generator.EntityTransformer; +import org.nuiton.topia.generator.TopiaMetaTransformer; + +import java.beans.PropertyChangeListener; +import java.beans.VetoableChangeListener; +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * The TopiaEntity is the main interface for each entities generated with {@link + * TopiaMetaTransformer}. An entity is simply a persistent bean mapped with + * hibernate. The manipulation on entities (create, update, delete, find) is + * made by the dao associated. The corresponding dao interface is {@link + * TopiaDAO}. + * <p/> + * Setter methods have to be used only in internal. They are in the interface to + * make easier their usages in internal. + * + * @author poussin <poussin@codelutin.com> + * @author fdesbois <fdesbois@codelutin.com> + * @version $Id$ + * @see EntityHibernateMappingGenerator + * @see EntityTransformer + */ +@SearchFields +public interface TopiaEntity extends Serializable { + + String TOPIA_ID = "topiaId"; + + String TOPIA_CREATE_DATE = "topiaCreateDate"; + + String TOPIA_VERSION = "topiaVersion"; + + String COMPOSITE = "composite"; + + String AGGREGATE = "aggregate"; + + /** + * Unique technical Id of the entity. This id contains the full qualified + * name of the entity interface. This id has also an index and his used to + * identify uniquely the entity in the database. + * + * @return the technical Id of the entity + */ + String getTopiaId(); + + /** + * Set the technical {@code id} of the entity. Careful, use this method only + * for copy. The technical id is generated by ToPIA when entity is created + * using {@link TopiaDAO#create(Object...)}. + * + * @param id technical id to set + */ + void setTopiaId(String id); + + /** + * Technical property to keep versionning of the entity. The version is + * incremented on each change of the entity. + * + * @return the current version of the entity + */ + long getTopiaVersion(); + + /** + * Set the technical {@code version} of the entity. Careful, use this method + * only for copy. The version is automatically incremented on entity + * changes. + * + * @param version technical version to set + */ + void setTopiaVersion(long version); + + /** + * Technical date creation of the entity. This date doesn't change through + * time and was initialized on entity creation when using {@link + * TopiaDAO#create(Object...)}. + * + * @return the creation date of the entity + */ + Date getTopiaCreateDate(); + + /** + * Set the technical creation {@code date} of the entity. Careful, use this + * method only for copy. This date is immutable and was created on entity + * creation. + * + * @param date technical create date to set + */ + void setTopiaCreateDate(Date date); + + /** + * @return all object that must be deleted if this object is deleted + * @throws TopiaException if any pb + */ + List<TopiaEntity> getComposite() throws TopiaException; + + /** + * @return all object that are aggregate with this instance, aggreate object + * are not removed automaticaly + * @throws TopiaException if any pb + */ + List<TopiaEntity> getAggregate() throws TopiaException; + + /** + * Add listener for property writing. + * + * @param propertyName name of property to listen + * @param listener the listener to register + */ + void addPropertyChangeListener(String propertyName, + PropertyChangeListener listener); + + /** + * Add listener for property writing. + * + * @param listener the listener to register + */ + void addPropertyChangeListener(PropertyChangeListener listener); + + void addVetoableChangeListener(String propertyName, + VetoableChangeListener vetoable); + + void addVetoableChangeListener(VetoableChangeListener vetoable); + + void removePropertyChangeListener(String propertyName, + PropertyChangeListener listener); + + void removePropertyChangeListener(PropertyChangeListener listener); + + void removeVetoableChangeListener(String propertyName, + VetoableChangeListener vetoable); + + void removeVetoableChangeListener(VetoableChangeListener vetoable); + + /** + * Add listener for property reading. + * + * @param propertyName the property name to listen + * @param listener the listener to register + */ + void addPropertyListener(String propertyName, + PropertyChangeListener listener); + + /** + * Add listener for property reading. + * + * @param listener the listener to register + */ + void addPropertyListener(PropertyChangeListener listener); + + void addVetoableListener(String propertyName, + VetoableChangeListener vetoable); + + void addVetoableListener(VetoableChangeListener vetoable); + + void removePropertyListener(String propertyName, + PropertyChangeListener listener); + + void removePropertyListener(PropertyChangeListener listener); + + void removeVetoableListener(String propertyName, + VetoableChangeListener vetoable); + + void removeVetoableListener(VetoableChangeListener vetoable); + + /** + * Route the entity using a {@code visitor}. + * + * @param visitor to used + * @throws TopiaException for all type of error + */ + void accept(EntityVisitor visitor) throws TopiaException; + +} //TopiaEntity diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaEntityAbstract.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaEntityAbstract.java new file mode 100644 index 0000000..ea41994 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaEntityAbstract.java @@ -0,0 +1,355 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence; + +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; + +import java.beans.PropertyChangeListener; +import java.beans.PropertyChangeSupport; +import java.beans.VetoableChangeListener; +import java.beans.VetoableChangeSupport; +import java.util.Date; +import java.util.List; + +/** + * Classe de base de toutes les entités, cela permet de concentrer le code + * technique dans cette classe. L'identifiant peut-etre n'importe quoi Aucune + * restriction n'est faite dessus, il peut meme changer entre deux types + * d'entité si cela ne pose pas d'autre probleme (heritage entre ces entités). + * + * @author poussin <poussin@codelutin.com> + * @version $Id$ + */ +public abstract class TopiaEntityAbstract implements TopiaEntity { + + /** serialVersionUID. */ + private static final long serialVersionUID = -7458577454878852241L; + + protected String topiaId; + + protected long topiaVersion; + + protected Date topiaCreateDate = new Date(); + + transient protected TopiaContext topiaContext; + + transient protected VetoableChangeSupport readVetoables; + + transient protected PropertyChangeSupport readListeners; + + transient protected VetoableChangeSupport writeVetoables; + + transient protected PropertyChangeSupport writeListeners; + + /** + * Initialize {@link #readVetoables} at first use or after deserialisation. + * + * @return readVetoables + */ + protected VetoableChangeSupport getReadVetoableChangeSupport() { + if (readVetoables == null) { + readVetoables = new VetoableChangeSupport(this); + } + return readVetoables; + } + + /** + * Initialize {@link #readListeners} at first use or after deserialisation. + * + * @return readListeners + */ + protected PropertyChangeSupport getReadPropertyChangeSupport() { + if (readListeners == null) { + readListeners = new PropertyChangeSupport(this); + } + return readListeners; + } + + /** + * Initialize {@link #writeVetoables} at first use or after deserialisation. + * + * @return writeVetoables + */ + protected VetoableChangeSupport getWriteVetoableChangeSupport() { + if (writeVetoables == null) { + writeVetoables = new VetoableChangeSupport(this); + } + return writeVetoables; + } + + /** + * Initialize {@link #writeListeners} at first use or after deserialisation. + * + * @return writeListeners + */ + protected PropertyChangeSupport getWritePropertyChangeSupport() { + if (writeListeners == null) { + writeListeners = new PropertyChangeSupport(this); + } + return writeListeners; + } + + @Override + public String getTopiaId() { + return topiaId; + } + + @Override + public void setTopiaId(String v) { + topiaId = v; + } + + @Override + public long getTopiaVersion() { + return topiaVersion; + } + + @Override + public void setTopiaVersion(long v) { + topiaVersion = v; + } + + @Override + public Date getTopiaCreateDate() { + return topiaCreateDate; + } + + @Override + public void setTopiaCreateDate(Date topiaCreateDate) { + this.topiaCreateDate = topiaCreateDate; + } + + public TopiaContext getTopiaContext() { + return topiaContext; + } + + /** + * @param context The context to set. + * @throws TopiaException if any pb ? + */ + public void setTopiaContext(TopiaContext context) throws TopiaException { + if (topiaContext == null) { + topiaContext = context; + } else { + throw new TopiaException("Remplacement du contexte interdit"); + } + } + + @Override + public List<TopiaEntity> getComposite() throws TopiaException { + throw new UnsupportedOperationException(); + } + + @Override + public List<TopiaEntity> getAggregate() throws TopiaException { + throw new UnsupportedOperationException(); + } + + /** + * On utilise la date de creation comme hash code, cette date ne varie pas + * au cours du temps + */ + @Override + public int hashCode() { + Date date = getTopiaCreateDate(); + //TC-20100220 : il se peut que la date de creation soit nulle + // lorsque l'entite est utilise comme objet d'edition d'un formulaire + // par exemple... + int result = date == null ? 0 : date.hashCode(); + return result; + } + + /** + * On est sur que les objets sont bien les memes car s'il n'ont pas d'id + * cela veut dire qu'il ne vienne pas de la meme session donc qu'il sont + * nouveau et different, ou bien qu'ils viennent de la meme session et dans + * ce cas l'egalite == fonctionne. + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof TopiaEntity)) { + return false; + } + TopiaEntity other = (TopiaEntity) obj; + if (getTopiaId() == null || other.getTopiaId() == null) { + return false; + } + boolean result = getTopiaId().equals(other.getTopiaId()); + return result; + } + + protected void fireOnPreRead(String propertyName, Object value) { + TopiaContextImplementor contextImplementor = + (TopiaContextImplementor) getTopiaContext(); + if (contextImplementor != null) { + contextImplementor.getFiresSupport().fireOnPreRead(getReadVetoableChangeSupport(), + this, propertyName, value); + } + } + + protected void fireOnPostRead(String propertyName, Object value) { + TopiaContextImplementor contextImplementor = + (TopiaContextImplementor) getTopiaContext(); + if (contextImplementor != null) { + contextImplementor.getFiresSupport().fireOnPostRead(getReadPropertyChangeSupport(), + this, propertyName, value); + } + } + + protected void fireOnPostRead(String propertyName, int index, + Object value) { + TopiaContextImplementor contextImplementor = + (TopiaContextImplementor) getTopiaContext(); + if (contextImplementor != null) { + contextImplementor.getFiresSupport().fireOnPostRead(getReadPropertyChangeSupport(), + this, propertyName, index, value); + } + } + + protected void fireOnPreWrite(String propertyName, Object oldValue, + Object newValue) { + TopiaContextImplementor contextImplementor = + (TopiaContextImplementor) getTopiaContext(); + if (contextImplementor != null) { + contextImplementor.getFiresSupport().fireOnPreWrite(getWriteVetoableChangeSupport(), + this, propertyName, oldValue, newValue); + } + } + + protected void fireOnPostWrite(String propertyName, Object oldValue, + Object newValue) { + TopiaContextImplementor contextImplementor = + (TopiaContextImplementor) getTopiaContext(); + if (contextImplementor != null) { + contextImplementor.getFiresSupport().fireOnPostWrite( + getWritePropertyChangeSupport(), this, propertyName, oldValue, newValue); + } + } + + protected void fireOnPostWrite(String propertyName, int index, + Object oldValue, Object newValue) { + TopiaContextImplementor contextImplementor = + (TopiaContextImplementor) getTopiaContext(); + if (contextImplementor != null) { + contextImplementor.getFiresSupport().fireOnPostWrite( + getWritePropertyChangeSupport(), this, propertyName, index, oldValue, + newValue); + } + } + + @Override + public void addPropertyChangeListener(String propertyName, + PropertyChangeListener listener) { + getWritePropertyChangeSupport().addPropertyChangeListener(propertyName, listener); + } + + @Override + public void addPropertyChangeListener(PropertyChangeListener listener) { + getWritePropertyChangeSupport().addPropertyChangeListener(listener); + } + + @Override + public void addVetoableChangeListener(String propertyName, + VetoableChangeListener vetoable) { + getWriteVetoableChangeSupport().addVetoableChangeListener(propertyName, vetoable); + } + + @Override + public void addVetoableChangeListener(VetoableChangeListener vetoable) { + getWriteVetoableChangeSupport().addVetoableChangeListener(vetoable); + } + + @Override + public void removePropertyChangeListener(String propertyName, + PropertyChangeListener listener) { + getWritePropertyChangeSupport().removePropertyChangeListener(propertyName, listener); + } + + @Override + public void removePropertyChangeListener(PropertyChangeListener listener) { + getWritePropertyChangeSupport().removePropertyChangeListener(listener); + } + + @Override + public void removeVetoableChangeListener(String propertyName, + VetoableChangeListener vetoable) { + getWriteVetoableChangeSupport().removeVetoableChangeListener(propertyName, vetoable); + } + + @Override + public void removeVetoableChangeListener(VetoableChangeListener vetoable) { + getWriteVetoableChangeSupport().removeVetoableChangeListener(vetoable); + } + + @Override + public void addPropertyListener(String propertyName, + PropertyChangeListener listener) { + getReadPropertyChangeSupport().addPropertyChangeListener(propertyName, listener); + } + + @Override + public void addPropertyListener(PropertyChangeListener listener) { + getReadPropertyChangeSupport().addPropertyChangeListener(listener); + } + + @Override + public void addVetoableListener(String propertyName, + VetoableChangeListener vetoable) { + getReadVetoableChangeSupport().addVetoableChangeListener(propertyName, vetoable); + } + + @Override + public void addVetoableListener(VetoableChangeListener vetoable) { + getReadVetoableChangeSupport().addVetoableChangeListener(vetoable); + } + + @Override + public void removePropertyListener(String propertyName, + PropertyChangeListener listener) { + getReadPropertyChangeSupport().removePropertyChangeListener(propertyName, listener); + } + + @Override + public void removePropertyListener(PropertyChangeListener listener) { + getReadPropertyChangeSupport().removePropertyChangeListener(listener); + } + + @Override + public void removeVetoableListener(String propertyName, + VetoableChangeListener vetoable) { + getReadVetoableChangeSupport().removeVetoableChangeListener(propertyName, vetoable); + } + + @Override + public void removeVetoableListener(VetoableChangeListener vetoable) { + getReadVetoableChangeSupport().removeVetoableChangeListener(vetoable); + } + +} //TopiaEntityAbstract diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaEntityContextable.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaEntityContextable.java new file mode 100644 index 0000000..e3747ad --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaEntityContextable.java @@ -0,0 +1,72 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin, Chatellier Eric, Chemit Tony + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence; + +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; + +/** + * {@link TopiaEntity} with {@link TopiaContext} support (injected by + * {@link TopiaContext} into entities). + * + * @author chatellier + * @version $Revision$ + * + * Last update : $Date$ + * By : $Author$ + */ +public interface TopiaEntityContextable extends TopiaEntity { + + String TOPIA_CONTEXT = "topiaContext"; + + /** + * Set topia context. + * + * @param topiaContext topia context + * @throws TopiaException if current topia entity context is not null + */ + void setTopiaContext(TopiaContext topiaContext) throws TopiaException; + + /** + * Get topia context. + * + * @return topia context + */ + TopiaContext getTopiaContext(); + + /** + * Update entity in persistence context. + * + * @throws TopiaException + */ + void update() throws TopiaException; + + /** + * Delete entity in persistence context. + * + * @throws TopiaException + */ + void delete() throws TopiaException; +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaEntityEnum.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaEntityEnum.java new file mode 100644 index 0000000..65607ab --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaEntityEnum.java @@ -0,0 +1,142 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence; + +import java.io.Serializable; + +/** + * The contract to be realised by the generated enumeration in any DAOHelper. + * <p/> + * Example : for a model Test, we will have a + * <code>TestDOAHelper.TestEntityEnum</code> enumeration generated. + * <p/> + * The contract gives some informations about the classes for any entity dealed + * by the dao helper. More precisely : + * <p/> + * - contract class of the entity (this must be an interface class) + * - the implementation fqn class of an entity (at generation time, we might + * not have the implementation class) + * - the implementation class (will be looked up at runtime execution, in that + * way we make possible to used a different implementation at runtime. + * <p/> + * - a method to accept any TopiaEntity class for this entity description + * + * @author tchemit <chemit@codelutin.com> + * @version $Id$ + * @since 2.2.0 + */ +public interface TopiaEntityEnum extends Serializable { + + /** + * This is a convinient method, as entity enum offers this + * method from {@link Enum#name()}. + * + * @return the name of the underlying entity type. + * @since 2.6.12 + */ + String name(); + + /** + * @return the name of database schema (null if none was filled). + * @since 2.9.2 + */ + String dbSchemaName(); + + /** + * @return the name of database table + * @since 2.9.2 + */ + String dbTableName(); + + + /** @return the contract class of the entity */ + Class<? extends TopiaEntity> getContract(); + + /** + * Note : this is a lazy accessor. + * + * @return the implementation class of the entity + */ + Class<? extends TopiaEntity> getImplementation(); + + /** @return the fully qualifed name of the implementation class of the entity */ + String getImplementationFQN(); + + /** + * @return the array of property names involved in the natural key + * of the entity. + */ + String[] getNaturalIds(); + + /** + * @return the array of property names which are marked as not-null. + * @since 2.6.9 + */ + String[] getNotNulls(); + + /** + * @return {@code true} if entity use natural ids, {@code false} otherwise. + * @since 2.6.9 + */ + boolean isUseNaturalIds(); + + /** + * @return {@code true} if entity use some not-null properties, + * {@code false} otherwise. + * @since 2.6.9 + */ + boolean isUseNotNulls(); + + /** + * Change the implementation class of the entity. + * <p/> + * Note : this method should reset all states of the objet + * (implementation class, operators,...). + * + * @param implementationFQN the new fully qualifed name of the new + * implementation class of the entity. + */ + void setImplementationFQN(String implementationFQN); + + /** + * Test if a given type of entity is matching the contract of this entity. + * <p/> + * Note : make sure to accept type only on the given contract class of this entity, + * can not accept an ancestor type, since there is a specific contract for this. + * <p/> + * Example : A -> B + * <pre> + * EntityEnum.A.accept(Class<A>) -> true + * EntityEnum.A.accept(Class<B>) -> false + * EntityEnum.B.accept(Class<B>) -> true + * EntityEnum.B.accept(Class<A>) -> false + * </pre> + * + * @param klass the type of an entity to test. + * @return {@code true} if given type is dealed directly by this entity, + * {@code false} otherwise. + */ + boolean accept(Class<? extends TopiaEntity> klass); +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaId.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaId.java new file mode 100644 index 0000000..b9dae84 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaId.java @@ -0,0 +1,186 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * + * TopiaId.java + * + * Created: 6 juil. 2004 + * + * @author Benjamin Poussin <poussin@codelutin.com> + * Copyright Code Lutin + * @version $Revision$ + * + * Mise a jour: $Date$ + * par : $Author$ + */ + +package org.nuiton.topia.persistence; + +import com.google.common.base.Function; +import org.nuiton.topia.TopiaNotFoundException; + +import java.io.Serializable; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * TODO-fdesbois-20100508 : Need translation of javadoc. + * <p/> + * Classe representant un Id, utilisable par JDO. Cette classe contient aussi un ensemble de methode + * static utile pour la manipulation des topiaId. + * <p/> + * TODO-tchemit-2012-08-16 Rename this class to {@code TopiaIds}. + * + * @author poussin <poussing@codelutin.com> + * @author tchemit <tchemit@codelutin.com> + * @author chatellier <chatellier@codelutin.com> + * @version $Id$ + */ +public class TopiaId implements Serializable { // TopiaId + + /** */ + private static final long serialVersionUID = 1L; + + /** + * Function to obtain {@link TopiaEntity#getTopiaId()} from any entity. + * + * @since 2.6.12 + */ + public static final Function<TopiaEntity, String> GET_TOPIA_ID = new Function<TopiaEntity, String>() { + + @Override + public String apply(TopiaEntity input) { + return input == null ? null : input.getTopiaId(); + } + }; + + public String topiaId; + + public TopiaId() { + } + + public TopiaId(String topiaId) { + this.topiaId = topiaId; + } + + @Override + public int hashCode() { + if (topiaId == null) { + //TODO-TC20100225 : use commons-loggin api instead of jdk one + Logger.getLogger(getClass().getName() + ".hashCode").log( + Level.WARNING, "Use null topiaId", new Throwable()); + return 0; + } + return topiaId.hashCode(); + } + + @Override + public boolean equals(Object o) { + return topiaId.equals(o); + } + + @Override + public String toString() { + return topiaId; + } + + /** + * Cree un topiaId pour une certaine classe + * + * @param clazz + * @return a generated topiaId + */ + public static String create(Class clazz) { + if (!clazz.isInterface()) { + throw new IllegalArgumentException( + "Only interface is permit to create id: " + clazz); + } + double random = Math.random(); + while (Double.toString(random).contains("E-")) { + random = Math.random(); + } + return clazz.getName() + '#' + System.currentTimeMillis() + '#' + + random; + } + + /** + * Extrait la classe du topiaId. + * + * @param topiaId + * @return class + * @throws TopiaNotFoundException + */ + public static Class getClassName(String topiaId) + throws TopiaNotFoundException { + String classname = getClassNameAsString(topiaId); + try { + Class result = Class.forName(classname); + return result; + } catch (ClassNotFoundException eee) { + throw new TopiaNotFoundException("Can't find class for " + topiaId, + eee); + } + } + + /** + * Return class name id topiaId is id, and empty string if topiaId is not an + * id. + * + * @param topiaId + * @return class name + */ + public static String getClassNameAsString(String topiaId) { + String result = ""; + int i = topiaId.indexOf('#'); + if (i > 0) { + result = topiaId.substring(0, i); + } + return result; + } + + /** + * Verifie si l'id passé en paramètre est bien un Id topia, c-a-d si la + * forme est bien classname#timemillis#random et si le classname est celui + * d'une classe valide, c-a-d que le systeme arrive a trouver. + * + * @param topiaId + * @return is valid topiaId + */ + public static boolean isValidId(String topiaId) { + try { + if (topiaId.matches(".*?#[0-9]+#[0-9.]+")) { + getClassName(topiaId); + return true; + } + return false; + } catch (Exception eee) { + //TODO-TC20100225 : use commons-loggin api instead of jdk one + Logger.getLogger(TopiaId.class.getName() + ".isValidId").log( + Level.WARNING, "Error during verfication of topiaId", eee); + return false; + } + } + +} // TopiaId + diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaPersistenceHelper.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaPersistenceHelper.java new file mode 100644 index 0000000..574c68f --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/TopiaPersistenceHelper.java @@ -0,0 +1,43 @@ +package org.nuiton.topia.persistence; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import org.nuiton.topia.TopiaContext; + +/** + * Object which helps to wrap some static generated code + * (dao helper, entityEnum). + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +public interface TopiaPersistenceHelper<T extends TopiaEntityEnum> { + + <E extends TopiaEntity> T getEntityEnum(Class<E> type); + + <E extends TopiaEntity> TopiaDAO<E> getDAO(TopiaContext tx, Class<E> type); + + <E extends TopiaEntity> TopiaDAO<E> getDAO(TopiaContext tx, T type); + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/CsvProgressModel.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/CsvProgressModel.java new file mode 100644 index 0000000..1d65c5d --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/CsvProgressModel.java @@ -0,0 +1,46 @@ +package org.nuiton.topia.persistence.csv; + +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import java.io.Serializable; + +/** + * Csv progress model contract. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.14 + */ +public interface CsvProgressModel extends Serializable { + + long getNbSteps(); + + void setNbSteps(long nbSteps); + + float getProgress(); + + void setProgress(float progress); + + void incrementsProgress(); +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/EntityCsvModel.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/EntityCsvModel.java new file mode 100644 index 0000000..1ed5ece --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/EntityCsvModel.java @@ -0,0 +1,255 @@ +package org.nuiton.topia.persistence.csv; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import com.google.common.base.Function; +import com.google.common.collect.Maps; + +import org.nuiton.decorator.Decorator; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.TopiaId; +import org.nuiton.topia.persistence.metadata.TableMeta; +import org.nuiton.csv.ValueParserFormatter; +import org.nuiton.csv.ext.AbstractImportExportModel; + +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Map; + +/** + * A model to import / export entities into csv files. + * + * @author tchemit <chemit@codelutin.com> + * @since 0.2 + */ +public class EntityCsvModel<T extends TopiaEntityEnum, E extends TopiaEntity> extends AbstractImportExportModel<E> { + + protected final TableMeta<T> tableMeta; + + protected boolean useOrdinalForEnum; + + public static <T extends TopiaEntityEnum, E extends TopiaEntity> EntityCsvModel<T, E> newModel( + char separator, + TableMeta<T> tableMeta) { + return new EntityCsvModel<T, E>(separator, tableMeta); + } + + public static <T extends TopiaEntityEnum, E extends TopiaEntity> EntityCsvModel<T, E> newModel( + char separator, + TableMeta<T> tableMeta, + String idHeader) { + return new EntityCsvModel<T, E>(separator, tableMeta, idHeader); + } + + @Override + public E newEmptyInstance() { + return (E) tableMeta.newEntity(); + } + + public void setUseOrdinalForEnum(boolean useOrdinalForEnum) { + this.useOrdinalForEnum = useOrdinalForEnum; + } + + public void addForeignKeyForExport(String propertyName, + Class<TopiaEntity> entityType) { + + Map<String, TopiaEntity> universe = Collections.emptyMap(); + + newColumnForExport(propertyName, + TopiaCsvCommons.newForeignKeyValue(entityType, + propertyName, + universe) + ); + } + + public <T> void addDecoratedForeignKeyForExport(String headerName, + String propertyName, + Decorator<T> decorator) { + modelBuilder.newColumnForExport( + headerName, + propertyName, + TopiaCsvCommons.newForeignKeyDecoratedValue(decorator)); + } + + public <E extends TopiaEntity> void addForeignKeyForImport(String headerName, + String propertyName, + Class<E> entityType, + Collection<E> entities, + Function<E, String> transform) { + + Map<String, E> universe = Maps.uniqueIndex(entities, transform); + + newMandatoryColumn(headerName, + propertyName, + TopiaCsvCommons.newForeignKeyValue(entityType, + propertyName, + universe) + ); + } + + public <E extends TopiaEntity> void addForeignKeyForAssociationForImport(String headerName, + String propertyName, + Class<E> entityType, + Collection<E> entities, + Function<E, String> transform) { + + Map<String, E> universe = Maps.uniqueIndex(entities, transform); + + newMandatoryColumn( + headerName, + propertyName, + TopiaCsvCommons.newForeignKeyValueAssociation(entityType, + propertyName, + universe) + ); + } + + public <E extends TopiaEntity> void addForeignKeyForImport(String propertyName, + Class<E> entityType, + Collection<E> entities) { + + Map<String, E> universe = Maps.uniqueIndex(entities, + TopiaId.GET_TOPIA_ID); + + newMandatoryColumn(propertyName, + TopiaCsvCommons.newForeignKeyValue(entityType, + propertyName, + universe) + ); + } + + public <E extends TopiaEntity> void addForeignKeyForImport(String propertyName, + Class<E> entityType, + Map<String, E> universe) { + + newMandatoryColumn(propertyName, + TopiaCsvCommons.newForeignKeyValue(entityType, + propertyName, + universe) + ); + } + public void addDefaultColumn(String propertyName, Class<?> type) { + addDefaultColumn(propertyName, propertyName, type); + } + + public void addDefaultColumn(String headerName, + String propertyName, + Class<?> type) { + + if (Date.class.equals(type)) { + newColumnForImportExport( + headerName, + propertyName, + TopiaCsvCommons.DAY_TIME_SECOND_WITH_TIMESTAMP); + } else if (double.class.equals(type)) { + newColumnForImportExport( + headerName, + propertyName, + TopiaCsvCommons.DOUBLE_PRIMITIVE); + } else if (Double.class.equals(type)) { + newColumnForImportExport( + headerName, + propertyName, + TopiaCsvCommons.DOUBLE); + } else if (long.class.equals(type)) { + newColumnForImportExport( + headerName, + propertyName, + TopiaCsvCommons.PRIMITIVE_LONG); + } else if (Long.class.equals(type)) { + newColumnForImportExport( + headerName, + propertyName, + TopiaCsvCommons.LONG); + } else if (float.class.equals(type)) { + newColumnForImportExport( + headerName, + propertyName, + TopiaCsvCommons.PRIMITIVE_FLOAT); + } else if (Float.class.equals(type)) { + newColumnForImportExport( + headerName, + propertyName, + TopiaCsvCommons.FLOAT); + } else if (int.class.equals(type)) { + newColumnForImportExport( + headerName, + propertyName, + TopiaCsvCommons.PRIMITIVE_INTEGER); + } else if (Integer.class.equals(type)) { + newColumnForImportExport( + headerName, + propertyName, + TopiaCsvCommons.INTEGER); + } else if (boolean.class.equals(type)) { + newColumnForImportExport( + headerName, + propertyName, + TopiaCsvCommons.PRIMITIVE_BOOLEAN); + } else if (Boolean.class.equals(type)) { + newColumnForImportExport( + headerName, + propertyName, + TopiaCsvCommons.BOOLEAN); + } else if (String.class.equals(type)) { + newColumnForImportExport( + headerName, + propertyName); + } else if (type.isEnum()) { + + Class<Enum> enumType = (Class<Enum>) type; + ValueParserFormatter<Enum> valueParserFormatter; + if (useOrdinalForEnum) { + valueParserFormatter = + TopiaCsvCommons.newEnumByOrdinalParserFormatter(enumType); + } else { + valueParserFormatter = + TopiaCsvCommons.newEnumByNameParserFormatter(enumType); + } + + newColumnForImportExport(headerName, + propertyName, + valueParserFormatter); + + } else { + + throw new IllegalStateException(String.format( + "For header %s, property %s, no specific handler " + + "found for type %s", headerName, propertyName, type)); + } + } + + protected EntityCsvModel(char separator, TableMeta<T> tableMeta) { + super(separator); + this.tableMeta = tableMeta; + } + + protected EntityCsvModel(char separator, TableMeta<T> tableMeta, + String idHeader) { + this(separator, tableMeta); + newColumnForImportExport(idHeader, TopiaEntity.TOPIA_ID); + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/TopiaCsvCommons.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/TopiaCsvCommons.java new file mode 100644 index 0000000..751f481 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/TopiaCsvCommons.java @@ -0,0 +1,275 @@ +package org.nuiton.topia.persistence.csv; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import com.google.common.collect.Lists; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.lang3.StringUtils; +import org.nuiton.decorator.Decorator; +import org.nuiton.topia.TopiaRuntimeException; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.util.StringUtil; +import org.nuiton.csv.Common; +import org.nuiton.csv.ValueFormatter; +import org.nuiton.csv.ValueParser; +import org.nuiton.csv.ValueParserFormatter; + +import java.sql.Timestamp; +import java.text.ParseException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Date; +import java.util.Map; + +/** + * More useful method added to {@link Common}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +public class TopiaCsvCommons extends Common { + + protected TopiaCsvCommons() { + // no instance of this helper + } + + public static final ValueParserFormatter<Date> DAY_TIME_SECOND_WITH_TIMESTAMP = + new DateValue("dd/MM/yyyy HH:mm:ss") { + + @Override + public Date parse(String value) throws ParseException { + + Date parse = super.parse(value); + if (parse != null) { + parse = new Timestamp(parse.getTime()); + } + return parse; + } + }; + + public static final ValueParserFormatter<Date> DAY_TIME_SECOND_MILI_WITH_TIMESTAMP = + new DateValue("dd/MM/yyyy HH:mm:ss.SSSS") { + + @Override + public Date parse(String value) throws ParseException { + + Date parse = super.parse(value); + if (parse != null) { + parse = new Timestamp(parse.getTime()); + } + return parse; + } + }; + public static final AssociationValueParser ASSOCIATION_VALUE_PARSER = new AssociationValueParser(); + + public static <E extends TopiaEntity> ForeignKeyValue<E> newForeignKeyValue(Class<E> type, String propertyName, Map<String, E> universe) { + return new ForeignKeyValue<E>(type, propertyName, universe); + } + + public static <E extends TopiaEntity> ForeignKeyValueForAssociation<E> newForeignKeyValueAssociation(Class<E> type, String propertyName, Map<String, E> universe) { + return new ForeignKeyValueForAssociation<E>(type, propertyName, universe); + } + + public static <E extends TopiaEntity> ValueFormatter<Collection<E>> newAssociationValueFormatter() { + return new AssociationValueParserFormatter<E>(null, null); + } + + public static <E> ForeignKeyDecoratedValue<E> newForeignKeyDecoratedValue(Decorator<E> decorator) { + return new ForeignKeyDecoratedValue<E>(decorator); + } + + /** + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ + public static class AssociationValueParser implements ValueParser<String[]> { + + @Override + public String[] parse(String value) throws ParseException { + String[] ids = value.split("\\|"); + return ids; + } + } + + /** + * @param <E> + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ + public static class AssociationValueParserFormatter<E extends TopiaEntity> implements ValueParserFormatter<Collection<E>> { + + protected final Class<E> entityType; + + protected final Map<String, E> universe; + + public AssociationValueParserFormatter( + Class<E> entityType, + Map<String, E> universe) { + this.entityType = entityType; + this.universe = universe; + } + + @Override + public Collection<E> parse(String value) throws ParseException { + Collection<E> result = Lists.newArrayList(); + if (StringUtils.isNotBlank(value)) { + + String[] ids = value.split("\\|"); + for (String id : ids) { + E association = universe.get(id); + association.setTopiaId(id); + result.add(association); + } + } + return result; + } + + @Override + public String format(Collection<E> e) { + + String value; + if (CollectionUtils.isEmpty(e)) { + value = ""; + } else { + Collection<String> ids = Lists.newArrayList(); + for (E e1 : e) { + ids.add(e1.getTopiaId()); + } + value = StringUtil.join(ids, "|", true); + } + return value; + } + } + + /** + * TODO + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ + public static class ForeignKeyDecoratedValue<E> implements ValueFormatter<E> { + + protected final Decorator<E> decorator; + + public ForeignKeyDecoratedValue(Decorator<E> decorator) { + this.decorator = decorator; + } + + @Override + public String format(E e) { + String value = ""; + if (e != null) { + value = decorator.toString(e); + } + return value; + } + } + + /** + * @param <E> + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ + public static class ForeignKeyValue<E extends TopiaEntity> implements ValueParserFormatter<E> { + + protected final String propertyName; + + protected final Class<E> entityType; + + protected final Map<String, E> universe; + + public ForeignKeyValue(Class<E> entityType, + String propertyName, + Map<String, E> universe) { + this.entityType = entityType; + this.propertyName = propertyName; + this.universe = universe; + } + + + @Override + public E parse(String value) throws ParseException { + E result = null; + if (StringUtils.isNotBlank(value)) { + + // get entity from universe + result = universe.get(value); + + if (result == null) { + + // can not find entity this is a big problem for us... + throw new TopiaRuntimeException( + "Could not find entity of type " + + entityType.getSimpleName() + " with '" + + propertyName + "' = " + value); + } + } + return result; + } + + @Override + public String format(E e) { + String value = ""; + if (e != null) { + value = e.getTopiaId(); + } + return value; + } + } + + public static class ForeignKeyValueForAssociation<E extends TopiaEntity> implements ValueParser<Collection<E>> { + + protected final String propertyName; + + protected final Class<E> entityType; + + protected final Map<String, E> universe; + + public ForeignKeyValueForAssociation(Class<E> entityType, + String propertyName, + Map<String, E> universe) { + this.entityType = entityType; + this.propertyName = propertyName; + this.universe = universe; + } + + @Override + public Collection<E> parse(String value) throws ParseException { + E result = null; + if (StringUtils.isNotBlank(value)) { + + // get entity from universe + result = universe.get(value); + + if (result == null) { + + // can not find entity this is a big problem for us... + throw new TopiaRuntimeException( + "Could not find entity with '" + propertyName + "' = " + value); + } + } + return Arrays.asList(result); + } + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/AbstractImportModel.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/AbstractImportModel.java new file mode 100644 index 0000000..94c83f3 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/AbstractImportModel.java @@ -0,0 +1,56 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.persistence.csv.in; + +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.csv.TopiaCsvCommons; + +import java.util.List; +import java.util.Map; + +/** + * Abstract import model which add the useful methdo about importing foreign keys. + * + * @param <E> type of entity to import + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +public abstract class AbstractImportModel<E> extends org.nuiton.csv.ext.AbstractImportModel<E> { + + public AbstractImportModel(char separator) { + super(separator); + } + + @Override + public void pushCsvHeaderNames(List<String> headerNames) { + } + + public <E extends TopiaEntity> void newForeignKeyColumn(String headerName, String propertyName, Class<E> entityType, String foreignKeyName, Map<String, E> universe) { + newMandatoryColumn(headerName, propertyName, TopiaCsvCommons.newForeignKeyValue(entityType, foreignKeyName, universe)); + } + + public <E extends TopiaEntity> void newForeignKeyColumn(String propertyName, Class<E> entityType, String foreignKeyName, Map<String, E> universe) { + newMandatoryColumn(propertyName, propertyName, TopiaCsvCommons.newForeignKeyValue(entityType, foreignKeyName, universe)); + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/CsvFileImportResult.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/CsvFileImportResult.java new file mode 100644 index 0000000..6e26320 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/CsvFileImportResult.java @@ -0,0 +1,111 @@ +package org.nuiton.topia.persistence.csv.in; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import com.google.common.collect.ImmutableSet; +import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.nuiton.topia.persistence.TopiaEntityEnum; + +import java.io.Serializable; +import java.util.Map; +import java.util.Set; + +/** + * To keep result of the import of a file. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +public class CsvFileImportResult<T extends TopiaEntityEnum> implements Serializable { + + private static final long serialVersionUID = 1L; + + /** Name of the csv file to import. */ + protected final String importFileName; + + /** type of entity to import csv datas. */ + protected final Set<T> entityTypes; + + /** Count of created entities. */ + protected final Map<T, Integer> numberCreated; + + /** Count of updated entities. */ + protected final Map<T, Integer> numberUpdated; + + public static <T extends TopiaEntityEnum> CsvFileImportResult<T> newResult(String importFileName, T... universe) { + CsvFileImportResult<T> result = new CsvFileImportResult<T>( + importFileName, universe + ); + return result; + } + + public CsvFileImportResult(String importFileName, T... universe) { + this.importFileName = importFileName; + entityTypes = Sets.newHashSet(); + numberCreated = Maps.newHashMap(); + numberUpdated = Maps.newHashMap(); + for (T t : universe) { + numberCreated.put(t, 0); + numberUpdated.put(t, 0); + } + } + + public Set<T> getEntityTypes() { + return ImmutableSet.copyOf(entityTypes); + } + + public int getNumberCreated(T entityType) { + return getInteger(numberCreated, entityType); + } + + public int getNumberUpdated(T entityType) { + return getInteger(numberUpdated, entityType); + } + + public String getImportFileName() { + return importFileName; + } + + public void incrementsNumberCreated(T entityType) { + increments(numberCreated, entityType); + } + + public void incrementsNumberUpdated(T entityType) { + increments(numberUpdated, entityType); + } + + protected int getInteger(Map<T, Integer> map, T entityType) { + Integer result = map.get(entityType); + return result; + } + + protected void increments(Map<T, Integer> map, T entityType) { + Integer result = map.get(entityType); + if (result == 0) { + entityTypes.add(entityType); + } + map.put(entityType, ++result); + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/CsvImportResult.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/CsvImportResult.java new file mode 100644 index 0000000..7a93404 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/CsvImportResult.java @@ -0,0 +1,118 @@ +package org.nuiton.topia.persistence.csv.in; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.csv.CsvProgressModel; + +import java.io.Serializable; + +/** + * A simple csv result bean just to keep the number of created or + * updated entities. + * + * @author tchemit <chemit@codelutin.com> + * @since 0.2 + */ +public class CsvImportResult<T extends TopiaEntityEnum> implements Serializable { + + private static final long serialVersionUID = 1L; + + /** type of entity to import csv datas. */ + protected final T entityType; + + /** Name of the csv file to import. */ + protected final String importFileName; + + /** Flag to authorize to create entities not found in db. */ + protected final boolean createIfNotFound; + + /** Count of created entities. */ + protected int numberCreated; + + /** Count of updated entities. */ + protected int numberUpdated; + + protected final CsvProgressModel progressModel; + + public static <T extends TopiaEntityEnum> CsvImportResult<T> newResult(T entityType, + String importFileName, + boolean createIfNotFound) { + CsvImportResult<T> result = newResult(entityType, importFileName, + createIfNotFound, null); + return result; + } + + public static <T extends TopiaEntityEnum> CsvImportResult<T> newResult(T entityType, + String importFileName, + boolean createIfNotFound, + CsvProgressModel progressModel) { + CsvImportResult<T> result = new CsvImportResult<T>(entityType, importFileName, + createIfNotFound, progressModel); + return result; + } + + protected CsvImportResult(T entityType, + String importFileName, + boolean createIfNotFound, + CsvProgressModel progressModel) { + this.entityType = entityType; + this.importFileName = importFileName; + this.createIfNotFound = createIfNotFound; + this.progressModel = progressModel; + } + + public T getEntityType() { + return entityType; + } + + + public String getImportFileName() { + return importFileName; + } + + public int getNumberCreated() { + return numberCreated; + } + + public int getNumberUpdated() { + return numberUpdated; + } + + public boolean isCreateIfNotFound() { + return createIfNotFound; + } + + public void incrementsNumberCreated() { + numberCreated++; + } + + public void incrementsNumberUpdated() { + numberUpdated++; + } + + public CsvProgressModel getProgressModel() { + return progressModel; + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/EntityAssociationImportModel.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/EntityAssociationImportModel.java new file mode 100644 index 0000000..a82bf9b --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/EntityAssociationImportModel.java @@ -0,0 +1,75 @@ +package org.nuiton.topia.persistence.csv.in; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.csv.TopiaCsvCommons; +import org.nuiton.topia.persistence.metadata.AssociationMeta; +import org.nuiton.csv.ImportModel; + +import java.util.Map; + +/** + * A model to import associations of entities from csv files. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +public class EntityAssociationImportModel<T extends TopiaEntityEnum> extends AbstractImportModel<Map<String, Object>> { + + protected final AssociationMeta meta; + + public static <T extends TopiaEntityEnum> ImportModel<Map<String, Object>> newImportModel(char separator, + AssociationMeta<T> meta) { + EntityAssociationImportModel<T> model = new EntityAssociationImportModel<T>( + separator, meta); + + // topiaId <-> topiaId + model.newMandatoryColumn( + TopiaEntity.TOPIA_ID, + TopiaCsvCommons.<Map<String, Object>, String>newMapProperty(TopiaEntity.TOPIA_ID) + ); + + // add association -> target + model.newMandatoryColumn( + meta.getName(), + TopiaCsvCommons.ASSOCIATION_VALUE_PARSER, + TopiaCsvCommons.<Map<String, Object>, String[]>newMapProperty("target") + ); + + return model; + } + + @Override + public Map<String, Object> newEmptyInstance() { + return null; + } + + public EntityAssociationImportModel(char separator, AssociationMeta<T> meta) { + super(separator); + this.meta = meta; + } + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/ImportModelFactory.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/ImportModelFactory.java new file mode 100644 index 0000000..1b4cb86 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/ImportModelFactory.java @@ -0,0 +1,47 @@ +package org.nuiton.topia.persistence.csv.in; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.metadata.AssociationMeta; +import org.nuiton.topia.persistence.metadata.TableMeta; +import org.nuiton.csv.ImportModel; + +import java.util.Map; + +/** + * To produce import model. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +public interface ImportModelFactory<T extends TopiaEntityEnum> { + + <E extends TopiaEntity> ImportModel<E> buildForImport(TableMeta<T> meta); + + ImportModel<Map<String, Object>> buildForImport(AssociationMeta<T> meta); + + boolean isNMAssociationMeta(AssociationMeta<T> meta); +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/ImportStrategy.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/ImportStrategy.java new file mode 100644 index 0000000..ad7301b --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/ImportStrategy.java @@ -0,0 +1,89 @@ +package org.nuiton.topia.persistence.csv.in; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.metadata.AssociationMeta; +import org.nuiton.topia.persistence.metadata.TableMeta; +import org.nuiton.csv.Import; +import org.nuiton.csv.ImportToMap; + +import java.io.Reader; + +/** + * Strategy to import some stuff. + * <p/> + * Implements it and then you can use it with helper methods + * {@link TopiaCsvImports#importTable(Reader, ImportStrategy, TableMeta, CsvImportResult)}, + * {@link TopiaCsvImports#importAssociation(Reader, ImportStrategy, AssociationMeta, CsvImportResult)}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +public interface ImportStrategy<T extends TopiaEntityEnum> { + + /** @return the model factory (will be used to produce the model to import). */ + ImportModelFactory<T> getModelFactory(); + + /** + * Import a table given a {@code importer} with an optional csv result. + * + * @param meta type of table to import + * @param importer the csv importer + * @param csvResult optional object where to put csv import result + * @throws TopiaException if any db problem + */ + <E extends TopiaEntity> void importTable(TableMeta<T> meta, Import<E> importer, + CsvImportResult<T> csvResult) throws TopiaException; + + /** + * Import a table given a {@code importer} with an optional csv result, + * and return them. + * + * @param meta type of table to import + * @param importer the csv importer + * @param csvResult optional object where to put csv import result + * @return imported entities + * @throws TopiaException if any db problem + * @since 2.6.14 + */ + <E extends TopiaEntity> Iterable<E> importTableAndReturnThem(TableMeta<T> meta, Import<E> importer, + CsvImportResult<T> csvResult) throws TopiaException; + + /** + * Import a association given a {@code importer} with an optional csv result. + * + * @param meta type of association to import + * @param importer the csv importer + * @param csvResult optional object where to put csv import result + * @throws TopiaException if any db problem + */ + void importAssociation(AssociationMeta<T> meta, + ImportToMap importer, + CsvImportResult<T> csvResult) throws TopiaException; + + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/TopiaCsvImports.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/TopiaCsvImports.java new file mode 100644 index 0000000..1ac9305 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/TopiaCsvImports.java @@ -0,0 +1,590 @@ +package org.nuiton.topia.persistence.csv.in; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaDAO; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.csv.CsvProgressModel; +import org.nuiton.topia.persistence.metadata.AssociationMeta; +import org.nuiton.topia.persistence.metadata.MetaFilenameAware; +import org.nuiton.topia.persistence.metadata.TableMeta; +import org.nuiton.topia.persistence.util.TopiaEntityHelper; +import org.nuiton.csv.Import; +import org.nuiton.csv.ImportModel; +import org.nuiton.csv.ImportToMap; + +import java.io.File; +import java.io.IOException; +import java.io.Reader; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +/** + * Helper for csv imports. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +public class TopiaCsvImports { + + /** Logger. */ + private static final Log log = LogFactory.getLog(TopiaCsvImports.class); + + protected static final String UPDATE_ASSOCIATION = + "UPDATE %s SET %s = '%%s' WHERE topiaId ='%%s';"; + + protected static final String INSERT_ASSOCIATION = + "INSERT INTO %s (%s,%s) VALUES('%%s','%%s');"; + + protected TopiaCsvImports() { + // no instance of this helper + } + + /** + * Discover all files that can be imported (as a table or an association) from a zipfile. + * + * @param entryPrefix prefix where to find files in the zip + * @param possibleMetas list of possible meta to be imported + * @param zipFile zip file where to seek for csv files to import + * @param missingEntries to fill missing files + * @param <T> type of topia entity enum + * @param <M> type of data to import (table or association) + * @return the map of found files indexed by his meta + */ + public static <T extends TopiaEntityEnum, M extends MetaFilenameAware<T>> Map<M, ZipEntry> discoverEntries( + String entryPrefix, + Iterable<M> possibleMetas, + ZipFile zipFile, + List<String> missingEntries) { + + Map<M, ZipEntry> result = Maps.newLinkedHashMap(); + + // check that all mandatories + for (M entry : possibleMetas) { + String filename = entry.getFilename(); + ZipEntry zipEntry = zipFile.getEntry(entryPrefix + filename); + + if (zipEntry == null) { + missingEntries.add(filename); + } else { + result.put(entry, zipEntry); + } + } + return result; + } + + /** + * Discover all files that can be imported (as a table or an association) from a directory. + * + * @param possibleMetas list of possible meta to be imported + * @param directory directory where to seek for csv files to import + * @param missingEntries to fill missing files + * @param <T> type of topia entity enum + * @param <M> type of data to import (table or association) + * @return the map of found files indexed by his meta + */ + public static <T extends TopiaEntityEnum, M extends MetaFilenameAware<T>> Map<M, File> discoverEntries( + Iterable<M> possibleMetas, + File directory, + List<String> missingEntries) { + + Map<M, File> result = Maps.newLinkedHashMap(); + + // check that all mandatories + for (M entry : possibleMetas) { + String filename = entry.getFilename(); + File zipEntry = new File(directory, filename); + + if (zipEntry.exists()) { + result.put(entry, zipEntry); + } else { + missingEntries.add(filename); + } + } + return result; + } + + /** + * To import a table (given by his {@code meta}) from a reader and a strategy. + * <p/> + * Result of import can be stored in an optional csv result. + * + * @param reader where to read csv data + * @param importStrategy import strategy used to store csv data + * @param meta meta of the data + * @param csvResult optional csv result + * @param <T> type of entity enum + * @param <E> type of data + * @throws TopiaException if any db problem while storing datas + * @throws IOException if any pb while reading csv data + * @see ImportStrategy#importTable(TableMeta, Import, CsvImportResult) + */ + public static <T extends TopiaEntityEnum, E extends TopiaEntity> void importTable(Reader reader, + ImportStrategy<T> importStrategy, + TableMeta<T> meta, + CsvImportResult<T> csvResult) throws TopiaException, IOException { + + + if (log.isInfoEnabled()) { + log.info("Will import " + meta); + } + + ImportModel<E> model = importStrategy.getModelFactory().buildForImport(meta); + Import<E> importer = Import.newImport(model, reader); + try { + importStrategy.importTable(meta, importer, csvResult); + } finally { + importer.close(); + } + } + + /** + * To import a table (given by his {@code meta}) from a reader and a strategy. + * <p/> + * Result of import can be stored in an optional csv result. + * + * @param reader where to read csv data + * @param importStrategy import strategy used to store csv data + * @param meta meta of the data + * @param csvResult optional csv result + * @param <T> type of entity enum + * @param <E> type of data + * @throws TopiaException if any db problem while storing datas + * @throws IOException if any pb while reading csv data + * @see ImportStrategy#importTable(TableMeta, Import, CsvImportResult) + */ + public static <T extends TopiaEntityEnum, E extends TopiaEntity> Iterable<E> importTableAndReturn(Reader reader, + ImportStrategy<T> importStrategy, + TableMeta<T> meta, + CsvImportResult<T> csvResult) throws TopiaException, IOException { + + + if (log.isInfoEnabled()) { + log.info("Will import " + meta); + } + + ImportModel<E> model = importStrategy.getModelFactory().buildForImport(meta); + Import<E> importer = Import.newImport(model, reader); + try { + return importStrategy.importTableAndReturnThem(meta, importer, csvResult); + } finally { + importer.close(); + } + } + + /** + * To import a association (given by his {@code meta}) from a reader and a strategy. + * <p/> + * Result of import can be stored in an optional csv result. + * + * @param reader where to read csv data + * @param importStrategy import strategy used to store csv data + * @param meta meta of the data + * @param csvResult optional csv result + * @param <T> type of entity enum + * @throws TopiaException if any db problem while storing datas + * @throws IOException if any pb while reading csv data + * @see ImportStrategy#importAssociation(AssociationMeta, ImportToMap, CsvImportResult) + */ + public static <T extends TopiaEntityEnum> void importAssociation(Reader reader, + ImportStrategy<T> importStrategy, + AssociationMeta<T> meta, + CsvImportResult<T> csvResult) throws IOException, TopiaException { + + if (log.isInfoEnabled()) { + log.info("Will import " + meta); + } + + // load a association input + ImportModelFactory<T> modelFactory = importStrategy.getModelFactory(); + ImportModel<Map<String, Object>> model = + modelFactory.buildForImport(meta); + ImportToMap importer = ImportToMap.newImportToMap(model, reader, false); + + try { + importStrategy.importAssociation(meta, importer, csvResult); + + } finally { + importer.close(); + } + } + + public static <T extends TopiaEntityEnum, E extends TopiaEntity> void importAllEntities(TopiaDAO<E> dao, + TableMeta<T> meta, + Import<E> importer, + CsvImportResult<T> csvResult) throws TopiaException { + + CsvProgressModel progressModel = csvResult == null ? null : + csvResult.getProgressModel(); + for (E entity : importer) { + + Map<String, Object> properties = meta.prepareCreate( + entity, entity.getTopiaId()); + E entityToSave = dao.create(properties); + + meta.copy(entity, entityToSave); + + if (csvResult != null) { + csvResult.incrementsNumberUpdated(); + if (progressModel != null) { + progressModel.incrementsProgress(); + } + } + } + } + + public static <T extends TopiaEntityEnum, E extends TopiaEntity> void importAllEntities(TopiaDAO<E> dao, + TableMeta<T> meta, + Import<E> importer, + CsvImportResult<T> csvResult, + int nbRowBuffer) throws TopiaException { + + TopiaContextImplementor context = dao.getContext(); + + CsvProgressModel progressModel = csvResult == null ? null : + csvResult.getProgressModel(); + + int compt = 0; + for (E entity : importer) { + + Map<String, Object> properties = meta.prepareCreate( + entity, entity.getTopiaId()); + E entityToSave = dao.create(properties); + + meta.copy(entity, entityToSave); + + if (csvResult != null) { + csvResult.incrementsNumberUpdated(); + if (progressModel != null) { + progressModel.incrementsProgress(); + } + } + compt++; + if (compt % nbRowBuffer == 0) { + // flush it + context.getHibernate().flush(); + } + } + } + + public static <T extends TopiaEntityEnum, E extends TopiaEntity> Iterable<E> importAllEntitiesAndReturnThem(TopiaDAO<E> dao, + TableMeta<T> meta, + Import<E> importer, + CsvImportResult<T> csvResult) throws TopiaException { + + CsvProgressModel progressModel = csvResult == null ? null : + csvResult.getProgressModel(); + List<E> result = Lists.newArrayList(); + for (E entity : importer) { + + Map<String, Object> properties = meta.prepareCreate( + entity, entity.getTopiaId()); + E entityToSave = dao.create(properties); + + meta.copy(entity, entityToSave); + + if (csvResult != null) { + csvResult.incrementsNumberUpdated(); + if (progressModel != null) { + progressModel.incrementsProgress(); + } + } + + result.add(entityToSave); + } + return result; + } + + public static <T extends TopiaEntityEnum, E extends TopiaEntity> void importNotExistingEntities(TopiaDAO<E> dao, + TableMeta<T> meta, + Map<String, TopiaEntity> universe, + Import<E> importer, + CsvImportResult<T> csvResult) throws TopiaException { + + CsvProgressModel progressModel = csvResult == null ? null : + csvResult.getProgressModel(); + for (E entity : importer) { + + String topiaId = entity.getTopiaId(); + + Map<String, Object> properties = meta.prepareCreate(entity, null); + E existingEntity = dao.findByProperties(properties); + if (existingEntity == null) { + + // new entity to create + E entityToSave = dao.create(properties); + String newTopiaId = entityToSave.getTopiaId(); + Date topiaCreateDate = entityToSave.getTopiaCreateDate(); + meta.copy(entity, entityToSave); + entityToSave.setTopiaId(newTopiaId); + entityToSave.setTopiaCreateDate(topiaCreateDate); + + if (log.isInfoEnabled()) { + log.info(String.format("Create entity [%s becomes %s] with naturalId %s", topiaId, newTopiaId, properties)); + } + universe.put(topiaId, entityToSave); + + if (csvResult != null) { + csvResult.incrementsNumberCreated(); + if (progressModel != null) { + progressModel.incrementsProgress(); + } + } + } else { + // existing entity, nothing to create + // just add a ref into universe to make translation possible by foreign keys + + if (log.isDebugEnabled()) { + log.debug(String.format("Existing entity [%s] with naturalId %s, do not create anything", topiaId, properties)); + } + + universe.put(topiaId, existingEntity); + } + } + } + + public static <T extends TopiaEntityEnum> void importAssociation(TopiaContext tx, AssociationMeta<T> meta, + ImportToMap importer, + CsvImportResult<T> csvResult, + int nbRowBuffer) throws TopiaException { + + CsvProgressModel progressModel = csvResult == null ? null : + csvResult.getProgressModel(); + T source = meta.getSource(); + T target = meta.getTarget(); + + StringBuilder builder = new StringBuilder(); + + String targetTableName = target.getContract().getSimpleName(); + String sourceTableName = source.getContract().getSimpleName(); + String table = targetTableName; + + String updateString = String.format(UPDATE_ASSOCIATION, table, sourceTableName); + + if (log.isDebugEnabled()) { + log.debug("Will apply " + updateString); + } + int compt = 0; + for (Map<String, Object> row : importer) { + String topiaId = (String) row.get(TopiaEntity.TOPIA_ID); + String[] associations = (String[]) row.get("target"); + for (String association : associations) { + if (StringUtils.isNotEmpty(association)) { + builder.append(String.format(updateString, topiaId, association)).append('\n'); + compt++; + if (compt % nbRowBuffer == 0) { + // flush it + tx.executeSQL(builder.toString()); + builder = new StringBuilder(); + } + } + } + if (csvResult != null) { + csvResult.incrementsNumberUpdated(); + if (progressModel != null) { + progressModel.incrementsProgress(); + } + } + } + if (builder.length() > 0) { + tx.executeSQL(builder.toString()); + } + } + + public static <T extends TopiaEntityEnum> void importNMAssociation(TopiaContext tx, + AssociationMeta<T> meta, + ImportToMap importer, + CsvImportResult<T> csvResult, + int nbRowBuffer) throws TopiaException { + + CsvProgressModel progressModel = csvResult == null ? null : + csvResult.getProgressModel(); + T source = meta.getSource(); + T target = meta.getTarget(); + + StringBuilder builder = new StringBuilder(); + + + String targetTableName = target.getContract().getSimpleName(); + String sourceTableName = source.getContract().getSimpleName(); + + // relation *-* + String table = TopiaEntityHelper.getNormalizedAssociationTableName( + sourceTableName, targetTableName); + + String updateString = String.format(INSERT_ASSOCIATION, table, sourceTableName, targetTableName); + + if (log.isDebugEnabled()) { + log.debug("Will apply " + updateString); + } + int compt = 0; + for (Map<String, Object> row : importer) { + String topiaId = (String) row.get(TopiaEntity.TOPIA_ID); + String[] associations = (String[]) row.get("target"); + for (String association : associations) { + if (StringUtils.isNotEmpty(association)) { + builder.append(String.format(updateString, topiaId, association)).append('\n'); + compt++; + if (compt % nbRowBuffer == 0) { + // flush it + tx.executeSQL(builder.toString()); + builder = new StringBuilder(); + } + } + } + if (csvResult != null) { + csvResult.incrementsNumberUpdated(); + if (progressModel != null) { + progressModel.incrementsProgress(); + } + } + } + if (builder.length() > 0) { + tx.executeSQL(builder.toString()); + } + } + + public static <T extends TopiaEntityEnum> void importNMAssociation(TopiaContext tx, + AssociationMeta<T> meta, + Map<String, TopiaEntity> universe, + ImportToMap importer, + CsvImportResult<T> csvResult, + int nbRowBuffer) throws TopiaException { + + CsvProgressModel progressModel = csvResult == null ? null : + csvResult.getProgressModel(); + T source = meta.getSource(); + T target = meta.getTarget(); + + StringBuilder builder = new StringBuilder(); + + + String targetTableName = target.getContract().getSimpleName(); + String sourceTableName = source.getContract().getSimpleName(); + + // relation *-* + String table = TopiaEntityHelper.getNormalizedAssociationTableName( + sourceTableName, targetTableName); + + String updateString = String.format(INSERT_ASSOCIATION, table, sourceTableName, targetTableName); + + if (log.isDebugEnabled()) { + log.debug("Will apply " + updateString); + } + int compt = 0; + for (Map<String, Object> row : importer) { + String topiaId = (String) row.get(TopiaEntity.TOPIA_ID); + String[] associations = (String[]) row.get("target"); + for (String association : associations) { + if (StringUtils.isNotEmpty(association)) { + TopiaEntity targetEntity = universe.get(association); + Preconditions.checkNotNull(targetEntity, "Could not find target entity from id: " + association); + builder.append(String.format(updateString, topiaId, targetEntity.getTopiaId())).append('\n'); + compt++; + if (compt % nbRowBuffer == 0) { + // flush it + tx.executeSQL(builder.toString()); + builder = new StringBuilder(); + } + } + } + if (csvResult != null) { + csvResult.incrementsNumberUpdated(); + if (progressModel != null) { + progressModel.incrementsProgress(); + } + } + } + if (builder.length() > 0) { + tx.executeSQL(builder.toString()); + } + } + + public static <T extends TopiaEntityEnum> void importAssociation(TopiaContext tx, + AssociationMeta<T> meta, + Map<String, TopiaEntity> universe, + ImportToMap importer, + CsvImportResult<T> csvResult, + int nbRowBuffer) throws TopiaException { + + CsvProgressModel progressModel = csvResult == null ? null : + csvResult.getProgressModel(); + + T source = meta.getSource(); + T target = meta.getTarget(); + + StringBuilder builder = new StringBuilder(); + + String targetTableName = target.getContract().getSimpleName(); + String sourceTableName = source.getContract().getSimpleName(); + String table = targetTableName; + + String updateString = String.format(UPDATE_ASSOCIATION, table, sourceTableName); + + if (log.isDebugEnabled()) { + log.debug("Will apply " + updateString); + } + int compt = 0; + for (Map<String, Object> row : importer) { + String topiaId = (String) row.get(TopiaEntity.TOPIA_ID); + String[] associations = (String[]) row.get("target"); + for (String association : associations) { + if (StringUtils.isNotEmpty(association)) { + + TopiaEntity targetEntity = universe.get(association); + Preconditions.checkNotNull(targetEntity, "Could not find target entity from id: " + association); + builder.append(String.format(updateString, topiaId, targetEntity.getTopiaId())).append('\n'); + compt++; + if (compt % nbRowBuffer == 0) { + // flush it + tx.executeSQL(builder.toString()); + builder = new StringBuilder(); + } + } + } + if (csvResult != null) { + csvResult.incrementsNumberUpdated(); + if (progressModel != null) { + progressModel.incrementsProgress(); + } + } + } + if (builder.length() > 0) { + tx.executeSQL(builder.toString()); + } + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/package-info.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/package-info.java new file mode 100644 index 0000000..9de54454 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/in/package-info.java @@ -0,0 +1,31 @@ +/** + * Package for csv import of entities. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +package org.nuiton.topia.persistence.csv.in; + +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/out/EntityAssociationExportModel.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/out/EntityAssociationExportModel.java new file mode 100644 index 0000000..277707f --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/out/EntityAssociationExportModel.java @@ -0,0 +1,64 @@ +package org.nuiton.topia.persistence.csv.out; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.metadata.AssociationMeta; +import org.nuiton.topia.persistence.csv.TopiaCsvCommons; +import org.nuiton.csv.ExportModel; +import org.nuiton.csv.ext.AbstractExportModel; + +/** + * A model to export associations of entities into csv files. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +public class EntityAssociationExportModel<T extends TopiaEntityEnum, E extends TopiaEntity> extends AbstractExportModel<E> { + + protected final AssociationMeta meta; + + public static <T extends TopiaEntityEnum, E extends TopiaEntity> ExportModel<E> newExportModel(char separator, + AssociationMeta<T> meta + ) { + EntityAssociationExportModel<T, E> model = new EntityAssociationExportModel<T, E>( + separator, + meta); + + // topiaId <-> topiaId + model.newColumnForExport(TopiaEntity.TOPIA_ID); + + model.newColumnForExport( + meta.getName(), + TopiaCsvCommons.newAssociationValueFormatter() + ); + return model; + } + + EntityAssociationExportModel(char separator, AssociationMeta<T> meta) { + super(separator); + this.meta = meta; + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/out/ExportEntityVisitor.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/out/ExportEntityVisitor.java new file mode 100644 index 0000000..4c37202 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/out/ExportEntityVisitor.java @@ -0,0 +1,212 @@ +package org.nuiton.topia.persistence.csv.out; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import com.google.common.base.Preconditions; +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaRuntimeException; +import org.nuiton.topia.persistence.EntityVisitor; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.TopiaPersistenceHelper; +import org.nuiton.util.TimeLog; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Collection; +import java.util.Map; + +/** + * Entity visitor to export data to csv files. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +public class ExportEntityVisitor<T extends TopiaEntityEnum> implements EntityVisitor, Closeable { + + /** Logger. */ + private static final Log log = LogFactory.getLog(ExportEntityVisitor.class); + + public static final TimeLog TIME_LOG = + new TimeLog(ExportEntityVisitor.class); + + /** Export for simple entity. */ + protected final Map<T, TopiaCsvExports.EntityExportContext<T>> entityExporters; + + protected final TopiaPersistenceHelper<T> persistenceHelper; + + public static <T extends TopiaEntityEnum> ExportEntityVisitor<T> newVisitor( + TopiaPersistenceHelper<T> persistenceHelper, + Map<T, TopiaCsvExports.EntityExportContext<T>> entityExporters) { + return new ExportEntityVisitor<T>( + persistenceHelper, + entityExporters + ); + } + + public ExportEntityVisitor(TopiaPersistenceHelper<T> persistenceHelper, + Map<T, TopiaCsvExports.EntityExportContext<T>> entityExporters) { + this.persistenceHelper = persistenceHelper; + this.entityExporters = entityExporters; + } + + public <E extends TopiaEntity> void export(Iterable<E> entities) { + for (E entity : entities) { + export(entity); + } + } + + public void export(TopiaEntity entity) { + Preconditions.checkNotNull(entity); + long s1 = TimeLog.getTime(); + try { + entity.accept(this); + } catch (TopiaException e) { + throw new TopiaRuntimeException( + "Could not export entity " + entity.getTopiaId(), e); + } finally { + TIME_LOG.log(s1, "export::" + entity.getTopiaId()); + } + } + + @Override + public void start(TopiaEntity entity) { + String topiaId = entity.getTopiaId(); + try { + if (log.isDebugEnabled()) { + log.debug("Starts export of entity " + topiaId); + } + TopiaCsvExports.EntityExportContext entityExporter = + getEntityContext(entity.getClass()); + Preconditions.checkNotNull(entityExporter); + entityExporter.write(entity); + } catch (Exception e) { + throw new TopiaRuntimeException( + "Could not export entity " + entity, e); + } finally { + if (log.isDebugEnabled()) { + log.debug("Ends export of entity " + topiaId); + } + } + } + + @Override + public void end(TopiaEntity entity) { + try { + if (log.isDebugEnabled()) { + log.debug("Starts export of association of entity " + + entity.getTopiaId()); + } + TopiaCsvExports.EntityExportContext entityExporter = + getEntityContext(entity.getClass()); + Preconditions.checkNotNull(entityExporter); + entityExporter.writeAssociations(entity); + } catch (Exception e) { + throw new TopiaRuntimeException( + "Could not export associations of entity " + entity, e); + } finally { + if (log.isDebugEnabled()) { + log.debug("Ends export of association of entity " + + entity.getTopiaId()); + } + } + } + + + @Override + public void visit(TopiaEntity entity, String propertyName, + Class<?> type, Object value) { + } + + @Override + public void visit(TopiaEntity entity, + String propertyName, + Class<?> collectionType, + Class<?> type, + Object value) { + + if (TopiaEntity.class.isAssignableFrom(type) && + getEntityContext((Class<? extends TopiaEntity>) type) != null) { + Collection<?> cValue = (Collection<?>) value; + + if (CollectionUtils.isNotEmpty(cValue)) { + + visitEntityCollection(entity, + propertyName, + collectionType, + type, + cValue); + } + } + } + + protected void visitEntityCollection(TopiaEntity entity, + String propertyName, + Class<?> collectionType, + Class<?> type, + Collection<?> cValue) { + for (Object currentValue : cValue) { + try { + ((TopiaEntity) currentValue).accept(this); + } catch (TopiaException e) { + if (log.isErrorEnabled()) { + log.error("Can not visit entity " + currentValue, e); + } + } + } + } + + @Override + public void visit(TopiaEntity entity, + String propertyName, + Class<?> collectionType, Class<?> type, + int index, + Object value) { + + // nothing to do + } + + @Override + public void close() throws IOException { + + // use at the end of visit (or later) + + for (TopiaCsvExports.EntityExportContext<T> exportContext : entityExporters.values()) { + exportContext.close(); + } + } + + @Override + public void clear() { + // prefer use the close api + } + + protected TopiaCsvExports.EntityExportContext getEntityContext(Class<? extends TopiaEntity> entityType) { + T entityEnum = persistenceHelper.getEntityEnum(entityType); + return entityEnum == null ? null : entityExporters.get(entityEnum); + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/out/ExportModelFactory.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/out/ExportModelFactory.java new file mode 100644 index 0000000..90ec3e6 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/out/ExportModelFactory.java @@ -0,0 +1,43 @@ +package org.nuiton.topia.persistence.csv.out; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.metadata.AssociationMeta; +import org.nuiton.topia.persistence.metadata.TableMeta; +import org.nuiton.csv.ExportModel; + +/** +* To produce export model. +* +* @author tchemit <chemit@codelutin.com> +* @since 2.6.12 +*/ +public interface ExportModelFactory<T extends TopiaEntityEnum> { + + <E extends TopiaEntity> ExportModel<E> buildForExport(TableMeta<T> meta); + + <E extends TopiaEntity> ExportModel<E> buildForExport(AssociationMeta<T> associationMeta); +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/out/PrepareDataForExport.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/out/PrepareDataForExport.java new file mode 100644 index 0000000..01d5b19 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/out/PrepareDataForExport.java @@ -0,0 +1,42 @@ +package org.nuiton.topia.persistence.csv.out; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.metadata.AssociationMeta; +import org.nuiton.topia.persistence.metadata.TableMeta; + +/** + * Prepare data to export. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +public interface PrepareDataForExport<T extends TopiaEntityEnum> { + + <E extends TopiaEntity> Iterable<E> prepareData(TableMeta<T> tableMeta); + + <E extends TopiaEntity> Iterable<E> prepareData(AssociationMeta<T> associationMeta); +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/out/TopiaCsvExports.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/out/TopiaCsvExports.java new file mode 100644 index 0000000..59291bd --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/out/TopiaCsvExports.java @@ -0,0 +1,406 @@ +package org.nuiton.topia.persistence.csv.out; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import com.google.common.base.Charsets; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaRuntimeException; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.metadata.AssociationMeta; +import org.nuiton.topia.persistence.metadata.MetaFilenameAware; +import org.nuiton.topia.persistence.metadata.Metadatas; +import org.nuiton.topia.persistence.metadata.TableMeta; +import org.nuiton.util.TimeLog; +import org.nuiton.csv.Export; +import org.nuiton.csv.ExportModel; +import org.nuiton.csv.ext.RepeatableExport; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.io.Writer; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + * Helper for csv exports. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +public class TopiaCsvExports { + + /** Logger. */ + private static final Log log = LogFactory.getLog(TopiaCsvExports.class); + + + public static final TimeLog TIME_LOG = new TimeLog(TopiaCsvExports.class); + + + protected TopiaCsvExports() { + // no instance of this helper + } + + public static <T extends TopiaEntityEnum> String exportData(TableMeta<T> tableMeta, + ExportModelFactory<T> modelFactory, + PrepareDataForExport<T> prepareDataForExport) { + + + long s1 = TimeLog.getTime(); + Export<TopiaEntity> export = prepareExport(tableMeta, modelFactory, prepareDataForExport); + TIME_LOG.log(s1, "exportData::prepareExport"); + long s2 = TimeLog.getTime(); + String result; + try { + result = export.toString(Charsets.UTF_8); + } catch (Exception eee) { + throw new TopiaRuntimeException("Can not export datas", eee); + } + TIME_LOG.log(s2, "exportData::exportToString"); + return result; + } + + public static <T extends TopiaEntityEnum> void exportData(TableMeta<T> tableMeta, + ExportModelFactory<T> modelFactory, + PrepareDataForExport<T> prepareDataForExport, + File file) { + + if (log.isInfoEnabled()) { + log.info("Export table " + tableMeta + " to " + file); + } + long s1 = TimeLog.getTime(); + Export<TopiaEntity> export = prepareExport(tableMeta, modelFactory, prepareDataForExport); + TIME_LOG.log(s1, "exportDatas::prepareExport"); + long s2 = TimeLog.getTime(); + try { + export.write(file, Charsets.UTF_8); + } catch (Exception eee) { + throw new TopiaRuntimeException("Can not export datas", eee); + } + TIME_LOG.log(s2, "exportData::exportToFile"); + } + + public static <T extends TopiaEntityEnum> void exportData(AssociationMeta<T> associationMeta, + ExportModelFactory<T> modelFactory, + PrepareDataForExport<T> prepareDataForExport, + File file) { + + if (log.isInfoEnabled()) { + log.info("Export association " + associationMeta + " to " + file); + } + long s1 = TimeLog.getTime(); + + Export<TopiaEntity> export = prepareExport(associationMeta, modelFactory, prepareDataForExport); + TIME_LOG.log(s1, "exportData::prepareExport"); + + long s2 = TimeLog.getTime(); + try { + export.write(file, Charsets.UTF_8); + } catch (Exception eee) { + throw new TopiaRuntimeException("Can not export datas", eee); + } + TIME_LOG.log(s2, "exportData::exportToFile"); + } + + public static <T extends TopiaEntityEnum, E extends TopiaEntity> Export<E> prepareExport(TableMeta<T> tableMeta, + ExportModelFactory<T> modelFactory, + PrepareDataForExport<T> prepareDataForExport) { + + Iterable<E> datas = prepareDataForExport.prepareData(tableMeta); + ExportModel<E> model = modelFactory.buildForExport(tableMeta); + return Export.newExport(model, datas); + } + + public static <T extends TopiaEntityEnum, E extends TopiaEntity> Export<E> prepareExport(AssociationMeta<T> associationMeta, + ExportModelFactory<T> modelFactory, + PrepareDataForExport<T> prepareDataForExport) { + + Iterable<E> datas = prepareDataForExport.prepareData(associationMeta); + ExportModel<E> model = modelFactory.buildForExport(associationMeta); + return Export.newExport(model, datas); + } + + public static <T extends TopiaEntityEnum> Map<T, EntityExportContext<T>> createReplicateEntityVisitorContexts(ExportModelFactory<T> modelFactory, + MetaFilenameAware<T>[] entityMetas, + Multimap<T, MetaFilenameAware<T>> associations, + File container) { + + Preconditions.checkNotNull(modelFactory); + Preconditions.checkNotNull(entityMetas); + Preconditions.checkNotNull(associations); + Preconditions.checkNotNull(container); + + Map<T, EntityExportContext<T>> contexts = Maps.newHashMap(); + + for (MetaFilenameAware<T> entityMeta : entityMetas) { + TableMeta<T> meta = (TableMeta<T>) entityMeta; + + ExportModel<TopiaEntity> model = modelFactory.buildForExport(meta); + + EntityExportContext<T> exportContext = EntityExportContext.newExportContext( + model, + meta, + container); + + T source = meta.getSource(); + + contexts.put(source, exportContext); + + for (MetaFilenameAware<T> metaFilenameAware : associations.get(source)) { + AssociationMeta<T> associationMeta = + (AssociationMeta<T>) metaFilenameAware; + + ExportModel<TopiaEntity> associationModel = + modelFactory.buildForExport(associationMeta); + exportContext.addAssociationExportContext(associationMeta, + associationModel, + container); + } + } + return contexts; + } + + + public static <T extends TopiaEntityEnum> Map<T, EntityExportContext<T>> createReplicateEntityVisitorContexts(ExportModelFactory<T> modelFactory, + Iterable<TableMeta<T>> entityMetas, + Iterable<AssociationMeta<T>> associationMetas, + File container) { + + Preconditions.checkNotNull(modelFactory); + Preconditions.checkNotNull(entityMetas); + Preconditions.checkNotNull(associationMetas); + Preconditions.checkNotNull(container); + + Multimap<T, AssociationMeta<T>> associations = Metadatas.split(associationMetas); + Map<T, EntityExportContext<T>> contexts = Maps.newHashMap(); + + for (TableMeta<T> meta : entityMetas) { + + ExportModel<TopiaEntity> model = modelFactory.buildForExport(meta); + + EntityExportContext<T> exportContext = EntityExportContext.newExportContext( + model, + meta, + container); + + T source = meta.getSource(); + + contexts.put(source, exportContext); + + for (AssociationMeta<T> associationMeta : associations.get(source)) { + + ExportModel<TopiaEntity> associationModel = + modelFactory.buildForExport(associationMeta); + exportContext.addAssociationExportContext(associationMeta, + associationModel, + container); + } + } + return contexts; + } + + /** + * to export entity as csv files. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ + public static class EntityExportContext<T extends TopiaEntityEnum> implements Closeable { + + /** meta to export. */ + protected final TableMeta<T> meta; + + /** Exporter object. */ + protected final RepeatableExport export; + + /** Where to export datas. */ + protected final Writer writer; + + /** + * Unique list to store data to export. (will be shared with + * association export contexts.) + */ + private final List<TopiaEntity> data; + + /** Association export context for this type of entity. */ + protected final Collection<AssociationExportContext<T>> associationExportContexts; + + protected final File entryFile; + + public static <T extends TopiaEntityEnum> EntityExportContext<T> newExportContext( + ExportModel<TopiaEntity> model, + TableMeta<T> meta, + File container) { + return new EntityExportContext<T>(model, meta, container); + } + + protected EntityExportContext(ExportModel<TopiaEntity> model, + TableMeta<T> meta, + File container) { + Preconditions.checkNotNull(model); + Preconditions.checkNotNull(meta); + Preconditions.checkNotNull(container); + + this.meta = meta; + data = Lists.newArrayList(); + export = RepeatableExport.newExport(model, data, true); + entryFile = meta.newFile(container); + if (log.isDebugEnabled()) { + log.debug("Creates EntityExportContext::" + meta + " - " + + entryFile.getName()); + } + writer = meta.newWriter(container); + associationExportContexts = Lists.newArrayList(); + } + + public void addAssociationExportContext(AssociationMeta<T> meta, + ExportModel<TopiaEntity> model, + File container) { + associationExportContexts.add( + new AssociationExportContext<T>(model, + meta, + container, + data) + ); + } + + @Override + public void close() throws IOException { + + try { + if (export.isHeaderWritten()) { + + if (log.isInfoEnabled()) { + log.info("Export table " + meta + " to " + entryFile); + } + writer.flush(); + } else { + // this file was not used, delete it + FileUtils.deleteQuietly(entryFile); + } + } finally { + IOUtils.closeQuietly(writer); + for (AssociationExportContext<T> c : associationExportContexts) { + c.close(); + } + } + } + + public void write(TopiaEntity data) throws Exception { + this.data.add(data); + try { + export.write(writer); + } finally { + this.data.clear(); + } + } + + public void writeAssociations(TopiaEntity data) throws Exception { + this.data.add(data); + try { + for (AssociationExportContext<T> c : associationExportContexts) { + AssociationMeta<T> cMeta = c.meta; + boolean emptyChild = cMeta.isChildEmpty(data); + if (!emptyChild) { + c.write(); + } + } + } finally { + this.data.clear(); + } + } + } + + /** + * To export associations as csv files. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ + public static class AssociationExportContext<T extends TopiaEntityEnum> implements Closeable { + + /** association meta to export. */ + protected final AssociationMeta<T> meta; + + /** Exporter object. */ + protected final RepeatableExport export; + + /** Where to export datas. */ + protected final Writer writer; + + protected final File entryFile; + + protected AssociationExportContext(ExportModel<TopiaEntity> model, + AssociationMeta<T> meta, + File container, + List<TopiaEntity> data) { + Preconditions.checkNotNull(model); + Preconditions.checkNotNull(meta); + Preconditions.checkNotNull(container); + Preconditions.checkNotNull(data); + this.meta = meta; + + export = RepeatableExport.newExport(model, data, true); + + entryFile = meta.newFile(container); + if (log.isDebugEnabled()) { + log.debug("Creates AssociationExportContext::" + meta + + " - " + entryFile.getName()); + } + writer = meta.newWriter(container); + } + + @Override + public void close() throws IOException { + try { + if (export.isHeaderWritten()) { + + if (log.isInfoEnabled()) { + log.info("Export association " + meta + " to " + entryFile); + } + writer.flush(); + } else { + + // this file was not used, delete it + FileUtils.deleteQuietly(entryFile); + } + } finally { + IOUtils.closeQuietly(writer); + } + } + + public void write() throws Exception { + export.write(writer); + } + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/out/package-info.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/out/package-info.java new file mode 100644 index 0000000..c2c7435 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/out/package-info.java @@ -0,0 +1,31 @@ +/** + * Package for csv export of entities. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +package org.nuiton.topia.persistence.csv.out; + +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/package-info.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/package-info.java new file mode 100644 index 0000000..2c529c2 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/csv/package-info.java @@ -0,0 +1,31 @@ +/** + * Base package for csv import and export of entities. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +package org.nuiton.topia.persistence.csv; + +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/AssociationMeta.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/AssociationMeta.java new file mode 100644 index 0000000..3b0aab0 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/AssociationMeta.java @@ -0,0 +1,146 @@ +package org.nuiton.topia.persistence.metadata; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import com.google.common.base.Charsets; +import org.nuiton.topia.TopiaRuntimeException; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.util.EntityOperator; +import org.nuiton.topia.persistence.util.EntityOperatorStore; +import org.nuiton.util.ObjectUtil; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.Serializable; +import java.io.Writer; +import java.util.Collection; + +/** + * Define the meta data of a entity association field. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +public class AssociationMeta<T extends TopiaEntityEnum> implements Serializable, MetaFilenameAware<T> { + + private static final long serialVersionUID = 1L; + + /** Association source entity type. */ + protected final T source; + + /** Association target entity type. */ + protected final T target; + + /** Name fo the association. */ + protected final String name; + + /** Operator of the source entity used to get / set associations. */ + protected transient EntityOperator<TopiaEntity> operator; + + protected static <T extends TopiaEntityEnum> AssociationMeta<T> newMeta(T source, + T target, + String name) { + return new AssociationMeta<T>(source, target, name); + } + + public AssociationMeta(T source, + T target, + String name) { + this.source = source; + this.target = target; + this.name = name; + } + + @Override + public T getSource() { + return source; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getFilename() { + return source.name() + "_" + name + CSV_EXTENSION; + } + + @Override + public File newFile(File container) { + return new File(container, getFilename()); + } + + @Override + public Writer newWriter(File container) { + File file = newFile(container); + try { + return new OutputStreamWriter(new FileOutputStream(file), + Charsets.UTF_8); + } catch (FileNotFoundException e) { + throw new TopiaRuntimeException("Could not find file " + file); + } + } + + public T getTarget() { + return target; + } + + public TopiaEntity newEntity() { + return ObjectUtil.newInstance(source.getImplementation()); + } + + public Object newAssociation() { + return ObjectUtil.newInstance(target.getImplementation()); + } + + public Collection<TopiaEntity> getChilds(TopiaEntity entity) { + Object o = getOperator().get(name, entity); + return (Collection<TopiaEntity>) o; + } + + public void setChilds(TopiaEntity entity, Collection<TopiaEntity> childs) { + getOperator().addAllChild(name, entity, childs); + } + + public boolean isChildEmpty(TopiaEntity entity) { + boolean result = getOperator().isChildEmpty(name, entity); + return result; + } + + public EntityOperator<TopiaEntity> getOperator() { + if (operator == null) { + operator = EntityOperatorStore.getOperator(source); + } + return operator; + } + + @Override + public String toString() { + return "<" + source + ":" + name + " " + target + ">"; + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/ColumnMeta.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/ColumnMeta.java new file mode 100644 index 0000000..79035cb --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/ColumnMeta.java @@ -0,0 +1,94 @@ +package org.nuiton.topia.persistence.metadata; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import org.nuiton.topia.persistence.TopiaEntity; + +import java.io.Serializable; +import java.util.Date; + +/** + * Define the meta data of a entity field. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +public class ColumnMeta implements Serializable { + + protected static ColumnMeta newMeta(String name, Class<?> type) { + return new ColumnMeta(name, type); + } + + private static final long serialVersionUID = 1L; + + /** Name of the column. */ + protected String name; + + /** Type of the column. */ + protected Class<?> type; + + public String getName() { + return name; + } + + public Class<?> getType() { + return type; + } + + public String getTypeSimpleName() { + return type.getSimpleName(); + } + + public String getColumnType() { + String result = "string"; + if (boolean.class.equals(type)) { + result = "boolean"; + } else if (Date.class.equals(type)) { + result = "date"; + } + return result; + } + + public boolean isFK() { + return TopiaEntity.class.isAssignableFrom(type); + } + + public boolean isDate() { + return Date.class.isAssignableFrom(type); + } + + public boolean isBoolean() { + return boolean.class.equals(type); + } + + public boolean isNumber() { + return !isBoolean() && (type.isPrimitive() || Number.class.isAssignableFrom(type)); + } + + protected ColumnMeta(String name, Class<?> type) { + this.name = name; + this.type = type; + } + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/DbMeta.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/DbMeta.java new file mode 100644 index 0000000..87d7fdc --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/DbMeta.java @@ -0,0 +1,130 @@ +package org.nuiton.topia.persistence.metadata; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + + +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.TopiaPersistenceHelper; + +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +/** + * Define metas about a db. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +public class DbMeta<T extends TopiaEntityEnum> implements Iterable<TableMeta<T>> { + + /** All metas of the db. */ + protected final List<TableMeta<T>> tables; + + /** All types non editables. */ + protected final Set<T> nonEditableTypes; + + protected final TopiaPersistenceHelper<T> persistenceHelper; + + public static <T extends TopiaEntityEnum> DbMeta<T> newDbMeta( + TopiaPersistenceHelper<T> typeProvider, + T[] universe, + T... nonEditables) { + return new DbMeta<T>(typeProvider, universe, nonEditables); + } + + public void addTables(List<TableMeta<T>> entities, + Iterable<T> types) { + for (T type : types) { + + TableMeta<T> tableMeta = getTable(type); + if (entities != null) { + entities.add(tableMeta); + } + } + } + + public void addAssociations(List<AssociationMeta<T>> associations, + Iterable<T> types) { + for (T type : types) { + + TableMeta<T> tableMeta = getTable(type); + if (associations != null) { + associations.addAll(tableMeta.getAssociations()); + } + } + } + + public List<String> getTableNames() { + List<String> result = Lists.newArrayList(); + for (TableMeta tableMeta : getTables()) { + result.add(tableMeta.getName()); + } + return result; + } + + public List<TableMeta<T>> getTables() { + return tables; + } + + public TableMeta<T> getTable(T entityType) { + Preconditions.checkNotNull(entityType); + TableMeta<T> result = null; + for (TableMeta<T> tableMeta : getTables()) { + if (entityType.equals(tableMeta.getSource())) { + result = tableMeta; + break; + } + } + return result; + } + + @Override + public Iterator<TableMeta<T>> iterator() { + return getTables().iterator(); + } + + protected DbMeta(TopiaPersistenceHelper<T> persistenceHelper, + T[] entityTypes, + T... nonEditableTypes) { + this.persistenceHelper = persistenceHelper; + this.nonEditableTypes = Sets.newHashSet(nonEditableTypes); + tables = Lists.newArrayList(); + for (T entityEnum : entityTypes) { + TableMeta<T> tableMeta = TableMeta.newMeta(entityEnum, persistenceHelper); + tables.add(tableMeta); + } + } + + public boolean isEditable(TableMeta<T> meta) { + return !nonEditableTypes.contains(meta.getSource()); + } + + public TopiaPersistenceHelper<T> getPersistenceHelper() { + return persistenceHelper; + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/MetaFilenameAware.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/MetaFilenameAware.java new file mode 100644 index 0000000..0155c91 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/MetaFilenameAware.java @@ -0,0 +1,51 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.persistence.metadata; + +import org.nuiton.topia.persistence.TopiaEntityEnum; + +import java.io.File; +import java.io.Writer; + +/** + * Contract to import or export some metas. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +public interface MetaFilenameAware<T extends TopiaEntityEnum> { + + String CSV_EXTENSION = ".csv"; + + T getSource(); + + String getName(); + + String getFilename(); + + File newFile(File container); + + Writer newWriter(File container); + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/Metadatas.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/Metadatas.java new file mode 100644 index 0000000..0f3fa35 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/Metadatas.java @@ -0,0 +1,64 @@ +package org.nuiton.topia.persistence.metadata; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import com.google.common.base.Function; +import com.google.common.collect.Maps; +import com.google.common.collect.Multimap; +import com.google.common.collect.Multimaps; +import org.nuiton.topia.persistence.TopiaEntityEnum; + +import java.util.Map; + +/** + * Useful methods around metadatas. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +public class Metadatas { + + public static <T extends TopiaEntityEnum, M extends MetaFilenameAware<T>> Multimap<T, M> split(Iterable<M> metas) { + Function<M, T> function = newMetaBySourcefunction(); + Multimap<T, M> associationsBySource = Multimaps.index(metas, function); + return associationsBySource; + } + + public static <T extends TopiaEntityEnum, M extends MetaFilenameAware<T>> Map<T, M> uniqueIndex(Iterable<M> metas) { + Function<M, T> function = newMetaBySourcefunction(); + Map<T, M> associationsBySource = Maps.uniqueIndex(metas, function); + return associationsBySource; + } + + protected static <T extends TopiaEntityEnum, M extends MetaFilenameAware<T>> Function<M, T> newMetaBySourcefunction() { + return new Function<M, T>() { + + @Override + public T apply(M input) { + return input.getSource(); + } + }; + } + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/TableMeta.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/TableMeta.java new file mode 100644 index 0000000..a073f3c --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/TableMeta.java @@ -0,0 +1,257 @@ +package org.nuiton.topia.persistence.metadata; +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import com.google.common.base.Charsets; +import com.google.common.base.Preconditions; +import com.google.common.collect.Lists; +import com.google.common.collect.Sets; +import org.nuiton.topia.TopiaRuntimeException; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.TopiaPersistenceHelper; +import org.nuiton.topia.persistence.util.EntityOperator; +import org.nuiton.topia.persistence.util.EntityOperatorStore; +import org.nuiton.util.ObjectUtil; +import org.nuiton.util.beans.Binder; +import org.nuiton.util.beans.BinderModelBuilder; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.OutputStreamWriter; +import java.io.Serializable; +import java.io.Writer; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Define metas of a given db table. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +public class TableMeta<T extends TopiaEntityEnum> implements Serializable, Iterable<ColumnMeta>, MetaFilenameAware<T> { + + private static final long serialVersionUID = 1L; + + public static <T extends TopiaEntityEnum> TableMeta<T> newMeta(T entityEnum, + TopiaPersistenceHelper<T> typeProvider) { + return new TableMeta<T>(entityEnum, typeProvider); + } + + /** Type of the entity. */ + protected final T source; + + /** List of columns of the entity. */ + protected List<ColumnMeta> columns; + + /** List of dependencies (says all property with a topiaentity type) */ + protected final Set<T> dependencies; + + /** List of associations of the entity. */ + protected List<AssociationMeta<T>> associations; + + /** Binder used to copy entities (lazy loaded). */ + protected Binder<TopiaEntity, TopiaEntity> binder; + + /** Entity operator used in generic algorithms. */ + protected transient EntityOperator<TopiaEntity> operator; + + protected boolean useNaturalIdsOrNotNulls; + + @Override + public T getSource() { + return source; + } + + @Override + public String getName() { + return source.name(); + } + + @Override + public String getFilename() { + return source.name() + ".csv"; + } + + @Override + public File newFile(File container) { + return new File(container, getFilename()); + } + + @Override + public Writer newWriter(File container) { + File file = newFile(container); + try { + return new OutputStreamWriter(new FileOutputStream(file), + Charsets.UTF_8); + } catch (FileNotFoundException e) { + throw new TopiaRuntimeException("Could not find file " + file); + } + } + + @Override + public String toString() { + return "<" + source + ">"; + } + + public Class<? extends TopiaEntity> getEntityType() { + return source.getContract(); + } + + public ColumnMeta getColumns(String columnName) { + Preconditions.checkNotNull(columnName); + ColumnMeta result = null; + for (ColumnMeta columnMeta : getColumns()) { + if (columnName.equals(columnMeta.getName())) { + result = columnMeta; + break; + } + } + return result; + } + + public String[] getColumnNamesAsArray() { + List<String> columnNames = getColumnNames(); + return columnNames.toArray(new String[columnNames.size()]); + } + + public List<String> getColumnNames() { + List<String> result = Lists.newLinkedList(); + for (ColumnMeta columnMeta : getColumns()) { + result.add(columnMeta.getName()); + } + return result; + } + + public List<ColumnMeta> getColumns() { + return columns; + } + + public List<AssociationMeta<T>> getAssociations() { + return associations; + } + + public Set<T> getDependencies() { + return dependencies; + } + + public AssociationMeta<T> getAssociations(String name) { + AssociationMeta<T> result = null; + for (AssociationMeta<T> meta : getAssociations()) { + if (name.equals(meta.getName())) { + result = meta; + break; + } + } + return result; + } + + public void copy(TopiaEntity source, TopiaEntity target) { + getBinder().copy(source, target); + } + + public Map<String, Object> prepareCreate(TopiaEntity bean, + String topiaId) { + Map<String, Object> result = getOperator().getNaturalIsdAndNotNulls(bean); + if (topiaId != null) { + result.put(TopiaEntity.TOPIA_ID, topiaId); + } + return result; + } + + @Override + public Iterator<ColumnMeta> iterator() { + return getColumns().iterator(); + } + + public TopiaEntity newEntity() { + return ObjectUtil.newInstance(source.getImplementation()); + } + + protected Binder<TopiaEntity, TopiaEntity> getBinder() { + if (binder == null) { + BinderModelBuilder<TopiaEntity, TopiaEntity> binderModelBuilder = + (BinderModelBuilder<TopiaEntity, TopiaEntity>) BinderModelBuilder.newEmptyBuilder(getEntityType()); + for (ColumnMeta columnMeta : this) { + binderModelBuilder.addSimpleProperties(columnMeta.getName()); + } + binder = binderModelBuilder.toBinder(); + } + return binder; + } + + protected TableMeta(T source, TopiaPersistenceHelper<T> typeProvider) { + Preconditions.checkNotNull(source); + this.source = source; + + associations = Lists.newArrayList(); + columns = Lists.newLinkedList(); + Set<T> deps = Sets.newHashSet(); + + // fill associations + List<String> associationProperties = getOperator().getAssociationProperties(); + for (String property : associationProperties) { + Class<?> propertyType = getOperator().getAssociationPropertyType(property); + if (TopiaEntity.class.isAssignableFrom(propertyType)) { + + // only use it for entity + T targetEnum = typeProvider.getEntityEnum((Class<TopiaEntity>) propertyType); + + AssociationMeta<T> meta = AssociationMeta.newMeta(source, + targetEnum, + property + ); + associations.add(meta); + } + } + + // fill properties (remove all asociations) + List<String> properties = Lists.newArrayList(getOperator().getProperties()); + properties.removeAll(associationProperties); + for (String property : properties) { + Class<?> propertyType = getOperator().getPropertyType(property); + ColumnMeta meta = ColumnMeta.newMeta(property, propertyType); + columns.add(meta); + if (meta.isFK()) { + T dependency = typeProvider.getEntityEnum((Class<TopiaEntity>) propertyType); + deps.add(dependency); + } + } + dependencies = deps; + + useNaturalIdsOrNotNulls = source.isUseNotNulls() || + source.isUseNaturalIds(); + } + + public EntityOperator<TopiaEntity> getOperator() { + if (operator == null) { + operator = EntityOperatorStore.getOperator(source); + } + return operator; + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/package-info.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/package-info.java new file mode 100644 index 0000000..3e622f1 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/metadata/package-info.java @@ -0,0 +1,31 @@ +/** + * Package to define metadatas over {@link TopiaEntity}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.12 + */ +package org.nuiton.topia.persistence.metadata; + +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/package-info.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/package-info.java new file mode 100644 index 0000000..03d3f08 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/package-info.java @@ -0,0 +1,37 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +/** + * This package contains interfaces and abstract classes for entities and daos. + * <p /> + * For daos : {@link org.nuiton.topia.persistence.TopiaDAO} as interface and + * {@link org.nuiton.topia.persistence.TopiaDAOImpl} as implementation. + * <p /> + * For entities : {@link org.nuiton.topia.persistence.TopiaEntity} as interface + * and {@link org.nuiton.topia.persistence.TopiaEntityAbstract} + * as abstract class inherited by generated entities for + * final applications. Generation is done using transformers from {@code + * org.nuiton.topia.generator} package. + * + */ +package org.nuiton.topia.persistence; diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/pager/FilterRule.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/pager/FilterRule.java new file mode 100644 index 0000000..f9335d6 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/pager/FilterRule.java @@ -0,0 +1,62 @@ +package org.nuiton.topia.persistence.pager; + +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import java.io.Serializable; + +/** + * Rule for a filtered pager. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.14 + */ +public class FilterRule implements Serializable { + + private static final long serialVersionUID = 1L; + + protected final FilterRuleOperator op; + + protected final String field; + + protected final String data; + + public FilterRule(FilterRuleOperator op, String field, String data) { + this.op = op; + this.field = field; + this.data = data; + } + + public FilterRuleOperator getOp() { + return op; + } + + public String getField() { + return field; + } + + public String getData() { + return data; + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/pager/FilterRuleGroupOperator.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/pager/FilterRuleGroupOperator.java new file mode 100644 index 0000000..f97180d --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/pager/FilterRuleGroupOperator.java @@ -0,0 +1,35 @@ +package org.nuiton.topia.persistence.pager; + +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/** + * Define the operator to apply between some rules. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.14 + */ +public enum FilterRuleGroupOperator { + OR, AND +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/pager/FilterRuleOperator.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/pager/FilterRuleOperator.java new file mode 100644 index 0000000..9092fd3 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/pager/FilterRuleOperator.java @@ -0,0 +1,176 @@ +package org.nuiton.topia.persistence.pager; + +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import java.util.Map; + +/** + * Operator used in a rule. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.14 + */ +public enum FilterRuleOperator { + /** Equals operator. */ + eq { + @Override + public String toHql(String paramName, String propertyName, Object data, Map<String, Object> filterParams) { + String ruleFilter = propertyName + " = :" + paramName; + filterParams.put(paramName, data); + return ruleFilter; + } + }, + /** Not equals operator. */ + ne { + @Override + public String toHql(String paramName, String propertyName, Object data, Map<String, Object> filterParams) { + String ruleFilter = propertyName + " != :" + paramName; + filterParams.put(paramName, data); + return ruleFilter; + } + }, + /** Contains operator. */ + cn { + @Override + public String toHql(String paramName, String propertyName, Object data, Map<String, Object> filterParams) { + String ruleFilter = propertyName + " like :" + paramName; + filterParams.put(paramName, "%" + data + "%"); + return ruleFilter; + } + }, + /** Not contains operator. */ + nc { + @Override + public String toHql(String paramName, String propertyName, Object data, Map<String, Object> filterParams) { + String ruleFilter = propertyName + " not like :" + paramName; + filterParams.put(paramName, "%" + data + "%"); + return ruleFilter; + } + }, + /** Begins with operator. */ + bw { + @Override + public String toHql(String paramName, String propertyName, Object data, Map<String, Object> filterParams) { + String ruleFilter = propertyName + " like :" + paramName; + filterParams.put(paramName, data + "%"); + return ruleFilter; + } + }, + /** Not between with operator. */ + bn { + @Override + public String toHql(String paramName, String propertyName, Object data, Map<String, Object> filterParams) { + String ruleFilter = propertyName + " not like :" + paramName; + filterParams.put(paramName, data + "%"); + return ruleFilter; + } + }, + /** Ends with operator. */ + ew { + @Override + public String toHql(String paramName, String propertyName, Object data, Map<String, Object> filterParams) { + String ruleFilter = propertyName + " like :" + paramName; + filterParams.put(paramName, "%" + data); + return ruleFilter; + } + }, + /** Not End with operator. */ + en { + @Override + public String toHql(String paramName, String propertyName, Object data, Map<String, Object> filterParams) { + String ruleFilter = propertyName + " not like :" + paramName; + filterParams.put(paramName, "%" + data); + return ruleFilter; + } + }, + /** Lesser than operator. */ + lt { + @Override + public String toHql(String paramName, String propertyName, Object data, Map<String, Object> filterParams) { + String ruleFilter = propertyName + " < :" + paramName; + filterParams.put(paramName, data); + return ruleFilter; + } + }, + /** Lesser or equals operator. */ + le { + @Override + public String toHql(String paramName, String propertyName, Object data, Map<String, Object> filterParams) { + String ruleFilter = propertyName + " <= :" + paramName; + filterParams.put(paramName, data); + return ruleFilter; + } + }, + /** Greater than operator. */ + gt { + @Override + public String toHql(String paramName, String propertyName, Object data, Map<String, Object> filterParams) { + String ruleFilter = propertyName + " > :" + paramName; + filterParams.put(paramName, data); + return ruleFilter; + } + }, + /** Greater or equals operator. */ + ge { + @Override + public String toHql(String paramName, String propertyName, Object data, Map<String, Object> filterParams) { + String ruleFilter = propertyName + " >= :" + paramName; + filterParams.put(paramName, data); + return ruleFilter; + } + }, + /** Is null operator. */ + nu { + @Override + public String toHql(String paramName, String propertyName, Object data, Map<String, Object> filterParams) { + String ruleFilter = propertyName + " is null"; + return ruleFilter; + } + }, + /** Is not null operator. */ + nn { + @Override + public String toHql(String paramName, String propertyName, Object data, Map<String, Object> filterParams) { + String ruleFilter = propertyName + " is not null"; + return ruleFilter; + } + }, + /** Is among operator. */ + in { + @Override + public String toHql(String paramName, String propertyName, Object data, Map<String, Object> filterParams) { + throw new UnsupportedOperationException(); + } + }, + /** Not is among operator. */ + ni { + @Override + public String toHql(String paramName, String propertyName, Object data, Map<String, Object> filterParams) { + throw new UnsupportedOperationException(); + } + }; + + public abstract String toHql(String paramName, String propertyName, Object data, Map<String, Object> filterParams); +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/pager/TopiaPagerBean.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/pager/TopiaPagerBean.java new file mode 100644 index 0000000..f38f3fa --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/pager/TopiaPagerBean.java @@ -0,0 +1,88 @@ +package org.nuiton.topia.persistence.pager; + +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import org.apache.commons.collections.CollectionUtils; +import org.nuiton.util.PagerBean; + +import java.util.List; + +/** + * Extension of a {@link PagerBean} to add filter and ordering capacities. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.14 + */ +public class TopiaPagerBean extends PagerBean { + + private static final long serialVersionUID = 1L; + + // sorting order - asc or desc + protected boolean sortAscendant; + + // get index row - i.e. user click to sort. + protected String sortColumn; + + protected FilterRuleGroupOperator groupOp; + + private List<FilterRule> rules; + + public boolean canFilter() { + return groupOp != null && CollectionUtils.isNotEmpty(rules); + } + + public FilterRuleGroupOperator getGroupOp() { + return groupOp; + } + + public void setGroupOp(FilterRuleGroupOperator groupOp) { + this.groupOp = groupOp; + } + + public List<FilterRule> getRules() { + return rules; + } + + public void setRules(List<FilterRule> rules) { + this.rules = rules; + } + + public boolean isSortAscendant() { + return sortAscendant; + } + + public void setSortAscendant(boolean sortAscendant) { + this.sortAscendant = sortAscendant; + } + + public String getSortColumn() { + return sortColumn; + } + + public void setSortColumn(String sortColumn) { + this.sortColumn = sortColumn; + } + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/pager/TopiaPagerBeanBuilder.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/pager/TopiaPagerBeanBuilder.java new file mode 100644 index 0000000..a4d210e --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/pager/TopiaPagerBeanBuilder.java @@ -0,0 +1,102 @@ +package org.nuiton.topia.persistence.pager; + +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * To build a {@link TopiaPagerBean} from scratch. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.14 + */ +public class TopiaPagerBeanBuilder { + + protected final TopiaPagerBean bean; + + public TopiaPagerBeanBuilder() { + this(new TopiaPagerBean()); + } + + public TopiaPagerBeanBuilder(TopiaPagerBean bean) { + this.bean = bean; + } + + public TopiaPagerBeanBuilder setFilterRuleGroup(FilterRuleGroupOperator groupOp) { + bean.setGroupOp(groupOp); + return this; + } + + public TopiaPagerBeanBuilder setSortcolumn(String sortColumn) { + bean.setSortColumn(sortColumn); + return this; + } + + public TopiaPagerBeanBuilder setSortAscendant(boolean sortAscendant) { + bean.setSortAscendant(sortAscendant); + return this; + } + + public TopiaPagerBeanBuilder setFilterOperationGroup(String groupOp) { + setFilterRuleGroup(FilterRuleGroupOperator.valueOf(groupOp)); + return this; + } + + public TopiaPagerBeanBuilder addRules(Collection<Map<String, String>> rules) { + for (Map<String, String> rule : rules) { + addRule(rule); + } + return this; + } + + public TopiaPagerBeanBuilder addRule(String op, String property, String value) { + FilterRuleOperator operator = FilterRuleOperator.valueOf(op); + return addRule(new FilterRule(operator, property, value)); + } + + public TopiaPagerBeanBuilder addRule(Map<String, String> ruleMap) { + String op = ruleMap.get("op"); + String property = ruleMap.get("field"); + String value = ruleMap.get("data"); + return addRule(op, property, value); + } + + public TopiaPagerBeanBuilder addRule(FilterRule rule) { + List<FilterRule> rules = bean.getRules(); + if (rules == null) { + rules = new LinkedList<FilterRule>(); + bean.setRules(rules); + } + rules.add(rule); + return this; + } + + public TopiaPagerBean toBean() { + return bean; + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/Collector.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/Collector.java new file mode 100644 index 0000000..fac9951 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/Collector.java @@ -0,0 +1,273 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.persistence.DepthEntityVisitor; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; + +import java.util.ArrayDeque; +import java.util.Collection; +import java.util.Deque; + +/** + * Un objet qui permet de parcourir des entites (via un + * {@link CollectorVisitor}) et de collecter des donnees pendant le parcours. + * <p/> + * La classe a un type qui est le type de retour de la methode + * {@link #detect(TopiaEntity...)}. + * <p/> + * On peut donc très facilement en faire un collecteur de donnees. + * + * @author tchemit <chemit@codelutin.com> + * @param <R> les donnes a retourner + * @since 2.2.0 + */ +public abstract class Collector<R> { + + private static final Log log = LogFactory.getLog(Collector.class); + + /** le visiteur utiliser pour trouver les types */ + protected CollectorVisitor visitor; + + /** la liste des contracts des entites connues. */ + protected TopiaEntityEnum[] contracts; + + public Collector(CollectorVisitor visitor, TopiaEntityEnum[] contracts) { + this.visitor = visitor == null ? new CollectorVisitor() : visitor; + this.visitor.setCollector(this); + this.contracts = contracts; + } + + public Collector(TopiaEntityEnum[] contracts) { + this(null, contracts); + } + + protected void clear() { + visitor.clear(); + } + + protected boolean onStarting(TopiaEntity e) { + return true; + } + + protected void onStarted(TopiaEntity e, boolean enter) { + } + + protected boolean onVisiting(TopiaEntity e, String name, Class<?> type, + Object value) { + return true; + } + + protected void onVisited(TopiaEntity e, String name, Class<?> type, + Object value, boolean enter) { + } + + protected boolean onVisiting(TopiaEntity e, String name, + Class<?> collectionType, + Class<?> type, Object value) { + return true; + } + + protected void onVisited(TopiaEntity e, String name, + Class<?> collectionType, + Class<?> type, Object value, boolean enter) { + } + + protected boolean onVisiting(TopiaEntity e, String name, + Class<?> collectionType, + Class<?> type, int index, Object value) { + return true; + } + + protected void onVisited(TopiaEntity e, String name, + Class<?> collectionType, + Class<?> type, + int index, Object value, boolean enter) { + } + + protected boolean onEnding(TopiaEntity e) { + return true; + } + + protected void onEnded(TopiaEntity e, boolean enter) { + } + + protected void beforeAll(CollectorVisitor visitor, + TopiaEntity... entities) { + } + + protected void before(CollectorVisitor visitor, TopiaEntity entity) { + if (log.isDebugEnabled()) { + log.debug("Will detect "+entity.getTopiaId()); + } + } + + protected void after(CollectorVisitor visitor, TopiaEntity entity) { + } + + protected abstract R afterAll(CollectorVisitor visitor, + TopiaEntity... entities); + + public R detect(TopiaEntity... entities) throws TopiaException { + + try { + beforeAll(visitor, entities); + + for (TopiaEntity e : entities) { + before(visitor, e); + e.accept(visitor); + after(visitor, e); + } + + R result = afterAll(visitor, entities); + + return result; + } finally { + clear(); + } + } + + public static class CollectorVisitor extends DepthEntityVisitor { + + /** la pile des entites en cours de parcours */ + protected Deque<TopiaEntity> stack = new ArrayDeque<TopiaEntity>(); + + Collector<?> collector; + + protected void setCollector(Collector<?> collector) { + this.collector = collector; + } + + protected Deque<TopiaEntity> getStack() { + return stack; + } + + protected Collection<TopiaEntity> getAlreadyExplored() { + return alreadyExplored; + } + + @Override + public void start(TopiaEntity e) { + // on ajoute l'objet courant dans la pile + stack.offerLast(e); + boolean canContinue = collector.onStarting(e); + if (canContinue) { + super.start(e); + } + collector.onStarted(e, canContinue); + } + + @Override + public void end(TopiaEntity e) { + boolean canContinue = collector.onEnding(e); + if (canContinue) { + super.end(e); + } + stack.removeLast(); + // on retire l'objet courant de la pile + collector.onEnded(e, canContinue); + } + + @Override + public void visit(TopiaEntity e, String name, Class<?> type, + Object value) { + boolean canContinue = collector.onVisiting(e, name, type, value); + if (canContinue) { + super.visit(e, name, type, value); + } + collector.onVisited(e, name, type, value, canContinue); + } + + @Override + public void visit(TopiaEntity e, String name, Class<?> collectionType, + Class<?> type, Object value) { + boolean canContinue = + collector.onVisiting(e, name, collectionType, type, value); + if (canContinue) { + super.visit(e, name, collectionType, type, value); + } + collector.onVisited(e, name, collectionType, type, value, + canContinue); + } + + @Override + public void visit(TopiaEntity e, String name, Class<?> collectionType, + Class<?> type, int index, Object value) { + boolean canContinue = + collector.onVisiting(e, name, collectionType, type, index, + value); + if (canContinue) { + super.visit(e, name, collectionType, type, index, value); + } + collector.onVisited(e, name, collectionType, type, index, value, + canContinue); + } + + @Override + public void clear() { + super.clear(); + stack.clear(); + } + } + + protected int stackSize() { + return visitor.getStack().size(); + } + + protected Deque<TopiaEntity> getStack() { + return visitor.getStack(); + } + + protected Collection<TopiaEntity> getAlreadyExplored() { + return visitor.getAlreadyExplored(); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + clear(); + } + + protected Class<? extends TopiaEntity> getContractClass(TopiaEntity e) { + if (contracts.length == 0) { + return null; + } + Class<? extends TopiaEntity> contractClass = + TopiaEntityHelper.getContractClass(contracts, e.getClass()); + if (contractClass != null) { + return contractClass; + } + return null; + } + + protected TopiaEntity getTopiaValue(Object value) { + return (TopiaEntity) (value != null && + value instanceof TopiaEntity ? value : null); + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/Creator.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/Creator.java new file mode 100644 index 0000000..bc0d61b --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/Creator.java @@ -0,0 +1,56 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence.util; + +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; + +/** + * A simple contract to hook the creation phase of an entity associated (or not!) to + * a parent entity. + * <p/> + * + * @author tchemit <chemit@codelutin.com> + * @param <P> the type of the parent of the entity to create (if entity has + * no parent then used the {@link Void} type). + * @param <E> the type of entity to create + */ +public interface Creator<P, E> { + /** + * Perform the creation of an entity. + * <p/> + * The given <code>from</code> entity should not have already been created in + * database ? it should only be here to prepare the creation of the entity. + * <p/> + * TODO Review this explanation :) + * + * @param tx the current available transaction + * @param parent the parent of the entity + * @param from the entity to create + * @return the really created entity in database + * @throws TopiaException if any db problem. + */ + E create(TopiaContext tx, P parent, E from) throws TopiaException; +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/DBMapping.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/DBMapping.java new file mode 100644 index 0000000..c1aae55 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/DBMapping.java @@ -0,0 +1,451 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.exception.SQLGrammarException; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImpl; +import org.nuiton.topia.persistence.TopiaEntity; +import org.hibernate.SQLQuery; +import org.hibernate.jdbc.Work; + +import java.io.IOException; +import java.io.InputStream; +import java.math.BigInteger; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Properties; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * Une classe qui permet d'obtenir les mapping de noms entre les entités et les objets de la base. + * <p/> + * On retrouve aussi ici des méthodes utils pour executer du code sql sur la base (notamment la gestion des séquences). + * + * @author tchemit <chemit@codelutin.com> + */ +public abstract class DBMapping { + + /** log */ + protected static final Log log = LogFactory.getLog(DBMapping.class); + + protected static final String CLASS_PATTERN = "(.+)\\.class\\.tagvalue\\.dbName"; + + protected static final String DBNAME_ATTRIBUTE_PATTERN = "(.+).attribute.(\\w+)\\.tagvalue\\.dbName"; + + protected static final String SEQUENCE_ATTRIBUTE_PATTERN = "(.+).attribute.(\\w+)\\.tagvalue\\.sequence"; + + protected static final String CREATE_SEQUENCE_FORMAT = "create sequence %1$s%2$s_%3$s_sequence start (select max(%3$s) from %1$s%2$s);"; + + protected static final String UPDATE_SEQUENCE_FORMAT = "alter sequence %1$s%2$s_%3$s_sequence restart with (select max(%3$s) from %1$s%2$s);"; + + protected static final String CURRENT_VALUE_SEQUENCE_FORMAT = "select %1$s%2$s_%3$s_sequence.currval"; + + protected static final String NEXT_VALUE_SEQUENCE_FORMAT = "select %1$s%2$s_%3$s_sequence.nextval"; + + protected static final String SCHEMA_FORMAT = "model.tagvalue.dbSchema"; + + protected static final String DOT = "."; + + protected Map<String, String> mappingBeanToDb; + + protected Map<String, Class<? extends TopiaEntity>> sequences; + + protected String schema; + + protected abstract Class<? extends TopiaEntity> getContractClass(Class<? extends TopiaEntity> entityClass) throws TopiaException; + + public DBMapping(String propertyFile, String path) throws IOException { + mappingBeanToDb = new TreeMap<String, String>(); + sequences = new TreeMap<String, Class<? extends TopiaEntity>>(); + + if (propertyFile == null) { + propertyFile = path; + } + + InputStream stream = getClass().getResourceAsStream(propertyFile); + if (stream == null) { + throw new IllegalStateException("no tagsvalues defined (did not find the resource : " + propertyFile + ")"); + } + + Properties props = new Properties(); + + try { + + props.load(stream); + + initMapping(props); + + } finally { + props.clear(); + stream.close(); + } + } + + public void init(TopiaContext ctxt, boolean doCreate, boolean doUpdate) throws TopiaException { + if (sequences.isEmpty()) { + // no sequence registed + return; + } + String firstSequenceKey = sequences.keySet().iterator().next(); + TopiaContext newContext = ctxt.beginTransaction(); + boolean exists = existSequence(firstSequenceKey, newContext); + + if (!exists) { + if (!doCreate) { + // not exists and do not create + return; + } + createSequences(newContext); + } else { + if (doUpdate) { + updateSequences(newContext); + } + } + newContext.commitTransaction(); + newContext.closeContext(); + } + + public void createSequences(TopiaContext ctxt) throws TopiaException { + if (log.isInfoEnabled()) { + log.info("start create db sequences..."); + } + for (String sequenceKey : sequences.keySet()) { + createSequence(sequenceKey, ctxt, false); + } + } + + public void updateSequences(TopiaContext ctxt) throws TopiaException { + if (log.isInfoEnabled()) { + log.info("start update db sequences..."); + } + for (String sequenceKey : sequences.keySet()) { + updateSequence(sequenceKey, ctxt, false); + } + } + + public boolean existSequence(String sequenceKey, TopiaContext ctxt) throws TopiaException { + return existSequence(sequenceKey, ctxt, true); + } + + public void createSequence(String sequenceKey, TopiaContext ctxt) throws TopiaException { + createSequence(sequenceKey, ctxt, true); + } + + public void updateSequence(String sequenceKey, TopiaContext ctxt) throws TopiaException { + updateSequence(sequenceKey, ctxt, true); + } + + public BigInteger getCurrentValueFromSequence(String sequenceKey, TopiaContext ctxt) throws TopiaException { + return getCurrentValueFromSequence(sequenceKey, ctxt, true); + } + + public BigInteger getNextValueFromSequence(String sequenceKey, TopiaContext ctxt) throws TopiaException { + return getNextValueFromSequence(sequenceKey, ctxt, true); + } + + public boolean existSequence(Class<? extends TopiaEntity> entityClass, String propertyName, TopiaContext ctxt) throws TopiaException { + String sequenceKey = checkSequence(entityClass, propertyName); + return existSequence(sequenceKey, ctxt, false); + } + + public void createSequence(Class<? extends TopiaEntity> entityClass, String propertyName, TopiaContext ctxt) throws TopiaException { + String sequenceKey = checkSequence(entityClass, propertyName); + createSequence(sequenceKey, ctxt, false); + } + + public void updateSequence(Class<? extends TopiaEntity> entityClass, String propertyName, TopiaContext ctxt) throws TopiaException { + String sequenceKey = checkSequence(entityClass, propertyName); + updateSequence(sequenceKey, ctxt, false); + } + + public BigInteger getCurrentValueFromSequence(Class<? extends TopiaEntity> entityClass, String propertyName, TopiaContext ctxt) throws TopiaException { + String sequenceKey = checkSequence(entityClass, propertyName); + return getCurrentValueFromSequence(sequenceKey, ctxt, false); + } + + public BigInteger getNextValueFromSequence(Class<? extends TopiaEntity> entityClass, String propertyName, TopiaContext ctxt) throws TopiaException { + String sequenceKey = checkSequence(entityClass, propertyName); + return getNextValueFromSequence(sequenceKey, ctxt, false); + } + + public Iterator<String> getSequenceKeysIterator() { + return sequences.keySet().iterator(); + } + + public boolean existSequence(String sequenceKey, TopiaContext ctxt, boolean check) throws TopiaException { + if (check) { + checkSequence(sequenceKey); + } + try { + getCurrentValueFromSequence(sequenceKey, ctxt, false); + + } catch (TopiaException e) { + // the sequence's name does not exist in database, so it is a grammer exception + if (e.getCause() != null && e.getCause().getClass() == SQLGrammarException.class) { + return false; + } + throw e; + } + return true; + } + + public void createSequence(String sequenceKey, TopiaContext ctxt, boolean check) throws TopiaException { + if (check) { + checkSequence(sequenceKey); + } + String sql = getSequenceSQL(CREATE_SEQUENCE_FORMAT, sequenceKey); + doSQLWork(ctxt, sql); + BigInteger currentValue = getNextValueFromSequence(sequenceKey, ctxt, false); + + if (log.isDebugEnabled()) { + log.debug(sequenceKey + " currentValue " + currentValue.intValue()); + } + } + + public void updateSequence(String sequenceKey, TopiaContext ctxt, boolean check) throws TopiaException { + if (check) { + checkSequence(sequenceKey); + } + String sql = getSequenceSQL(UPDATE_SEQUENCE_FORMAT, sequenceKey); + doSQLWork(ctxt, sql); + BigInteger currentValue = getNextValueFromSequence(sequenceKey, ctxt, false); + if (log.isDebugEnabled()) { + log.debug(sequenceKey + " currentValue " + currentValue.intValue()); + } + } + + public BigInteger getCurrentValueFromSequence(String sequenceKey, TopiaContext ctxt, boolean check) throws TopiaException { + if (check) { + checkSequence(sequenceKey); + } + String sql = getSequenceSQL(CURRENT_VALUE_SEQUENCE_FORMAT, sequenceKey); + TopiaContext newCtxt = ctxt.beginTransaction(); + BigInteger bigInteger = getBigInteger(newCtxt, sql, BigInteger.ZERO); + newCtxt.closeContext(); + return bigInteger; + } + + public BigInteger getNextValueFromSequence(String sequenceKey, TopiaContext ctxt, boolean check) throws TopiaException { + if (check) { + checkSequence(sequenceKey); + } + String sql = getSequenceSQL(NEXT_VALUE_SEQUENCE_FORMAT, sequenceKey); + return getBigInteger(ctxt, sql, BigInteger.ZERO); + } + + /** + * @param entityClass the seek entity class + * @param property the name of the property to translate + * @return the DB name for the given property + * @throws TopiaException + * if any db pb + */ + protected String getDBProperty(Class<? extends TopiaEntity> entityClass, String property) throws TopiaException { + Class<? extends TopiaEntity> contractClass = getContractClass(entityClass); + String key = contractClass.getName() + DOT + property; + + String colName = mappingBeanToDb.get(key); + if (colName == null) { + colName = property; + } + return colName; + } + + /** + * @param entityClass the seek entity class + * @return the DB name for the given property + * @throws TopiaException + * if any db pb + */ + protected String getDBTable(Class<? extends TopiaEntity> entityClass) throws TopiaException { + Class<? extends TopiaEntity> contractClass = getContractClass(entityClass); + String key = contractClass.getName(); + String colName = mappingBeanToDb.get(key); + + if (colName == null) { + colName = contractClass.getSimpleName().toLowerCase(); + } + return colName; + } + + protected String getSequenceSQL(String pattern, Class<? extends TopiaEntity> entityClass, String propertyName) throws TopiaException { + String dbTable = getDBTable(entityClass); + String dbPropertyName = getDBProperty(entityClass, propertyName); + String sql = String.format(pattern, schema, dbTable, dbPropertyName); + if (log.isTraceEnabled()) { + log.trace("sql : " + sql); + } + return sql; + } + + protected String getSequenceSQL(String pattern, String sequenceKey) throws TopiaException { + Class<? extends TopiaEntity> entityClass = sequences.get(sequenceKey); + String dbTable = getDBTable(entityClass); + String propertyName = getSequencePropertyName(sequenceKey); + String dbPropertyName = getDBProperty(entityClass, propertyName); + String sql = String.format(pattern, schema, dbTable, dbPropertyName); + if (log.isTraceEnabled()) { + log.trace("sql : " + sql); + } + return sql; + } + + protected BigInteger getBigInteger(TopiaContext ctxt, String sql, BigInteger defaultSize) throws TopiaException { + BigInteger size = defaultSize; + if (ctxt != null) { + try { + SQLQuery query = ((TopiaContextImpl) ctxt).getHibernate().createSQLQuery(sql); + size = (BigInteger) query.list().get(0); + } catch (SQLGrammarException e) { + // could not obtain sequence + throw new TopiaException(e); + } + } + return size; + } + + protected void doSQLWork(TopiaContext ctxt, final String sql) throws TopiaException { + if (ctxt != null) { + //ctxt.beginTransaction(); + ((TopiaContextImpl) ctxt).getHibernate().doWork(new Work() { + public void execute(Connection connection) throws SQLException { + if (log.isDebugEnabled()) { + log.debug(sql); + } + Statement stmt = connection.createStatement(); + stmt.execute(sql); + } + }); + //ctxt.commitTransaction(); + } + } + + protected String getSequencePropertyName(String sequenceKey) { + int dotIndex = sequenceKey.lastIndexOf(DOT); + return sequenceKey.substring(dotIndex + 1); + } + + protected String checkSequence(Class<? extends TopiaEntity> entityClass, String propertyName) throws IllegalArgumentException, TopiaException { + Class<? extends TopiaEntity> contractClass = getContractClass(entityClass); + String sequenceKey = contractClass.getName() + DOT + propertyName; + if (!sequences.containsKey(sequenceKey)) { + throw new IllegalArgumentException("could not find the sequence " + sequenceKey); + } + return sequenceKey; + } + + protected String checkSequence(String sequenceKey) throws IllegalArgumentException, TopiaException { + if (!sequences.containsKey(sequenceKey)) { + throw new IllegalArgumentException("could not find the sequence " + sequenceKey); + } + return sequenceKey; + } + + @SuppressWarnings({"unchecked"}) + protected void initMapping(Properties props) throws IOException { + + if (props.containsKey(SCHEMA_FORMAT)) { + schema = props.getProperty(SCHEMA_FORMAT) + DOT; + } else { + schema = ""; + } + + Pattern classPattern = Pattern.compile(CLASS_PATTERN); + + Pattern dbNameAttributePattern = Pattern.compile(DBNAME_ATTRIBUTE_PATTERN); + + Pattern sequenceAttributePattern = Pattern.compile(SEQUENCE_ATTRIBUTE_PATTERN); + + for (Entry<Object, Object> entry : props.entrySet()) { + String key = String.valueOf(entry.getKey()); + String value = String.valueOf(entry.getValue()); + Matcher matcher; + + matcher = dbNameAttributePattern.matcher(key); + if (matcher.matches()) { + // find a attribute property + String clazz = matcher.group(1); + String attribute = matcher.group(2); + mappingBeanToDb.put(clazz + "." + attribute, value); + + continue; + } + matcher = classPattern.matcher(key); + if (matcher.matches()) { + // find a class property + String clazz = matcher.group(1); + mappingBeanToDb.put(clazz, value); + continue; + } + matcher = sequenceAttributePattern.matcher(key); + if (matcher.matches()) { + // find a attribute property + String clazz = matcher.group(1); + String attribute = matcher.group(2); + try { + boolean useSequence = Boolean.valueOf(value); + if (useSequence) { + Class<?> value1 = Class.forName(clazz); + if (TopiaEntity.class.isAssignableFrom(value1)) { + sequences.put(clazz + "." + attribute, (Class<? extends TopiaEntity>) value1); + } else { + log.warn("can not create a sequence on a non TopiaEntity class " + clazz); + } + + } + } catch (Exception e) { + log.warn("could not convert sequence value for entry " + key+" for reason "+e.getMessage()); + } + + } + } + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + close(); + } + + public void close() { + if (mappingBeanToDb != null) { + mappingBeanToDb.clear(); + } + if (sequences != null) { + sequences.clear(); + } + } + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/Deletor.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/Deletor.java new file mode 100644 index 0000000..906f8e6 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/Deletor.java @@ -0,0 +1,47 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence.util; + +import org.nuiton.topia.TopiaContext; + +/** + * A simple contract to hook the deletion of an entity. + * + * @author tchemit <chemit@codelutin.com> + * @param <P> the parent of the entity to delete (if the entity has no parent + * says is not in a association) + * of another entity, just used the {@link Void} type). + * @param <E> the type of the entity to delete. + */ +public interface Deletor<P, E> { + /** + * Hook to delete an entity from a prent entity. + * + * @param tx current transaction + * @param parent the parent of the entity + * @param from the entity to delete. + */ + void delete(TopiaContext tx, P parent, E from); +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/DiffState.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/DiffState.java new file mode 100644 index 0000000..83b5400 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/DiffState.java @@ -0,0 +1,136 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence.util; + +import java.util.ArrayList; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; + +/** + * Pour qualifier l'etat d'une entite lors du calcul d'un differentiel entre + * deux entites. + * + * @author tchemit <chemit@codelutin.com> + * @see TopiaEntityHelper#buildDifferentiel(List, List) + * @since 2.2.0 + */ +public enum DiffState { + + /** + * nouvel entite dans le storage de reference. + * <p/> + * A ajouter dans le storage local. + */ + NEW, + /** + * entite modifie dans le storage de reference (voir topiaVersion). + * <p/> + * A mettre a jour dans le storage local. + */ + MODIFIED, + /** + * entité supprimée dans le storage de reference. + * <p/> + * A supprimer du storage local (apres remplacement par autre chose...) + */ + REMOVED; + + /** + * Construit un dictionnaire avec pour tous les états une liste vide. + * + * @return le dictionnaire crée + */ + public static DiffStateMap newMap() { + DiffStateMap result = + new DiffStateMap(); + for (DiffState state : values()) { + result.put(state, new ArrayList<String>()); + } + return result; + } + + /** + * Ajoute dans le premier dictionnaire, les listes du second dictionnaire. + * + * @param mainMap le dictionnaire principale + * @param toAdd le dictionne a ajouter dans le dictionnaire principale + */ + public static void addAll(DiffStateMap mainMap, + DiffStateMap toAdd) { + for (DiffState state : values()) { + List<String> newList = toAdd.get(state); + if (newList != null && !newList.isEmpty()) { + mainMap.get(state).addAll(newList); + } + } + } + + /** + * Nettoye un dictionnaire donnee de toute ses donnees. + * + * @param mainMap le dictionnaire a nettoyer. + */ + public static void clear(DiffStateMap mainMap) { + for (DiffState state : values()) { + List<String> newList = mainMap.get(state); + if (newList != null) { + newList.clear(); + } + mainMap.remove(state); + } + } + + /** + * Supprime toutes les entrees vides du dictionnaire . + * + * @param mainMap le dictionnaire a nettoyer + */ + public static void removeEmptyStates( + DiffStateMap mainMap) { + for (DiffState state : values()) { + List<String> newList = mainMap.get(state); + if (newList == null || newList.isEmpty()) { + mainMap.remove(state); + } + } + } + + public static class DiffStateMap extends EnumMap<DiffState, List<String>> { + private static final long serialVersionUID = 1L; + + public DiffStateMap() { + super(DiffState.class); + } + + public DiffStateMap(EnumMap<DiffState, ? extends List<String>> m) { + super(m); + } + + public DiffStateMap(Map<DiffState, ? extends List<String>> m) { + super(m); + } + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/EntityListUpdator.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/EntityListUpdator.java new file mode 100644 index 0000000..c2a072f --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/EntityListUpdator.java @@ -0,0 +1,157 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence.util; + +import org.apache.commons.beanutils.PropertyUtilsBean; +import org.apache.commons.lang3.StringUtils; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.persistence.TopiaEntity; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Collection; + +/** + * A implementation of {@link ListUpdator} for {@link TopiaEntity} type. + * <p/> + * Some factory methods are defined to simplify the generic cast, prefer used them + * instead of the (protected) constructor. + * + * @author tchemit <chemit@codelutin.com> + * @param <P> type of parent of childs + * @param <E> type of child + */ +public class EntityListUpdator<P extends TopiaEntity, E extends TopiaEntity> implements ListUpdator<P, E> { + + public static <P extends TopiaEntity, E extends TopiaEntity> EntityListUpdator<P, E> newEntityListUpdator(Class<P> parentClass, Class<E> childClass, String propertyName) { + return new EntityListUpdator<P, E>(parentClass, childClass, propertyName); + } + + /** name of the field containing the childs */ + protected String propertyName; + + /** descriptor of the filed containing the childs */ + protected PropertyDescriptor descriptor; + + protected Method getMethod; + + protected Method addMethod; + + protected Method removeMethod; + + protected Method removeAllMethod; + + protected Method sizeMethod; + + protected Method emptyMethod; + + protected EntityListUpdator(Class<P> parentClass, Class<E> childClass, String propertyName) { + this.propertyName = propertyName; + + for (PropertyDescriptor propertyDescriptor : new PropertyUtilsBean().getPropertyDescriptors(parentClass)) { + if (propertyDescriptor.getName().equals(propertyName)) { + descriptor = propertyDescriptor; + } + } + String cap = StringUtils.capitalize(propertyName); + try { + getMethod = parentClass.getMethod("get" + cap + "ByTopiaId", String.class); + addMethod = parentClass.getMethod("add" + cap, childClass); + removeMethod = parentClass.getMethod("remove" + cap, childClass); + removeAllMethod = parentClass.getMethod("clear" + cap); + sizeMethod = parentClass.getMethod("size" + cap); + emptyMethod = parentClass.getMethod("is" + cap + "Empty"); + } catch (NoSuchMethodException e) { + throw new RuntimeException(e); + } + } + + @Override + public String getPropertyName() { + return propertyName; + } + + @Override + public E getChild(P parent, String topiaId) { + return EntityListUpdator.<E>invokeWithResult(getMethod, parent, topiaId); + } + + @Override + public Collection<E> getChilds(P parent) { + return EntityListUpdator.<Collection<E>>invokeWithResult(descriptor.getReadMethod(), parent); + } + + @Override + public int size(P parent) { + return EntityListUpdator.<Integer>invokeWithResult(sizeMethod, parent); + } + + @Override + public boolean isEmpty(P parent) { + return EntityListUpdator.<Boolean>invokeWithResult(emptyMethod, parent); + } + + @Override + public void setChilds(P parent, Collection<E> childs) { + invoke(descriptor.getWriteMethod(), parent, childs); + } + + @Override + public void addToList(P parent, E bean) throws TopiaException { + invoke(addMethod, parent, bean); + } + + @Override + public void removeFromList(P parent, E bean) throws TopiaException { + invoke(removeMethod, parent, bean); + } + + @Override + public void removeAll(P parent) { + invoke(removeAllMethod, parent); + } + + protected static void invoke(Method m, Object bean, Object... args) { + try { + m.invoke(bean, args); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings({"unchecked"}) + protected static <V> V invokeWithResult(Method m, Object bean, Object... args) { + try { + return (V) m.invoke(bean, args); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/EntityOperator.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/EntityOperator.java new file mode 100644 index 0000000..2f3b7cf --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/EntityOperator.java @@ -0,0 +1,797 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence.util; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.util.ObjectUtil; + +import java.beans.BeanInfo; +import java.beans.IntrospectionException; +import java.beans.Introspector; +import java.beans.PropertyDescriptor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +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.List; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; + +/** + * Un objet qui permet d'effecuter des operations de manipulation des donnees + * dans les entites du type donne. + * <p/> + * L'objet connait la liste des proprietes et des associations du type donne et + * permet de modifier ces valeurs : + * <p/> + * {@link #get(String, TopiaEntity)} + * {@link #set(String, TopiaEntity, Object)} + * {@link #copy(String, TopiaEntity, TopiaEntity)} + * {@link #getChild(String, TopiaEntity, String)} + * {@link #addChild(String, TopiaEntity, Object)} + * {@link #removeChild(String, TopiaEntity, Object)} + * ... + * <p/> + * D'autres methodes permettent d'effectuer des operations en lot (sur plusieurs + * proprietes en meme temps) sur les proprietes : + * <p/> + * {@link #copyProperties(TopiaEntity, TopiaEntity, boolean, String...)} + * {@link #obtainProperties(TopiaEntity, String...)} + * {@link #clearProperties(TopiaEntity, String...)} + * <p/> + * Note : cet objet ne permet pas d'operation vers les bases. + * + * @param <B> type de l'entite + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + */ +public class EntityOperator<B extends TopiaEntity> { + + /** Logger */ + private static Log log = LogFactory.getLog(EntityOperator.class); + + /** the constant of the entity */ + protected final TopiaEntityEnum contract; + + /** list of property names available on the entity. */ + protected List<String> properties; + + /** + * list of property names available on the entity used in a natural ids or + * marked as not-null. + * + * @since 2.6.9 + */ + protected Set<String> naturalIdsOnNotNullsProperties; + + /** list of association names available on the entity. */ + protected List<String> associationProperties; + + /** cache of getter methods. */ + protected Method[] getMethods; + + /** cache of setter methods. */ + protected Method[] setMethods; + + /** cache of assocation {@code get} methods. */ + protected Method[] childGetMethods; + + /** cache of assocation {@code add} methods. */ + protected Method[] childAddMethods; + + /** cache of assocation {@code addAll} methods. */ + protected Method[] childAddAllMethods; + + /** cache of assocation {@code remove} methods. */ + protected Method[] childRemoveMethods; + + /** cache of assocation {@code size} methods. */ + protected Method[] childSizeMethods; + + /** cache of assocation {@code isEmpty} methods. */ + protected Method[] childIsEmptyMethods; + + /** cache of assocation {@code clear}methods. */ + protected Method[] childClearMethods; + + protected EntityOperator(TopiaEntityEnum contract) { + if (contract == null) { + throw new NullPointerException( + "'contract' parameter can not be null!"); + } + this.contract = contract; + init(); + } + + /** + * Recupere la valeur de la propriete donnee. + * <p/> + * Note : cela apellera la methode <code>getXXX()</code>. + * + * @param name le nom de la propriete + * @param bean l'instance a interroger + * @return la valeur de la propriete + */ + public Object get(String name, B bean) { + int index = checkPropertyIndex(name); + Object result = invokeWithResult(getMethods[index], bean); + return result; + } + + /** + * Positionner la valeur donne de la propriete donnee. + * <p/> + * Note : cela apellera la methode <code>setXXX(value)</code>. + * + * @param name le nom de la propriete + * @param bean l'instance a mettre a jour + * @param value la valeur a positionner + */ + public void set(String name, B bean, Object value) { + int index = checkPropertyIndex(name); + invoke(setMethods[index], bean, value); + } + + /** + * Pour obtenir un dictionnaire de la clef naturelle (clef métier) du {@code bean} + * donne. + * + * @param bean le bean a inspecter + * @return le dictionnaire de la clef naturel du bean + * @see TopiaEntityEnum#getNaturalIds() + * @since 2.4.1 + */ + public Map<String, Object> getNaturalId(B bean) { + Map<String, Object> result = new TreeMap<String, Object>(); + String[] ids = contract.getNaturalIds(); + if (ids != null) { + for (String id : ids) { + result.put(id, get(id, bean)); + } + } + return result; + } + + /** + * Pour obtenir un dictionnaire des propriétés marqués not-null du + * {@code bean} donne. + * + * @param bean le bean a inspecter + * @return le dictionnaire des propriétés marquées not-null du bean + * @see TopiaEntityEnum#getNotNulls() + * @since 2.6.9 + */ + public Map<String, Object> getNotNull(B bean) { + Map<String, Object> result = new TreeMap<String, Object>(); + String[] ids = contract.getNotNulls(); + if (ids != null) { + for (String id : ids) { + result.put(id, get(id, bean)); + } + } + return result; + } + + /** + * Get all properties from a natural id or marked as not-null. + * + * @return all property names froma natural id or marked as not-null + * @since 2.6.9 + */ + public String[] getNaturalIdsOnNotNullsProperties() { + return naturalIdsOnNotNullsProperties.toArray(new String[naturalIdsOnNotNullsProperties.size()]); + } + + /** + * Pour obtenir un dictionnaire des propriétés marqués not-null et la clef naturelle + * du {@code bean} donne. + * <p/> + * Cette methode est utilisée pour faire un dao.create, pour s'assurer que + * tout ce qui ne doit pas pas être à null est bien fourni à la création de + * l'objet, sinon on obtient des erreurs. + * + * @param bean le bean a inspecter + * @return le dictionnaire des propriétés marquées not-null + la clef + * naturelle du bean + * @see TopiaEntityEnum#getNotNulls() + * @see TopiaEntityEnum#getNaturalIds() + * @since 2.6.9 + */ + public Map<String, Object> getNaturalIsdAndNotNulls(B bean) { + Map<String, Object> result = new TreeMap<String, Object>(); + for (String id : naturalIdsOnNotNullsProperties) { + result.put(id, get(id, bean)); + } + return result; + } + + /** + * Copie une propriete de src vers dst. + * <p/> + * Note : cela apellera la methode <code>setXXX(value)</code>. + * + * @param name le nom de la propriete + * @param from l'instance a interroger + * @param dst l'instance a mettre a jour + */ + public void copy(String name, B from, B dst) { + int index = checkPropertyIndex(name); + Object value = invokeWithResult(getMethods[index], from); + invoke(setMethods[index], dst, value); + } + + /** + * Positionner la valeur nulle de la propriete donnee. + * <p/> + * Note : cela apellera la methode <code>setXXX(nullValue)</code>. + * + * @param name le nom de la propriete + * @param bean l'instance a mettre a jour + */ + public void setNull(String name, B bean) { + int index = checkPropertyIndex(name); + Class<?> propertyType = getPropertyType(index); + Method method = setMethods[index]; + Object nullValue = ObjectUtil.getNullValue(propertyType); + invoke(method, bean, nullValue); + } + + /** + * Recupere une entite d'association a partir de son id. + * <p/> + * Note : cela apellera la methode <code>getXXXByTopiaId(topiaId)</code>. + * + * @param name le nom de la propriete d'association + * @param bean l'instance a interroger + * @param topiaId l'id de l'entite recherchee + * @return l'entite + */ + public Object getChild(String name, B bean, String topiaId) { + int index = checkAssociationPropertyIndex(name); + Object result = invokeWithResult(childGetMethods[index], bean, topiaId); + return result; + } + + /** + * Test s'il existe des entites d'association pour la propriete donnee. + * <p/> + * Note : cela apellera la methode <code>isXXXEmpty()</code>. + * + * @param name le nom de la propriete d'association + * @param bean l'instance a interroger + * @return {@code true} si pas d'entite d'association + */ + public boolean isChildEmpty(String name, B bean) { + int index = checkAssociationPropertyIndex(name); + Boolean result = invokeWithResult(childIsEmptyMethods[index], bean); + return result; + } + + /** + * Ajoute une entite d'association. + * <p/> + * Note : cela apellera la methode <code>addXXX(child)</code>. + * + * @param name le nom de la propriete d'association + * @param bean l'instance a mettre a jour + * @param child l'entite a ajouter + */ + public void addChild(String name, B bean, Object child) { + int index = checkAssociationPropertyIndex(name); + invoke(childAddMethods[index], bean, child); + } + + /** + * Ajoute toutes les entites d'association. + * <p/> + * Note : cela apellera la methode <code>addXXX(child)</code>. + * + * @param name le nom de la propriete d'association + * @param bean l'instance a mettre a jour + * @param childs les entites a ajouter + */ + public void addAllChild(String name, B bean, Collection<?> childs) { + int index = checkAssociationPropertyIndex(name); + invoke(childAddAllMethods[index], bean, childs); + } + + /** + * Retire une entite d'association. + * <p/> + * Note : cela apellera la methode <code>removeXXX(child)</code>. + * + * @param name le nom de la propriete d'association + * @param bean l'instance a mettre a jour + * @param child l'entite a retirer + */ + public void removeChild(String name, B bean, Object child) { + int index = checkAssociationPropertyIndex(name); + invoke(childRemoveMethods[index], bean, child); + } + + /** + * Retourne le nombre d'entite d'association. + * <p/> + * Note : cela apellera la methode <code>sizeXXX(childs)</code>. + * + * @param name le nom de la propriete d'association + * @param bean l'instance a mettre a jour + * @return le nombre d'entite d'associaotion + */ + public int sizeChild(String name, B bean) { + int index = checkAssociationPropertyIndex(name); + Integer result = invokeWithResult(childSizeMethods[index], bean); + return result; + } + + /** + * Retire toutes les entites d'association. + * <p/> + * Note : cela apellera la methode <code>clearXXX(childs)</code>. + * + * @param name le nom de la propriete d'association + * @param bean l'instance a mettre a jour + */ + public void clearChild(String name, B bean) { + int index = checkAssociationPropertyIndex(name); + invoke(childClearMethods[index], bean); + } + + /** + * Recopie toutes les proprietes donnes depuis src vers dst. + * <p/> + * Note : si aucune propriete n'est donnee, on utilise toutes les proprietes + * connues par l'operateur. + * + * @param from l'entite a interroger + * @param dst l'entite a mettre a jour + * @param tech un drapeau pour recopier aussi les infos techniques + * @param properties les proprietes a recopier + */ + public void copyProperties(B from, B dst, boolean tech, + String... properties) { +// if (from == null) { +// +// from = newInstance(); +// } + if (tech) { + TopiaEntityHelper.bindTechnical(from, dst); + } + if (from == null) { + // reset all fields + clearProperties(dst, properties); + } else { + // copyProperties all fields + Collection<String> names = getProperties(properties); + + for (String name : names) { + copy(name, from, dst); + } + } + } + + /** + * Obtenir dans un dictionnaire, les valeurs des proprietes donnees. + * <p/> + * Si aucune proropiete n'est donne, alors on utilise toutes les proprietes + * connu par l'operateur. + * + * @param from l'object a scanne + * @param properties les proprietes a retenir (vide si on les veut toutes) + * @return le dictionnaire des valeurs des proprietes + */ + public Map<String, Object> obtainProperties(B from, String... properties) { + if (from == null) { + return Collections.emptyMap(); + } + Collection<String> names = getProperties(properties); + + Map<String, Object> result = new HashMap<String, Object>(); + + for (String name : names) { + Object read = get(name, from); + if (read != null) { + result.put(name, read); + } + } + return result; + } + + /** + * Met a null toutes les proprietes donnees. + * <p/> + * Si aucune proropiete n'est donnee, alors on utilise toutes les proprietes + * connu par l'operateur. + * + * @param from l'object a scanne + * @param properties les proprietes a retenir (vide si on les veut toutes) + */ + public void clearProperties(B from, String... properties) { + if (from == null) { + return; + } + Collection<String> names = getProperties(properties); + + for (String name : names) { + setNull(name, from); + } + } + + public List<String> getProperties() { + return properties; + } + + public List<String> getAssociationProperties() { + return associationProperties; + } + + public Class<?> getPropertyType(String name) { + int index = checkPropertyIndex(name); + return getPropertyType(index); + } + + public Class<?> getAssociationPropertyType(String name) { + int index = checkAssociationPropertyIndex(name); + return getAssociationPropertyType(index); + } + + @SuppressWarnings("unchecked") + public Class<B> getClazz() { + return (Class<B>) contract.getContract(); + } + + @Override + public boolean equals(Object obj) { + return obj != null && + obj instanceof EntityOperator<?> && + contract == ((EntityOperator<?>) obj).contract; + } + + @Override + public int hashCode() { + return contract.hashCode(); + } + + @Override + public Object clone() { + try { + return super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException("could not clone " + this + + " for reason : " + e.getMessage(), e); + } + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + properties = null; + naturalIdsOnNotNullsProperties = null; + associationProperties = null; + getMethods = null; + setMethods = null; + childGetMethods = null; + childAddMethods = null; + childAddAllMethods = null; + childRemoveMethods = null; + childSizeMethods = null; + childClearMethods = null; + childIsEmptyMethods = null; + } + + @Override + public String toString() { + return super.toString() + '<' + getClazz() + '>'; + } + + protected Collection<String> getProperties(String[] properties) { + Collection<String> names; + if (properties.length == 0) { + names = this.properties; + } else { + names = Arrays.asList(properties); + } + return names; + } + + protected Class<?> getPropertyType(int index) { + Method method = getMethods[index]; + return method.getReturnType(); + } + + protected Class<?> getAssociationPropertyType(int index) { + Method method = childGetMethods[index]; + return method.getReturnType(); + } + + @SuppressWarnings("unchecked") + protected B newInstance() { + try { + return (B) contract.getImplementation().newInstance(); + } catch (InstantiationException ex) { + throw new RuntimeException(ex); + } catch (IllegalAccessException ex) { + throw new RuntimeException(ex); + } + } + + protected void init() { + List<Method> getters = new ArrayList<Method>(); + List<Method> setters = new ArrayList<Method>(); + List<Method> childGetters = new ArrayList<Method>(); + List<Method> childAdders = new ArrayList<Method>(); + List<Method> childAddersAll = new ArrayList<Method>(); + List<Method> childRemovers = new ArrayList<Method>(); + List<Method> childSize = new ArrayList<Method>(); + List<Method> childIsEmpty = new ArrayList<Method>(); + List<Method> childClearers = new ArrayList<Method>(); + Set<Class<?>> explored = new HashSet<Class<?>>(); + properties = new ArrayList<String>(); + associationProperties = new ArrayList<String>(); + naturalIdsOnNotNullsProperties = new HashSet<String>(); + try { + + + if (log.isDebugEnabled()) { + log.debug("===== start for " + getClazz()); + } + + init(getClazz(), explored, properties, associationProperties, + getters, setters, childGetters, childAdders, + childAddersAll, childRemovers, childSize, + childClearers, childIsEmpty); + + if (!properties.isEmpty()) { + int size = properties.size(); + + getMethods = getters.toArray(new Method[size]); + setMethods = setters.toArray(new Method[size]); + properties = Collections.unmodifiableList(properties); + } + + if (!associationProperties.isEmpty()) { + int size = associationProperties.size(); + + childGetMethods = childGetters.toArray(new Method[size]); + childAddMethods = childAdders.toArray(new Method[size]); + childAddAllMethods = childAddersAll.toArray(new Method[size]); + childRemoveMethods = childRemovers.toArray(new Method[size]); + childSizeMethods = childSize.toArray(new Method[size]); + childIsEmptyMethods = childIsEmpty.toArray(new Method[size]); + childClearMethods = childClearers.toArray(new Method[size]); + associationProperties = Collections.unmodifiableList( + associationProperties); + } + + if (contract.isUseNaturalIds()) { + Collections.addAll(naturalIdsOnNotNullsProperties, + contract.getNaturalIds()); + } + if (contract.isUseNotNulls()) { + Collections.addAll(naturalIdsOnNotNullsProperties, + contract.getNotNulls()); + } + if (log.isDebugEnabled()) { + log.debug("===== end for " + getClazz() + " (" + + properties.size() + " properties, " + + associationProperties.size() + " associations)"); + } + } catch (IntrospectionException e) { + throw new RuntimeException(e); + } finally { + getters.clear(); + setters.clear(); + childGetters.clear(); + childAdders.clear(); + childAddersAll.clear(); + childRemovers.clear(); + childSize.clear(); + childClearers.clear(); + childIsEmpty.clear(); + explored.clear(); + } + } + + private Method getMethod(String name, int nbParams, + Method[] methodDescriptors) { + for (Method m : methodDescriptors) { + if (name.equals(m.getName()) && + m.getParameterTypes().length == nbParams) { + return m; + } + } + return null; + } + + private int checkPropertyIndex(String name) { + int index = properties.indexOf(name); + if (index == -1) { + throw new IllegalArgumentException("property " + name + + " is unknown for " + this); + } + return index; + } + + private int checkAssociationPropertyIndex(String name) { + int index = associationProperties.indexOf(name); + if (index == -1) { + throw new IllegalArgumentException("association property " + + name + " is unknown for " + this); + } + return index; + } + + protected static void invoke(Method m, Object bean, Object... args) { + try { + if (log.isTraceEnabled()) { + log.trace(bean.getClass() + "#" + m.getName()); + } + m.invoke(bean, args); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + @SuppressWarnings({"unchecked"}) + protected static <V> V invokeWithResult(Method m, Object bean, + Object... args) { + try { + if (log.isTraceEnabled()) { + log.trace(bean.getClass() + "#" + m.getName()); + } + return (V) m.invoke(bean, args); + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + protected void init(Class<?> entityClass, + Set<Class<?>> explored, + List<String> properties, + List<String> associationProperties, + List<Method> getters, + List<Method> setters, + List<Method> childGetters, + List<Method> childAdders, + List<Method> childAddersAll, + List<Method> childRemovers, + List<Method> childSize, + List<Method> childClearers, + List<Method> childIsEmpty) throws IntrospectionException { + + if (entityClass == null) { + throw new NullPointerException("entityClass can not be null!"); + } + if (!entityClass.isInterface()) { + throw new IllegalArgumentException("entityClass parameter " + + entityClass + " is not interface!"); + } + if (explored.contains(entityClass)) { + return; + } + + explored.add(entityClass); + + if (log.isDebugEnabled()) { + log.debug("enter " + entityClass); + } + + BeanInfo beanInfo = Introspector.getBeanInfo(entityClass); + PropertyDescriptor[] propertyDescriptors = + beanInfo.getPropertyDescriptors(); + + Method[] methods = entityClass.getMethods(); + + for (PropertyDescriptor propertydescriptor : propertyDescriptors) { + String propertyName = propertydescriptor.getName(); + if (propertydescriptor.getReadMethod() != null && + propertydescriptor.getWriteMethod() != null) { + // on a detecte une propriete (lecture/ecriture) + if (log.isDebugEnabled()) { + log.debug("detected property : " + propertyName); + } + properties.add(propertyName); + getters.add(propertydescriptor.getReadMethod()); + setters.add(propertydescriptor.getWriteMethod()); + } + + Class<?> propertyType = propertydescriptor.getPropertyType(); + String cap = StringUtils.capitalize(propertyName); + if (Collection.class.isAssignableFrom(propertyType)) { + // on ne connait pas le type de l'association, on fait donc + // une approximation (nom + nombre de params) + // on pourrait tester que les parametre (ou le type de retour) + // est un TopiaEntity TODO + Method getMethod = getMethod("get" + cap + "ByTopiaId", 1, + methods); + Method addMethod = getMethod("add" + cap, 1, methods); + Method addAllMethod = getMethod("addAll" + cap, 1, methods); + Method removeMethod = getMethod("remove" + cap, 1, methods); + Method sizeMethod = getMethod("size" + cap, 0, methods); + Method clearMethod = getMethod("clear" + cap, 0, methods); + Method isEmptyMethod = getMethod("is" + cap + "Empty", 0, + methods); + if (addMethod != null && + addAllMethod != null && + removeMethod != null && + sizeMethod != null && + getMethod != null && + clearMethod != null && + isEmptyMethod != null) { + // on a detecte une association + if (log.isDebugEnabled()) { + log.debug("detected association : " + propertyName); + } + childGetters.add(getMethod); + childAdders.add(addMethod); + childAddersAll.add(addAllMethod); + childRemovers.add(removeMethod); + childSize.add(sizeMethod); + childClearers.add(clearMethod); + childIsEmpty.add(isEmptyMethod); + associationProperties.add(propertyName); + } + } + } + Class<?>[] interfaces = entityClass.getInterfaces(); + if (interfaces.length > 0) { + for (Class<?> c : interfaces) { + if (TopiaEntity.class.equals(c) || + !TopiaEntity.class.isAssignableFrom(c) || + explored.contains(c)) { + // on ne traite pas + continue; + } + init(c, + explored, + properties, + associationProperties, + getters, + setters, + childGetters, + childAdders, + childAddersAll, + childRemovers, + childSize, + childClearers, + childIsEmpty); + } + } + + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/EntityOperatorStore.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/EntityOperatorStore.java new file mode 100644 index 0000000..d3df855 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/EntityOperatorStore.java @@ -0,0 +1,90 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence.util; + +import java.util.HashMap; +import java.util.Map; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; + +/** + * Un cache d'operateurs. + * + * @author tchemit <chemit@codelutin.com> + * @see EntityOperator + */ +public class EntityOperatorStore { + + /** + * l'instance partagee + */ + private static final EntityOperatorStore shared = new EntityOperatorStore(); + /** + * le cache d'operateurs + */ + protected final Map<TopiaEntityEnum, EntityOperator<?>> store; + + /** + * + * @param <E> le type de l'entite + * @param contract le contrat de l'entite + * @return l'operator associe au contrat + * @see EntityOperator + */ + @SuppressWarnings("unchecked") + public static <E extends TopiaEntity> EntityOperator<E> getOperator( + TopiaEntityEnum contract) { + + EntityOperator<E> operator = + (EntityOperator<E>) shared.store.get(contract); + if (operator == null) { +// synchronized (shared.store) { + operator = new EntityOperator<E>(contract); + shared.store.put(contract, operator); +// } + } + return operator; + //TODO on devrait peut-etre toujours retourner un clone ? + //EntityOperator<E> clone = (EntityOperator<E>) operator.clone(); + //return clone; + } + + public static void clear() { +// synchronized (shared.store) { + shared.store.clear(); +// } + } + + protected EntityOperatorStore() { + store = new HashMap<TopiaEntityEnum, EntityOperator<?>>(); + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + store.clear(); + } + +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/ListUpdator.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/ListUpdator.java new file mode 100644 index 0000000..d6eba7f --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/ListUpdator.java @@ -0,0 +1,116 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence.util; + +import org.nuiton.topia.TopiaException; + +import java.util.Collection; + +/** + * A simple contract to allow you to update some collections of an entity. + * <p/> + * The purpose of the contract is to make possible (via a StorageService for example) some + * automatic and generic behaviour when you want to set a collection of childs into a entity. + * + * @author tchemit <chemit@codelutin.com> + * @param <P> the type of the entity which contains the collection to update. + * @param <E> the type of entities in the collection. + */ +public interface ListUpdator<P, E> { + + /** + * Obtain the name of the property containing childs. + * + * @return the name of the property containing childs. + */ + String getPropertyName(); + + /** + * Obtain a child from the entity given his id. + * + * @param parent the entity to query + * @param topiaId the id of the researched child entity. + * @return the child entity or <code>null</code>, if not found. + */ + + E getChild(P parent, String topiaId); + + /** + * Obtain the collection of childs from the entity. + * + * @param parent the entity to query. + * @return the collection of childs + */ + Collection<E> getChilds(P parent); + + /** + * Obtain the number of childs for the given parent. + * + * @param parent the entity to query + * @return the number of childs for the given entity + */ + int size(P parent); + + /** + * Tests if the given entity has some childs. + * + * @param parent the entity to query + * @return {@code true} is the given parent has no child. + */ + boolean isEmpty(P parent); + + /** + * Set the childs of an entity + * + * @param parent the entity to be setted + * @param childs the collection of childs to set. + */ + void setChilds(P parent, Collection<E> childs); + + /** + * Add a erntity to his parent + * + * @param parent the entity to modifiy + * @param e the entity to add in parent. + * @throws TopiaException if any db problem while operation + */ + void addToList(P parent, E e) throws TopiaException; + + /** + * Remove from a prent entity a given child. + * + * @param parent the entity to modifiy + * @param e the child to remove. + * @throws TopiaException if any pb while operation. + */ + void removeFromList(P parent, E e) throws TopiaException; + + /** + * Remove all childs of the given parent. + * + * @param parent the entity to modify + */ + void removeAll(P parent); +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/Loador.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/Loador.java new file mode 100644 index 0000000..d1fb2de --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/Loador.java @@ -0,0 +1,73 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence.util; + +import java.io.Serializable; +import java.util.Map; + +/** + * A simple contract to load an object from another one. + * + * @author tchemit <chemit@codelutin.com> + * @param <E> type of bean to load + */ +public interface Loador<E> extends Serializable { + + /** + * Obtains the type of the entity. + * + * @return the type of entity + */ + Class<E> getSourceType(); + + /** + * Obtain from an entity all data to bind to another one according the + * definition of the loador. + * <p/> + * This method is usefull when you can not directly used the + * {@link #load(Object, Object, boolean, String...)} method. + * <p/> + * For example, when an entity has a immutable business key (says with an + * hibernate naturalId for example), + * and that you want to create the data in a db, you must give all the + * properties at the create time so this method allow you to do it). + * + * @param from the entity to bind + * @param propertyNames subset of properties to load + * @return the map of properties to bind from the given entity. + */ + Map<String, Object> obtainProperties(E from, String... propertyNames); + + /** + * Bind an entity to another. + * + * @param from the source entity + * @param dst the destination entity + * @param tech a flag to bind or not the technical values of the entity + * (says TopiaId, TopiaVersion and TopiaCreateDate). + * @param propertyNames subset of properties to load + */ + void load(E from, E dst, boolean tech, String... propertyNames); +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/TopiaEntityBinder.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/TopiaEntityBinder.java new file mode 100644 index 0000000..7b14f38 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/TopiaEntityBinder.java @@ -0,0 +1,83 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence.util; + +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.util.beans.Binder; + +/** + * A {@link Binder} dedicated to {@link TopiaEntity} dealing with technical + * values. + * <p/> + * There is an internal sate {@link #empty} to deal with default values of + * properties of an entity. + * <p/> + * <b>Note:</b> Should remove {@link Loador} contract soon. + * + * @author tchemit < chemit@codelutin.com > + * @param <E> the type of entity + * @since 2.3.0 + */ +public class TopiaEntityBinder<E extends TopiaEntity> extends Binder<E, E> + implements Loador<E> { + private static final long serialVersionUID = 1L; + + protected E empty; + + public void setEmpty(E empty) { + this.empty = empty; + } + + @Override + public void load(E source, E target, boolean tech, + String... propertyNames) { + if (source == null) { + source = empty; + } + if (tech) { + TopiaEntityHelper.bindTechnical(source, target); + } + copy(source, target, propertyNames); + } + + + @Override + protected Object bind(Binder binder, Object read) throws IllegalAccessException, InstantiationException { + + Object result = read.getClass().newInstance(); + + if (binder instanceof Loador) { + + // load entity + ((Loador) binder).load(read, result, true); + } else { + + // simple copy + binder.copy(read, result); + } + + return result; + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/TopiaEntityHelper.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/TopiaEntityHelper.java new file mode 100644 index 0000000..ba93258 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/TopiaEntityHelper.java @@ -0,0 +1,1210 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaDAO; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.util.FileUtil; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.net.URI; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.Deque; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.TreeMap; + +import static org.nuiton.i18n.I18n.t; + +/** + * Une classe avec des méthodes utiles sur les entités. + * + * @author tchemit <chemit@codelutin.com> + */ +public class TopiaEntityHelper { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + private static final Log log = LogFactory.getLog(TopiaEntityHelper.class); + + /** Le pattern d'une reference sur une association */ + public static final String ASSOCIATION_PATTERN = + "%1$s[@" + TopiaEntity.TOPIA_ID + "=\"%2$s\"]"; + + /** + * Bind les valeurs techniques depuis une entitée vers une autre. + * + * @param from l'entité source + * @param dst l'entité destination + */ + public static void bindTechnical(TopiaEntity from, TopiaEntity dst) { + if (from == null) { + dst.setTopiaId(null); + dst.setTopiaVersion(0); + dst.setTopiaCreateDate(null); + } else { + dst.setTopiaId(from.getTopiaId()); + dst.setTopiaVersion(from.getTopiaVersion()); + dst.setTopiaCreateDate(from.getTopiaCreateDate()); + } + } + + /** + * Récupère une entité qui doit exister à partir de son id. + * <p/> + * Si l'entité n'existe pas, on déclanche une exception {@link + * IllegalArgumentException}. + * + * @param dao la dao pour récupérer la valeur + * @param topiaId l'id de l'entité recherchée + * @param <E> le type de l'entité + * @return l'entité recherché + * @throws TopiaException pour tout pb lors de la récupération de + * l'entité + * @throws IllegalArgumentException si l'entité n'existe pas. + */ + public static <E extends TopiaEntity> E getExistingEntity( + TopiaDAO<E> dao, String topiaId) throws TopiaException, + IllegalArgumentException { + E entity = dao.findByTopiaId(topiaId); + if (entity == null) { + throw new IllegalArgumentException( + "could not find entity with topiaId " + topiaId); + } + return entity; + } + + /** + * Récupère une entité dans une liste d'entités à partir de son {@link + * TopiaEntity#getTopiaId()}. + * + * @param entities la liste des entités à scanner + * @param topiaId l'id de l'entité recherchée + * @param <E> le type de l'entité + * @return l'entité trouvée, ou <code>null</code> si elle n'est pas + * trouvée. + */ + public static <E extends TopiaEntity> E getEntityByTopiaId( + Collection<E> entities, String topiaId) { + if (entities != null) { + for (E e : entities) { + if (topiaId.equals(e.getTopiaId())) { + return e; + } + } + } + return null; + } + + /** + * Teste si une entité possède un topiaId. + * + * @param paramName le nom du paramètre à afficher en casd'erreur + * @param bean l'entité à tester + * @param <E> le type de l'entité + * @throws IllegalStateException si l'entité n'a pas de topiaId + * @throws NullPointerException si l'entité est null + */ + public static <E extends TopiaEntity> void checkNotNullAndExistingEntity( + String paramName, E bean) throws IllegalStateException, + NullPointerException { + if (bean == null) { + throw new NullPointerException(paramName + " can not be null"); + } + if (bean.getTopiaId() == null) { + // can not create bean here + throw new IllegalStateException( + "can not create " + bean.getClass() + " here..."); + } + } + + /** + * Teste si une entité ne possède pas un topiaId. + * + * @param paramName le nom de paramètre à afficher en cas d'erreur + * @param bean l'entité à tester + * @param <E> le type del'entité + * @throws NullPointerException si l'entité est nulle + * @throws IllegalStateException si l'entité possède un topiaId. + */ + public static <E extends TopiaEntity> void checkNotNullAndNoneExistingEntity( + String paramName, E bean) throws NullPointerException, + IllegalStateException { + if (bean == null) { + throw new NullPointerException(paramName + " can not be null"); + } + if (bean.getTopiaId() != null) { + // can not create bean here + throw new IllegalStateException( + "can not update " + bean.getClass() + " here..."); + } + } + + /** + * Create a new database from a sql dump locating in a gzip file. + * + * @param dbDirectory the directory where to create the db + * @param topiaContext the topiaContext to use to create the databse + * @param resource the url of the sql dump gzip file to use + * @throws TopiaException if any pb while creating db + * @throws IOException if any io exception + * @throws NullPointerException if parameters are null + */ + public static void createDBFromSQL( + File dbDirectory, + TopiaContext topiaContext, + URI resource) throws IOException, + TopiaException, NullPointerException { + if (dbDirectory == null) { + throw new NullPointerException("dbDirectory can not be null"); + } + if (topiaContext == null) { + throw new NullPointerException("topiaContext can not be null"); + } + if (resource == null) { + throw new NullPointerException("resource can not be null"); + } + + File databaseDump; + if (resource.isOpaque()) { + + ByteArrayOutputStream output = + FileUtil.readBytesFrom(resource.toURL().openStream(), 8048); + File tempFile = File.createTempFile("topiaDumpDatabase", ".sql.gz"); + + FileOutputStream stream = new FileOutputStream(tempFile); + try { + output.writeTo(stream); + } finally { + stream.close(); + } + + tempFile.deleteOnExit(); + databaseDump = tempFile; + + } else { + + databaseDump = new File(resource); + } + + if (!dbDirectory.exists()) { + if (!dbDirectory.mkdirs()) { + throw new IOException( + "could not create directory " + dbDirectory); + } + } + + log.info("create database from [" + databaseDump + "] at [" + + dbDirectory + "]"); + + TopiaContext ctxt = topiaContext.beginTransaction(); + + try { + ctxt.restore(databaseDump); + ctxt.commitTransaction(); + } catch (TopiaException e) { + ctxt.rollbackTransaction(); + throw e; + } finally { + ctxt.closeContext(); + } + } + + /** + * Save the given database to a gzip file. + * + * @param gzipFile the file where to store db + * @param topiaContext the topiaContext of the db to store + * @throws TopiaException if any pb while saving db + * @throws NullPointerException if parameters are null + * @throws IOException if could not create gzipFile container + * directory + */ + public static void saveDB( + File gzipFile, TopiaContext topiaContext) + throws TopiaException, IOException, NullPointerException { + if (gzipFile == null) { + throw new NullPointerException("gzipFile can not be null"); + } + if (topiaContext == null) { + throw new NullPointerException("topiaContext can not be null"); + } + if (!gzipFile.getParentFile().exists()) { + if (!gzipFile.getParentFile().mkdirs()) { + throw new IOException("could not create directory " + + gzipFile.getParentFile()); + } + } + log.info("store database to [" + gzipFile + "]"); + + TopiaContext ctxt = null; + + try { + ctxt = topiaContext.beginTransaction(); + ctxt.backup(gzipFile, true); + } finally { + if (ctxt != null) { + ctxt.closeContext(); + } + } + } + + /** + * Obtain a new {@link Comparator} pour {@link TopiaEntity} based on the + * {@link TopiaEntity#getTopiaId()} method. + * + * @return the new instanciated comparator + */ + public static Comparator<TopiaEntity> getTopiaIdComparator() { + return new Comparator<TopiaEntity>() { + + @Override + public int compare(TopiaEntity o1, TopiaEntity o2) { + if (o1.getTopiaId() == null) { + return -1; + } + if (o2.getTopiaId() == null) { + return 1; + } + return o1.getTopiaId().compareTo(o2.getTopiaId()); + } + }; + } + + /** + * Filter a list of entities, and keep only the ones from a given type. + * + * @param entities the list of entities to filter + * @param filterClass the type of entities to keep + * @return the list of filtered entities for the given entity type in the + * list + * @since 2.1.4 + */ + public static List<TopiaEntity> filter( + Collection<TopiaEntity> entities, + Class<? extends TopiaEntity> filterClass) { + List<TopiaEntity> result = new ArrayList<TopiaEntity>(); + for (TopiaEntity e : entities) { + if (filterClass.isAssignableFrom(e.getClass())) { + result.add(e); + } + } + return result; + } + + public static Class<? extends TopiaEntity> getContractClass( + TopiaEntityEnum[] contracts, + Class<? extends TopiaEntity> klass) { + if (contracts == null || contracts.length == 0) { + // pas de contract connus... + return null; + } + + // on recupere l'ensemble des interfaces connues pour la klass donnee + Set<Class<? extends TopiaEntity>> klassInterfaces = getInterfaces( + klass, + new HashSet<Class<? extends TopiaEntity>>() + ); + + for (TopiaEntityEnum contract : contracts) { + Class<? extends TopiaEntity> contractClass = contract.getContract(); + if (klassInterfaces.contains(contractClass)) { + return contractClass; + } + } + return null; + } + + public static Set<Class<? extends TopiaEntity>> getContractClasses( + TopiaEntityEnum[] contracts, + Iterable<Class<? extends TopiaEntity>> klasses) { + Set<Class<? extends TopiaEntity>> result = new HashSet<Class<? extends TopiaEntity>>(); + if (contracts == null || contracts.length == 0) { + // pas de contract connus... + return result; + } + + for (Class<? extends TopiaEntity> klass : klasses) { + Class<? extends TopiaEntity> c = getContractClass(contracts, klass); + if (c != null) { + result.add(c); + } + } + return result; + } + + public static TopiaEntityEnum getEntityEnum( + Class<? extends TopiaEntity> klass, TopiaEntityEnum... contracts) { + if (contracts == null || contracts.length == 0) { + // pas de contract connus... + return null; + } + Class<? extends TopiaEntity> contractClass = + getContractClass(contracts, klass); + if (contractClass != null) { + for (TopiaEntityEnum t : contracts) { + if (t.accept(contractClass)) { + return t; + } + } + } + return null; + } + + /** + * Filtre un ensemble de classes d'entites en ne conservant que les contrats + * des entites. + * + * @param contracts les contracts connus + * @param classes l'ensemble des classes a filter + * @return l'ensemble des contrats filtres + * @since 2.2.0 + */ + public static Set<Class<? extends TopiaEntity>> retainContracts( + TopiaEntityEnum[] contracts, + Set<Class<? extends TopiaEntity>> classes) { + Map<Class<? extends TopiaEntity>, Class<? extends TopiaEntity>> dico; + dico = new HashMap<Class<? extends TopiaEntity>, + Class<? extends TopiaEntity>>(); + try { + Set<Class<? extends TopiaEntity>> result = + new HashSet<Class<? extends TopiaEntity>>(); + + for (Class<? extends TopiaEntity> c : classes) { + Class<? extends TopiaEntity> contractClass = dico.get(c); + if (contractClass == null) { + // not already detected class + for (TopiaEntityEnum contract : contracts) { + if (contract.accept(c)) { + contractClass = contract.getContract(); + dico.put(c, contractClass); + } + } + } + if (contractClass != null) { + if (log.isDebugEnabled()) { + log.debug("detected type : " + contractClass); + } + result.add(contractClass); + } + } + return result; + } finally { + dico.clear(); + } + } + + /** + * Ontenir l'ensemble des contrats d'entites a partir des descriptions + * d'entites. + * + * @param contracts les contracts connus + * @return l'ensemble des contrats d'entites + * @since 2.2.0 + */ + @SuppressWarnings({"unchecked"}) + public static Class<? extends TopiaEntity>[] getContracts( + TopiaEntityEnum[] contracts) { + Class<?>[] result = new Class<?>[contracts.length]; + int index = 0; + for (TopiaEntityEnum e : contracts) { + result[index++] = e.getContract(); + } + return (Class<? extends TopiaEntity>[]) result; + } + + /** + * Collecte l'ensemble des types d'entites (via un parcours en profondeur). + * <p/> + * On retourne toujours les contrats des entites et jamais les + * implantations. + * + * @param contracts les definitions d'entites connues + * @param entities les entites a parcourir + * @return l'ensemble des types d'entites decouverts (uniquement les + * contrats des entites). + * @throws TopiaException if a problem while visiting entities + */ + public static Set<Class<? extends TopiaEntity>> detectTypes( + final TopiaEntityEnum[] contracts, + TopiaEntity... entities) throws TopiaException { + + Collector<Set<Class<? extends TopiaEntity>>> collector; + + collector = + new Collector<Set<Class<? extends TopiaEntity>>>(contracts) { + + /** + * La liste des types d'entités détectées + */ + protected Set<Class<? extends TopiaEntity>> detectedTypes = + new HashSet<Class<? extends TopiaEntity>>(); + + @Override + protected boolean onStarting(TopiaEntity e) { + super.onStarting(e); + Class<? extends TopiaEntity> entityClass = e.getClass(); + if (log.isDebugEnabled()) { + log.debug(entityClass + " : " + e.getTopiaId()); + } + detectedTypes.add(entityClass); + return true; + } + + @Override + protected Set<Class<? extends TopiaEntity>> afterAll( + CollectorVisitor visitor, TopiaEntity... entities) { + Set<Class<? extends TopiaEntity>> result = + retainContracts(contracts, detectedTypes); + return result; + } + }; + + Set<Class<? extends TopiaEntity>> result = collector.detect(entities); + return result; + } + + /** + * Collecte l'ensemble des entites (via un parcours en profondeur) avec un + * filtrage sur les types d'entites a retourner. + * <p/> + * On retourne toujours les contrats des entites et jamais les + * implantations. + * + * @param contracts les definitions d'entites connues + * @param types l'ensemble des types acceptables + * @param entities les entites a parcourir + * @return l'ensemble des entites decouverts (on converse uniquement les + * entites dont le contract est dans l'ensemble voulu). + * @throws TopiaException if a pb while visiting entities + */ + public static Map<Class<? extends TopiaEntity>, List<TopiaEntity>> + detectEntities(final TopiaEntityEnum[] contracts, + final Set<Class<? extends TopiaEntity>> types, + TopiaEntity... entities) throws TopiaException { + + Collector<Map<Class<? extends TopiaEntity>, List<TopiaEntity>>> collector; + collector = new Collector<Map<Class<? extends TopiaEntity>, List<TopiaEntity>>>(contracts) { + + /** + * le dictionnaire des références détectées indexées par leur type + */ + protected Map<Class<? extends TopiaEntity>, List<TopiaEntity>> detectedRefs = + new HashMap<Class<? extends TopiaEntity>, List<TopiaEntity>>(); + + @Override + protected void onStarted(TopiaEntity e, boolean enter) { + super.onStarted(e, enter); + Class<? extends TopiaEntity> entityClass = getContractClass(e); + + if (entityClass == null || !types.contains(entityClass)) { + // entite non connue ou rejetee (pas dans un type accepte) + return; + } + + if (log.isDebugEnabled()) { + log.debug(entityClass + " : " + e.getTopiaId()); + } + + List<TopiaEntity> refs = detectedRefs.get(entityClass); + if (refs == null) { + refs = new ArrayList<TopiaEntity>(); + detectedRefs.put(entityClass, refs); + } + refs.add(e); + } + + @Override + protected Map<Class<? extends TopiaEntity>, List<TopiaEntity>> + afterAll(CollectorVisitor visitor, TopiaEntity... entities) { + return detectedRefs; + } + }; + + Map<Class<? extends TopiaEntity>, List<TopiaEntity>> result = + collector.detect(entities); + return result; + } + + /** + * Collecte l'ensemble des ids d'entites (via un parcours en profondeur) + * avec un filtrage sur les types d'entites a retourner. + * <p/> + * On retourne toujours les contrats des entites et jamais les + * implantations. + * + * @param contracts les definitions d'entites connues + * @param types l'ensemble des types acceptables + * @param entities les entites a parcourir + * @return l'ensemble des ids d'entites decouverts (on converse uniquement + * les entites dont le contract est dans l'ensemble voulu). + * @throws TopiaException if a pb while visiting entities + */ + public static TopiaEntityIdsMap + detectEntityIds(final TopiaEntityEnum[] contracts, + final Set<Class<? extends TopiaEntity>> types, + TopiaEntity... entities) throws TopiaException { + + Collector<TopiaEntityIdsMap> collector; + + collector = new Collector<TopiaEntityIdsMap>(contracts) { + + /** + * le dictionnaire des références détectées indexées par leur type + */ + protected TopiaEntityIdsMap detectedRefs = + new TopiaEntityIdsMap(); + + @Override + protected void onStarted(TopiaEntity e, boolean enter) { + super.onStarted(e, enter); + Class<? extends TopiaEntity> entityClass = getContractClass(e); + + if (entityClass == null || !types.contains(entityClass)) { + // entite non connue ou rejetee (pas dans un type accepte) + return; + } + + if (log.isDebugEnabled()) { + log.debug(entityClass + " : " + e.getTopiaId()); + } + + List<String> refs = detectedRefs.get(entityClass); + if (refs == null) { + refs = new ArrayList<String>(); + detectedRefs.put(entityClass, refs); + } + refs.add(e.getTopiaId()); + } + + @Override + protected TopiaEntityIdsMap + afterAll(CollectorVisitor visitor, TopiaEntity... entities) { + return detectedRefs; + } + }; + + TopiaEntityIdsMap result = collector.detect(entities); + return result; + } + + /** + * Collecte toutes les references d'un ensemble d'entites donnees par leur + * topiaId sur un ensemble d'entites donne. + * + * @param contracts les definitions d'entites connues + * @param expressions l'ensemble des ids a detecter + * @param entities les entites a parcourir + * @return l'ensemble des references decouvertes. + * @throws TopiaException if a pb while visiting entities + * @see TopiaEntityRef + */ + public static SortedMap<TopiaEntity, List<TopiaEntityRef>> detectReferences( + TopiaEntityEnum[] contracts, + String[] expressions, + TopiaEntity entities) throws TopiaException { + return detectReferences(contracts, expressions, + Collections.singleton(entities)); + } + + /** + * Collecte toutes les references d'un ensemble d'entites donnees par leur + * topiaId sur un ensemble d'entites donne. + * + * @param contracts les definitions d'entites connues + * @param expressions l'ensemble des ids a detecter + * @param entities les entites a parcourir + * @return l'ensemble des references decouvertes. + * @throws TopiaException if a pb while visiting entities + * @see TopiaEntityRef + */ + public static SortedMap<TopiaEntity, List<TopiaEntityRef>> + detectReferences( + final TopiaEntityEnum[] contracts, + final String[] expressions, + Collection<? extends TopiaEntity> entities) throws TopiaException { + + Collector<SortedMap<TopiaEntity, List<TopiaEntityRef>>> collector; + collector = new Collector<SortedMap<TopiaEntity, List<TopiaEntityRef>>>( + contracts) { + + /** la liste des ids a accepter ou rejecter selon acceptMode */ + List<String> ids = Arrays.asList(expressions); + + /** le dictionnaire des références détectées indexées par leur type */ + SortedMap<TopiaEntity, List<TopiaEntityRef>> refs;{ + refs = new TreeMap<TopiaEntity, List<TopiaEntityRef>>( + getTopiaIdComparator()); + } + + /** le path courant depuis le depart */ + Deque<TopiaEntity> path = new LinkedList<TopiaEntity>(); + + StringBuilder accessorExpression = new StringBuilder(); + + TopiaEntity root; + + @Override + protected void before(CollectorVisitor visitor, TopiaEntity entity) { + super.before(visitor, entity); + // on vide le visiteur car on a le droit de reparcourir + // des donnees deja visitees, on peut donc nettoyer dans + // le visiteur la liste des noeud explores (ainsi cela engendrea + // moins de tests) + visitor.clear(); + } + + @Override + protected boolean onStarting(TopiaEntity e) { + if (log.isDebugEnabled()) { + log.debug(e.getTopiaId()); + } + if (root == null) { + // start come in start method since + // last clear method invocation + root = e; + addPath(e, "$root", -1); + if (accept(e)) { + registerEntity(e); + } + } + return true; + } + + @Override + protected void onEnded(TopiaEntity e, boolean enter) { + super.onEnded(e, enter); + if (root == e) { + // global visit is done + root = null; + removePath(); + } + } + + @Override + protected boolean onVisiting(TopiaEntity e, + String name, + Class<?> type, Object value) { + TopiaEntity e1 = getTopiaValue(value); + if (e1 != null) { + // on est sur une entite + addPath(e1, name, -1); + if (accept(e1)) { + registerEntity(e1); + } + // le DepthEntityVisitor gere les cycles de facon trop + // strict : on peut accepter de visiter une entite + // meme si elle a deja rencontree, du moment qu'elle + // n'est pas dans le chemin de parcours + if (visitor.getAlreadyExplored().contains(e1)) { + boolean contains = visitor.getStack().contains(e1); + if (log.isDebugEnabled()) { + log.debug("already enter " + e1.getTopiaId() + + ", can reenter ? " + !contains); + } + if (!contains) { + try { + // on se substitue au depth visitor + // et on accepte le reparcours + // ce qui est necessaire pour obtenir toutes + // les references (but de ce collecteur) + e1.accept(visitor); + } catch (TopiaException ex) { + if (log.isErrorEnabled()) { + log.error("Error on depth exploration", ex); + } + } + } + } + return true; + } + return false; + } + + @Override + protected void onVisited(TopiaEntity e, + String name, + Class<?> type, + Object value, boolean enter) { + super.onVisited(e, name, type, value, enter); + if (enter) { + removePath(); + } + } + + @Override + protected boolean onVisiting(TopiaEntity e, + String name, + Class<?> collectionType, + Class<?> type, + int index, Object value) { + TopiaEntity e1 = getTopiaValue(value); + if (e1 != null) { + addPath(e1, name, index); + if (accept(e1)) { + registerEntity(e1); + } + // le DepthEntityVisitor gere les cycles de facon trop + // strict : on peut accepter de visiter une entite + // meme si elle a deja rencontree, du moment qu'elle + // n'est pas dans le chemin de parcours + if (visitor.getAlreadyExplored().contains(e1)) { + boolean contains = visitor.getStack().contains(e1); + if (log.isDebugEnabled()) { + log.debug("already enter " + e1.getTopiaId() + + ", can reenter ? " + !contains); + } + if (!contains) { + try { + // on se substitue au depth visitor + // et on accepte le reparcours + // ce qui est necessaire pour obtenir toutes + // les references (but de ce collecteur) + e1.accept(visitor); + } catch (TopiaException ex) { + if (log.isErrorEnabled()) { + log.error("Error on depth exploration", ex); + } + } + } + } + return true; + } + return false; + } + + @Override + protected void onVisited(TopiaEntity e, + String name, + Class<?> collectionType, + Class<?> type, + int index, Object value, boolean enter) { + super.onVisited(e, name, collectionType, type, index, value, + enter); + if (enter) { + removePath(); + } + } + + @Override + protected SortedMap<TopiaEntity, List<TopiaEntityRef>> afterAll( + CollectorVisitor visitor, TopiaEntity... entities) { + return refs; + } + + @Override + protected void clear() { + super.clear(); + ids = null; + refs = null; + path.clear(); + path = null; + accessorExpression = null; + root = null; + } + + boolean accept(TopiaEntity e) { + return ids.contains(e.getTopiaId()); + } + + void addPath(TopiaEntity e, String name, int index) { + path.add(e); + if (accessorExpression.length() > 0) { + accessorExpression.append(TopiaEntityRef.SEPARATOR); + } + accessorExpression.append(name); + if (index > -1) { + String association = String.format( + ASSOCIATION_PATTERN, "", e.getTopiaId()); + accessorExpression.append(association); + } +// if (index > -1) { +// accessorExpression.append("[@topiaId=\""); +// accessorExpression.append(e.getTopiaId()); +// accessorExpression.append("\"]"); +// } + if (log.isTraceEnabled()) { + log.trace("add to stack : " + e.getTopiaId() + + ", new size : " + path.size() + ", path : " + + accessorExpression.toString()); + } + } + + void removePath() { + TopiaEntity e = path.removeLast(); + if (path.isEmpty()) { + accessorExpression.setLength(0); + } else { + int index = accessorExpression.lastIndexOf( + TopiaEntityRef.SEPARATOR); + if (index > -1) { + accessorExpression.delete(index, + accessorExpression.length()); + } + } + if (log.isTraceEnabled()) { + log.trace("remove from stack : " + e.getTopiaId() + + ", new size : " + path.size() + ", path : " + + accessorExpression.toString()); + } + } + + void registerEntity(TopiaEntity e) { + + List<TopiaEntityRef> list = refs.get(e); + if (list == null) { + // first time for this entity + list = new ArrayList<TopiaEntityRef>(); + refs.put(e, list); + } + + String expression = accessorExpression.toString(); + + // add the path for the detected + list.add(new TopiaEntityRef( + root, + e, + expression, + path.toArray(new TopiaEntity[path.size()])) + ); + + if (log.isDebugEnabled()) { + log.debug(expression + " (" + e.getTopiaId() + ") - " + + list.size() + " , root:" + root.getTopiaId()); + } + if (log.isTraceEnabled()) { + log.trace(e.getTopiaId() + " : new size " + list.size() + + ", path : " + expression); + } + } + }; + + SortedMap<TopiaEntity, List<TopiaEntityRef>> result = + collector.detect( + entities.toArray(new TopiaEntity[entities.size()]) + ); + return result; + } + + /** + * Construit la liste des topiaId d'une liste donnée d'entités. + * + * @param entities la liste des entités + * @return la liste des topiaId + */ + public static List<String> getTopiaIdList( + Collection<? extends TopiaEntity> entities) { + List<String> ids = new ArrayList<String>(entities.size()); + for (TopiaEntity entity : entities) { + ids.add(entity.getTopiaId()); + } + return ids; + } + + /** + * Construit un tableau des topiaId d'une liste donnée d'entités. + * + * @param entities la liste des entités + * @return le tableau des topiaId + * @since 2.5.2 + */ + public static String[] getTopiaIdArray( + List<? extends TopiaEntity> entities) { + String[] ids = new String[entities.size()]; + int i = 0; + for (TopiaEntity entity : entities) { + ids[i++] = entity.getTopiaId(); + } + return ids; + } + + /** + * Construit une list d'entite dont les ids sont tous dans la liste d'ids + * donnee. + * + * @param <E> le type des entites de la liste + * @param list la liste a filter + * @param topiaIds la liste des ids a retenir + * @return la nouvelle liste filtree + */ + public static <E extends TopiaEntity> List<E> retainEntities( + List<E> list, List<String> topiaIds) { + + List<E> r = new ArrayList<E>(list == null ? 0 : list.size()); + if (list != null) { + for (E e : list) { + if (topiaIds.contains(e.getTopiaId())) { + r.add(e); + } + } + } + return r; + } + + /** + * Construit le dictionnaire des differences entre deux listes d'entites. + * + * @param <E> le type des entites + * @param referentiel la liste considere comme reference + * @param locale la liste a mettre a jour + * @return le dictionnaire des etats des entites ajoutees, modifiees ou + * obsoletes. + * @see DiffState + * @since 2.2.0 + */ + public static <E extends TopiaEntity> DiffState.DiffStateMap + buildDifferentiel(List<E> referentiel, List<E> locale) { + DiffState.DiffStateMap result = DiffState.newMap(); + + // construction des deux listes d'id + List<String> referentielIdList = getTopiaIdList(referentiel); + List<String> localeIdList = getTopiaIdList(locale); + + for (E aReferentiel : referentiel) { + TopiaEntity referentielEntity = aReferentiel; + String refId = referentielEntity.getTopiaId(); + if (localeIdList.contains(refId)) { + // id existant sur le storage locale + + TopiaEntity localeEntity = null; + // il se peut que les listes ne soient pas triées sur le même + // ordre, on parcourt donc la liste cible pour retrouver + // l'entité cible + for (TopiaEntity e : locale) { + if (e.getTopiaId().equals(refId)) { + localeEntity = e; + break; + } + } + boolean wasModified = referentielEntity.getTopiaVersion() > + localeEntity.getTopiaVersion(); + + if (wasModified) { + result.get(DiffState.MODIFIED).add(refId); + } + } else { + // id non existant sur le second storage: donc en état 'a creer' + result.get(DiffState.NEW).add(refId); + } + } + // recuperation des ids existant uniquement sur le second storage + // pour detecter les entites non presente sur le premier storage + localeIdList.removeAll(referentielIdList); + if (!localeIdList.isEmpty()) { + // il existe sur le storage locale, ils sont donc en etat 'a supprimer' + List<String> toRemove = result.get(DiffState.REMOVED); + for (String id : localeIdList) { + toRemove.add(id); + } + } + referentielIdList.clear(); + localeIdList.clear(); + return result; + } + + @SuppressWarnings({"unchecked"}) + public static Set<Class<? extends TopiaEntity>> getInterfaces( + Class<? extends TopiaEntity> klass, + Set<Class<? extends TopiaEntity>> klassInterfaces) { + if (klassInterfaces.contains(klass)) { + // deja traite + return klassInterfaces; + } + if (klass.isInterface()) { + // on ajoute l'interface dans la liste + // avec remplacement de toutes interfaces parent (promotion) + // afin de n'avoir que les interfaces de plus plus haut niveau + addInterface(klassInterfaces, klass); + return klassInterfaces; + } + + Class<?>[] interfaces = klass.getInterfaces(); + if (interfaces.length > 0) { + // des interfaces detectees, on les ajoutes + for (Class<?> interfac : interfaces) { + getInterfaces( + (Class<? extends TopiaEntity>) interfac, + klassInterfaces + ); + } + } + + if (klass.getSuperclass() != null && + TopiaEntity.class.isAssignableFrom(klass.getSuperclass())) { + getInterfaces( + (Class<? extends TopiaEntity>) klass.getSuperclass(), + klassInterfaces + ); + } + return klassInterfaces; + } + + protected static void addInterface( + Set<Class<? extends TopiaEntity>> interfaces, + Class<? extends TopiaEntity> klass) { + Iterator<Class<? extends TopiaEntity>> iterator = interfaces.iterator(); + for (; iterator.hasNext(); ) { + Class<? extends TopiaEntity> next = iterator.next(); + if (next.isAssignableFrom(klass)) { + // cette interface herite de klass + // donc on la supprime + iterator.remove(); + continue; + } + if (klass.isAssignableFrom(next)) { + // cette interface herite de klass + // donc on la supprime + return; + } + + } + interfaces.add(klass); + } + + @SuppressWarnings({"unchecked"}) + public static <E extends TopiaEntity> List<E> getEntities( + TopiaContextImplementor srcCtxt, + List<E> entityList, + boolean canBeNull) throws TopiaException { + List<E> srcList = new ArrayList<E>(entityList.size()); + for (E e : entityList) { + E e2 = (E) srcCtxt.findByTopiaId(e.getTopiaId()); + if (e2 == null && !canBeNull) { + continue; + } + srcList.add(e2); + } + return srcList; + } + + public static TopiaEntity[] getEntities(TopiaContext srcCtxt, + String... entityList) + throws TopiaException { + TopiaEntity[] srcList = new TopiaEntity[entityList.length]; + int index = 0; + for (String id : entityList) { + TopiaEntity e2 = srcCtxt.findByTopiaId(id); + srcList[index++] = e2; + } + return srcList; + } + + public static List<? extends TopiaEntity> getEntitiesList( + TopiaContext srcCtxt, + String... entityList) throws TopiaException { + List<TopiaEntity> srcList = new ArrayList<TopiaEntity>(entityList.length); + for (String id : entityList) { + TopiaEntity e2 = srcCtxt.findByTopiaId(id); + srcList.add(e2); + } + return srcList; + } + + public static void checkNotNull(String methodName, + String parameterName, + Object value) { + if (value == null) { + throw new NullPointerException( + t("topia.persistence.error.null.param", + methodName, parameterName)); + } + } + + public static void checkParameters(Class<?>[] paramsType, + Object... params) { + checkSize(paramsType.length, params); + for (int i = 0, j = paramsType.length; i < j; i++) { + checkType(paramsType, i, params); + } + } + + public static void checkSize(int size, Object[] params) { + if (params.length != size) { + throw new IllegalArgumentException( + t("topia.persistence.error.invalid.size", size, params.length)); + } + } + + public static void checkType(Class<?>[] paramsType, + int index, + Object[] params) { + Class<?> requiredType = paramsType[index]; + Object value = params[index]; + if (value == null) { + throw new IllegalArgumentException( + t("topia.persistence.error.null.param.in.array", index)); + } + Class<?> foundType = value.getClass(); + + if (!requiredType.isAssignableFrom(foundType)) { + throw new IllegalArgumentException( + t("topia.persistence.error.invalid.type.in.array", + index, requiredType, foundType)); + } + } + + /** + * Given two names (representing two types of entity), obtains the + * association table name in the format {@code X_Y} where X est the table + * name smaller (in natural order on {@link String}). + * <p/> + * Example: from {@code A} and {@code B}, we get {@code A_B}. + * + * @param table1 the first table + * @param table2 the second table + * @return the normalized association table name + * @since 2.6.12 + */ + public static String getNormalizedAssociationTableName(String table1, + String table2) { + String result; + if (table1.compareTo(table2) > 0) { + // table1 > table 2 + result = table2 + "_" + table1; + } else { + // table2 > table1 + result = table1 + "_" + table2; + } + return result; + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/TopiaEntityIdsMap.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/TopiaEntityIdsMap.java new file mode 100644 index 0000000..7b0b2d6 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/TopiaEntityIdsMap.java @@ -0,0 +1,53 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.persistence.util; + +import org.nuiton.topia.persistence.TopiaEntity; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +/** + * A map of ids of entities grouped by their type. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.4.3 + */ +public class TopiaEntityIdsMap extends HashMap<Class<? extends TopiaEntity>, List<String>> { + private static final long serialVersionUID = 1L; + + public void addIds(Class<? extends TopiaEntity> type, Iterable<String> ids) { + List<String> list = get(type); + if (list == null) { + list = new ArrayList<String>(); + put(type, list); + } + for (String id : ids) { + if (!list.contains(id)) { + list.add(id); + } + } + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/TopiaEntityMap.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/TopiaEntityMap.java new file mode 100644 index 0000000..0028257 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/TopiaEntityMap.java @@ -0,0 +1,112 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.persistence.util; + +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * A dictionnary of {@link TopiaEntity} associated to a {@link TopiaEntityEnum}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.3 + */ +public abstract class TopiaEntityMap<K extends TopiaEntityEnum, V extends TopiaEntity> extends HashMap<K, List<? extends V>> { + + private static final long serialVersionUID = 1L; + + public TopiaEntityMap(int initialCapacity, float loadFactor) { + super(initialCapacity, loadFactor); + } + + public TopiaEntityMap(int initialCapacity) { + super(initialCapacity); + } + + public TopiaEntityMap() { + } + + public TopiaEntityMap(Map<? extends K, ? extends List<? extends V>> m) { + super(m); + } + + /** + * Obtains from a entity his key. + * + * @param e the entity on which to find the key. + * @return the key of the given entity. + */ + protected abstract K getType(Class<?> e); + + public <T extends V> List<T> get(Class<T> type) { + K key = getType(type); + List<T> vs = (List<T>) get(key); + return vs; + } + + /** + * Adds the given entity to the dictonary only if it does not exists. + * <p/> + * Will return {@code true} if entity was added, {@code false} otherwise. + * + * @param entity the entity to add + * @param <T> the type of entity to add + * @return {@code true} if entity was added, {@code false} otherwise. + */ + public <T extends V> boolean addUniqueEntity(T entity) { + List<T> list = getList(entity); + if (!list.contains(entity)) { + list.add(entity); + return true; + } + return false; + } + + /** + * Adds the given entity to the dictonary (even if it does already exists). + * + * @param entity the entity to add + * @param <T> the type of entity to add + */ + public <T extends V> void addEntity(T entity) { + List<T> list = getList(entity); + list.add(entity); + } + + @SuppressWarnings({"unchecked"}) + protected <T extends V> List<T> getList(T entity) { + K type = getType(entity.getClass()); + List<T> list = (List<T>) get(type); + if (list == null) { + list = new ArrayList<T>(); + put(type, list); + } + return list; + } +} diff --git a/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/TopiaEntityRef.java b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/TopiaEntityRef.java new file mode 100644 index 0000000..3d9f488 --- /dev/null +++ b/topia-persistence/src/main/java/org/nuiton/topia/persistence/util/TopiaEntityRef.java @@ -0,0 +1,101 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence.util; + +import org.nuiton.topia.persistence.TopiaEntity; + +/** + * Definition of a reference of an entity from a root entity. + * <p/> + * the method {@link #getAccessorExpression()} return the jxpath to access the + * ref from the root object. + * <p/> + * TODO Javadoc... + * + * @since 2.2.0 + */ +public class TopiaEntityRef implements Comparable<TopiaEntityRef> { + + public static final String SEPARATOR = "/"; + + /** the root entity */ + TopiaEntity root; + + /** the entity ref */ + TopiaEntity ref; + + /** the jxpath used to acces ref from root */ + String accessorExpression; + + TopiaEntity[] path; + + public TopiaEntityRef(TopiaEntity root, TopiaEntity ref, String accessorExpression, TopiaEntity[] path) { + this.root = root; + this.ref = ref; + this.accessorExpression = accessorExpression; + this.path = path; + } + + public TopiaEntity getRoot() { + return root; + } + + public TopiaEntity getRef() { + return ref; + } + + public String getAccessorExpression() { + return accessorExpression; + } + + public TopiaEntity[] getPath() { + return path; + } + + public TopiaEntity getInvoker() { + return path == null || path.length < 2 ? null : path[path.length - 2]; + } + + public String getInvokerProperty() { + if (path == null || path.length < 2) { + // no invoker defined + return null; + } + int pos = path.length - 1; + String[] expressions = accessorExpression.split(SEPARATOR); + String invokerProperty = expressions[pos]; + return invokerProperty; + } + + @Override + public String toString() { + return super.toString() + " : " + accessorExpression; + } + + @Override + public int compareTo(TopiaEntityRef o) { + return ref.getTopiaId().compareTo(o.getRef().getTopiaId()); + } +} diff --git a/topia-persistence/src/main/resources/META-INF/cache/argouml.org/profiles/uml14/default-java.xmi b/topia-persistence/src/main/resources/META-INF/cache/argouml.org/profiles/uml14/default-java.xmi new file mode 100644 index 0000000..ed25343 --- /dev/null +++ b/topia-persistence/src/main/resources/META-INF/cache/argouml.org/profiles/uml14/default-java.xmi @@ -0,0 +1,78 @@ +<?xml version="1.0" encoding="UTF-8"?> +<XMI xmi.version="1.2" xmlns:UML="org.omg.xmi.namespace.UML" timestamp="Fri May 02 13:16:12 EDT 2008"> + <XMI.header> <XMI.documentation> + <XMI.exporter>ArgoUML (using Netbeans XMI Writer version 1.0)</XMI.exporter> + <XMI.exporterVersion>PRE-0.25.5(5) revised on $Date$ </XMI.exporterVersion> + </XMI.documentation> + <XMI.metamodel xmi.name="UML" xmi.version="1.4"/></XMI.header> + <XMI.content> + <UML:Model xmi.id=".:000000000000087A" name="UML Profile for Java" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:ModelElement.stereotype> + <UML:Stereotype xmi.idref="-64--88-1-3--3c483fe3:119aa9cff00:-8000:00000000000007A3"/> + </UML:ModelElement.stereotype> + <UML:ModelElement.taggedValue> + <UML:TaggedValue xmi.id="-64--88-1-3--3c483fe3:119aa9cff00:-8000:00000000000007A4" isSpecification="false"> + <UML:TaggedValue.dataValue>This profile contains common Java classes and datatypes which are not contained in the UML Standard Elements profile. It only includes the basics, but may be easily extended to include anything else which is useful.</UML:TaggedValue.dataValue> + <UML:TaggedValue.type> + <UML:TagDefinition href="http://argouml.org/profiles/uml14/default-uml14.xmi#.:000000000000087C"/> + </UML:TaggedValue.type> + </UML:TaggedValue> + </UML:ModelElement.taggedValue> + <UML:Namespace.ownedElement> + <UML:Package xmi.id=".:0000000000000869" name="java" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Namespace.ownedElement> + <UML:Package xmi.id=".:000000000000085A" name="lang" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Namespace.ownedElement> + <UML:Class xmi.id=".:0000000000000850" name="Object" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false" isActive="false"/> + <UML:Class xmi.id=".:0000000000000851" name="Char" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false" isActive="false"/> + <UML:Class xmi.id=".:0000000000000852" name="Byte" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false" isActive="false"/> + <UML:Class xmi.id=".:0000000000000853" name="Boolean" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false" isActive="false"/> + <UML:Class xmi.id=".:0000000000000854" name="Short" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false" isActive="false"/> + <UML:Class xmi.id=".:0000000000000855" name="Integer" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false" isActive="false"/> + <UML:Class xmi.id=".:0000000000000856" name="Long" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false" isActive="false"/> + <UML:Class xmi.id=".:0000000000000857" name="Float" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false" isActive="false"/> + <UML:Class xmi.id=".:0000000000000858" name="Double" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false" isActive="false"/> + <UML:Class xmi.id=".:0000000000000859" name="String" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false" isActive="false"/> + </UML:Namespace.ownedElement> + </UML:Package> + <UML:Package xmi.id=".:0000000000000863" name="util" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Namespace.ownedElement> + <UML:Class xmi.id=".:000000000000085B" name="Vector" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false" isActive="false"/> + <UML:Interface xmi.id=".:000000000000085C" name="Collection" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"/> + <UML:Interface xmi.id=".:000000000000085D" name="Iterator" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"/> + <UML:Interface xmi.id=".:000000000000085E" name="List" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"/> + <UML:Class xmi.id=".:000000000000085F" name="Date" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false" isActive="false"/> + <UML:Class xmi.id=".:0000000000000860" name="Time" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false" isActive="false"/> + <UML:Interface xmi.id=".:0000000000000861" name="Set" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"/> + <UML:Interface xmi.id=".:0000000000000862" name="SortedSet" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"/> + </UML:Namespace.ownedElement> + </UML:Package> + <UML:Package xmi.id=".:0000000000000866" name="math" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Namespace.ownedElement> + <UML:Class xmi.id=".:0000000000000864" name="BigDecimal" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false" isActive="false"/> + <UML:Class xmi.id=".:0000000000000865" name="BigInteger" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false" isActive="false"/> + </UML:Namespace.ownedElement> + </UML:Package> + <UML:Package xmi.id=".:0000000000000868" name="net" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Namespace.ownedElement> + <UML:Class xmi.id=".:0000000000000867" name="URL" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false" isActive="false"/> + </UML:Namespace.ownedElement> + </UML:Package> + </UML:Namespace.ownedElement> + </UML:Package> + <UML:DataType xmi.id=".:000000000000086B" name="void" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"/> + <UML:DataType xmi.id=".:000000000000086C" name="int" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"/> + <UML:DataType xmi.id=".:000000000000086D" name="short" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"/> + <UML:DataType xmi.id=".:000000000000086E" name="long" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"/> + <UML:DataType xmi.id=".:000000000000086F" name="double" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"/> + <UML:DataType xmi.id=".:0000000000000870" name="float" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"/> + <UML:DataType xmi.id=".:0000000000000871" name="char" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"/> + <UML:DataType xmi.id=".:0000000000000872" name="byte" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"/> + <UML:DataType xmi.id=".:0000000000000873" name="boolean" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"/> + <UML:Stereotype xmi.id="-64--88-1-3--3c483fe3:119aa9cff00:-8000:00000000000007A3" name="profile" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Model</UML:Stereotype.baseClass> + </UML:Stereotype> + </UML:Namespace.ownedElement> + </UML:Model> + </XMI.content> +</XMI> \ No newline at end of file diff --git a/topia-persistence/src/main/resources/META-INF/cache/argouml.org/profiles/uml14/default-uml14.xmi b/topia-persistence/src/main/resources/META-INF/cache/argouml.org/profiles/uml14/default-uml14.xmi new file mode 100644 index 0000000..e5db611 --- /dev/null +++ b/topia-persistence/src/main/resources/META-INF/cache/argouml.org/profiles/uml14/default-uml14.xmi @@ -0,0 +1,196 @@ +<?xml version="1.0" encoding="UTF-8"?> +<XMI xmi.version="1.2" xmlns:UML="org.omg.xmi.namespace.UML" timestamp="Tue Aug 14 18:51:04 BRT 2007"> + <XMI.header> <XMI.documentation> + <XMI.exporter>ArgoUML (using Netbeans XMI Writer version 1.0)</XMI.exporter> + <XMI.exporterVersion>PRE-0.25.4(5) revised on $Date$ </XMI.exporterVersion> + </XMI.documentation> + <XMI.metamodel xmi.name="UML" xmi.version="1.4"/></XMI.header> + <XMI.content> + <UML:Model xmi.id=".:000000000000087A" name="UML 1.4 Standard Elements" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:ModelElement.taggedValue> + <UML:TaggedValue xmi.id=".:000000000000087B" isSpecification="false"> + <UML:TaggedValue.dataValue>This model contains the UML Standard Elements from Appendix A of the UML 1.4 specification.</UML:TaggedValue.dataValue> + <UML:TaggedValue.type> + <UML:TagDefinition xmi.idref=".:000000000000087C"/> + </UML:TaggedValue.type> + </UML:TaggedValue> + </UML:ModelElement.taggedValue> + <UML:Namespace.ownedElement> + <UML:Stereotype xmi.id=".:0000000000000821" name="access" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Permission</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000822" name="friend" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Permission</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000823" name="import" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Permission</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000824" name="association" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>AssociationEnd</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000825" name="global" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>AssociationEnd</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000826" name="local" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>AssociationEnd</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000827" name="parameter" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>AssociationEnd</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000828" name="self" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>AssociationEnd</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000829" name="become" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Flow</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:000000000000082A" name="copy" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Flow</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:000000000000082B" name="create" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Usage</UML:Stereotype.baseClass> + <UML:Stereotype.baseClass>BehavioralFeature</UML:Stereotype.baseClass> + <UML:Stereotype.baseClass>CallEvent</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:000000000000082C" name="call" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Usage</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:000000000000082D" name="instantiate" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Usage</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:000000000000082E" name="send" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Usage</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000830" name="destroy" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>BehavioralFeature</UML:Stereotype.baseClass> + <UML:Stereotype.baseClass>CallEvent</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000833" name="derive" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Abstraction</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000834" name="realize" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Abstraction</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000835" name="refine" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Abstraction</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000836" name="trace" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Abstraction</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000837" name="document" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Abstraction</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000838" name="executable" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Abstraction</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000839" name="file" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Abstraction</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:000000000000083A" name="library" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Abstraction</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:000000000000083B" name="table" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Abstraction</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:000000000000083C" name="facade" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Package</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:000000000000083D" name="framework" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Package</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:000000000000083E" name="metamodel" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Package</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:000000000000083F" name="stub" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Package</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000840" name="implementation" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Generalization</UML:Stereotype.baseClass> + <UML:Stereotype.baseClass>Class</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000842" name="type" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Class</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000843" name="implicit" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Association</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000844" name="invariant" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Constraint</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000845" name="postcondition" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Constraint</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000846" name="precondition" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Constraint</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000847" name="metaclass" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Class</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000848" name="powertype" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Class</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000849" name="process" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Classifier</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:000000000000084A" name="thread" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Classifier</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:000000000000084B" name="utility" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Classifier</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:000000000000084C" name="requirement" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Comment</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:000000000000084D" name="responsibility" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Comment</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:000000000000084E" name="topLevel" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Package</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:000000000000084F" name="systemModel" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Package</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:000000000000086A" name="signalflow" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>ObjectFlowState</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000874" name="appliedProfile" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Package</UML:Stereotype.baseClass> + <UML:Stereotype.baseClass>Dependency</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000875" name="auxiliary" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Class</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000876" name="modelLibrary" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Package</UML:Stereotype.baseClass> + <UML:Stereotype.baseClass>Dependency</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000877" name="profile" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Package</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000878" name="source" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Abstraction</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:Stereotype xmi.id=".:0000000000000879" name="stateInvariant" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Stereotype.baseClass>Constraint</UML:Stereotype.baseClass> + </UML:Stereotype> + <UML:TagDefinition xmi.id=".:000000000000087C" name="documentation" isSpecification="false" tagType="String"> + <UML:TagDefinition.multiplicity> + <UML:Multiplicity xmi.id=".:000000000000087D"> + <UML:Multiplicity.range> + <UML:MultiplicityRange xmi.id=".:000000000000087E" lower="0" upper="1"/> + </UML:Multiplicity.range> + </UML:Multiplicity> + </UML:TagDefinition.multiplicity> + </UML:TagDefinition> + <UML:DataType xmi.id="-84-17--56-5-43645a83:11466542d86:-8000:000000000000087C" name="Integer" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"/> + <UML:DataType xmi.id="-84-17--56-5-43645a83:11466542d86:-8000:000000000000087D" name="UnlimitedInteger" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"/> + <UML:DataType xmi.id="-84-17--56-5-43645a83:11466542d86:-8000:000000000000087E" name="String" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"/> + <UML:Enumeration xmi.id="-84-17--56-5-43645a83:11466542d86:-8000:0000000000000880" name="Boolean" isSpecification="false" isRoot="false" isLeaf="false" isAbstract="false"> + <UML:Enumeration.literal> + <UML:EnumerationLiteral xmi.id="-84-17--56-5-43645a83:11466542d86:-8000:0000000000000881" name="TRUE" isSpecification="false"/> + <UML:EnumerationLiteral xmi.id="-84-17--56-5-43645a83:11466542d86:-8000:0000000000000882" name="FALSE" isSpecification="false"/> + </UML:Enumeration.literal> + </UML:Enumeration> + </UML:Namespace.ownedElement> + </UML:Model> + </XMI.content> +</XMI> \ No newline at end of file diff --git a/topia-persistence/src/main/resources/META-INF/services/org.nuiton.eugene.models.stereotype.StereotypeDefinitionProvider b/topia-persistence/src/main/resources/META-INF/services/org.nuiton.eugene.models.stereotype.StereotypeDefinitionProvider new file mode 100644 index 0000000..08c0a2b --- /dev/null +++ b/topia-persistence/src/main/resources/META-INF/services/org.nuiton.eugene.models.stereotype.StereotypeDefinitionProvider @@ -0,0 +1 @@ +org.nuiton.topia.generator.TopiaStereoTypes \ No newline at end of file diff --git a/topia-persistence/src/main/resources/META-INF/services/org.nuiton.eugene.models.tagvalue.TagValueDefinitionProvider b/topia-persistence/src/main/resources/META-INF/services/org.nuiton.eugene.models.tagvalue.TagValueDefinitionProvider new file mode 100644 index 0000000..d8b0182 --- /dev/null +++ b/topia-persistence/src/main/resources/META-INF/services/org.nuiton.eugene.models.tagvalue.TagValueDefinitionProvider @@ -0,0 +1 @@ +org.nuiton.topia.generator.TopiaTagValues \ No newline at end of file diff --git a/topia-persistence/src/main/resources/i18n/topia-persistence_en_GB.properties b/topia-persistence/src/main/resources/i18n/topia-persistence_en_GB.properties new file mode 100644 index 0000000..9fd90d6 --- /dev/null +++ b/topia-persistence/src/main/resources/i18n/topia-persistence_en_GB.properties @@ -0,0 +1,33 @@ +topia.persistence.error.class.not.found=Persistence class %1$s not found +topia.persistence.error.context.already.closed=Context was alredy closed +topia.persistence.error.context.is.closed=Context is closed, no operation is possible. +topia.persistence.error.create.schema=Schema could not be created for following reason \: %1$s +topia.persistence.error.empty.doc=Empty document +topia.persistence.error.no.hibernate.session=No hinernate session found, you must at first, open a new transaction via 'beaginTransaction' method. +topia.persistence.error.null.param=The method '%1$s' requires a non null parameter '%2$s'. +topia.persistence.error.on.backup= +topia.persistence.error.on.clear=Error %s on clear operation +topia.persistence.error.on.commit=An error occurs while commit operation \: %1$s +topia.persistence.error.on.export=An error occurs while export operation \: %1$s +topia.persistence.error.on.loding.xml.doc=Could not read xml document for following reason \: %1$s +topia.persistence.error.on.query=An error occurs while query operation \: %1$s +topia.persistence.error.on.replicate=An error occurs while a replication operation \: %s +topia.persistence.error.on.restore= +topia.persistence.error.on.rollback=An error occurs while rollback operation \: %1$s +topia.persistence.error.open.transaction.failed=An error occurs while asking a new transaction \: %1$s +topia.persistence.error.replicate.entity=Could not replicate entity '%1$s' pour following reason \: %2$s +topia.persistence.error.replicate.on.same.context=Can not do a replication operation on same database. +topia.persistence.error.rootContext.access=You are on root context, you MUST open a transaction to perform any database access. +topia.persistence.error.service.not.found=The service %1$s was not found. +topia.persistence.error.service.not.retreaved=Could not retrive service %1$s for following reason \: %2$s +topia.persistence.error.service.unknown=The service %1$s of type %2$s was not found. +topia.persistence.error.unsupported.class=The following entity type %1$s is not managed by this context, you probably forgot to declare it. +topia.persistence.error.unsupported.operation.on.closed.context=This context is closed, it is not possible to release the operation '%1$s' +topia.persistence.error.unsupported.operation.on.root.context=Unsupported operation %s on root context +topia.persistence.error.update.schema=Schema could not be updated for following reason \: %1$s +topia.persistence.service.loaded=Service '%1$s' loaded (implementation %2$s) +topia.persistence.supported.classes.for.context=List of supported persistence classes \: %1$s +topia.persistence.warn.service.not.found=The service named '%1$s' could not be found for following reason \: \: %2$s +topia.persistence.warn.service.not.loaded=The service with key '%1$s' has a different name '%2$s' \! (service not activated) +topia.persistence.warn.service.not.postInit=The service named '%1$s' could not be pre-initialized (service not activated) +topia.persistence.warn.service.not.preInit=The service named '%1$s' could not be post-initialized (service not activated) diff --git a/topia-persistence/src/main/resources/i18n/topia-persistence_es_ES.properties b/topia-persistence/src/main/resources/i18n/topia-persistence_es_ES.properties new file mode 100644 index 0000000..7367c76 --- /dev/null +++ b/topia-persistence/src/main/resources/i18n/topia-persistence_es_ES.properties @@ -0,0 +1,33 @@ +topia.persistence.error.class.not.found=No ha sido encontrada la clase persistente %1$s +topia.persistence.error.context.already.closed=El contexto ya ha sido cerrado +topia.persistence.error.context.is.closed=Este contexto ya ha sido cerrado, no se puede comenzar una transacción +topia.persistence.error.create.schema=No se puede crear el esquema debido a la razón siguiente \: %2$s +topia.persistence.error.empty.doc=Documento vacío +topia.persistence.error.no.hibernate.session=No se encuentra ninguna sesión hibernada, debe iniciar una transacción con el método 'BeginTransaction' +topia.persistence.error.null.param=El método '%1$s' requiere un parámetro '%2$s' no nulo. +topia.persistence.error.on.backup= +topia.persistence.error.on.clear= +topia.persistence.error.on.commit=Se produjo un error durante el guardado \: %1$s +topia.persistence.error.on.export=Se produjo un error durante la exportación \: %1$s +topia.persistence.error.on.loding.xml.doc=No se puede leer el documento debido a \: %1$s +topia.persistence.error.on.query=Se produjo un error duratne la busqueda (consulta %1$s) \: %2$s +topia.persistence.error.on.replicate=Se produjo un error durante la vuelta a atrás (replication) \: %1$s +topia.persistence.error.on.restore= +topia.persistence.error.on.rollback=Se produjo un error durante la vuelta a atrás (rollback) \: %1$s +topia.persistence.error.open.transaction.failed= +topia.persistence.error.replicate.entity=Error de repliación de la entidad %1$s\ndebido a la siguiente razón \: %2$s +topia.persistence.error.replicate.on.same.context=No se puede duplicar en la misma base +topia.persistence.error.rootContext.access=Está usted en la raíz, debe abrir una transacción para acceder a los datos. +topia.persistence.error.service.not.found=El servicio %1$s no está disponible +topia.persistence.error.service.not.retreaved=El servicio %1$s debido a \: %2$s +topia.persistence.error.service.unknown=El servicio '%1$s' de clase '%2$s' es desconocido +topia.persistence.error.unsupported.class=La clase %1$n no está soportada por TopiaContext, puede que se halla olvidado de añadir su mapa +topia.persistence.error.unsupported.operation.on.closed.context=El contexto está cerrado, no se puede realizar la operación %1$s +topia.persistence.error.unsupported.operation.on.root.context= +topia.persistence.error.update.schema=El esquema no se puede actualizar debido a \: %2$s +topia.persistence.service.loaded=Servicio '%1$s' cargado por '%2$s' +topia.persistence.supported.classes.for.context=Classes supportées par ce TopiaContext \: %1$s +topia.persistence.warn.service.not.found=El nombre del servicio '%1$s' no ha sido encontrado debido a \: %2$s +topia.persistence.warn.service.not.loaded=Le service de clé '%1$s' tiene un nombre de servicio '%2$s' diferente \! (servicio desactivado) +topia.persistence.warn.service.not.postInit=El servicio '%1$s' no puede ser inicializado (servicio desactivado) +topia.persistence.warn.service.not.preInit=El servicio '%1$s' no puede ser inicializado (servicio desactivado) diff --git a/topia-persistence/src/main/resources/i18n/topia-persistence_fr_FR.properties b/topia-persistence/src/main/resources/i18n/topia-persistence_fr_FR.properties new file mode 100644 index 0000000..8b2a16e --- /dev/null +++ b/topia-persistence/src/main/resources/i18n/topia-persistence_fr_FR.properties @@ -0,0 +1,33 @@ +topia.persistence.error.class.not.found=La classe persistante %1$s n'a pas été trouvée +topia.persistence.error.context.already.closed=Ce contexte a deja ete ferme +topia.persistence.error.context.is.closed=Ce contexte a ete ferme, impossible de commencer une transaction +topia.persistence.error.create.schema=Le schéma n'a pas pu être crée pour la raison suivante \: %2$s +topia.persistence.error.empty.doc=Document vide +topia.persistence.error.no.hibernate.session=Aucune session hibernate trouvée, vous devez démarré une transaction avec la méthode 'beginTransaction' +topia.persistence.error.null.param=La méthode '%1$s' requière un paramètre '%2$s' non null. +topia.persistence.error.on.backup= +topia.persistence.error.on.clear=Une erreur est apparue pendant le clear \: %s +topia.persistence.error.on.commit=Une erreur est apparue pendant le commit \: %1$s +topia.persistence.error.on.export=Une erreur est apparue pendant l'export \: %1$s +topia.persistence.error.on.loding.xml.doc=Lecture du document impossible pour la raison suivante \: %1$s +topia.persistence.error.on.query=Une erreur est apparue pendant le recherche (requête %1$s) \: %2$s +topia.persistence.error.on.replicate=Une erreur lors de la réplication est survenue \: %s +topia.persistence.error.on.restore= +topia.persistence.error.on.rollback=Une erreur est apparue pendant le rollback \: %1$s +topia.persistence.error.open.transaction.failed=Une erreur est apparue pendant la demande de transaction \: %1$s +topia.persistence.error.replicate.entity=Echec de replication de l'entite %1$s\npour la raison suivante \: %2$s +topia.persistence.error.replicate.on.same.context=Impossible de dupliquer dans la même base +topia.persistence.error.rootContext.access=Vous êtes sur le root context, vous devez ouvrir une transaction pour pouvoir accéder aux données. +topia.persistence.error.service.not.found=Le service %1$s n'est pas disponible +topia.persistence.error.service.not.retreaved=La service %1$s n'a pas pu être récupéré pour la raison suivante \: %2$s +topia.persistence.error.service.unknown=Le service '%1$s' ayant pour classe '%2$s' n'est pas connu +topia.persistence.error.unsupported.class=La classe %1$s n'est pas supportée par ce TopiaContext, vous avez sans doute oublié d'ajouter son mapping +topia.persistence.error.unsupported.operation.on.closed.context=Ce contexte a été fermé, impossible de réaliser l'opération %1$s +topia.persistence.error.unsupported.operation.on.root.context=Opération interdite %s sur le rootContext +topia.persistence.error.update.schema=Le schéma n'a pas pu être mis à jour pour la raison suivante \: %2$s +topia.persistence.service.loaded=Service '%1$s' chargé par '%2$s' +topia.persistence.supported.classes.for.context=Classes supportées par ce TopiaContext \: %1$s +topia.persistence.warn.service.not.found=Le nom du service '%1$s' n'a pas été trouvé pour la raison suivante \: %2$s +topia.persistence.warn.service.not.loaded=Le service de clé '%1$s' a un nom de service '%2$s' différent \! (service désactivé) +topia.persistence.warn.service.not.postInit=Le service '%1$s' n'a pas pu être initialisé (service désactivé) +topia.persistence.warn.service.not.preInit=Le service '%1$s' n'a pas pu être finalisé (service désactivé) diff --git a/topia-persistence/src/site/resources/ClassDiagram_BookAuthor.png b/topia-persistence/src/site/resources/ClassDiagram_BookAuthor.png new file mode 100644 index 0000000..0956209 Binary files /dev/null and b/topia-persistence/src/site/resources/ClassDiagram_BookAuthor.png differ diff --git a/topia-persistence/src/site/resources/ClassDiagram_BookAuthorWithOperation.png b/topia-persistence/src/site/resources/ClassDiagram_BookAuthorWithOperation.png new file mode 100644 index 0000000..66fff2f Binary files /dev/null and b/topia-persistence/src/site/resources/ClassDiagram_BookAuthorWithOperation.png differ diff --git a/topia-persistence/src/site/resources/ContactUseCases.png b/topia-persistence/src/site/resources/ContactUseCases.png new file mode 100644 index 0000000..5b22a1d Binary files /dev/null and b/topia-persistence/src/site/resources/ContactUseCases.png differ diff --git a/topia-persistence/src/site/resources/ServiceCall.png b/topia-persistence/src/site/resources/ServiceCall.png new file mode 100644 index 0000000..da51553 Binary files /dev/null and b/topia-persistence/src/site/resources/ServiceCall.png differ diff --git a/topia-persistence/src/site/resources/modelForTopiaQuery.png b/topia-persistence/src/site/resources/modelForTopiaQuery.png new file mode 100644 index 0000000..f698f72 Binary files /dev/null and b/topia-persistence/src/site/resources/modelForTopiaQuery.png differ diff --git a/topia-persistence/src/site/resources/modelForTopiaQuery.zargo b/topia-persistence/src/site/resources/modelForTopiaQuery.zargo new file mode 100644 index 0000000..cddaaff Binary files /dev/null and b/topia-persistence/src/site/resources/modelForTopiaQuery.zargo differ diff --git a/topia-persistence/src/site/resources/topia.zargo b/topia-persistence/src/site/resources/topia.zargo new file mode 100644 index 0000000..01b8226 Binary files /dev/null and b/topia-persistence/src/site/resources/topia.zargo differ diff --git a/topia-persistence/src/site/rst/TopiaDocumentation.rst b/topia-persistence/src/site/rst/TopiaDocumentation.rst new file mode 100644 index 0000000..9ff949d --- /dev/null +++ b/topia-persistence/src/site/rst/TopiaDocumentation.rst @@ -0,0 +1,68 @@ +.. - +.. * #%L +.. * ToPIA :: Persistence +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +===== +ToPIA +===== + +But +=== + +- abstraction de la persistence +- sauvegarde/restauration en XML +- sécurité sur les instances d'objets +- génération de code même si non obligatoire pour utiliser ToPIA + +Ce qu'il serait bien de récuperer par rapport à la version 2 + +- support des sous-transactions + +peut-être plus tard + +- gestion des services +- distribution transparente + +La vision ToPIA +=============== + +Un point d'entre le TopiaContext sur lequel on ouvre des transactions ce qui +retourne un autre TopiaContext à partir duquel on récupère les DAO des +différentes entités qui permettent de faire les traitements que l'on souhaite. + +Ensuite on peut appeler la methode commit de ce sous TopiaContext pour mettre +à jour la base de données et permettre à d'autres TopiaContext d'avoir la +nouvelle vision de nos objets. + +Chaque TopiaContext créé sur le TopiaContext root est indépendant. + +Lorsque l'on travaille avec un objet provenant d'un TopiaContext sur lequel +on a fait un rollback, celui-ci n'est plus valide et il vaut mieux en recupérer +une version correcte sur le TopiaContext. + +Après avoir fait un commit ou un rollback sur un TopiaContext on peut continuer +a l'utiliser et refaire des commits pour synchroniser de temps en temps les +modification faites sur la base. + +Un TopiaContext peu servir aussi longtemps que l'on souhaite, il ne maintient +pas inutilement de transaction sur la base. diff --git a/topia-persistence/src/site/rst/devel/Devel.rst b/topia-persistence/src/site/rst/devel/Devel.rst new file mode 100644 index 0000000..f164491 --- /dev/null +++ b/topia-persistence/src/site/rst/devel/Devel.rst @@ -0,0 +1,171 @@ +.. - +.. * #%L +.. * ToPIA :: Persistence +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +=================== +TopiaContextFactory +=================== + +Le topia context est créé en faisant la demande sur le *TopiaContextFactory*. +On peut passer en paramètre au *TopiaContextFactory* un object *Property* qui +permet de configurer le context. Si aucun fichier est passé en paramètre, un fichier de +propriété **TopiaContextImpl.properties** est recherché dans le classpath. + +Fichier de configuration +======================== + +Le fichier de configuration est un fichier lisible par un objet *Property*. +Il contient différentes informations : +- liste des entités à gérer +- option de connexion à la base de données +- les services à activer + +Liste des entités à gérer +------------------------- + +Il est possible de définir les entités soit directement en indiquant un +répertoire contenant les mappings hibernate:: + + topia.persistence.directories=<path> + +soit en indiquant une liste de classe séparé par des virgule:: + + topia.persistence.classes=<list de classe> + +Le mieux est d'utiliser la liste de classe qui est non spécifique à une +persistence ou à une plateforme. + +Option de connexion à la base de données +---------------------------------------- + +Hibernate +~~~~~~~~~ + +Les options pour hibernate peuvent être directement dans le fichier de configuration principal ou dans un fichier de configuration secondaire. Dans ce cas il faut indiquer dans le fichier de configuration principal le chemin de ce fichier:: + + topia.persistence.properties.file=<filesystem or classpath path> + +Les options hibernates sont les options hibernate classique : + +- hibernate.connection.username +- hibernate.connection.password +- hibernate.dialect +- hibernate.connection.driver_class +- hibernate.connection.url + +Les services à activer +---------------------- + +Il est possible d'indiquer les services à activer grâce aux options:: + + topia.service.<service name>=<class> + topia.service.security=org.nuiton.topia.security.TopiaSecurityServiceImpl + topia.service.index=org.nuiton.topia.index.LuceneIndexer + topia.service.history=org.nuiton.topia.history.TopiaHistoryServiceImpl + +<service name> doit correspondre au nom de service que <class> retourne, sinon il est désactivé. + +Les services disponibles actuellement sont: + +- Service d'indexation full text +- Service d'historisation des modifications des entités +- Service de sécurité +- Service d'upgrade automatique des données hibernates + +TopiaContext +============ + +Le *TopiaContext* permet de récupérer les services, d'ouvrir des transactions +en récupérant des nouveaux *TopiaContext* fils, de s'enregistrer en tant que +listener pour recevoir les notifications de modification sur les entités. + +Sur les *TopiaContext* fils, on peut récupérer des DAO qui permettent de +créer, modifier, rechercher les entités. + +Entité +====== + +Normalement **Topia** est fait pour pouvoir générer n'importe quel type de POJO +et les rendre persistants. Mais il est plus simple d'utiliser la génération +de code fournit avec **Topia** pour générer les entités à partir d'un +diagramme UML. Dans ce cas toutes les entités héritent de *TopiaEntity* qui +contient des méthodes pour s'enregistrer sur les modifications des attributs +ou pouvoir lever un droit de veto sur une modification. Sont aussi +disponible les méthodes: + +- getTopiaId +- getTopiaVersion +- getTopiaCreationDate +- getTopiaContext +- getComposite: Retourne tous les objets qui seront effacé si cet objet est effacé +- getAggregate: Retourne les objets en lien avec celui-ci + +Lorsque l'on utilise la génération on peut implanter une classe qui hérite +du *Abstract* généré et qui se finisse par Impl. Par exemple si dans notre +modèle nous avons la classe *Toto* il sera généré une interface *Toto* et +une classe abstraite *TotoAbstract*, il faudra alors implanter *TotoImpl*:: + + extends TotoAbstract extends TopiaEntityAbstract implements Toto + extends TopiaEntityAbstract implements *TopiaEntity + +Cette classe impl permet d'ajouter des méthodes métiers à l'entité ou des +méthode d'accès différent sur les attributs. + +DAO +=== + +Il n'y a plus qu'un seul type de persistence possible pour les DAO, à savoir Hibernate (depuis la 2.2.0). Il est possible d'étendre un DAO +pour lui ajouter différentes méthodes plus complexes ou plus spécifiques que celles proposées par défaut : + +- findAll +- findAllByProperties(Map<String, Object> properties) +- findAllByProperties(String param, Object property, ...) +- findByProperty(String property) +- tous les find pour chaque property existante de l'entité +- ... + +Pour spécifier d'autres méthodes, il suffit d'utiliser directement le modèle UML qui sert à la génération des entités. +Un stéréotype <<dao>> doit être précisé sur une méthode d'entité pour indiquer qu'elle doit se situer dans un DAO. +Le fichier *DAOImpl* associé ne sera plus généré et devra être créé par le développeur en héritant du *DAOAbstract*. +(voir FAQ pour un exemple sur l'extension d'un DAO). + +Note sur les signatures de méthodes + Par commodité, les méthodes retournant une liste d'entités commencent par findAllBy tandis que celles retournant une seule entité + commencent par findBy. + +TopiaService +============ + +Pour implanter un TopiaService il faut absolument créer une interface qui +hériter de l'interface *TopiaService* et mettre dans cette interface un +attribut static qui définit le nom du service:: + + public static final String SERVICE_NAME = "monservice"; + +Un service peut avoir besoin de nouvelles entités pour fonctionner. Pour cela +il faut retourner la liste des entités grâce à la méthode +**getPersistenceClasses**. + +Après instanciation du service par le **TopiaContext** celui-ci est +initialisé grâce à la méthode **init(TopiaContextImplementor)**. Il peut +alors se mettre listener sur le context ou les DAO par exemple. diff --git a/topia-persistence/src/site/rst/devel/HibernateMapping.rst b/topia-persistence/src/site/rst/devel/HibernateMapping.rst new file mode 100644 index 0000000..7ad2c35 --- /dev/null +++ b/topia-persistence/src/site/rst/devel/HibernateMapping.rst @@ -0,0 +1,120 @@ +.. - +.. * #%L +.. * ToPIA :: Persistence +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +================= +Mapping hibernate +================= + +Ce document décrit les choix de mapping faits en fonction +du diagramme de classe UML. + +JDBC +==== + +Généralité +---------- + +- Tous les objets utilisent le versionnement dans un champs version:: + + <version name="topiaVersion" type="long" node="@topiaVersion"/> + +- On utilise les méthodes d'accès pour accêder aux propriétés + +Héritage +-------- + +- Seules les classes concrêtes ont un mapping (au travers de l'interface + impl) +- Chaque classe à un fichier de mapping séparé. + +On utilisera union-subclass + +Identifiant +----------- + +Lors de la description de la classe, on peut indiquer de ne pas générer de clé +technique. Dans ce cas, la clé métier est utilisée (tagvalue: technicalKey=false). +Par défaut une clé technique est utilisée et est de la forme uuid.hex:: + + <id name="topiaId" column="topiaId" type="string"> + <generator class="uuid.hex"/> + </id> + +La description de la clé métier est faite par un tagvalue sur la classe +(tagvalue: key=prop1,prop2,prop3). Si une clé technique est utilisée, une +contrainte d'unicité est tout de même faite sur la clé métier. + +La clé métier sert aussi pour le toString, equals, hashCode. + +Si l'id d'une entité est composé de plusieurs champs, ces champs doivent-être +rassemblés dans une classe indépendante de l'entité. Par contre le mapping +assemble l'entity et son id dans la même table:: + + <composite-id name="#field" class="#class"> + <key-property name="#field1" column="#col1"/> + <key-property name="#field2" column="#col2"/> + <key-property name="#field3" column="#col3"/> + </composite> + +Si l'id est un objet simple il peut être directement mis dans l'entité. + +Relation 1-0 +------------ + + + +Relation 1-1 +------------ + +Relation 0-N +------------ + +Relation 1-N +------------ + +Relation N-N +------------ + +Classe d'association +-------------------- + +Composition +----------- + +Le composant peut changer de propriétaire (set méthode) mais le +propriétaire perd en même temps le lien vers son composé. + +Aggregation +----------- + +Si une classe est aggrégée avec une autre, alors elle suit la vie de l'entité +à laquelle elle est aggrégée (cascade delete, update) + +Elle ne peut pas être affectée a une autre entité. Pas de set sur cette classe +vers l'autre classe. + +XML +=== + +Toutes les propriétés sont des éléments sauf la clé technique qui est un +attribut. diff --git a/topia-persistence/src/site/rst/devel/Isolation.rst b/topia-persistence/src/site/rst/devel/Isolation.rst new file mode 100644 index 0000000..f7e5d65 --- /dev/null +++ b/topia-persistence/src/site/rst/devel/Isolation.rst @@ -0,0 +1,92 @@ +.. - +.. * #%L +.. * ToPIA :: Persistence +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +=========================== +Isolation des TopiaContexts +=========================== + + +Remarque: les requètes ne sont pas bonnes, mais sont là pour donner l'idée +générale. + +Pour mettre en place l'isolation entre les différents TopiaContexts créés par +beginTransaction, il faut ajouter un nouvelle propriété sur les +TopiaEntity: TopiaContextId. Cette propriété prend la valeur de l'id du +TopiaContext qui a créé ou chargé l'objet. On voit donc que plusieurs objets +ayant le même id peuvent exister, le TopiaContextId doit donc faire partie +de la clé de l'objet. Il faut aussi ajouter un champs TopiaDeleted pour +savoir si un objet a été effacé ou non. + +Dans le mapping hibernate pour chaque class d'entité, on a un filtre: +where TopiaContextId == :id || (TopiaContextId > 0 && TopiaContextId <= :id) + +Ce filtre n'est pas suffisant car on a toutes les versions commités de +l'objet jusqu'a :id, il faut un autre filtre apres les recherches pour +n'avoir que les dernieres versions des entites et supprimer dans ce groupe +les entitées marquées et effacées. +where TopiaContextId = max(TopiaContextId) && isDeleted=false group by topiaId + +Lors d'un beginTransaction le nouveau TopiaContext active ce filtre pour son +propre id. + +De cette manière un TopiaContext ne voit que les objets qu'il a créé et/ou +modifié, ou les objets plus ancien que lui. + +Cet Id est: - System.nanoTime() et donc toujours négatif. + +Lors d'un commit d'un TopiaContext, on passe tous les TopiaContextId de +toutes les entités qui sont égales a l'id du TopiaContext en sa version +positive: update <table> set TopiaContextId = -:id where TopiaContextId = :id +De plus le TopiaContext renouvelle son id et modifie tous les filtres pour +qu'il aie la vision d'objets créés par d'autre TopiaContext avant son commit. + +Lors d'un rollback il suffit de faire le renouvellement de l'id et la mise à +jour des filtres. + +Sous-transaction +================ + +La gestion des TopiaContextId pourrait offrir les sous-transactions, si +dans le filtre on ajoutait comme condition, au lieu de TopiaContextId == :id, +TopiaContextId in (:idList) ou idList serait la liste des ids du +TopiaContext courant ainsi que des TopiaContext parents. + +Historisation +============= + +La gestion des TopiaContextId permet d'offrir l'historisation de tous les +objets. Car tous les objets commités sont conservés avec un TopiaContextId +différent pour chaque version. + +Implantation +============ + +- Mettre en place les champs supplémentaires: TopiaContextId, TopiaDeleted +- Modifier la clé primaire pour ajouter TopiaContextId +- Ajouter les filtres dans les mappings +- Ajouter un id sur les TopiaContext +- Modifier le comportement de DAO.delete pour qu'il marque juste l'objet + comme effacé sans l'effacer TopiaDeleted=true +- Pour toutes les recherches ajouter un filtre pour ne retourner que les + derniere version non deleted diff --git a/topia-persistence/src/site/rst/devel/SchemaMigration.rst b/topia-persistence/src/site/rst/devel/SchemaMigration.rst new file mode 100644 index 0000000..6bb1999 --- /dev/null +++ b/topia-persistence/src/site/rst/devel/SchemaMigration.rst @@ -0,0 +1,177 @@ +.. - +.. * #%L +.. * ToPIA :: Persistence +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin, Chatellier Eric +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +========================================================= +Comment migrer les version d'un schema de base de données +========================================================= + +Lorsque des modifications sont effectuer dans une application qui impacte +le schema de base de données, celui doit subir des migrations. +ToPIA fournit une API visant a assister le developpeur pour migrer un schema +de base de données. + +Approche +-------- + +TODO approche migration par copy +TOOD approche migration par requette sql + +Configuration +------------- + +Pour commencer, topia doit connaitre l'ensemble des versions de la base +de données, et la version actuelle de l'application. Si la version de l'application +diffère de celle de la base de données, une migration sera effectuée. +Après la migration, la version de la base de données est égale à celle de +l'application. + +Dépendances +~~~~~~~~~~~ + +La migration est effectuée par le service de migration de ToPIA. Il est +necessaire de l'ajouter explicitement. + +:: + + <dependency> + <groupId>org.nuiton.topia</groupId> + <artifactId>topia-service-migration</artifactId> + <version>2.5.3</version> + <scope>compile</scope> + </dependency> + +Class de definition des migration +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Pour commencer, il faut créer la classe definissant les versions. + +:: + + public class DatabaseMigrationClass extends TopiaMigrationCallbackByClass { + + protected static final Version VERSION_1 = new Version("1"); + protected static final Version VERSION_2 = new Version("2"); + protected static final Version VERSION_3 = new Version("3"); + + public DatabaseMigrationClass() { + super(new MigrationResolver()); + } + + protected static class MigrationResolver implements MigrationCallBackForVersionResolver { + + @Override + public Class<? extends MigrationCallBackForVersion> getCallBack(Version version) { + Class<? extends MigrationCallBackForVersion> result = null; + + if (version.equals(VERSION_1)) { + result = MigrationV0V1.class; + } + else if (version.equals(VERSION_2)) { + result = MigrationV1V2.class; + } + else if (version.equals(VERSION_3)) { + result = MigrationV2V3.class; + } + return result; + } + + } + + @Override + public Version[] getAvailableVersions() { + Version[] result = new Version[] { VERSION_1, VERSION_2, VERSION_3 }; + return result; + } + + @Override + public Version getApplicationVersion() { + Version appVersion = new Version(MyAppDAOHelper.getModelVersion()); + return appVersion; + } + + @Override + public boolean askUser(Version dbVersion, List<Version> versions) { + return true; + } + + } + +Classes de migration de version +------------------------------- + +Ensuite, il faut créer une classe par migration. + +Par exemple, voici la classe migrant le shema de la version 0 à la version 1 : +MigrationV0V1. + +:: + + public class MigrationV0V1 extends MigrationCallBackForVersion { + + public MigrationV0V1(Version version, TopiaMigrationCallbackByClass callBack) { + super(version, callBack); + } + + @Override + protected void prepareMigrationScript(TopiaContextImplementor tx, + List<String> queries, boolean showSql, boolean showProgression) + throws TopiaException { + queries.add("alter table SETOFVESSELS add column TECHNICALEFFICIENCYEQUATION VARCHAR(255);"); + queries.add("alter table STRATEGY add column INACTIVITYEQUATIONUSED BIT default false;"); + queries.add("alter table STRATEGY add column INACTIVITYEQUATION VARCHAR(255);"); + queries.add("alter table STRATEGYMONTHINFO alter NUMBEROFTRIPS double;"); + queries.add("alter table STRATEGYMONTHINFO alter MININACTIVITYDAYS double;"); + } + } + +Configuration du topia context +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Pour finir, il faut ajouter l'utilisation de la migration par ces classe +dans le topia context. En effet, la migration est effectuée automatiquement +lors de l'ouverture d'un TopiaContext. + +Configuration: + +:: + + config.put(TopiaMigrationService.TOPIA_SERVICE_NAME, TopiaMigrationEngine.class.getName()); + config.put(TopiaMigrationService.MIGRATION_CALLBACK, DatabaseMigrationClass.getName()); + +ou dans un fichier de properties : + +:: + + topia.service.migration=org.nuiton.topia.migration.TopiaMigrationEngine + topia.service.migration.callback=org.test.myapp.DatabaseMigrationClass + + +Kettle +------ + +Une autre idee est de ne pas utiliser hibernate mais kettle pour la +migration des données. Nous aurions de la même façon dans le nom des tables +un numero de version de schéma. Un fichier kettle decrirait la migration +d'une version à une autre. Et les différents fichiers serait chaînés pour +arriver au schéma souhaité. diff --git a/topia-persistence/src/site/rst/devel/Todo.rst b/topia-persistence/src/site/rst/devel/Todo.rst new file mode 100644 index 0000000..ceba050 --- /dev/null +++ b/topia-persistence/src/site/rst/devel/Todo.rst @@ -0,0 +1,255 @@ +.. - +.. * #%L +.. * ToPIA :: Persistence +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +==== +TODO +==== + +Recherches à faire +------------------ + +- des persistences hibernates sur LDAP ou FlatFile +- des outils de migration/evolution de schema + +Generic DAO +----------- +http://www.hibernate.org/328.html:: + + public abstract class GenericHibernateDAO<T> { + ... + Class<T> persistentClass = (Class<T>) ((ParameterizedType) getClass() + .getGenericSuperclass()).getActualTypeArguments()[0]; + + +Regarder si TopiaContext ne pourrait pas être remplacé par un PicoContainer +http://www.hibernate.org/180.html +http://www.hibernate.org/182.html + +doc sur l'optimisation des requetes HQL +http://www.jroller.com/page/wakaleo/?anchor=hibernate_optimisation + +Generation +---------- + +- (2) import/export XML dans ToPIA, à mettre dans le mapping hibernate (en partie fait, mais très stable...) + + +Gestion des versions des POJO +----------------------------- + +mettre en place serialVersionUID sur les entités + +Gestion des droits et de la sécurité +------------------------------------ + +Le créateur de l'objet n'est pas dans l'objet lui même mais dans une table a +part. + +Owner: topiaId de l'entity, id du propriétaire + +Les droits des objets sont dans une table a part +(voir http://www.hibernate.org/140.html). + +Il serait bon que les droits s'appliquent sur un group ou un user. Et qu'un user +puisse appartenir a un group, et qu'un group puisse aussi appartenir a un group + +Il faut aussi modifier le Policy ou autre pour pouvoir lire les permissions +dans hibernate lorsqu'on nous les demandes. Mettre des méthodes statique dans +une classe contenant une session hibernate statique pour permettre l'ajout +de permission a l'execution, sans qu'on est besoin de relire toutes les +permissions a chaque fois, la session servant de cache (mais les sessions +ne sont pas multithread d'ou le synchronise) + +synchronized public void addPermission(TopiaPermission); +synchronized public List<TopiaPermission> getAllPermission(); + +Entity directement dans Topia +----------------------------- + +- User: login, password, email +- Group: name, (User|Group)* +- Permission: action(create, load, update, delete, admin permission), classname, + principal, topiaId de l'entity ou null si le droit sur toutes les entites de ce type + + +Les services +------------ + +..image: ServiceCall.png + +Les services on besoin d'avoir le context de l'appelant pour pouvoir faire +des choix: +- choix de la base de données +- droit de l'utilisateur +- ... + +Pour cela le premier argument de chaque methode generee doit etre un token +retourner par le service d'authentification. + +Le service d'authentification (LA/SSO?) permet de recuperer les informations +du TopiaClientContext qui contient des informations utiles: +- applicationId (identifiant de l'application appelant (im.codelutin.com, im.libre-entreprise.com, ...)) +- clientId (identifiant du client appelant (gaim, exodus, gabber, ...)) +- userLogin +- userPassword + +On a un TopiaServiceManager qui permet de recuperer un proxy sur le service +souhaité. Le proxy n'a pas en premier argument ce TopiaClientContext, il est +ajouté automatiquement lors de l'appel. De cette maniere cote client cela +est completement transparent. + + +# Cote client +ChoremServiceHelper csh = ChoremServiceHelper.getInstance("codelutin.chorem.com", "mentawai", "poussin", "xxxxxxxx"); +CRMService crm = csm.getCRMService(); // retourne un proxy sur le service +List<Person> persons = crm.getAllPerson(); // retourne la liste de toutes les personnes visible par poussin + +# Cote serveur dans getAllPerson(String token) +TopiaClientContext tcc = TopiaAuthenticationService.getTopiaClientContext(token); +Properties prop = ChoremUtil.getContextProperties(tcc); +ChoremContext context = ChoremContext.getContext(prop); +List<Person> result = ChoremDAOHelper.getCRMDAO(context).getAllPerson(); +return result; + +Le moyen de recuperer le TopiaAuthentificationService doit etre specifier +dans les fichiers de configuration de l'application. Il doit y avoir +plusieurs implantation pour ce service: +- class avec methode static pour les applications standalone +- EJB +- service web +- ... + +L'application doit pouvoir plugger sa propre methode d'authentification +(acces a un LDAP, a une BD, a un Liberty Alliance, ...), chaque implantation +doit utiliser cette methode d'authentification (delegation). + +De meme pour l'implantation des services, les services doivent être des +classes Java normal (sans le token en parametre) mais contenant une methode +setTopiaContext() qui permet de mettre a jour + + +Autre +----- + +- tag-value transaction sur les operations des services avec les valeurs + traditionnelles de la spec EJB (required, requiresNew, mandatory, + supports, notSupported, never ). ex auto=required +- dernier user ayant modifié une entité +- tag-value auto avec un pattern sur les attributs. ex auto=now +- tag-value mask avec un pattern sur les attributs. ex : mask=price + puis dans les fichiers de traductions : price=#+,## +- tag-value enumName avec un pattern sur les attributs. ex : + enum=projectStatus + La valeur des énumérations est conservé dans un fichier de configuration + qui peut-etre surchargé par des valeurs dans une table en base de + données : projectStatus=a faire, fait, fini +- tag-value i18n avec un pattern sur les attributs. ex : i18n=true (en partie fait :) ) +- Generation des UI par defaut (JAXX et JSP) +- prendre en compte le contenu de l'onglet doc des entités et attributs + jusqu'au -- pour les tooltips (doc tooltips/doc user/doc dev) + +A reflechir (voir si c vraiment utile) +-------------------------------------- +- pouvoir monitorer un attribut (user, date, oldValue, new Value) + Ces attributs ont une valeur tagguée versioned à vrai ou faux. + -> getHistory[Attribut]():list<History> + +Amélioration templates +---------------------- + +- préférer définir des variables plutôt que d'injecter du code dans les templates : + +Example : + +avant + +:: + + /*{table="<%=GeneratorUtil.getDBName(attr.getDeclaringElement()) + "_" + getName(attr)%>"*/ + + +après + +:: + + String dbName = GeneratorUtil.getDBName(attr.getDeclaringElement()) + "_" + getName(attr); + +ou + +:: + + String attrName = getName(attr); + String dbName = GeneratorUtil.getDBName(attr.getDeclaringElement()); + + /*{table="<%=dbName+"_"+attrName%>"}*/ + +- mettre une javadoc pour dire ce que la méthode va faire + +- incorporer des tests unitaires de génération :) + +- créer des constantes pour les stereotypes tagValues et les utiliser... + +- aller à la chasse aux constantes (exemple propertiesPattern dans DAOAbstractGenerator) + +- faire la chasse au code inutilisé + +- ajouter hasTagValue sur ObjectModelElement dans lutingenerator + +- réfléchir comment générer les imports pour ne plus utilisers les FQN + +- vérifier les javadocs générées (ajouter les exceptions,...) + +- reussir a résoudre les casts dans les DAOAbstraxct sur la méthode getEntityClass + +- réusiner les codes de génération d'attributs : + +:: + + String lazy = " lazy=\""; + if (notEmpty(attr.getTagValue(GeneratorUtil.TAG_LAZY))){ + lazy += attr.getTagValue(GeneratorUtil.TAG_LAZY); + } else { + lazy += "true"; + } + lazy += "\""; + +mais il ya d'autres cas à prévoir... : + +:: + + String fetch = ""; + if (notEmpty(attr.getTagValue(GeneratorUtil.TAG_FETCH))){ + fetch = " fetch=\"" + attr.getTagValue(GeneratorUtil.TAG_FETCH) + "\""; + } + +- Dans lutingenerator, renommer Util en GeneratorUtil et dans ToPIA, + GeneratorUtil en TopiaGeneratorUtil + +- supprimer les les boucles sur Iterator quand c'est possible et utiliser pluot des for-each + +- étudier la nécessité des GeneratorUtil.toLowerCaseFirstLetter(assocAttrName) présents dans EntityAbstractGenerator à propos des classes d'assoc + +- TODO Check wether this method could be used to generate getter and setters + +- Uniformiser et faire hériter les générateurs des interfaces et classe d'impl pour que le générateur de la classe d'impl soit capable de générer aussi l'interface diff --git a/topia-persistence/src/site/rst/devel/event.rst b/topia-persistence/src/site/rst/devel/event.rst new file mode 100644 index 0000000..9fa83bf --- /dev/null +++ b/topia-persistence/src/site/rst/devel/event.rst @@ -0,0 +1,92 @@ +.. - +.. * #%L +.. * ToPIA :: Persistence +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +====================== +Gestion des évènements +====================== + +On peut se mettre listener sur deux sortes d'évènements : les TopiaEntityEvent et +les TopiaVetoableEntityEvent. La difference entre les deux se situe sur le moment de +l'appel. + + +TopiaVetoableEntityEvent +------------------------ + +Les TopiaVetoableEntityEvent sont appelés avant l'action, ce qui +permet de l'interdire en levant une exception (par exemple pour gérer la +sécurité). + +Les TopiaVetoableEntityEvent contiennent la classe à laquelle se rapporte l'event +et si possible l'identifiant de l'objet qui sera impacté. Cela n'est pas +toujours le cas; par exemple lors de la creation, l'identifiant n'existe pas forcément +et donc ne peut-etre donné. + +Propagation +~~~~~~~~~~~ + +Les TopiaVetoableEntityEvent sont aussi levés pour tous les contexts pères +du context qui a produit l'event et ceci juste apres avoir prévenu les +listeners du context courant. + +TopiaEntityEvent +---------------- + +Les TopiaEntityEvent sont appelés après l'action pour prévenir du changement. + +Les TopiaEntityEvent contiennent l'entity à laquelle se rapporte l'event + +Propagation +~~~~~~~~~~~ + +Les TopiaEntityEvent sont propagé au context pere lors du commit du context. + +Si un rollback est fait, alors ce sont les listeners du context eux-même qui +sont prévenus du changement. + +Implantation +============ + +Les DAO sont responsables de l'appel des méthodes fire sur le context qui les +a créés lors de l'appel sur ceux-ci des methodes create, update, delete, find +(pour le chargement). + +Le cas Hibernate +---------------- + +Dans le DAO hibernate le context est listener des différents évènements levés +par la session. Mais ces évènements ne sont levés que lors du commit. Dans Topia +on souhaite avoir directement un évènement lors d'un create, update ou delete +sur le DAO. Le DAO hibernate lève donc des évènements lorsque l'on appelle +ces méthodes. + +On a aussi des évènements lors du commit. Il est donc possible qu'on aie, +par exemple, le même évènement Update envoyé deux fois au listener si on fait +un update sur le DAO suivi du commit sur le context. + +On conserve tout de meme le mécanisme d'évènement levé au moment du commit par +hibernate car il est beaucoup plus puissant. Il permet aussi d'être prévenu de +modifications apportées aux constituants d'une entité et non pas seulement des +évènements portants sur l'entité elle-même comme c'est le cas dans le mecanisme +implanté dans les DAO. diff --git a/topia-persistence/src/site/rst/devel/project.rst b/topia-persistence/src/site/rst/devel/project.rst new file mode 100644 index 0000000..2c366eb --- /dev/null +++ b/topia-persistence/src/site/rst/devel/project.rst @@ -0,0 +1,35 @@ +.. - +.. * #%L +.. * ToPIA :: Persistence +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin, Chatellier Eric +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +============================================= +Liste de projets similaires ou se rapprochant +============================================= + +subPersistence +-------------- + +http://subpersistence.sourceforge.net/ + +Librairie d'abstraction de libraire de mapping O/R +Supporte Hibernate pour l'instant. Semble vouloir supporter aussi Castor. diff --git a/topia-persistence/src/site/rst/devel/security.rst b/topia-persistence/src/site/rst/devel/security.rst new file mode 100644 index 0000000..8f6a146 --- /dev/null +++ b/topia-persistence/src/site/rst/devel/security.rst @@ -0,0 +1,52 @@ +.. - +.. * #%L +.. * ToPIA :: Persistence +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +======== +sécurité +======== + +Implantation de la sécurité +=========================== + +La sécurité est un simple objet java qui hérite de TopiaHelper + +Il se met alors listener sur tous les events vetoables et lève des exceptions +si la personne qui essaie de charger/créer/modifier/supprimer l'objet n'en a pas +le droit. + +Le service de sécurité vient avec son fichier de configuration. Dans ce fichier +il y a les paramètres utiles au service pour retrouver les informations sur les +droits. + +TopiaHelper est en fait une interface qui contient la méthode +init(TopiaContext, Properties) + + +package +------- + +:org.nuiton.topia.security: pour tout ce qui touche a la sécurité + (TopiaAuthenticationHelper, TopiaAuthorisationHelper, LoginModule, Filter, ...) +:org.nuiton.topia.security.entities: pour les entities utiles à la définition + de la sécurité (user, group, permission) diff --git a/topia-persistence/src/site/rst/index.rst b/topia-persistence/src/site/rst/index.rst new file mode 100755 index 0000000..5ac01a4 --- /dev/null +++ b/topia-persistence/src/site/rst/index.rst @@ -0,0 +1,113 @@ +.. - +.. * #%L +.. * ToPIA :: Persistence +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +===== +ToPIA +===== + +.. contents:: + +Présentation +------------ + +ToPIA, pour Tools for Portable and Independant Architecture, est un framework +d'abstraction des plateformes techniques. + +C'est à dire ? +~~~~~~~~~~~~~~ + +Le cycle de développement en Y traditionnel peut être représenté de la manière +suivante : + +Les deux branches, la branche fonctionnelle, porteuse de la solution logique, et +la branche technique, qui définit la plateforme technique sur laquelle la +solution sera implantée, se rejoignent en amont de la conception détaillée et du +codage. Cette projection peut être accompagnée de génération de code, mais il +reste toujours une certaine quantité de code écrite à la main. + +Cette séparation des responsabilités et ce processsus donne pleine satisfaction +lors du développement initial d'un logiciel. La vie d'un logiciel commence +toutefois au moment de sa mise en production et les évolutions qui y seront +apportées sont de deux natures : + - Évolutions fonctionnelles : Ajout de fonctionnalités, correction de bugs, ... + - Evolutions techniques : Changement de bases de données, changement de techno + sur les UI, nouveaux accès (SOAP ?), ... + +Si les évolutions fonctionnelles sont prises en compte par la nature itérative +des processus de développement, les évolutions techniques sont elles difficiles. + +Comment passer d'hibernate à JDO, de Swing au client léger, des EJB aux +conteneurs légers ? En supprimant les dépendances de votre code sur les briques +techniques, en codant sur une plateforme technique abstraite, interfaces et +facades au travers desquels vous pourrez manipuler les différentes solutions +techniques que vous retiendrez. Hibernate et JDO ne sont que deux API d'accès +aux données; Swing et client léger, des UI; les EJB et les conteneurs légers, +des logiques métiers déportées... + +Et ToPIA alors ? +~~~~~~~~~~~~~~~~ + +ToPIA propose une telle plateforme. Vous codez en regard d'une API qui abstrait +la distribution (1-tiers ?, 2-tiers ?, 3-tiers ?, n-tiers ? quels protocoles +d'accès ?), la persistence (quelles bases bien sûr, mais également quel +framework ?, quelle API de requètage (EJB-QL, JDO-QL, ... ?), quel méchanisme +transactionnel ?), quelle UI ? Puis vous projettez votre application sur la +palteforme de votre choix. + +Fonctionnement classique +------------------------ + +Le framework ToPIA permet dans un projet de générer la partie métier de l'application, +c'est à dire les classes JAVA ainsi que le mapping Hibernate pour la persistance. +Cela représente un travail considérable en moins pour le développeur et une +flexibilité importante dans les évolutions futures de la parties métier. + +L'utilisation du framework ToPIA se découpe en plusieurs phases, elles sont +itératives : + 1. Création du diagramme de classe sur ArgoUML_ par exemple + 2. Exportation du modèle en XMI + 3. Génération des fichiers JAVA et Hibernate par Eugene_ + 4. Implentation des méthodes + +ToPIA-service +------------- + +Le framework ToPIA peut être complèté par une multitude de services. Il existe +actuellement trois : + + - `service pour la sécurité`_ : permet la gestion des authentification et des + autorisations. Il repose sur le mécanisme de JAAS. + - `service pour la gestion des mises à jour`_ : permet de mettre à jour une version + de base à partir d'un modèle versionné. + - `service de réplication`_ : permet de répliquer des données de base à base. + - ``service pour la gestion d'un historique`` : permet de conserver l'ensemble des + actions réalisées sur la base de données (**plus maintenu depuis la 2.3.3**). + - ``service pour la recherche`` : permet de rechercher un mot clé parmis les entités + de l'application. Il repose sur la librairie lucene d'apache (**plus maintenu depuis la 2.3.3**). + +.. _service pour la sécurité: ../topia-service-security +.. _service pour la gestion des mises à jour: ../topia-service-migration +.. _service de réplication: ../topia-service-replication +.. _Eugene: http://maven-site.nuiton.org/eugene/ +.. _ArgoUML: http://argouml.tigris.org/ diff --git a/topia-persistence/src/site/rst/user/FAQ.rst b/topia-persistence/src/site/rst/user/FAQ.rst new file mode 100644 index 0000000..ae87291 --- /dev/null +++ b/topia-persistence/src/site/rst/user/FAQ.rst @@ -0,0 +1,60 @@ +.. - +.. * #%L +.. * ToPIA :: Persistence +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +=== +FAQ +=== + +Problème lors du chargement d'une entity +======================================== + +Le chargement utilise les méthodes Set des propriétés, il faut donc faire +attention si on les surcharge. + +Il faut faire attention lors de l'ajout, sur l'entity, de méthodes qui ont la +même signature que des méthods Set de propriétés avec juste des arguments +différents, car elles peuvent être utilisées à la place de la vrai méthode +pour quelque valeur (par exemple null). + +Faire fonctionner les Web Services Topia via RMI +================================================ +L'utilisation des Web Services sur RMI impose la génération de classes sauvées +sur disque. +Elle sont construites à la volée et sauvées dans le dossier "./topiagen". + +Ce dossier doit donc se trouver dans le classpath. + +Comment commit (ou rollback) une transaction directement à partir d'un DAO ? +============================================================================ + +Contrairement à ce que l'on pourrait croire aux premiers abords, les méthodes commitTransaction() +et rollbackTransaction() disponibles dans les DAO ne sont pas prévus à cet effet. Il faut toujours +utiliser le contexte pour commit (ou rollback) une transaction. Donc pour le faire à partir d'un DAO : +dao.getContext().commitTransaction() // on récupère le context (transaction sur le DAO) et on commit. +Le principe reste identique pour le rollback. +Pour revenir sur les méthodes commitTransaction() et rollbackTransaction() disponibles sur le DAO, elles +servent de "callback" au moment du commit (ou rollback) de la transaction. Le context se chargera d'appeler +tous les commitTransaction() des DAO, ceci afin de permettre un traitement spécifique au DAO au moment du commit. +Ce traitement peut donc être effectué directement à partir du dao via cette méthode sans passer par le context. + diff --git a/topia-persistence/src/site/rst/user/ModelGeneration.rst.vm b/topia-persistence/src/site/rst/user/ModelGeneration.rst.vm new file mode 100644 index 0000000..e9781d9 --- /dev/null +++ b/topia-persistence/src/site/rst/user/ModelGeneration.rst.vm @@ -0,0 +1,102 @@ +.. - +.. * #%L +.. * ToPIA :: Persistence +.. * +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2010 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +====================== +Génération des modèles +====================== + + +Le module "topia-persistence" de topia est capable de +générer la persistence à partir d'un modèle UML. + +Nous allons détailler ici comme inclure la génération dans le +cycle de compilation maven. + + +Architecture maven +------------------ + ++---------------------------------+-----------------------------------------+ +| src/main/xmi/mymodel.zargo | exemple de modele UML avec argoUML | ++---------------------------------+-----------------------------------------+ +| src/main/xmi/mymodel.properties | fichier de propriétés attaché au modele | ++---------------------------------|-----------------------------------------+ + + +Configuration du pom.xml +------------------------ + +Pour générer les sources dans la phase "generate-sources" de maven, et utiliser +les sources générées, et faut configurer le pom. + +Plugin eugene +============= + +:: + +<plugin> + <groupId>org.nuiton.eugene</groupId> + <artifactId>eugene-maven-plugin</artifactId> + <version>${eugeneVersion}</version> + <executions> + <execution> + <id>Generator</id> + <phase>generate-sources</phase> + <configuration> + <inputs>zargo</inputs> + <fullPackagePath>org.company.package</fullPackagePath> + <extractedPackages>org.company.package</extractedPackages> + <templates>org.nuiton.topia.generator.TopiaMetaGenerator</templates> + <defaultPackage>org.company.package</defaultPackage> + </configuration> + <goals> + <goal>generate</goal> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.nuiton.topia</groupId> + <artifactId>topia-persistence</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> +</plugin> + +Pour plus d'information à propos d'eugene, merci de consulter le site : +http://maven-site.nuiton.org/eugene/ + +Dépendances du projet +===================== + +Il faut enfin ajouter "topia-persistence" en dépendance du projet : + +:: + +<dependency> + <groupId>org.nuiton.topia</groupId> + <artifactId>topia-persistence</artifactId> + <version>${project.version}</version> +</dependency> diff --git a/topia-persistence/src/site/rst/user/TopiaQuery.rst b/topia-persistence/src/site/rst/user/TopiaQuery.rst new file mode 100644 index 0000000..ed43891 --- /dev/null +++ b/topia-persistence/src/site/rst/user/TopiaQuery.rst @@ -0,0 +1,543 @@ +.. - +.. * #%L +.. * ToPIA :: Persistence +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +TopiaQuery +========== + +:Author: Florian Desbois +:Contact: topia-devel@list.nuiton.org ou topia-users@list.nuiton.org +:Revision: $Revision$ +:Date: $Date$ + +L'objet TopiaQuery permet de créer plus simplement des requêtes HQL pour éviter +les concaténations complexes lors de l'utilisation de la méthode **find** +du TopiaContext. + +Chacune des parties de la requête sont indépendantes et seront concaténés au +moment de l'exécution. Il est donc possible d'ajouter des éléments à la +requête à n'importe quel moment de sa construction (from, select, where, order, +group, ...). + +La TopiaQuery peut donc être construite à un endroit et exécutée à un autre. +Une même requête peut également être réutilisée pour être passée en sous-requête +ou exécutée plusieurs fois à la suite avec certains légers changement de +paramètres. + +.. contents:: + +Modèle exemple +-------------- + +Ci-dessous un modèle simple utilisé pour les exemples de cette documentation. + +.. image:: modelForTopiaQuery.png + +Instantiation +------------- + +La TopiaQuery nécessite obligatoirement une entité référence pour être exécutée. +Cette entité correspondra à l'élément principal du FROM de la requête et si +nécessaire sera ajouté automatiquement au SELECT. + +Il y a plusieurs façons d'instancier la TopiaQuery : + +Directement depuis un DAO :: + + TopiaContext transaction = rootContext.beginTransaction(); + BoatDAO dao = ModelDAOHelper.getBoatDAO(transaction); + TopiaQuery query = dao.createQuery(); + +ou depuis un topiaContext :: + + TopiaContext transaction = rootContext.beginTransaction(); + TopiaQuery query = transaction.createQuery(Boat.class, "B"); + +L'intérêt de passer par un DAO est de pouvoir par la suite executer la requête +avec ce même DAO. Il est également possible d'utiliser des alias pour l'élément +principal de la requête pour pouvoir plus facilement gérer les cas de jointure. +Il suffit de préciser l'alias au moment de l'instanciation :: + + TopiaContext transaction = rootContext.beginTransaction(); + BoatDAO dao = ModelDAOHelper.getBoatDAO(transaction); + TopiaQuery query = dao.createQuery("E"); + +Ajout d'éléments au WHERE +------------------------- + +Les méthodes de base nécessaires concernent l'ajout d'élements dans le WHERE de la +requête. Plusieurs méthodes sont disponibles suivant les besoins pour ajouter +simplement un élément au where :: + + TopiaQuery query = boatDAO.createQuery(); + + // Recherche sur l'immatriculation du navire : immatriculation = 142154 + query.addEquals("immatriculation", 142154); + + // Recherche toutes les dates de construction < 2006 + query.addWhere("buildYear", Op.LT, 2006); + + // Recherche des navires ayant un nom + query.addNotNull("name"); + + // depuis 2.3.4 + // Recherche des navires n'ayant pas de nom + query.addNull("name"); + + // Recherche des navires ayant une date de construction 2003, 2004 ou 2006 + query.addEquals("buildYear", 2003, 2004, 2006); + + // depuis 2.3.4 + // Recherche entre deux dates + TopiaQuery queryContact = contactDAO.createQuery(); + Calendar dateBegin = new GregorianCalendar(2010,2,3); + Calendar dateEnd = new GregorianCalendar(2010,5,6); + queryContact.addBetween("creationDate", dateBegin.getTime(), dateEnd.getTime()); + + // depuis 2.3.4 + // Utilisation d'une sous-requête (les paramètres de la sous-requête seront + // ajoutés automatiquement à la requête principale en gérant les doublons + // (sur la valeur et la clé)). + // Le ? correspond à l'endroit ou sera injecté la requête, attention aux + // parenthèses. + queryContact.addSubQuery("boat IN elements(?)", query); + +Il est fortement conseillé d'utiliser les constantes des entités pour les noms +de leurs propriétés :: + + query.addEquals(Boat.IMMATRICULATION, 142154); + query.addNotNull(Boat.NAME); + ... + +La TopiaQuery peut être chaînée, les méthodes permettant l'ajout d'éléments +renvoient toutes la même TopiaQuery avec l'élément ajouté :: + + TopiaContext transaction = rootContext.beginTransaction(); + BoatDAO dao = ModelDAOHelper.getBoatDAO(transaction); + dao.createQuery().addEquals(Boat.IMMATRICULATION, 142154).addNotNull(Boat.NAME); + +Opérateurs +---------- + +Une enum interne à la TopiaQuery permet de manipuler les opérateurs nécessaires +aux ajouts dans le WHERE : + +- TopiaQuery.Op.EQ : Opérateur = +- TopiaQuery.Op.GT : Opérateur > +- TopiaQuery.Op.GE : Opérateur >= +- TopiaQuery.Op.LIKE : Opérateur LIKE +- TopiaQuery.Op.LT : Opérateur < +- TopiaQuery.Op.LE : Opérateur <= +- TopiaQuery.Op.NOT_NULL : Opérateur IS NOT NULL +- TopiaQuery.Op.NULL : Opérateur IS NULL +- TopiaQuery.Op.NEQ : Opérateur != + +Autres parties de la requête +---------------------------- + +Ajout d'élément au FROM +~~~~~~~~~~~~~~~~~~~~~~~ + +Il est souvent nécessaire d'ajouter une autre entité au FROM de la requête, +pour ce faire, il existe deux méthodes : + +- addFrom(Class entityClass) : ajoute une entité au FROM + (Ex : addFrom(Contact.class);) + +- addFrom(Class entityClass, String alias) : ajoute une entité au FROM avec un + alias (Ex : addFrom(Contact.class, "C");) + +C'est généralement la dernière méthode qui sera la plus utilisé, l'utilisation +des alias facilitant grandement les liaisons entre les entités. + +Depuis la 2.3.4, deux méthodes ont été rajoutés pour le cas des jointures : + +- addJoin(Class entityClass, String alias, boolean fetch) : utilise un inner + join pour lier une entité à celle de la requête. Concrètement l'opérateur + JOIN Hql sera utilisé : 'FROM Contact C JOIN C.boat' :: + + TopiaContext transaction = rootContext.beginTransaction(); + ContactDAO dao = ModelDAOHelper.getBoatDAO(transaction); + TopiaQuery query = dao.createQuery("C").addJoin("C.boat", null, false); + +- addLeftJoin(Class entityClass, String alias, boolean fetch) : même chose que + le addJoin sauf que l'opérateur LEFT JOIN Hql sera utilisé. + + +Voir le `Chargement des donnees` pour l'utilisation du fetch. + +Ajout d'élément au SELECT +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Deux méthodes sont disponibles pour le cas du SELECT, une méthode addSelect +qui se chargera d'ajouter une propriété au SELECT, et une méthode setSelect +qui définira directement quel est le SELECT souhaité. Pour le cas du addSelect, +la méthode gère automatiquement l'entité principale utilisé pour instancier +la requête, il n'est donc pas nécessaire de l'ajouter manuellement. + +TODO : find an example + +Le setSelect quant à lui est utilisé pour les aggregations par exemple ou +pour récupérer des parties précises du résultat :: + + TopiaContext transaction = rootContext.beginTransaction(); + BoatDAO dao = ModelDAOHelper.getBoatDAO(transaction); + // On souhaite connaître le nombre de résultats uniquement + TopiaQuery query = dao.createQuery(). + addNotNull(Boat.NAME). + setSelect("COUNT(*)"); + +autre exemple :: + + // On souhaite récupérer uniquement les noms des navires + TopiaQuery query = dao.createQuery(). + addNotNull(Boat.NAME). + setSelect(Boat.NAME); + +Note + Pour l'ajout d'une contrainte sur l'unicité des résultats, vous pouvez + utiliser la méthode addDistinct() qui permet l'ajout du mot clé DISTINCT sur + le SELECT de la requête. + +Ajout d'élément au GROUP BY +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Dans certains cas, il peut être nécessaire d'utiliser un GROUP BY +pour les aggregations :: + + TopiaContext transaction = rootContext.beginTransaction(); + ContactDAO dao = ModelDAOHelper.getContactDAO(transaction); + // On souhaite connaître le nombre de contacts par navire + TopiaQuery query = dao.createQuery(). + setSelect("COUNT(*)"). + addGroup(Contact.BOAT); + +Ajout d'élément au ORDER BY +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Il est possible également d'ajouter un ordre aux résultats :: + + TopiaContext transaction = rootContext.beginTransaction(); + BoatDAO dao = ModelDAOHelper.getBoatDAO(transaction); + // Tous les noms des navires triés + TopiaQuery query = dao.createQuery(). + addNotNull(Boat.NAME). + setSelect(Boat.NAME). + addOrder(Boat.NAME); + +Une autre méthode est disponible pour l'ordre descendant : addOrderDesc. +Cependant rien ne vous interdit la syntaxe suivante :: + + addOrder(Boat.NAME, Boat.BUILD_YEAR + " desc"); + +Requête complexe +---------------- + +L'intérêt majeur de la TopiaQuery est de pouvoir la manipuler à travers des +méthodes tout en lui ajoutant des éléments suivant certaines conditions. +Il peut être également utile d'utiliser une TopiaQuery comme sous-requête d'une +autre. De plus certains mots clés comme EXISTS ou encore l'utilisation de +méthode HQL ne possèdent pas leurs propres méthodes. Vous pouvez cependant +utiliser la méthode de base **addWhere(String str)** qui permet l'ajout au WHERE +directement (avec ajout automatique des parenthèses). Dans ce cas, il est +souvent nécessaire d'ajouter des paramètres HQL à la requête (:monParam) qui +devront être ajouté à la TopiaQuery en utilisant la méthode +**addParam(String name, Object value)**. Ex :: + + TopiaContext transaction = rootContext.beginTransaction(); + ContactDAO dao = ModelDAOHelper.getContactDAO(transaction); + Date beginDate = ... + Date endDate = ... + TopiaQuery query1 = dao.createQuery("C"). + addWhere("C." + Contact.VALIDATION + " IS NOT NULL OR " + + "C." + Contact.CREATION_DATE + " BETWEEN :begin AND :end"). + addParam("begin", beginDate).addParam("end", endDate); + +La méthode **fullQuery()** permettra de récupérer la requête sous forme +de chaîne pour pouvoir la manipuler comme sous-requête. Il est possible +également de récupérer les paramètres via la méthode **getParams()** pour +les rajouter à la requête global :: + + // suite du code précédent + // On souhaite la date de création la plus récente sur la requête précédente + query1.setSelect("MAX(C." + Contact.CREATION_DATE + ")"); + + // Préparation de la deuxième requête + TopiaQuery query2 = dao.createQuery("C2"); + + // sélection spécifique pour un navire + if (immatriculation != null) { + query2.addEquals("C2." + Contact.BOAT + "." + Boat.IMMATRICULATION, + immatriculation); + + // Ajout d'une condition sur le navire dans la première requête + query1.addWhere("C." + Contact.BOAT + " = C2." + Contact.BOAT); + } + + // Utilisation de la première requête comme sous-requête + query2.add("C2." + Contact.CREATION_DATE + " = (" + query1.fullQuery() + ")"); + // Ajout des paramètres nécessaires de la première requête dans la deuxième + query2.addParams(query1.getParams()); + +Depuis la 2.3.4, la méthode **addSubQuery(String statement, TopiaQuery subquery)** +permet de faciliter l'injection d'une sous-requête notamment pour la gestion +des paramètres automatiquement (avec prise en charge des doublons) :: + + // Même chose que précédemment en utilisant la méthode addSubQuery + query2.addSubQuery("C2." + Contact.CREATION_DATE + " = (?)", query1); + +La requête sous forme HQL :: + + SELECT C2 FROM Contact C2 + WHERE C2.boat.immatriculation = :immatriculation + AND C2.creationDate = (SELECT MAX(C.creationDate) FROM Contact C + WHERE (C.validation IS NOT NULL OR + C.creationDate BETWEEN :begin AND :end) + AND C.boat = C2.boat); + +Attention + Il ne faut pas utiliser la méthode addEquals(String str, Object value) pour + une comparaison entre deux propriétés comme précédemment : + *query1.addWhere("C." + Contact.BOAT + " = C2." + Contact.BOAT)*. + L'appel *query1.addEquals("C." + Contact.BOAT, "C2." + Contact.BOAT)* ne + fonctionnera pas comme souhaité. + +Résultats +--------- + +Plusieurs méthodes sont disponibles pour récupérer les résultats de la requête. +Pour chaque méthode, il est nécessaire de l'appeler avec le contexte +topia. La méthode de base est la méthode execute() qui renvoie +une liste non typé à l'instar de la méthode find(...) du TopiaContext. Il +est cependant possible de récupérer directement un objet, un entier (pour +un aggregat par exemple) ou une chaîne de caractères suivant le select de la +requête. Pour le count très utile dans de nombreux cas, il est mis à disposition +la méthode **executeCount()** qui se chargera de remplacer temporairement le +select par un COUNT(*). L'avantage c'est que votre requête ne perd pas son +SELECT d'origine pour pouvoir être par exemple executé de façon différente par +la suite. + +Note + Si la requête contient un DISTINCT (via la méthode addDistinct()), le + executeCount() gèrera automatiquement la contrainte sur la requête : + "SELECT COUNT(DISTINCT B) FROM Boat B ..." + +Limitation des résultats +~~~~~~~~~~~~~~~~~~~~~~~~ + +Il est possible de limiter le nombre de résultats lors de l'exécution pour +optimiser la requête dans le cas d'une pagination par exemple. Pour ce faire +il faut utiliser les méthodes **setLimit(int start, int end)** et/ou +**setMaxResults(int max)** :: + + // 18 premiers résultats + query.setLimit(0, 17); + // équivalent à + query.setMaxResults(18); + // résultats du 50ème au 60ème + query.setLimit(49, 59); + +// depuis 2.3.4 + +Pour éviter d'embarquer ces paramètres à chaque fois qu'ils sont nécessaires +lors d'un filtrage paginée, un bean, *EntityFilter* est disponible. +Il contient les attributs suivants : + +- startIndex : index de début des résultats. + +- endIndex : index de fin des résultats. + +- orderBy : propriétés à ordonner (l'ajout des mots clés 'asc' et 'desc' est + possible). + +- referenceId : identifiant d'une référence utile à la requête. + +- referenceProperty : nom de la propriété correspondant à la valeur du referenceId. + +Exemple : Nous souhaitons les contacts 30 à 60 triés par 'creationDate' décroissant et +'personName' croissant pour un navire donné par son topiaId :: + + EntityFilter filter = new EntityFilter(); + filter.setStartIndex(30); + filter.setEndIndex(60); + filter.setOrderBy("creationDate desc, personName"); + filter.setReferenceId(boat.getTopiaId()); + // ou filter.setReference(boat); + filter.setReferenceProperty("boat"); + + TopiaQuery query = contactDAO.createQuery().addFilter(filter); + +L'intérêt de l'EntityFilter est de pouvoir l'instancier et le manipuler directement +depuis votre interface (Swing, Web, ...) et de l'utiliser sur une requête métier. +La méthode **addFilter(EntityFilter filter)** permettra d'injecter les paramètres +s'ils possèdent une valeur. + +Note + L'ordre définit par défaut est celui de création par ordre décroissant : + *topiaCreateDate desc*. + +Utilisation des DAO +~~~~~~~~~~~~~~~~~~~ + +Les DAO fournissent également quelques méthodes permettant de récupérer plus +facilement les résultats avec le type souhaité : + +- **countByQuery(TopiaQuery query)** : compte le nombre de résultats de la + requête. +- **existByQuery(TopiaQuery query)** : renvoie vrai si la requête à retourner + au moins 1 résultat. +- **findByQuery(TopiaQuery query)** : renvoie une entité (un seul résultat) +- **findAllByQuery(TopiaQuery query)** : renvoie une liste d'entités +- **findAllMappedByQuery(TopiaQuery query)** : renvoie une map d'entités avec + pour clé le topiaId de l'entité. +- **findAllMappedByQuery(TopiaQuery query, Class keyClass, String keyProperty)** : + renvoie une map d'entités avec pour clé la propriété passée en argument. + +Exemple :: + + TopiaContext transaction = rootContext.beginTransaction(); + BoatDAO dao = ModelDAOHelper.getBoatDAO(transaction); + TopiaQuery query = dao.createQuery(); + ... + // pour vérifier l'existance de résultat + boolean hasResult = dao.existByQuery(query); + + // pour savoir le nombre de résultats + int count = dao.countByQuery(query); + + // pour récupérer les résultats + Map<String, Boat> boatMap = dao.findAllMappedByQuery(query); + // ou + List<Boat> boatList = dao.findAllByQuery(query); + // ou juste le premier résultat + Boat boat = dao.findByQuery(query); + // ou avec pour clé l'immatriculation du navire (unique) + Map<Integer, Boat> boatMapImma = dao.findAllMappedByQuery(query, + Integer.class, Boat.IMMATRICULATION); + + +Résultats complexes +~~~~~~~~~~~~~~~~~~~ + +Certains cas de requête peuvent avoir des résultats plus complexes, notamment +lorsqu'il s'agit de propriétés de différentes entités ou avec l'utilisation d' +aggrégats (AVG, SUM, COUNT). Dans ce cas il faut utiliser la méthode de base +**findByQuery(TopiaQuery query)** depuis une transaction +qui renverra une liste non typée. Lorsqu'il y a plus d'un élément dans le +select la liste renvoyée est une List<Object[]>, le tableau pour chaque ligne +correspondant aux valeurs des résultats. Exemple :: + + TopiaContext transaction = rootContext.beginTransaction(); + ContactDAO dao = ModelDAOHelper.getContactDAO(transaction); + String boatImma = Contact.BOAT + "." + Boat.IMMATRICULATION; + // On souhaite le nombre de contacts par navire + TopiaQuery query = dao.createQuery(). + setSelect(boatImma, "COUNT(*)").addGroup(boatImma); + + List<Object[]> results = transaction.findByQuery(query); + // Parcours des résultats + for (Object[] result : results) { + Integer immatriculation = (Integer)result[0]; + Long count = (Long)result[1]; + } + +Note + Les aggrégats renvoient principalement un type Long et non Integer. + +Chargement des donnees +---------------------- + +Généralement une fois la requête exécutée, la transaction utilisée est directement +fermée (topiaContext.closeContext()). Dans ce cas, il est souvent nécessaire de +charger certaines entités pour éviter les malencontreuses LazyException d'Hibernate. +Plusieurs possibilités s'offrent à vous : + +Chargement automatique +~~~~~~~~~~~~~~~~~~~~~~ + +Hibernate permet de déclarer explicitement que certaines relations doivent se +charger dès la récupération des entités. Il faut pour cela utiliser le tagValue +*lazy* dans le fichier de properties du modèle. Par exemple pour charger +le navire associé à chaque contact récupéré via une requête, il faut préciser :: + + myapp.entity.Contact.attribute.boat.tagvalue.lazy=false + +(*myapp.entity.Contact* étant le nom qualifié de la classe Contact) + +Ainsi chaque contact récupéré aura automatiquement son navire associé de chargé. + +**Attention** cependant, il ne faut pas abuser du tagvalue *lazy* car sinon Hibernate +risque de charger une bonne partie de votre base de données à chaque fois, ce qui +peut s'avérer extrêment coûteux. Cette utilisation doit être limitée au cas d'une +simple association comme c'est ici le cas, le Boat chargé étant **indispensable** +à l'utilisation du Contact. + +Chargement manuel +~~~~~~~~~~~~~~~~~ + +La TopiaQuery fournit deux méthodes intéressantes pour le chargement manuelle : + + - addLoad(String...) : permet de charger les relations une fois la requête + exécutée. + + - addFetch(String...) : permet de charger les relations directement au moment + de la requête en utilisant une jointure et le mot clé 'FETCH' Hql. + +Le addLoad est pour le moment limité à des relations unitaires (autant que votre +modèle vous le permet, ex : entityA.entityB.entityC) ou à une seule relation +multiple directe ou indirecte (entityA.entitiesB). Si nous prenons le cas du +modèle d'exemple, il est possible de charger le navire associé aux contacts en +utilisant : queryContact.addLoad(Contact.BOAT); + +Dans le cas du addLoad, plusieurs requêtes seront exécutées suivant le nombre +de Contact résultats. Il est dans ce cas plus judicieux d'utiliser le addFetch +qui chargera directement les Boat au moment de la récupération des Contact : +queryContact.addFetch(Contact.BOAT); L'alias peut s'avérer indispensable pour +l'utilisation du addFetch, voir la javadoc pour l'utilisation des arguments. + +Note + Les méthodes de jointures permettent de faire un fetch directement : + addJoin(String property, String alias, boolean fetch); + Voir la partie de la documentation concernant le FROM de la requête. + +Attention + Hibernate ne supporte pas plus de 3 ou 4 jointures suivant leurs complexités ! + Il est donc important de limiter l'utilisation des fetch aux cas simples. + +Que choisir ? +~~~~~~~~~~~~~ + +- Relation unitaire (N-1) obligatoire : utiliser le tagValue *lazy* + +- Requête relativement simple (moins de deux jointures) : utiliser le addFetch + +- Requête complexe : utiliser le addLoad + +- Si addLoad non utilisable (trop de chargement de collections) : effectuer un + chargement manuel en parcourant les résultats. Pour le chargement d'une + entité simple, il suffit d'utiliser le getter correspondant pour la charger, + tandis que pour une collection, l'appel à la méthode size associée permet de + charger l'intégralité des éléments. + + + diff --git a/topia-persistence/src/site/rst/user/continue_devel.rst b/topia-persistence/src/site/rst/user/continue_devel.rst new file mode 100644 index 0000000..a0d3646 --- /dev/null +++ b/topia-persistence/src/site/rst/user/continue_devel.rst @@ -0,0 +1,56 @@ +.. - +.. * #%L +.. * ToPIA :: Persistence +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +========================== +Continuer le développement +========================== + +Vous avez maintenant les clés pour développer votre application. Voici quelques +éléments qui vous permettront de poursuivre les développements. + +Gestion des évènements +====================== + +TODO + +Les visiteurs d'entités +======================= + +TODO + +Écrire une migration +==================== + +TODO + +Débugguer l'application +======================= + + * Ajouter ``-Deugene.verbose`` sur la ligne de commande pour voir ce que + EUGene fait pendant la phase de génération. + + * Ne pas hésiter à examiner les fichiers ``.hbm.xml`` générés pour voir + s'ils correspondent bien à ce que vous attendiez. De même, on + peut examiner le fichier ``.objectmodel`` pour voir si l'interprétation + du fichier zargo est bien celle qu'on attend. diff --git a/topia-persistence/src/site/rst/user/extend_model.rst b/topia-persistence/src/site/rst/user/extend_model.rst new file mode 100644 index 0000000..ec94964 --- /dev/null +++ b/topia-persistence/src/site/rst/user/extend_model.rst @@ -0,0 +1,141 @@ +.. - +.. * #%L +.. * ToPIA :: Persistence +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +================= +Étendre le modèle +================= + +Nous avons réussi à manipuler les entités d'un modèle simple. Voyons comment +rendre notre modèle plus complet pour nous rapprocher de l'utilisation de ToPIA +dans le contexte d'une véritable application. + +Ajouter une méthode à une entité +================================ + +Dans ArgoUML, vous pouvez ajouter une méthode sur une entité. Pour cela, +ajouter la méthode dans le modèle, sur l'entité elle-même. Si besoin, il +faudra ajouter au modèle les types des paramètres ou de la valeur de retour. + +ToPIA utilise le patron de conception `Generation Gap`_ pour séparer le code +généré du code écrit par le développeur. Ainsi, le ré-génération n'écrase +que du code généré et jamais du code utilisateur. Vous ne devez jamais +modifier les classe générées mais seulement toucher aux implémentations +qui héritent de ces classes. + +Vous remarquerez que ToPIA a bien ajouté la méthode dans l'interface de +l'entité mais à laisser l'implémentation abstraite. C'est donc à vous +d'écrire le code dans le Impl. + +Forcer le chargement d'une association +====================================== + + + +Utiliser une clé métier pour identifier les entité (naturalId) +============================================================== + +Chaque entité possède une cré primaire topiaId, mais dans certains cas il est +important d'avoir une notion d'unicité sur une ou plusieurs propriétés d'une +entité, ce qu'on appele une clé métier. Le comportement souhaité serait de +préserver l'unicité sur ces propriétés ainsi que la création d'un index pour +optimiser l'accès aux données. + +Il est donc nécessaire de préciser cette notion au niveau du modèle, cela +par le biais du tagValue (également propriété hibernate) **naturalId** :: + + fr.ird.observe.entities.referentiel.ParametrageTaillePoidsFaune.class.tagvalue.naturalIdMutable=false + fr.ird.observe.entities.referentiel.ParametrageTaillePoidsFaune.attribute.sexe.tagvalue.naturalId=true + fr.ird.observe.entities.referentiel.ParametrageTaillePoidsFaune.attribute.ocean.tagvalue.naturalId=true + fr.ird.observe.entities.referentiel.ParametrageTaillePoidsFaune.attribute.ocean.tagvalue.notNull=false + fr.ird.observe.entities.referentiel.ParametrageTaillePoidsFaune.attribute.espece.tagvalue.naturalId=true + +Ici, les propriétés "sexe", "ocean" et "espece" forment la clé métier de +l'entité "ParametrageTaillePoidsFaune". Par défaut les propriétés +d'une clé métier sont non null, mais il est possible de préciser dans certains +cas qu'une propriété peut l'être (comme ici avec "ocean"). + +Le naturalId provoque trois résultats : + +- création d'un index unique en base. +- vérification dans hibernate de l'intégrité de l'entité à la création : + il est donc nécessaire de créer l'entité avec des valeurs pour les propriétés + de la clé métier (méthode create sur le dao). +- impossibilité de modifier les valeurs de ces propriétés sur une entité + existante ; à moins de le préciser dans la configuration via le tagValue + "naturalIdMutable" placé à "true" (déconseillé). + +Le DAO de l'entité se voit ajouté les méthodes suivantes : + +- createByNaturalId : qui permet de crée l'entité avec tout les naturalId +- createByNotNull : qui permet de crée l'entité avec tout les naturalId not null (donc obligatoire) +- existByNaturalId : qui permet de vérifié que l'entité correspondant au naturalId existe +- findByNaturalId : qui permet de chercher l'entité sur les naturalId + +Ajouter des requêtes complexes +============================== + +Ajouter une méthode à un DAO +---------------------------- + +Il est possible d'étendre un DAO avec des méthodes définies à partir du modèle +(méthode différente entre version 2.2 et 2.4). + +Il suffit de créer une interface contenant les méthodes à ajouter au dao. +Cette interface doit avoir le stéréotype <<dao>>. De plus l'interface doit +dépendre (lien de dépendance nommé explicitement 'dao') de la classe de l'entité. +Le DAOImpl ne sera pas généré et devra être défini à la charge du développeur. + +Exemple (présent dans topiatest.zargo dans les sources de topia-persistence) : + +Ajout d'une méthode findAllByCompany sur une entité Contact. Chaque société à un ensemble d'employés (Employee) qui ont des +contacts (Tel, Adresse, ...). Ces contacts ne sont pas unique à chaque employé. Il peut être intéressant de connaître directement +tous les contacts d'une société directement avec le DAO:: + + public class ContactDAOImpl<E extends Contact> extends ContactDAOAbstract<E> { + + @Override + public Set<Contact> findAllByCompany(Company company) throws TopiaException { + ... + } + + } + +Une erreur de compilation apparaîtra si ce fichier n'existe pas (car non généré). La signature de la méthode +est présente dans le DAOAbstract (ici ContactDAOAbstract), d'où le @Override. + +Note + L'ajout du throws TopiaException est automatique et n'a pas besoin d'être spécifié au niveau du modèle. + +Implémenter la méthode avec TopiaQuery +-------------------------------------- + +Vous pouvez, dans le DAO écrire vos requêtes directement en HQL, toutefois, l'API TopiaQuery vous +permet d'écrire les requêtes avec une API générée, qui suit les évolutions du modèle plutôt que +de coder le HQL en dur. + +Voir TopiaQuery_. + + +.. _Generation Gap: http://fr.wikibooks.org/wiki/Patrons_de_conception/Generation_gap +.. _TopiaQuery: TopiaQuery.html diff --git a/topia-persistence/src/site/rst/user/howto.rst.vm b/topia-persistence/src/site/rst/user/howto.rst.vm new file mode 100644 index 0000000..fb5ed94 --- /dev/null +++ b/topia-persistence/src/site/rst/user/howto.rst.vm @@ -0,0 +1,156 @@ +.. - +.. * #%L +.. * ToPIA :: Persistence +.. * +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2011 CodeLutin, Chatellier Eric +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +====== +How to +====== + +Ce document a pour objectif de prendre en main le framework Topia. Le document +est constitué de 4 parties, la modélisation, la génération, la configuration et +l'utilisation. + +Modélisation +------------ + +La première étape constiste à réaliser le modèle UML soit avec Argouml +(http://argouml.tigris.org) soit avec Poséidon (http://www.gentleware.com). + +Le stérotype «entity» sur les classes permet de préciser que la classe est +persistée par Topia. D'autre stérotypes (extern, service, ...) sont disponibles +mais ne sont pas présentés. + +Le fichier de modélisation peut être complété par un fichier de configuration +nommé <nom fichier modélisation>.properties. Il contient des informations +techniques propres aux librairies utilisé par Topia. + +Exemple : + +:: + + # Précise comment est mappé un type + # model.tagvalue.<type java>=<type base de donnée> + model.tagvalue.java.lang.String=text + + # Précise le schema de la base de données par défaut + # model.tagvalue.dbSchema=<nom schéma> + model.tagvalue.dbSchema=test + + # Précise l'entête de l'ensemble des fichiers générés + # model.tagvalue.copyright=<valeur> + model.tagvalue.copyright=/* *##%\n Copyright (C) 2011 Code Lutin\n *##% */ + + # Force le chargement d'un attribut + # <nom paquetage>.<nom entité>.attribute.<nom attribut>.tagvalue.lazy=false + org.codelutin.Person.attribute.association.tagvalue.lazy=false + +Génération +---------- + +La génération des fichiers Topia se font par l'utilisation du plugin maven +Eugene_. Ce plugin est disponible sur le repository central. + +Configuration du plugin maven : + +:: + + <plugin> + <groupId>org.nuiton.eugene</groupId> + <artifactId>eugene-maven-plugin</artifactId> + <version>${eugeneVersion}</version> + <executions> + <execution> + <phase>process-sources</phase> + <!--Configuration of model generator--> + <configuration> + <inputs>zargo</inputs> + <fullPackagePath>org.nuiton.topiatest</fullPackagePath> + <defaultPackage>org.nuiton.topiatest</defaultPackage> + <extractedPackages>org.nuiton.topiatest</extractedPackages> + <templates> + org.nuiton.topia.generator.TopiaMetaTransformer + </templates> + </configuration> + <goals> + <goal>generate</goal> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>org.nuiton.topia</groupId> + <artifactId>topia-persistence</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> + </plugin> + +Le plugin utilise Eugene_ avec les templates de génération de Topia, le +fichier argouml ou poséidon doit se trouver dans le répertoire +``src/main/xmi``. Les fichiers sont générés dans le répertoire +``target/generated-sources/java``. Il faut préciser au plugin les paquetages +à traiter avec le ``defaultPackage`` et ``extractedPackages``. + +Ajout dans des dépendances de Topia pour la compilation des fichiers générés : + +:: + + <dependency> + <groupId>org.nuiton.topia</groupId> + <artifactId>topia-persistence</artifactId> + <version>2.5.2</version> + <scope>compile</scope> + </dependency> + +Pour une classe modélisée, nous retrouvons 6 fichiers : + * Une interface + * Une classe abstraite + * Une implantation + * Une configuration hibernate + * Un DAO abstrait + * Une implantation du DAO + +Une classe Helper est aussi générée, permettant l'accès à l'ensemble des DAO. + +Configuration +------------- + +Un fichier contenant les informations de connexion à la base de données et de +configuration de Topia doit être créé au niveau des ressources du projet. +Par convention, ce fichier est nommé ``TopiaContextImpl.properties``. + + +Utilisation +----------- + +Implantation des méthodes +~~~~~~~~~~~~~~~~~~~~~~~~~ + +Si des méthodes ont été définies dans le modèle de données, il faut créer +l'implantation correspondante. Le paquetage doit porter le même nom que celui de +la génération. + + + +.. _Eugene: http://maven-site.nuiton.org/eugene/ diff --git a/topia-persistence/src/site/rst/user/start.rst.vm b/topia-persistence/src/site/rst/user/start.rst.vm new file mode 100644 index 0000000..8cfdee6 --- /dev/null +++ b/topia-persistence/src/site/rst/user/start.rst.vm @@ -0,0 +1,247 @@ +.. - +.. * #%L +.. * ToPIA :: Persistence +.. * +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2011 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +================================ +Démarrer un projet ToPIA - Maven +================================ + +Comment démarrer un projet à partir de rien. + +Créer le projet +=============== + +Pour commencer, il faut créer un projet maven en respectant les conventions +habituelles. Partons d'un projet maven vide : + +:: + + mvn archetype:generate -DgroupId=com.masociete -DartifactId=monapplication -DarchetypeArtifactId=maven-archetype-archetype + +Ajoutez un dossier ``src/main/xmi``, c'est ici que nous allons placer le modèle. + +:: + + topia-tutorial + ├── pom.xml + └── src + ├── main + │ ├── java + │ │ └── org + │ │ └── nuiton + │ │ └── topia + │ │ └── tutorial + │ │ └── library + │ ├── resources + │ │ ├── library-config.properties + │ │ └── log4j.properties + │ └── xmi + │ ├── library.properties + │ └── library.zargo + └── test + ├── java + └── resources + +Dans le pom, on ajoute ToPIA en dépendance, et on configure la génération : + +:: + + <properties> + <topiaVersion>${project.version}</topiaVersion> + <eugeneVersion>${eugeneVersion}</eugeneVersion> + </properties> + + <dependencies> + <dependency> + <groupId>org.nuiton.topia</groupId> + <artifactId>topia-persistence</artifactId> + <version>${topiaVersion}</version> + </dependency> + </dependencies> + + + <plugins> + + <!-- ... --> + + <plugin> + <groupId>org.nuiton.eugene</groupId> + <artifactId>eugene-maven-plugin</artifactId> + <version>${eugeneVersion}</version> + <configuration> + <inputs>zargo</inputs> + <resolver>org.nuiton.util.FasterCachedResourceResolver</resolver> + </configuration> + <executions> + <execution> + <id>generate-entities</id> + <phase>generate-sources</phase> + <configuration> + <!-- Corresponding to extracted package from zargo file --> + <fullPackagePath>org.nuiton.topia.tutorial.library</fullPackagePath> + <!-- DefaultPackage used for DAOHelper generation --> + <defaultPackage>org.nuiton.topia.tutorial.library.model</defaultPackage> + <templates> + org.nuiton.topia.generator.TopiaMetaTransformer + </templates> + </configuration> + <goals> + <goal>generate</goal> + </goals> + </execution> + </executions> + </plugin> + + </plugins> + + +Créer le modèle +=============== + +Utiliser ArgoUML pour décrire le modèle +--------------------------------------- + +Avec ArgoUML, créez un nouveau modèle et enregistrez-le dans ``xmi``. Nous +allons créer un modèle simplifié pour une application de gestion de +bibliothèque. + +Nous avons modélisé les entités ``Book`` et ``Author`` avec les champs +appropriés. Faites attention à bien placer les entités dans le bon package, sinon +elles ne seront pas générées. Pour que ToPIA sache que ces classes décrivent +des entités qui ont vocation à être sauvées en base, il faut leur ajouter le +stéréotype ``entity``. + +N'oubliez pas de donner un nom et une version au modèle. + +Les données associées aux éléments du modèle +-------------------------------------------- + +Le modeleur n'est pas suffisant pour décrire toute les subtilités du modèle. +Le modèle se voulant indépendant de la plate-forme cible, on ne peut y inclure +directement des notions spécifiques à ToPIA. C'est pourquoi nous allons pouvoir +ajouter ces informations spécifiques via le fichier ``library-config.properties``. + + +C'est prêt ! +============ + +Il suffit de construire l'application avec maven. + +:: + + mvn compile + + +La compilation va provoquer la phase de génération. Si on examine le contenu +de notre dossier target : + +:: + + topia-tutorial + ├── pom.xml + ├── src + │ ├── main + │ │ ├── java + │ │ │ └── org + │ │ │ └── nuiton + │ │ │ └── topia + │ │ │ └── tutorial + │ │ │ └── library + │ │ ├── resources + │ │ │ ├── library-config.properties + │ │ │ └── log4j.properties + │ │ └── xmi + │ │ ├── library.properties + │ │ └── library.zargo + │ └── test + │ ├── java + │ └── resources + └── target + ├── classes + │ ├── library-config.properties + │ ├── log4j.properties + │ └── org + │ └── nuiton + │ └── topia + │ └── tutorial + │ └── library + │ ├── LibraryConfig.class + │ └── model + │ ├── AuthorAbstract.class + │ ├── Author.class + │ ├── AuthorDAOAbstract.class + │ . + │ . + │ . + └── generated-sources + ├── annotations + ├── java + │ └── org + │ └── nuiton + │ └── topia + │ └── tutorial + │ └── library + │ └── model + │ ├── AuthorAbstract.java + │ ├── AuthorDAOAbstract.java + │ ├── AuthorDAOImpl.java + │ ├── AuthorDAO.java + │ ├── AuthorImpl.hbm.xml + │ ├── Author.java + │ ├── BookAbstract.java + │ ├── BookDAOAbstract.java + │ ├── BookDAOImpl.java + │ ├── BookDAO.java + │ ├── BookImpl.hbm.xml + │ ├── BookImpl.java + │ ├── Book.java + │ └── LibraryDAOHelper.java + ├── models + │ ├── library.objectmodel + │ └── library.properties + └── xmi + ├── library.properties + └── library.xmi + + + +On peut voir que ToPIA a généré ``LibraryDAOHelper`` qui va nous permettre de +récupérer les différents DAO et, pour chaque entité de notre modèle : + * Une interface + * Une classe abstraite, qui implémente déjà tout le contrat + * Une implantation (vide, seulement les constructeurs) + * Un DAO abstrait + * Une implantation du DAO + * Un fichier de mapping hibernate (*.hbm.xml) + +Comme il s'agit d'une phase de compilation, maven va ajouter tout le code généré aux +classpath le code généré sera compilé et lié en même temps que le reste de votre application. +Tous les fichiers .class se retrouveront dans target/classes, dans la suite du processus, on +ne distingue plus le code généré du code utilisateur. + +Par la suite, si vous changez le modèle, tout sera re-généré. À chaque ``clean`` +via maven, tout ce qui a été généré est effacé. Vous pouvez donc re-généré autant +de fois que nécessaire. Attention, Maven ne détecte pas le changement du modèle +si le fichier .zargo a été modifié : il faut donc faire un clean à chaque fois +afin de forcer le re-génération du modèle dans sa dernière version. diff --git a/topia-persistence/src/site/rst/user/start_using_api.rst b/topia-persistence/src/site/rst/user/start_using_api.rst new file mode 100644 index 0000000..b53fd44 --- /dev/null +++ b/topia-persistence/src/site/rst/user/start_using_api.rst @@ -0,0 +1,196 @@ +.. - +.. * #%L +.. * ToPIA :: Persistence +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +====================== +Commencer à développer +====================== + +Nous pouvons maintenant ouvrir le projet dans un IDE et commencer à développer. + +Configurer une base de données embarquée +======================================== + +Pour assurer la persitance de nos entités, il faut faire appel à +un SGBD supporté par Hibernate. Pour cela, un peu de configuration JDBC est +nécessaire. Dans le fichier ``src/main/resources/library-config.properties``, on +définit + +:: + + # Pour démarrer, on utilisera une base H2 + hibernate.connection.url=jdbc:h2:file:target/db/h2data + + # Configuration JDBC + hibernate.dialect=org.hibernate.dialect.H2Dialect + hibernate.connection.username=sa + hibernate.connection.password= + hibernate.connection.driver_class=org.h2.Driver + + # On demande à Hibernate de créer toutes les tables nécessaires + # au besoin + hibernate.hbm2ddl.auto=update + + # Un peu de config pour savoir ce que fait hibernate, cette directive + # affiche toutes les requêtes effectuées sur la base dans la console + hibernate.show_sql=true + +Sans oublier les dépendances nécessaires à l'exécution :: + + <dependencies> + ... + + <!-- Implémentation pour les logger --> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + <version>${sl4jVersion}</version> + <scope>runtime</scope> + </dependency> + + <!-- Driver pour la base de données H2 --> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + <version>${h2Version}</version> + </dependency> + + </dependencies> + + +Développer un premier service +============================= + +Gestion des transactions +------------------------ + +>>> Properties conf = new Properties(); +URL url = Resource.getURL("TopiaContextImpl.properties"); +conf.load(new FileInputStream(new File(url.toURI()))); + +Permet de charger un fichier contenant les informations de connexion à la base +et de configuration de Topia. + +>>> TopiaContext rootContext = TopiaContextFactory.getContext(conf); + +Récupère le context de départ pour la création de sous context pour la gestion +des entités. + +>>> TopiaContext transaction = rootContext.beginTransaction(); + +Création d'un sous context pour la gestion des entités. + +>>> transaction.commitTransaction(); + +Validation de l'état du context, les modifications des entités sont reportées +dans la base de données. + +>>> transaction.rollBackTransaction(); + +Invalidation de l'état du context, les modifications des entités ne sont pas +reportées dans la base de données. + +>>> transaction.closeContext(); + +Relâche la connexion JDBC d'un sous context et de ses sous contexts sans +validation de l'état des contexts. + +>>> rootContext.closeContext(); + +Relâche l'ensemble des ressources (cache, pools, ...) et relâche la connexion +JDBC des sous contexts avec validation de l'état des contexts. + +Manipulation des entités +------------------------ + +>>> PersonDAO personDAO = <nom modele>DAOHelper.getPersonDAO(transaction); + +Récupération d'un DAO pour accéder à une entité. + +>>> Person person = personDAO.findByTopiaId(id); + +Les méthodes find permettent de récupérer une ou plusieures entités selon différents +critères. + +>>> person.setName("tutu"); + +Manipulation de l'entité par les getters et setters. + +>>> Person person = personDAO.create(); + +Création d'une entité. Si vous utilisez l'instanciation classique java avec le +new, l'entité n'est pas persistée. + +>>> personDAO.update(person); + +Modification d'un entité. + +>>> personDAO.delete(person); + +Suppression d'une entité. + +Configuration I18n +------------------ + +ToPIA contient une internationnalisation des messages utilisateurs (logs). +Il est préférable d'initialiser I18n pour accélérer la recherche des messages. + +Au démarrage de l'application :: + + I18nInitializer i18nInitializer = new DefaultI18nInitializer("library-bundle"); + I18n.init(i18nInitializer, new Locale("fr", "FR")); + +Configuration maven :: + + <!-- Plugin i18n pour gérer le bundle de messages --> + <plugin> + <groupId>org.nuiton.i18n</groupId> + <artifactId>i18n-maven-plugin</artifactId> + <version>${nuitonI18nVersion}</version> + <executions> + <!-- Cette exécution permet de rassembler tous les messages i18n + dans un seul 'properties bundle' nommé library-bundle + --> + <execution> + <id>make-bundle</id> + <configuration> + <bundleOutputName>library-bundle</bundleOutputName> + </configuration> + <goals> + <goal>bundle</goal> + </goals> + </execution> + </executions> + </plugin> + +Vous pourrez également utiliser `nuiton-i18n`_ pour l'internationnalisation de +votre application. + +.. _nuiton-i18n: http://maven-site.nuiton.org/i18n/ + +Exemple complet +--------------- + +Un exemple complet dans StartTest_. + +.. _StartTest: http://svn.nuiton.org/svn/topia/trunk/topia-tutorial/src/test/java/org/nuito... diff --git a/topia-persistence/src/site/rst/user/tagvalues.rst b/topia-persistence/src/site/rst/user/tagvalues.rst new file mode 100644 index 0000000..10fecb9 --- /dev/null +++ b/topia-persistence/src/site/rst/user/tagvalues.rst @@ -0,0 +1,81 @@ +.. - +.. * #%L +.. * ToPIA :: Persistence +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +Tag values +========== + +Topia templates are customizable using tag values. + +Here are current tag values defined in topia. They can be defined in source +model or model property file. + +Topia tag values +---------------- + ++--------------------------+-----------+----------------------------------------------------+ +| Name | Category | Description | ++==========================+===========+====================================================+ +| orderBy | Hibernate | Define order-by attribute in hibernate mapping for | +| | | a specific relation | ++--------------------------+-----------+----------------------------------------------------+ +| dbName | Hibernate | Database table name | ++--------------------------+-----------+----------------------------------------------------+ +| dbSchema | Hibernate | Database schema | ++--------------------------+-----------+----------------------------------------------------+ +| length | Hibernate | Attribute length in database | ++--------------------------+-----------+----------------------------------------------------+ +| annotation | all | Add annotation on attribute | ++--------------------------+-----------+----------------------------------------------------+ +| copyright | all | Generated files header copyright | ++--------------------------+-----------+----------------------------------------------------+ +| access | hibernate | Field access type | ++--------------------------+-----------+----------------------------------------------------+ +| i18n | entity | Generate i18n ??? | ++--------------------------+-----------+----------------------------------------------------+ +| naturalId | hibernate | Generate hibernate naturalId | ++--------------------------+-----------+----------------------------------------------------+ +| naturalIdMutable | hibernate | Tell hibernate if a natural id key is mutable | ++--------------------------+-----------+----------------------------------------------------+ +| inverse | hibernate | To configure with part of a N-N relation is inverse| ++--------------------------+-----------+----------------------------------------------------+ +| lazy | hibernate | Hibernate multiple attribute fetch property | +| | | (default to ''hibernate default value'') | ++--------------------------+-----------+----------------------------------------------------+ +| notNull | hibernate | Hibernate attribute not null property | +| | | (default to ''hibernate default value'') | ++--------------------------+-----------+----------------------------------------------------+ +| hibernateProxyInterface | hibernate | To not generate proxy information in | +| | | hibernate mappings. | ++--------------------------+-----------+----------------------------------------------------+ +| useEnumerationName | hibernate | To make an entity attribute of type enum be stored | +| | | in DB with its name (instead of ordinal, which is | +| | | the default in Hibernate) | ++--------------------------+-----------+----------------------------------------------------+ + +Eugene tag values +----------------- + +Eugene contains also his tag values. See `eugene documentation`_ for more détails. + +.. _eugene documentation: http://maven-site.nuiton.org/eugene/ diff --git a/topia-persistence/src/site/site.xml b/topia-persistence/src/site/site.xml new file mode 100644 index 0000000..5da4bf9 --- /dev/null +++ b/topia-persistence/src/site/site.xml @@ -0,0 +1,92 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Persistence + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin, Chatellier Eric + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<project name="${project.name}"> + + <bannerLeft> + <name>${project.name}</name> + <href>index.html</href> + </bannerLeft> + + <body> + + <breadcrumbs> + <item name="${project.name}" href="index.html"/> + </breadcrumbs> + + <menu ref="parent"/> + + <menu name="Tutoriel" inherit="top"> + <item name="Démarrer avec ToPIA et Maven" href="user/start.html"/> + <item name="Commencer à développer avec l'API ToPIA" href="user/start_using_api.html"/> + <item name="Étendre le modèle" href="user/extend_model.html"/> + <item name="Continer le développement" href="user/continue_devel.html"/> + </menu> + + <menu name="Utilisateur" inherit="top"> + <item name="Presentation" href="index.html"/> + <!--item name="Utilisation" href="user/howto.html" /> + <item name="Génération des modèles" href="user/ModelGeneration.html"/> + <item name="Tag values" href="user/tagvalues.html" /--> + <item name="TopiaQuery" href="user/TopiaQuery.html"/> + <item name="FAQ" href="user/FAQ.html"/> + </menu> + + <menu name="Developpeur"> + <item name="Todo" href="devel/Todo.html"/> + <item name="Mapping hibernate" href="devel/HibernateMapping.html"/> + <item name="Isolation" href="devel/Isolation.html"/> + <item name="Schema migration" href="devel/SchemaMigration.html"/> + <item name="Securité" href="devel/security.html"/> + <item name="TopiaContextFactory" href="devel/Devel.html"/> + <item name="Projets similaires" href="devel/project.html"/> + <item name="Gestion des evenements" href="user/event.html"/> + </menu> + + <menu name="Téléchargement"> + <item + href="${repository.home.url}/org/nuiton/topia/${project.artifactId}/${project.version}/${project.build.finalName}.jar" + name="Librairie (jar)"/> + <item + href="${repository.home.url}/org/nuiton/topia/${project.artifactId}/${project.version}/${project.build.finalName}-javadoc.jar" + name="Javadoc (jar)"/> + <item + href="${repository.home.url}/org/nuiton/topia/${project.artifactId}/${project.version}/${project.build.finalName}-sources.jar" + name="Sources (jar)"/> + <item + href="${repository.home.url}/org/nuiton/topia/${project.artifactId}/${project.version}/${project.build.finalName}-tests.jar" + name="Test Librairie (jar)"/> + <item + href="${repository.home.url}/org/nuiton/topia/${project.artifactId}/${project.version}/${project.build.finalName}-test-javadoc.jar" + name="Test Javadoc (jar)"/> + <item + href="${repository.home.url}/org/nuiton/topia/${project.artifactId}/${project.version}/${project.build.finalName}-test-sources.jar" + name="Test Sources (jar)"/> + </menu> + + <menu ref="reports"/> + + </body> +</project> diff --git a/topia-persistence/src/test/java/org/nuiton/topia/TestHelper.java b/topia-persistence/src/test/java/org/nuiton/topia/TestHelper.java new file mode 100644 index 0000000..c1a0434 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/TestHelper.java @@ -0,0 +1,159 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.cfg.Environment; +import org.junit.Ignore; +import org.nuiton.util.FileUtil; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +/** + * Helper for all topia tests. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5 + */ +@Ignore +// this is not a test :) +public class TestHelper { + + private static final Log log = LogFactory.getLog(TestHelper.class); + + protected static File testBasedir; + + protected static File targetdir; + + protected static File dirDatabase; + + public static final String DEFAULT_CONFIGURATION_LOCATION = "/TopiaContextImpl.properties"; + + public static File getTestWorkdir() { + if (testBasedir == null) { + String base = System.getProperty("java.io.tmpdir"); + if (base == null || base.isEmpty()) { + base = new File("").getAbsolutePath(); + } + testBasedir = new File(base); + log.info("basedir for test " + testBasedir); + } + return testBasedir; + } + + public static File getTestBasedir(Class<?> testClass) throws IOException { + File dir = getTestWorkdir(); + File result = new File(dir, testClass.getName()); + if (result.exists()) { + + // when calling this method (always in a BeforeClass method), we wants + // to clean the directory, this is a new build + FileUtils.deleteDirectory(result); + } + + // always create the directory + FileUtil.createDirectoryIfNecessary(result); + return result; + } + + public static TopiaContext initTopiaContext(File testDirectory, + String dbname) + throws IOException, TopiaNotFoundException { + + + TopiaContext topiaContext = initTopiaContext( + testDirectory, + DEFAULT_CONFIGURATION_LOCATION, + dbname + ); + return topiaContext; + } + + public static TopiaContext initTopiaContext(File testDirectory, + String dbPropertiesPath, + String dbname) + throws IOException, TopiaNotFoundException { + + Properties configuration = initTopiaContextConfiguration( + testDirectory, + dbPropertiesPath, + dbname); + return TopiaContextFactory.getContext(configuration); + } + + public static Properties initTopiaContextConfiguration(File testDirectory, + String dbPropertiesPath, + String dbname) + throws IOException { + + Properties configuration = loadHibernateConfiguration(dbPropertiesPath); + + // make sure we always use a different directory + + String dbPath = getDbName(testDirectory, dbname); + + if (log.isInfoEnabled()) { + log.info("dbPath = " + dbPath); + } + configuration.setProperty( + Environment.URL, +// "hibernate.connection.url", + "jdbc:h2:file:" + dbPath); + + return configuration; + } + + public static Properties loadHibernateConfiguration(String dbPropertiesPath) throws IOException { + InputStream stream = TestHelper.class.getResourceAsStream(dbPropertiesPath); + + Properties configuration = new Properties(); + + configuration.load(stream); + configuration.setProperty( + TopiaContextFactory.CONFIG_PERSISTENCE_CLASSES, + TopiaTestDAOHelper.getImplementationClassesAsString()); + return configuration; + } + + public static String getDbName(File testDirectory, String dbname) { + return new File(testDirectory, + dbname + '_' + System.nanoTime()).getAbsolutePath(); + } + + public static Properties initTopiaContextConfiguration(File testDirectory, + String dbname) + throws IOException { + + return initTopiaContextConfiguration( + testDirectory, + DEFAULT_CONFIGURATION_LOCATION, + dbname + ); + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/TopiaContextFactoryTest.java b/topia-persistence/src/test/java/org/nuiton/topia/TopiaContextFactoryTest.java new file mode 100644 index 0000000..90f90b0 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/TopiaContextFactoryTest.java @@ -0,0 +1,188 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.nuiton.topia.framework.TopiaContextImpl; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Properties; + +/** + * Created: 8 mai 2010 + * + * @author fdesbois <fdesbois@codelutin.com> + * @version $Id$ + */ +public class TopiaContextFactoryTest { + + private static final Log log = + LogFactory.getLog(TopiaContextFactoryTest.class); + + protected static File testBasedir; + + protected Properties properties; + + @BeforeClass + public static void init() throws IOException { + + testBasedir = TopiaDatabase.getTestSpecificDirectory(TopiaContextFactoryTest.class, "dummy"); + + } + + @Before + public void setUp() throws Exception { + properties = new Properties(); + properties.setProperty("prop1", "value1"); + properties.setProperty("prop2", "value2"); + TopiaContextFactory.contextCache.clear(); + } + + @Test + public void testGetContextOpened() throws Exception { + log.debug("## testGetContextOpened"); + + /** PREPARE DATA **/ + String databaseName = "h2data-testGetContextByPropertie"; + File dbDirectory = new File(testBasedir, databaseName); + String url = "jdbc:h2:file:" + dbDirectory; + properties.setProperty("hibernate.connection.url", url); + + TopiaContextImpl test = new TopiaContextImpl(properties); + TopiaContextFactory.contextCache.put(properties, test); + + /** EXEC METHOD **/ + List<String> result = TopiaContextFactory.getContextOpened(); + Assert.assertEquals(1, result.size()); + Assert.assertEquals(url, result.get(0)); + } + + @Test + public void testRemoveContext() throws Exception { + log.debug("## testRemoveContext"); + + /** PREPARE DATA **/ + TopiaContextImpl test = new TopiaContextImpl(properties); + TopiaContextFactory.contextCache.put(properties, test); + + /** EXEC METHOD **/ + TopiaContextFactory.removeContext(test); + Assert.assertEquals(0, TopiaContextFactory.contextCache.size()); + } + + //@Test + + public void testGetContext() throws Exception { + // TODO-fdesbois-20100508 : only used TopiaUtil.getProperties, need tests for this method + } + + @Test + public void testGetContextByProperties() throws Exception { + log.debug("## testGetContextByProperties"); + + /** PREPARE DATA **/ + Properties propertiesParent = new Properties(properties); + propertiesParent.setProperty("prop3", "value3"); + + Properties propertiesAll = new Properties(); + propertiesAll.setProperty("prop1", "value1"); + propertiesAll.setProperty("prop2", "value2"); + propertiesAll.setProperty("prop3", "value3"); + + /** EXEC METHOD **/ + + log.info("test 0 : add null properties"); + try { + TopiaContextFactory.getContext(null); + } catch (Exception eee) { + Assert.assertEquals(NullPointerException.class, eee.getClass()); + } + + log.info("test 1 : add new properties, will instantiate a new" + + " TopiaContext"); + TopiaContext test1 = TopiaContextFactory.getContext(propertiesParent); + Assert.assertNotNull(test1); + Assert.assertEquals(1, TopiaContextFactory.contextCache.size()); + + log.info("test 2 : with same properties, will retrieve existing" + + " TopiaContext"); + TopiaContext test2 = TopiaContextFactory.getContext(propertiesParent); + Assert.assertEquals(test1, test2); + Assert.assertEquals(1, TopiaContextFactory.contextCache.size()); + + log.info("test 3 : use other properties, will instantiate a different" + + "TopiaContext"); + TopiaContext test3 = TopiaContextFactory.getContext(properties); + log.debug("cache size : " + TopiaContextFactory.contextCache.size()); + log.debug("result : " + test1); + log.debug("result3 : " + test3); + Assert.assertNotSame(test1, test3); + Assert.assertEquals(2, TopiaContextFactory.contextCache.size()); + + log.info("test 4 : use other properties but equivalent to existing " + + "TopiaContext"); + // Test flating of properties + TopiaContext test4 = TopiaContextFactory.getContext(propertiesAll); + Assert.assertEquals(test1, test4); + Assert.assertEquals(2, TopiaContextFactory.contextCache.size()); + + log.info("test5a : reinstantiate new TopiaContext after one is closed."); + // TEST + // Strange behavior the closed flag of context stay true if + // hibernateFactory is not loaded from real properties +// test1.closeContext(); +// Assert.assertTrue(test1.isClosed()); + + // Add properties for Hibernate to have real opened topiaContext + String databaseName = "h2data-testGetContextByPropertie"; + File f = new File(testBasedir, databaseName); + + properties.setProperty("hibernate.connection.username", "sa"); + properties.setProperty("hibernate.connection.password", ""); + properties.setProperty("hibernate.connection.driver_class", "org.h2.Driver"); + properties.setProperty("hibernate.connection.url", + "jdbc:h2:file:" + f.getAbsolutePath()); + + + TopiaContext test5 = TopiaContextFactory.getContext(properties); + Assert.assertNotSame(test1, test5); + Assert.assertEquals(3, TopiaContextFactory.contextCache.size()); + + log.info("test5b : beginTransaction to properly close the context"); + test5.beginTransaction(); + + test5.closeContext(); + + TopiaContext result = TopiaContextFactory.getContext(properties); + Assert.assertNotSame(test5, result); + Assert.assertEquals(3, TopiaContextFactory.contextCache.size()); + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/TopiaDatabase.java b/topia-persistence/src/test/java/org/nuiton/topia/TopiaDatabase.java new file mode 100644 index 0000000..b28c195 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/TopiaDatabase.java @@ -0,0 +1,191 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.rules.TestWatcher; +import org.junit.runner.Description; + +import java.io.File; +import java.io.InputStream; +import java.util.Properties; + +/** + * Put this class as a Rule in test to obtain a new isolated db for each test. + * <p/> + * Here is a simple example of usage : + * <pre> + * public class MyTest { + * + * \@Rule + * public final TopiaDatabase db = new TopiaDatabase(); + * + * \@Test + * public void testMethod() throws TopiaException { + * + * TopiaContext tx = db.beginTransaction(); + * ... + * } + * </pre> + * The db created will be unique for each test method (and for each build also). + * <p/> + * You don't need to close any transaction, it will be done for you and the end + * of each method test. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.8 + */ +public class TopiaDatabase extends TestWatcher { + + /** Logger. */ + private static final Log log = LogFactory.getLog(TopiaDatabase.class); + + /** A time-stamp, allow to make multiple build and keep the tests data. */ + public static final String TIMESTAMP = String.valueOf(System.nanoTime()); + + private File testBasedir; + + private Properties dbConfiguration; + + private TopiaContext rootCtxt; + + private final String configurationPath; + + public TopiaDatabase() { + this(TestHelper.DEFAULT_CONFIGURATION_LOCATION); + } + + public TopiaDatabase(String configurationPath) { + this.configurationPath = configurationPath; + } + + @Override + protected void starting(Description description) { + + // get test directory + testBasedir = getTestSpecificDirectory( + description.getTestClass(), + description.getMethodName()); + + if (log.isDebugEnabled()) { + log.debug("testBasedir = " + testBasedir); + } + + // create the root context + try { + + dbConfiguration = new Properties(); + InputStream stream = + getClass().getResourceAsStream(configurationPath); + + try { + dbConfiguration.load(stream); + } finally { + stream.close(); + } + dbConfiguration.setProperty( + TopiaContextFactory.CONFIG_PERSISTENCE_CLASSES, + TopiaTestDAOHelper.getImplementationClassesAsString()); + + // make sure we always use a different directory + + String dbPath = new File(testBasedir, "db").getAbsolutePath(); + + if (log.isDebugEnabled()) { + log.debug("dbPath = " + dbPath); + } + dbConfiguration.setProperty( + TopiaContextFactory.CONFIG_URL, "jdbc:h2:file:" + dbPath); + + onDbConfigurationCreate(dbConfiguration, testBasedir, dbPath); + + rootCtxt = TopiaContextFactory.getContext(dbConfiguration); + } catch (Exception e) { + throw new IllegalStateException( + "Could not start db at " + testBasedir, e); + } + } + + @Override + public void finished(Description description) { + + if (rootCtxt != null && !rootCtxt.isClosed()) { + try { + rootCtxt.closeContext(); + } catch (TopiaException e) { + if (log.isErrorEnabled()) { + log.error("Could not close topia root context", e); + } + } + } + rootCtxt = null; + dbConfiguration = null; + } + + public File getTestBasedir() { + return testBasedir; + } + + public TopiaContext getRootCtxt() { + return rootCtxt; + } + + public Properties getDbConfiguration() { + return dbConfiguration; + } + + public TopiaContext beginTransaction() throws TopiaException { + return rootCtxt.beginTransaction(); + } + + protected void onDbConfigurationCreate(Properties configuration, + File testDir, + String dbPath) { + + } + + public static File getTestSpecificDirectory(Class<?> testClassName, String methodName) { + // Trying to look for the temporary folder to store data for the test + String tempDirPath = System.getProperty("java.io.tmpdir"); + if (tempDirPath == null) { + // can this really occur ? + tempDirPath = ""; + if (log.isWarnEnabled()) { + log.warn("'\"java.io.tmpdir\" not defined"); + } + } + File tempDirFile = new File(tempDirPath); + + // create the directory to store database data + String dataBasePath = testClassName.getName() + + File.separator // a directory with the test class name + + methodName// a sub-directory with the method name + + '_' + + TIMESTAMP; // and a timestamp + File databaseFile = new File(tempDirFile, dataBasePath); + return databaseFile; + } +} + diff --git a/topia-persistence/src/test/java/org/nuiton/topia/framework/EntityStateTest.java b/topia-persistence/src/test/java/org/nuiton/topia/framework/EntityStateTest.java new file mode 100644 index 0000000..f35be71 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/framework/EntityStateTest.java @@ -0,0 +1,95 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.framework; + +import org.junit.Assert; +import org.junit.Test; + +/** + * EntityStateTest.java + * <p/> + * Created: 22 nov. 06 12:15:11 + * + * @author poussin <poussin@codelutin.com> + * @version $Revision$ + * <p/> + * Last update: $Date$ + * by : $Author$ + */ +public class EntityStateTest { + + /** + * Test les changements d'etat de {@link EntityState}. + * + * @throws Exception + */ + @Test + public void testState() throws Exception { + EntityState state = new EntityState(); + + state.addLoad(); + Assert.assertTrue(state.isLoad()); + Assert.assertFalse(state.isRead()); + Assert.assertFalse(state.isCreate()); + Assert.assertFalse(state.isUpdate()); + Assert.assertFalse(state.isDelete()); + + state.addRead(); + Assert.assertTrue(state.isLoad()); + Assert.assertTrue(state.isRead()); + Assert.assertFalse(state.isCreate()); + Assert.assertFalse(state.isUpdate()); + Assert.assertFalse(state.isDelete()); + + //state.addRead(); + state.addCreate(); + Assert.assertTrue(state.isLoad()); + Assert.assertTrue(state.isRead()); + Assert.assertTrue(state.isCreate()); + Assert.assertFalse(state.isUpdate()); + Assert.assertFalse(state.isDelete()); + + state.addUpdate(); + Assert.assertTrue(state.isLoad()); + Assert.assertTrue(state.isRead()); + Assert.assertTrue(state.isCreate()); + Assert.assertTrue(state.isUpdate()); + Assert.assertFalse(state.isDelete()); + + state.addDelete(); + Assert.assertTrue(state.isLoad()); + Assert.assertTrue(state.isRead()); + Assert.assertTrue(state.isCreate()); + Assert.assertTrue(state.isUpdate()); + Assert.assertTrue(state.isDelete()); + + state = new EntityState(); + state.addDelete(); + Assert.assertFalse(state.isRead()); + Assert.assertFalse(state.isCreate()); + Assert.assertFalse(state.isUpdate()); + Assert.assertTrue(state.isDelete()); + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaConnectionProviderHardCoded.java b/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaConnectionProviderHardCoded.java new file mode 100644 index 0000000..d2a194a --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaConnectionProviderHardCoded.java @@ -0,0 +1,266 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.framework; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.HibernateException; +import org.hibernate.cfg.Environment; +import org.hibernate.engine.jdbc.connections.internal.ConnectionProviderInitiator; +import org.hibernate.engine.jdbc.connections.spi.ConnectionProvider; +import org.hibernate.internal.util.ReflectHelper; +import org.hibernate.internal.util.config.ConfigurationHelper; +import org.hibernate.service.UnknownUnwrapTypeException; +import org.hibernate.service.spi.Configurable; +import org.hibernate.service.spi.Stoppable; + +/** + * Customized connection provider. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.3 + */ +public class TopiaConnectionProviderHardCoded implements ConnectionProvider, Configurable, Stoppable { + + private static final long serialVersionUID = 7911628440635459964L; + + private String url; + + private Properties connectionProps; + + private Integer isolation; + + /** + * Our pool of connections which are not closed and availables. + */ + protected final List<Connection> pool; + + private int poolSize; + + private boolean stopped; + + private int checkedOut = 0; + + private boolean autocommit; + + /** + * Logger. + */ + private static final Log log = + LogFactory.getLog(TopiaConnectionProviderHardCoded.class); + + public TopiaConnectionProviderHardCoded() { + pool = new ArrayList<Connection>(); + } + + @Override + public void configure(Map configurationValues) throws HibernateException { + + poolSize = ConfigurationHelper.getInt(Environment.POOL_SIZE, configurationValues, 20); //default pool size 20 + if (log.isDebugEnabled()) { + log.debug("Connection pool size: " + poolSize); + } + + autocommit = ConfigurationHelper.getBoolean(Environment.AUTOCOMMIT, configurationValues); + if (log.isDebugEnabled()) { + log.debug("autocommit mode: " + autocommit); + } + + isolation = ConfigurationHelper.getInteger(Environment.ISOLATION, configurationValues); + if (isolation != null) { + if (log.isDebugEnabled()) { + log.debug("JDBC isolation level: " + + Environment.isolationLevelToString(isolation)); + } + } + + String driverClass = ConfigurationHelper.getString(Environment.DRIVER, configurationValues); + if (driverClass == null) { + + if (log.isWarnEnabled()) { + log.warn("no JDBC Driver class was specified by property " + + Environment.DRIVER); + } + } else { + try { + // trying via forName() first to be as close to DriverManager's semantics + Class.forName(driverClass); + } catch (ClassNotFoundException cnfe) { + try { + ReflectHelper.classForName(driverClass); + } catch (ClassNotFoundException e) { + String msg = "JDBC Driver class not found: " + driverClass; + log.error(msg, e); + throw new HibernateException(msg, e); + } + } + } + + // use a dummy directory to make sure only the connection provider knows + // the real directory where db is and then make sure hibernate always + // use the connection provider... + String directory = + (String) configurationValues.get(TopiaConnectionProviderTest.TEST_URL); + + url = directory; +// url = props.getProperty(Environment.URL); + +// if (url == null) { +// String msg = "JDBC URL was not specified by property " + Environment.URL; +// log.error(msg); +// throw new HibernateException(msg); +// } + + connectionProps = ConnectionProviderInitiator.getConnectionProperties(configurationValues); + + if (log.isDebugEnabled()) { + log.debug("using driver: " + driverClass + " at URL: " + url); + } + + // if debug level is enabled, then log the password, otherwise mask it + if (log.isTraceEnabled()) { + log.debug("connection properties: " + connectionProps); + } else if (log.isDebugEnabled()) { + log.debug("connection properties: " + + ConfigurationHelper.maskOut(connectionProps, "password")); + } + } + + @Override + public Connection getConnection() throws SQLException { + if (log.isTraceEnabled()) { + log.trace("total checked-out connections: " + checkedOut); + } + + synchronized (pool) { + if (!pool.isEmpty()) { + int last = pool.size() - 1; + if (log.isTraceEnabled()) { + log.trace("using pooled JDBC connection, pool size: " + last); + } + checkedOut++; + Connection pooled = pool.remove(last); + if (isolation != null) { + pooled.setTransactionIsolation(isolation.intValue()); + } + if (pooled.getAutoCommit() != autocommit) { + pooled.setAutoCommit(autocommit); + } + return pooled; + } + } + + log.debug("opening new JDBC connection"); + Connection conn = DriverManager.getConnection(url, connectionProps); + if (isolation != null) conn.setTransactionIsolation(isolation); + if (conn.getAutoCommit() != autocommit) conn.setAutoCommit(autocommit); + + if (log.isDebugEnabled()) { + log.debug("created connection to: " + url + ", Isolation Level: " + conn.getTransactionIsolation()); + } +// if ( log.isTraceEnabled() ) + checkedOut++; + + return conn; + } + + @Override + public void closeConnection(Connection conn) throws SQLException { +// if ( log.isDebugEnabled() ) + checkedOut--; + + synchronized (pool) { + int currentSize = pool.size(); + if (currentSize < poolSize) { + if (log.isTraceEnabled()) { + log.trace("returning connection to pool, pool size: " + (currentSize + 1)); + } + pool.add(conn); + return; + } + } + + log.debug("closing JDBC connection"); + + conn.close(); + } + + @Override + protected void finalize() throws Throwable { + if (!stopped) { + stop(); + } + super.finalize(); + } + + @Override + public void stop() { + + if (log.isDebugEnabled()) { + log.debug("cleaning up connection pool: " + url); + } + + for (Connection connection : pool) { + try { + connection.close(); + } catch (SQLException sqle) { + if (log.isWarnEnabled()) { + log.warn("problem closing pooled connection", sqle); + } + } + } + pool.clear(); + stopped = true; + } + + @Override + public boolean supportsAggressiveRelease() { + return false; + } + + @Override + public boolean isUnwrappableAs(Class unwrapType) { + return ConnectionProvider.class.equals(unwrapType) || + getClass().isAssignableFrom(unwrapType); + } + + @Override + @SuppressWarnings({"unchecked"}) + public <T> T unwrap(Class<T> unwrapType) { + if (ConnectionProvider.class.equals(unwrapType) || + getClass().isAssignableFrom(unwrapType)) { + return (T) this; + } else { + throw new UnknownUnwrapTypeException(unwrapType); + } + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaConnectionProviderTest.java b/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaConnectionProviderTest.java new file mode 100644 index 0000000..0aed561 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaConnectionProviderTest.java @@ -0,0 +1,139 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.framework; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaContextFactory; +import org.nuiton.topia.TopiaDatabase; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaTestDAOHelper; +import org.nuiton.topia.test.entities.Person; +import org.nuiton.topia.test.entities.PersonDAO; +import org.nuiton.topiatest.Personne; + +import java.io.File; +import java.util.Locale; +import java.util.Properties; + +import static org.junit.Assert.assertNotNull; + +/** + * To test the {@link TopiaConnectionProvider} and make sure all connections + * are done from here... + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5.3 + */ +public class TopiaConnectionProviderTest { + +// private static final Log log = +// LogFactory.getLog(TopiaConnectionProviderTest.class); + + public static final String TEST_URL = "testURL"; + + @Rule + public final TopiaDatabase db = + new TopiaDatabase("/TopiaConnectionProviderHardcoded.properties") { + + @Override + protected void onDbConfigurationCreate(Properties configuration, + File testdir, + String dbPath) { + + Assert.assertFalse(testdir.exists()); + + String dbPathFake = new File(testdir, "fake" + File.separator + "db").getAbsolutePath(); + + Assert.assertFalse(new File(dbPathFake).getParentFile().exists()); + + configuration.setProperty("dbPath", dbPath); + configuration.setProperty("dbPathFake", dbPathFake); + + // give the path where connection provider will create db + configuration.setProperty(TEST_URL, + "jdbc:h2:file:" + dbPath); + + // give a fake db path (we will make sure it is never create after hibernate usage). + configuration.setProperty(TopiaContextFactory.CONFIG_URL, + "jdbc:h2:file:" + dbPathFake); + } + }; + + @Test + public void testWithHardcoded() throws Exception { + +// Properties dbProperties = TestHelper.loadHibernateConfiguration( +// "/TopiaConnectionProviderHardcoded.properties"); +// +// File directory = new File(TestHelper.getDbName(testBasedir, "testWithHardcoded")); + + String dbPath = (String) db.getDbConfiguration().get("dbPath"); + String dbPathFake = (String) db.getDbConfiguration().get("dbPathFake"); + +// new File(directory, "real" + File.separator + "db").getAbsolutePath(); +// Assert.assertFalse(new File(dbPath).getParentFile().exists()); + +// String dbPathFake = new File(directory, "fake" + File.separator + "db").getAbsolutePath(); + +// Assert.assertFalse(new File(dbPathFake).getParentFile().exists()); + +// // give the path where connection provider will create db +// dbProperties.setProperty(TEST_URL, "jdbc:h2:file:" + dbPath); +// +// // give a fake db path (we will make sure it is never create after hibernate usage). +// dbProperties.setProperty(Environment.URL, "jdbc:h2:file:" + dbPathFake); +// +// root = TopiaContextFactory.getContext(dbProperties); + + Locale.setDefault(Locale.FRANCE); + + doStuffOnDb(); + + // the db file must have been created + Assert.assertTrue(new File(dbPath).getParentFile().exists()); + + // make sure the fake db path was never used + Assert.assertFalse(new File(dbPathFake).getParentFile().exists()); + } + + private void doStuffOnDb() throws TopiaException { + TopiaContext transaction = db.beginTransaction(); + + try { + PersonDAO dao = TopiaTestDAOHelper.getPersonDAO(transaction); + + Person personne = dao.create(Personne.PROPERTY_NAME, "Jack Bauer"); + transaction.commitTransaction(); + String idPersonne = personne.getTopiaId(); + assertNotNull(idPersonne); + + transaction.commitTransaction(); + } finally { + transaction.closeContext(); + } + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaContextImplTest.java b/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaContextImplTest.java new file mode 100644 index 0000000..652a12c --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaContextImplTest.java @@ -0,0 +1,551 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.framework; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.h2.Driver; +import org.hibernate.cfg.Configuration; +import org.hibernate.mapping.PersistentClass; +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.nuiton.i18n.I18n; +import org.nuiton.topia.TopiaContextFactory; +import org.nuiton.topia.TopiaDatabase; +import org.nuiton.topia.TopiaNotFoundException; +import org.nuiton.topiatest.persistence.Entity1; +import org.nuiton.topiatest.persistence.Entity1Impl; +import org.nuiton.topiatest.service.FakeService; +import org.nuiton.topiatest.service.TestService; + +import java.io.File; +import java.util.HashMap; +import java.util.Locale; +import java.util.Map; +import java.util.Properties; + +/** + * Created: 10 mai 2010 + * + * @author fdesbois <fdesbois@codelutin.com> + * @version $Id$ + */ +public class TopiaContextImplTest { + + private static final Log log = + LogFactory.getLog(TopiaContextImplTest.class); + + protected Properties properties = new Properties(); + + static File testBasedir; + + @BeforeClass + public static void setUpClass() throws Exception { + + testBasedir = TopiaDatabase.getTestSpecificDirectory(TopiaContextImplTest.class, "dummy"); + I18n.init(null, Locale.FRENCH); + } + + @Before + public void setUp() throws Exception { + properties.clear(); + } + + @After + public void tearDown() throws Exception { + } + + @Test + public void testLoadServices() throws Exception { + if (log.isDebugEnabled()) { + log.debug("## testLoadServices"); + } + + /** PREPARE DATA **/ + properties.setProperty("topia.service.test", + TestService.class.getName()); + + TopiaContextImpl context = new TopiaContextImpl(); + + /** EXEC METHOD **/ + if (log.isInfoEnabled()) { + log.info("test 1 : load a simple TestService from properties"); + } + Map<String, TopiaService> results = context.loadServices(properties); + Assert.assertEquals(1, results.size()); + Assert.assertTrue(results.containsKey("test")); + TopiaService service = results.get("test"); + Assert.assertEquals(TestService.class, service.getClass()); + + if (log.isInfoEnabled()) { + log.info("test 2 : load with wrong key : will display a WARN"); + } + properties.clear(); + + properties.setProperty("topia.service.fake", + TestService.class.getName()); + + results = context.loadServices(properties); + Assert.assertEquals(0, results.size()); + Assert.assertFalse(results.containsKey("fake")); + + if (log.isInfoEnabled()) { + log.info("test 3 : load with fake service name : will display an ERROR"); + } + properties.clear(); + + properties.setProperty("topia.service.test", "FAKE"); + + results = context.loadServices(properties); + Assert.assertEquals(0, results.size()); + Assert.assertFalse(results.containsKey("test")); + } + + @Test + public void testGetServices() throws Exception { + if (log.isDebugEnabled()) { + log.debug("## testGetServices"); + } + + /** PREPARE DATA **/ + properties.setProperty("topia.service.test", + TestService.class.getName()); + + // Calling the constructor with properties will load the services + TopiaContextImpl context = new TopiaContextImpl(properties); + + // Instantiate a child context and set its parent + TopiaContextImpl child = new TopiaContextImpl(context); + + /** EXEC METHOD **/ + if (log.isInfoEnabled()) { + log.info("test 1 : with child context"); + } + Map<String, TopiaService> test1 = child.getServices(); + Assert.assertEquals(1, test1.size()); + Assert.assertTrue(test1.containsKey("test")); + + if (log.isInfoEnabled()) { + log.info("test 2 : test serviceEnabled method"); + } + boolean test2 = child.serviceEnabled("test"); + Assert.assertTrue(test2); + + if (log.isInfoEnabled()) { + log.info("test 3 : test getService method"); + } + TopiaService test3 = child.getService("test"); + Assert.assertEquals(TestService.class, test3.getClass()); + + if (log.isInfoEnabled()) { + log.info("test 4 : test serviceEnabled from class TestService"); + } + boolean test4 = child.serviceEnabled(TestService.class); + Assert.assertTrue(test4); + + if (log.isInfoEnabled()) { + log.info("test 5 : test getService from class TestService"); + } + TestService test5 = child.getService(TestService.class); + Assert.assertNotNull(test5); + + if (log.isInfoEnabled()) { + log.info("test 6 : test serviceEnabled error with class FakeService"); + } + // FakeService doesn't contains property SERVICE_NAME used by + // serviceEnabled method + // Even it's properly loaded the serviceEnabled method will return false + properties.clear(); + properties.setProperty("topia.service.fake", + FakeService.class.getName()); + TopiaContextImpl otherContext = new TopiaContextImpl(properties); + + boolean test6 = otherContext.serviceEnabled(FakeService.class); + Assert.assertFalse(test6); + + if (log.isInfoEnabled()) { + log.info("test 7 : test getService with error TopiaNotFoundException" + + " : service not loaded"); + } + // TestService is not loaded in otherContext + try { + TestService test7 = otherContext.getService(TestService.class); + Assert.fail(); + } catch (TopiaNotFoundException eee) { + //log.error(eee.getClass().getSimpleName() + " : " + eee.getMessage()); + //Assert.assertEquals(TopiaNotFoundException.class, eee.getClass()); + } catch (Exception e) { + Assert.fail(); + } + } + + @Test + public void testContextHierarchy() throws Exception { + if (log.isDebugEnabled()) { + log.debug("## testContextHierarchy"); + } + + /** PREPARE DATA **/ + TopiaContextImpl context = new TopiaContextImpl(properties); + + /** EXEC METHODS **/ + if (log.isInfoEnabled()) { + log.info("test 1 : constructor with parent context"); + } + TopiaContextImpl test1 = new TopiaContextImpl(context); + Assert.assertEquals(context, test1.parentContext); + + if (log.isInfoEnabled()) { + log.info("test 2 : addChildContext"); + } + TopiaContextImpl test2 = new TopiaContextImpl(properties); + TopiaContextImpl child2 = new TopiaContextImpl(); + test2.addChildContext(child2); + Assert.assertEquals(1, test2.childContext.size()); + + if (log.isInfoEnabled()) { + log.info("test 3 : removeChildContext"); + } + TopiaContextImpl test3 = new TopiaContextImpl(properties); + TopiaContextImpl child3 = new TopiaContextImpl(test3); + test3.childContext.add(child3); + test3.removeChildContext(child3); + Assert.assertEquals(0, test3.childContext.size()); + + // No remove if context is closed + test3.childContext.add(child3); + test3.closed = true; + test3.removeChildContext(child3); + Assert.assertEquals(1, test3.childContext.size()); + + if (log.isInfoEnabled()) { + log.info("test 4 : getRootContext"); + } + TopiaContextImplementor test4 = child3.getRootContext(); + Assert.assertEquals(test3, test4); + + // Note : existing test is already done for concurrency problem on + // getChildContext(). Go to : http://www.nuiton.org/repositories/browse/sandbox/testTopiaPostgresError/tru... + } +// +// @Test +// public void testCreateSchema() throws Exception { +// } +// +// @Test +// public void testShowCreateSchema() throws Exception { +// } +// +// @Test +// public void testUpdateSchema() throws Exception { +// } +// +// @Test +// public void testGetHibernate() throws Exception { +// } +// + + @Test + public void testGetHibernateFactory() throws Exception { + if (log.isDebugEnabled()) { + log.debug("## testGetHibernateFactory"); + } + + /** PREPARE DATA **/ + TopiaContextImpl context = new TopiaContextImpl(); + context.services = new HashMap<String, TopiaService>(); + + String basedir = System.getenv("basedir"); + if (basedir == null) { + + // says basedir is where we start tests. + basedir = new File("").getAbsolutePath(); + } + + if (log.isDebugEnabled()) { + log.debug("baseDir : " + basedir); + } + File persistenceDir = new File(basedir, + "target" + File.separator + + "test-classes" + File.separator + + "org" + File.separator + + "nuiton" + File.separator + + "topiatest" + File.separator + + "persistence"); + if (log.isDebugEnabled()) { + log.debug("persistenceDir : " + persistenceDir); + } + File resourcesDir = new File(basedir, + "target" + File.separator + + "test-classes"); + + /** EXEC METHOD **/ + if (log.isInfoEnabled()) { + log.info("test 1 : load mappings from directory"); + } + + properties.setProperty(TopiaContextFactory.CONFIG_PERSISTENCE_DIRECTORIES, + persistenceDir.getAbsolutePath()); + context.config = properties; + + Configuration test1 = context.getHibernateConfiguration(); + PersistentClass persistentClass = + test1.getClassMapping(Entity1Impl.class.getName()); + Assert.assertNotNull(persistentClass); + Assert.assertEquals(Entity1.class, persistentClass.getProxyInterface()); + +// for (Iterator<RootClass> it = test1.getClassMappings(); it.hasNext();) { +// RootClass o = it.next(); +// log.debug("entity : " + o.getEntityName()); +// } + + if (log.isInfoEnabled()) { + log.info("test 2 : load mappings for all entities"); + } + //reset from previous test + context = new TopiaContextImpl(); + context.services = new HashMap<String, TopiaService>(); + properties.clear(); + + // use property TOPIA_PERSISTENCE_CLASSES + properties.setProperty(TopiaContextFactory.CONFIG_PERSISTENCE_CLASSES, + Entity1Impl.class.getName()); + context.config = properties; + + Configuration test2 = context.getHibernateConfiguration(); + persistentClass = test2.getClassMapping(Entity1Impl.class.getName()); + Assert.assertNotNull(persistentClass); + Assert.assertEquals(Entity1.class, persistentClass.getProxyInterface()); + + if (log.isInfoEnabled()) { + log.info("test 3 : add properties from file"); + } + //reset from previous test + context = new TopiaContextImpl(); + context.services = new HashMap<String, TopiaService>(); + properties.clear(); + + // use property TOPIA_PERSISTENCE_PROPERTIES_FILE to add default + // properties from file + properties.setProperty(TopiaContextFactory.CONFIG_PERSISTENCE_PROPERTIES_FILE, + resourcesDir + File.separator + "TopiaContextImpl.properties"); + context.config = properties; + + Configuration test3 = context.getHibernateConfiguration(); + Assert.assertEquals( + test3.getProperty("hibernate.connection.driver_class"), + Driver.class.getName()); + + // Note : maybe add a test to load classes from services + } + +// @Test +// public void replicateEntity() throws Exception { +// +// Properties configSource = TestHelper.initTopiaContextConfiguration( +// testBasedir, +// "/TopiaContextImpl.properties", +// "replicateSource"); +// +// Properties configTarget = TestHelper.initTopiaContextConfiguration( +// testBasedir, +// "/TopiaContextImpl.properties", +// "replicateTarget"); +// +// +// TopiaContext contextSource = null; +// TopiaContext contextTarget = null; +// +// try { +// contextSource = TopiaContextFactory.getContext(configSource); +// contextTarget = TopiaContextFactory.getContext(configTarget); +// +// TopiaContext txSource; +// TopiaContext txTarget; +// PersonDAO daoSource, daoTarget; +// PetDAO petDAOSource, petDAOTarget; +// Person personSource, personTarget; +// Pet petSource, petTarget; +// +// txSource = contextSource.beginTransaction(); +// daoSource = TopiaTestDAOHelper.getPersonDAO(txSource); +// petDAOSource = TopiaTestDAOHelper.getPetDAO(txSource); +// +// personSource = daoSource.create(Person.PROPERTY_FIRSTNAME, " firstName", +// Person.PROPERTY_NAME, " name" +// ); +// +// petSource = petDAOSource.create(Pet.PROPERTY_NAME, "name", +// Pet.PROPERTY_TYPE, "type", +// Pet.PROPERTY_PERSON, personSource +// ); +// +// personSource.addPet(petSource); +// +// txSource.commitTransaction(); +// +// daoSource = TopiaTestDAOHelper.getPersonDAO(txSource); +// +// personSource = daoSource.findByTopiaId(personSource.getTopiaId()); +// Assert.assertNotNull(personSource); +// +// petSource = petDAOSource.findByTopiaId(petSource.getTopiaId()); +// Assert.assertNotNull(petSource); +// Assert.assertEquals(1, personSource.sizePet()); +// Assert.assertEquals(petSource, personSource.getPet().iterator().next()); +// +// txTarget = contextTarget.beginTransaction(); +// +// txSource.replicateEntity(txTarget, petSource); +// txSource.replicateEntity(txTarget, personSource); +// +// txTarget.commitTransaction(); +// +// daoTarget = TopiaTestDAOHelper.getPersonDAO(txTarget); +// petDAOTarget = TopiaTestDAOHelper.getPetDAO(txTarget); +// +// personTarget = daoTarget.findByTopiaId(personSource.getTopiaId()); +// Assert.assertNotNull(personTarget); +// Assert.assertEquals(personSource, personTarget); +// Assert.assertEquals(1, personTarget.sizePet()); +// +// petTarget = petDAOTarget.findByTopiaId(petSource.getTopiaId()); +// Assert.assertNotNull(petTarget); +// Assert.assertEquals(petSource, petTarget); +// +// Assert.assertEquals(petTarget, personTarget.getPet().iterator().next()); +// +// +// } finally { +// closeDb(contextSource); +// closeDb(contextTarget); +// } +// +// } +// +// protected static void closeDb(TopiaContext contextSource) { +// if (contextSource != null && !contextSource.isClosed()) +// try { +// contextSource.clear(false); +// } catch (TopiaException e) { +// if (log.isErrorEnabled()) { +// log.error("Could not close db " + contextSource, e); +// } +// } +// } + + +// +// @Test +// public void testGetHibernateConfiguration() throws Exception { +// } +// +// @Test +// public void testGetDAO() throws Exception { +// } +// +// @Test +// public void testBeginTransaction() throws Exception { +// } +// +// @Test +// public void testCommitTransaction() throws Exception { +// } +// +// @Test +// public void testRollbackTransaction() throws Exception { +// } +// +// @Test +// public void testCloseContext() throws Exception { +// } +// +// @Test +// public void testIsClosed() throws Exception { +// } +// +// @Test +// public void testFindByTopiaId() throws Exception { +// } +// +// @Test +// public void testFind() throws Exception { +// } +// +// @Test +// public void testFind2() throws Exception { +// } +// +// @Test +// public void testExecute() throws Exception { +// } +// +// @Test +// public void testAdd() throws Exception { +// } +// +// @Test +// public void testImportXML() throws Exception { +// } +// +// @Test +// public void testExportXML() throws Exception { +// } +// +// +// @Test +// public void testReplicateEntity() throws Exception { +// } +// +// @Test +// public void testReplicateEntities() throws Exception { +// } +// +// @Test +// public void testGetFiresSupport() throws Exception { +// } +// +// @Test +// public void testBackup() throws Exception { +// } +// +// @Test +// public void testRestore() throws Exception { +// } +// +// @Test +// public void testClear() throws Exception { +// } +// +// @Test +// public void testGetPersistenceClasses() throws Exception { +// } +// +// @Test +// public void testIsSchemaExist() throws Exception { +// } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaContextReplicateTest.java b/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaContextReplicateTest.java new file mode 100644 index 0000000..cf77c6a --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaContextReplicateTest.java @@ -0,0 +1,156 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.framework; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaContextFactory; +import org.nuiton.topia.TopiaDatabase; +import org.nuiton.topia.TopiaTestDAOHelper; +import org.nuiton.topia.test.entities.Person; +import org.nuiton.topia.test.entities.PersonDAO; +import org.nuiton.topia.test.entities.Pet; +import org.nuiton.topia.test.entities.PetDAO; + +import java.io.File; +import java.util.Properties; + +/** + * To test replication sugin TopiaContext. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.8 + */ +public class TopiaContextReplicateTest { + + @Rule + public final TopiaDatabase dbSource = + new TopiaDatabase() { + + @Override + protected void onDbConfigurationCreate(Properties configuration, File testDir, String dbPath) { + configuration.setProperty( + TopiaContextFactory.CONFIG_URL, "jdbc:h2:file:" + dbPath + "-source"); + + } + }; + + @Rule + public final TopiaDatabase dbTarget = + new TopiaDatabase() { + + @Override + protected void onDbConfigurationCreate(Properties configuration, File testDir, String dbPath) { + configuration.setProperty( + TopiaContextFactory.CONFIG_URL, "jdbc:h2:file:" + dbPath + "-target"); + + } + }; + + @Test + public void replicateEntity() throws Exception { +// +// Properties configSource = TestHelper.initTopiaContextConfiguration( +// testBasedir, +// "/TopiaContextImpl.properties", +// "replicateSource"); +// +// Properties configTarget = TestHelper.initTopiaContextConfiguration( +// testBasedir, +// "/TopiaContextImpl.properties", +// "replicateTarget"); +// + + TopiaContext contextSource = dbSource.getRootCtxt(); + TopiaContext contextTarget = dbTarget.getRootCtxt(); + +// try { +// contextSource = TopiaContextFactory.getContext(configSource); +// contextTarget = TopiaContextFactory.getContext(configTarget); + + TopiaContext txSource; + TopiaContext txTarget; + PersonDAO daoSource, daoTarget; + PetDAO petDAOSource, petDAOTarget; + Person personSource, personTarget; + Pet petSource, petTarget; + + txSource = contextSource.beginTransaction(); + daoSource = TopiaTestDAOHelper.getPersonDAO(txSource); + petDAOSource = TopiaTestDAOHelper.getPetDAO(txSource); + + personSource = daoSource.create(Person.PROPERTY_FIRSTNAME, " firstName", + Person.PROPERTY_NAME, " name" + ); + + petSource = petDAOSource.create(Pet.PROPERTY_NAME, "name", + Pet.PROPERTY_TYPE, "type", + Pet.PROPERTY_PERSON, personSource + ); + + personSource.addPet(petSource); + + txSource.commitTransaction(); + + daoSource = TopiaTestDAOHelper.getPersonDAO(txSource); + + personSource = daoSource.findByTopiaId(personSource.getTopiaId()); + Assert.assertNotNull(personSource); + + petSource = petDAOSource.findByTopiaId(petSource.getTopiaId()); + Assert.assertNotNull(petSource); + Assert.assertEquals(1, personSource.sizePet()); + Assert.assertEquals(petSource, personSource.getPet().iterator().next()); + + txTarget = contextTarget.beginTransaction(); + + txSource.replicateEntity(txTarget, petSource); + txSource.replicateEntity(txTarget, personSource); + + txTarget.commitTransaction(); + + daoTarget = TopiaTestDAOHelper.getPersonDAO(txTarget); + petDAOTarget = TopiaTestDAOHelper.getPetDAO(txTarget); + + personTarget = daoTarget.findByTopiaId(personSource.getTopiaId()); + Assert.assertNotNull(personTarget); + Assert.assertEquals(personSource, personTarget); + Assert.assertEquals(1, personTarget.sizePet()); + + petTarget = petDAOTarget.findByTopiaId(petSource.getTopiaId()); + Assert.assertNotNull(petTarget); + Assert.assertEquals(petSource, petTarget); + + Assert.assertEquals(petTarget, personTarget.getPet().iterator().next()); + + +// } finally { +// closeDb(contextSource); +// closeDb(contextTarget); +// } + + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaQueryTest.java b/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaQueryTest.java new file mode 100644 index 0000000..692b515 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaQueryTest.java @@ -0,0 +1,188 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.framework; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Assert; +import org.junit.Test; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topiatest.QueriedEntity; + +/** @author fdesbois */ +public class TopiaQueryTest { + + private static final Log log = LogFactory.getLog(TopiaQueryTest.class); + + @Test + public void testAdd() { + + // Test with one paramValue + String value = "topia"; + TopiaQuery query = new TopiaQuery(QueriedEntity.class); + query.addEquals(QueriedEntity.PROPERTY_TEST_ADD, value); + Assert.assertEquals( + "FROM org.nuiton.topiatest.QueriedEntity " + + "WHERE testAdd = :testAdd", + query.fullQuery()); + + // Test with null paramValue + //String nullValue = null; + query = new TopiaQuery(QueriedEntity.class); + query.addEquals(QueriedEntity.PROPERTY_TEST_ADD, new Object[]{null}); + Assert.assertEquals( + "FROM org.nuiton.topiatest.QueriedEntity " + + "WHERE testAdd IS NULL", + query.fullQuery()); + + // Test with two paramValues + String value2 = "eugene"; + query = new TopiaQuery(QueriedEntity.class); + query.addEquals(QueriedEntity.PROPERTY_TEST_ADD, value, value2); + Assert.assertEquals( + "FROM org.nuiton.topiatest.QueriedEntity " + + "WHERE testAdd IN (:testAdd1, :testAdd2)", + query.fullQuery()); + + // Test with two paramValues + null + query = new TopiaQuery(QueriedEntity.class); + query.addEquals(QueriedEntity.PROPERTY_TEST_ADD, value, value2, null); + Assert.assertEquals( + "FROM org.nuiton.topiatest.QueriedEntity " + + "WHERE testAdd IN (:testAdd1, :testAdd2) OR testAdd IS NULL", + query.fullQuery()); + } + + @Test + public void testAddSubQuery() { + + // Test 1 : Subquery with two params with different values + TopiaQuery query = new TopiaQuery(QueriedEntity.class). + addEquals(QueriedEntity.PROPERTY_TEST_ADD, "value1"); + // Exist 2 params + Assert.assertEquals(2, query.getParams().size()); + + TopiaQuery subquery = new TopiaQuery(QueriedEntity.class). + addEquals(QueriedEntity.PROPERTY_TEST_ADD, "value2"); + + query.addSubQuery("Q1 = (?)", subquery); + log.debug(query); + // Add other params from subquery + Assert.assertEquals(4, query.getParams().size()); + + + // Test 2 : Subquery with two params with different values + // one of them is null + query = new TopiaQuery(QueriedEntity.class). + addWhere(QueriedEntity.PROPERTY_TEST_ADD, TopiaQuery.Op.EQ, null); + // Exist 0 param (null value) + Assert.assertEquals(0, query.getParams().size()); + + subquery = new TopiaQuery(QueriedEntity.class). + addEquals(QueriedEntity.PROPERTY_TEST_ADD, "value1"); + + query.addSubQuery("Q1 = (?)", subquery); + log.debug(query); + // Add 2 params from subquery + Assert.assertEquals(2, query.getParams().size()); + + + // Test 3 : Subquery with two params with same value + query = new TopiaQuery(QueriedEntity.class, "Q1"). + addEquals(QueriedEntity.PROPERTY_TEST_ADD, "value1"); + // Exist 2 params + Assert.assertEquals(2, query.getParams().size()); + + subquery = new TopiaQuery(QueriedEntity.class, "Q2"). + addEquals(QueriedEntity.PROPERTY_TEST_ADD, "value1"); + + query.addSubQuery("Q1 = (?)", subquery); + log.debug(query); + // Still 2 params + Assert.assertEquals(2, query.getParams().size()); + + // non-regression test, when parameters of main query is not empty + // and sub-query use different parameters from the one used in the + // main query, parameters needed by the subquery are lost in the + // resulting query + + // Test 4 : add a subquery with its own parameters + query = new TopiaQuery(QueriedEntity.class, "Q1"). + addEquals(QueriedEntity.TOPIA_ID, "ID1"); + + subquery = new TopiaQuery(QueriedEntity.class, "Q2"). + addEquals(QueriedEntity.PROPERTY_TEST_ADD, "value"); + + query.addSubQuery(QueriedEntity.PROPERTY_TEST_ADD + " IN (?)", subquery); + + log.debug(query); + Assert.assertEquals(4, query.getParams().size()); + } + + /** Test of addFilter method, of class TopiaQuery. */ + @Test + public void testAddFilter() { + log.info("testAddFilter"); + + EntityFilter filter = new TopiaFilter(); + filter.setStartIndex(1); + filter.setEndIndex(40); + filter.setOrderBy(QueriedEntity.PROPERTY_TEST_ADD); + + TopiaQuery query = new TopiaQuery(QueriedEntity.class).addFilter(filter); + + log.debug("Query : " + query); + + Assert.assertEquals("FROM " + QueriedEntity.class.getName() + + " ORDER BY " + QueriedEntity.PROPERTY_TEST_ADD, + query.fullQuery()); + + filter.setOrderBy(null); + + query = new TopiaQuery(QueriedEntity.class).addFilter(filter); + + log.debug("Query : " + query); + + Assert.assertEquals("FROM " + QueriedEntity.class.getName() + + " ORDER BY " + TopiaEntity.TOPIA_CREATE_DATE + " DESC", + query.fullQuery()); + + } + + @Test + // cf http://nuiton.org/issues/1745 + public void testWhereWithFunction() { + + TopiaQuery query = new TopiaQuery(QueriedEntity.class); + query.addWhere("lower(name)", TopiaQuery.Op.LIKE, "%azerty%"); + + log.debug("Query : " + query); + + Assert.assertEquals("FROM " + QueriedEntity.class.getName() + + " WHERE lower(name) LIKE :lower_name_", + query.fullQuery()); + } + +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaUtilTest.java b/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaUtilTest.java new file mode 100644 index 0000000..9015c68 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/framework/TopiaUtilTest.java @@ -0,0 +1,115 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.framework; + +import org.junit.Rule; +import org.junit.Test; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaDatabase; +import org.nuiton.topia.test.entities.PersonImpl; +import org.nuiton.topiatest.Personne; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +/** + * Test pour les methodes de {@link TopiaUtil}. + * + * @author tchemit <chemit@codelutin.com> + * @version $Revision$ + * <p/> + * Last update: $Date$ + * by : $Author$ + */ +public class TopiaUtilTest { + + protected static final String PERSON_ID = "org.nuiton.topiatest.Personne#1226701039001#0.6502325993664224"; + + protected static final String PERSON_ID2 = "org.nuiton.topiatest.Personne#1226701039001#0.6502325993664999"; + + @Rule + public final TopiaDatabase db = new TopiaDatabase(); + + @Test + public void testGetTopiaIdPattern() throws Exception { + String expected; + String actual; + + expected = "org\\.nuiton\\.topiatest\\.Personne#(?:\\d+?)#(?:\\d+)\\.(?:\\d+)"; + actual = TopiaUtil.getTopiaIdPattern(Personne.class); + assertEquals(expected, actual); + } + + @Test + public void testGetTopiaPattern() throws Exception { + String expected; + Pattern pattern; + + expected = "(\\d+)-(org\\.nuiton\\.topiatest\\.Personne#(?:\\d+?)#(?:\\d+)\\.(?:\\d+))-(org\\.nuiton\\.topiatest\\.Personne#(?:\\d+?)#(?:\\d+)\\.(?:\\d+))(.*)"; + pattern = TopiaUtil.getTopiaPattern("(\\d+)-%1$s-%1$s(.*)", Personne.class); + assertEquals(expected, pattern.toString()); + + String expression = 123 + "-" + PERSON_ID + "-" + PERSON_ID2 + "-afterall"; + + Matcher matcher = pattern.matcher(expression); + + assertTrue(matcher.matches()); + + assertTrue(matcher.matches()); + assertEquals(4, matcher.groupCount()); + assertEquals("123", matcher.group(1)); + assertEquals(PERSON_ID, matcher.group(2)); + assertEquals(PERSON_ID2, matcher.group(3)); + assertEquals("-afterall", matcher.group(4)); + } + + @Test(expected = IllegalArgumentException.class) + public void testIsSchemaExistFailed() throws Exception { + TopiaUtil.isSchemaExist(db.getRootCtxt(), "fake"); + } + + @Test + public void testIsSchemaExist() throws Exception { + TopiaContext rootContext = db.getRootCtxt(); + boolean actual = TopiaUtil.isSchemaExist(rootContext, + PersonImpl.class.getName()); + + // FIXME echatellier 20130315 ce test fail depuis probablement a cause + // de hibernate.hbm2ddl.auto=update, à confirmer... + + assertFalse("Schema is not supposed to exist, but isSchemaExist=" + actual, actual); + TopiaContext tx = rootContext.beginTransaction(); + + tx.createSchema(); + actual = TopiaUtil.isSchemaExist(rootContext, PersonImpl.class.getName()); + + assertTrue(actual); + + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/generator/QueryHelperTransformerTest.java b/topia-persistence/src/test/java/org/nuiton/topia/generator/QueryHelperTransformerTest.java new file mode 100644 index 0000000..d863465 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/generator/QueryHelperTransformerTest.java @@ -0,0 +1,67 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.generator; + +import org.junit.Assert; +import org.junit.Test; +import org.nuiton.eugene.java.JavaBuilder; +import org.nuiton.eugene.models.object.xml.ObjectModelClassImpl; + +import java.util.HashMap; + +/** + * Created: 24 juin 2010 + * + * @author fdesbois <fdesbois@codelutin.com> + * @version $Id$ + */ +public class QueryHelperTransformerTest { + + @Test + public void testCreateAliasConstant() { + QueryHelperTransformer transformer = new QueryHelperTransformer(); + transformer.aliases = new HashMap<String, String>(); + transformer.helperClass = new ObjectModelClassImpl(); + transformer.setBuilder(new JavaBuilder("TopiaTest")); + + String entityName = "Department"; + transformer.createAliasConstant(entityName); + Assert.assertEquals(1, transformer.aliases.size()); + Assert.assertTrue(transformer.aliases.containsKey("D")); + + entityName = "Depot"; + transformer.createAliasConstant(entityName); + Assert.assertEquals(2, transformer.aliases.size()); + Assert.assertTrue(transformer.aliases.containsKey("DE")); + + entityName = "Deposite"; + transformer.createAliasConstant(entityName); + Assert.assertEquals(3, transformer.aliases.size()); + Assert.assertTrue(transformer.aliases.containsKey("DEP")); + + entityName = "Dep"; + transformer.createAliasConstant(entityName); + Assert.assertEquals(4, transformer.aliases.size()); + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/generator/TopiaGeneratorUtilTest.java b/topia-persistence/src/test/java/org/nuiton/topia/generator/TopiaGeneratorUtilTest.java new file mode 100644 index 0000000..7898c2b --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/generator/TopiaGeneratorUtilTest.java @@ -0,0 +1,63 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.generator; + +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** @author tchemit <chemit@codelutin.com> */ +public class TopiaGeneratorUtilTest { + + @Deprecated + @Test + public void testConvertVariableNameToConstantName() { + + String variableName = "abc"; + String expResult = "ABC"; + String result = TopiaGeneratorUtil.convertVariableNameToConstantName(variableName); + assertEquals(expResult, result); + + variableName = "ABC"; + expResult = "ABC"; + result = TopiaGeneratorUtil.convertVariableNameToConstantName(variableName); + assertEquals(expResult, result); + + variableName = "abC"; + expResult = "AB_C"; + result = TopiaGeneratorUtil.convertVariableNameToConstantName(variableName); + assertEquals(expResult, result); + + variableName = "AbC"; + expResult = "AB_C"; + result = TopiaGeneratorUtil.convertVariableNameToConstantName(variableName); + assertEquals(expResult, result); + + variableName = "AbC"; + expResult = "AB_C"; + result = TopiaGeneratorUtil.convertVariableNameToConstantName(variableName); + assertEquals(expResult, result); + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/generator/TopiaTestCase.java b/topia-persistence/src/test/java/org/nuiton/topia/generator/TopiaTestCase.java new file mode 100644 index 0000000..636f34c --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/generator/TopiaTestCase.java @@ -0,0 +1,169 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.generator; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaDatabase; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaTestDAOHelper; +import org.nuiton.topiatest.Company; +import org.nuiton.topiatest.CompanyDAO; +import org.nuiton.topiatest.Department; +import org.nuiton.topiatest.DepartmentDAO; + +/** + * TopiaTestCase. + * + * @author chatellier + * @version $Revision$ + * <p/> + * Last update : $Date$ + * By : $Author$ + */ +public class TopiaTestCase { + + /** Logger */ + private final static Log log = LogFactory.getLog(TopiaTestCase.class); + + @Rule + public final TopiaDatabase db = new TopiaDatabase(); + +// /** Proprietes */ +// protected static Properties config; +// +// /** TopiaContext */ +// protected static TopiaContext context; + +// /** +// * Init les proprietes de connection a la base +// * +// * @throws IOException for any IO error while getting configuration. +// */ +// @BeforeClass +// public static void init() throws IOException { +// +// File testBasedir = TestHelper.getTestBasedir(TopiaTestCase.class); +// +// config = TestHelper.initTopiaContextConfiguration( +// testBasedir, +// "/TopiaContextImpl.properties", +// "TopiaTestCaseDb"); +//// config = new Properties(); +//// config.setProperty("topia.persistence.classes", TopiaTestDAOHelper.getImplementationClassesAsString()); +//// +//// config.setProperty(Environment.USER, "sa"); +//// config.setProperty(Environment.PASS, ""); +//// config.setProperty(Environment.CURRENT_SESSION_CONTEXT_CLASS, "thread"); +//// config.setProperty(Environment.DIALECT, "org.hibernate.dialect.H2Dialect"); +//// config.setProperty(Environment.DRIVER, "org.h2.Driver"); +//// config.setProperty(Environment.URL, "jdbc:h2:file:" + testBasedir + "/db/data_" + System.currentTimeMillis()); +// } +// +// @AfterClass +// public static void after() throws TopiaException { +// // destroy database +// context.clear(false); +// } + +// /** Create base with schema created. */ +// @Before +// public void setUp() { +// +// if (log.isDebugEnabled()) { +// log.debug("Junit beforeTest"); +// } +// +// try { +// context = TopiaContextFactory.getContext(config); +// +// try { +// context.createSchema(); +// } catch (TopiaException e) { +// log.error("Erreur à la creation du schema", e); +// } +// } catch (TopiaNotFoundException e) { +// log.error("Erreur à la creation du topia context", e); +// } +// } + + @Test + public void testCompositeAssociations() throws TopiaException { + if (log.isDebugEnabled()) { + log.debug("Junit Test testCompositeAssociations"); + } + +// try { + TopiaContext newContext = db.beginTransaction(); + + CompanyDAO companyDAO = TopiaTestDAOHelper.getCompanyDAO(newContext); + DepartmentDAO departmentDAO = TopiaTestDAOHelper.getDepartmentDAO(newContext); + + Company company = companyDAO.create(); + company.setName("Ma société"); + + + Department dep1 = departmentDAO.create(); + dep1.setName("Departement 1"); + Department dep2 = departmentDAO.create(); + dep2.setName("Departement 2"); + Department dep3 = departmentDAO.create(); + dep3.setName("Departement 3"); + Department dep4 = departmentDAO.create(); + dep4.setName("Departement 7"); + + departmentDAO.update(dep1); + departmentDAO.update(dep2); + departmentDAO.update(dep3); + departmentDAO.update(dep4); + + company.addDepartment(dep1); + company.addDepartment(dep2); + company.addDepartment(dep3); + company.addDepartment(dep4); + + companyDAO.update(company); + newContext.commitTransaction(); + + newContext = db.beginTransaction(); + + companyDAO = TopiaTestDAOHelper.getCompanyDAO(newContext); + + company = companyDAO.findByTopiaId(company.getTopiaId()); + + Assert.assertEquals(company.getName(), "Ma société"); + Assert.assertEquals(company.getDepartment().size(), 4); + + newContext.commitTransaction(); +// newContext.closeContext(); +// } catch (TopiaException e) { +// log.error("Erreur pendant le test testCompositeAssociations", e); +// } + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/persistence/EntityVisitorExportXmlTest.java b/topia-persistence/src/test/java/org/nuiton/topia/persistence/EntityVisitorExportXmlTest.java new file mode 100644 index 0000000..0b28e63 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/persistence/EntityVisitorExportXmlTest.java @@ -0,0 +1,131 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaDatabase; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaTestDAOHelper; +import org.nuiton.topiatest.Address; +import org.nuiton.topiatest.AddressDAO; +import org.nuiton.topiatest.Company; +import org.nuiton.topiatest.CompanyDAO; +import org.nuiton.topiatest.Department; +import org.nuiton.topiatest.DepartmentDAO; +import org.nuiton.topiatest.Employe; +import org.nuiton.topiatest.EmployeDAO; + +/** + * Test de visitor. + * + * @author chatellier + * @version $Revision$ + * <p/> + * Last update : $Date$ + * By : $Author$ + */ +public class EntityVisitorExportXmlTest { + + private static final Log log = + LogFactory.getLog(EntityVisitorExportXmlTest.class); + + @Rule + public final TopiaDatabase db = new TopiaDatabase(); + + /** + * Prepare test. + * <p/> + * Add all tests commons data + * + * @throws TopiaException if could not create datas + */ + @Before + public void setUp() throws TopiaException { + + TopiaContext newContext = db.beginTransaction(); + try { + // company + CompanyDAO companyDAO = TopiaTestDAOHelper.getCompanyDAO(newContext); + Company clCompany = companyDAO.create(Company.PROPERTY_NAME, "CodeLutin"); + + // employe + EmployeDAO employeDAO = TopiaTestDAOHelper.getEmployeDAO(newContext); + Employe empl1 = employeDAO.create(Employe.PROPERTY_NAME, "boss", Employe.PROPERTY_SALARY, 30000); + + AddressDAO adressDAO = TopiaTestDAOHelper.getAddressDAO(newContext); + Address addr1 = adressDAO.create(Address.PROPERTY_CITY, "Nantes", Address.PROPERTY_ADRESS, "12 Avenue Jules Vernes"); + empl1.setAddress(addr1); + + Employe empl2 = employeDAO.create(Employe.PROPERTY_NAME, "boss2", Employe.PROPERTY_SALARY, 29000); + Address addr2 = adressDAO.create(Address.PROPERTY_CITY, "Nantes", Address.PROPERTY_ADRESS, "12 Avenue Jules Vernes"); + empl2.setAddress(addr2); + + // departement + DepartmentDAO departmentDAO = TopiaTestDAOHelper.getDepartmentDAO(newContext); + Department depComm = departmentDAO.create(Department.PROPERTY_NAME, "Commercial"); + depComm.setLeader(empl1); + + Department depDev = departmentDAO.create(Department.PROPERTY_NAME, "Dev"); + depDev.setLeader(empl2); + clCompany.addDepartment(depComm); + clCompany.addDepartment(depDev); + + newContext.commitTransaction(); + } finally { + + newContext.closeContext(); + } + } + + + /** + * Test l'export XML via un visiteur. + * <p/> + * Parcourt en profondeur. + * + * @throws TopiaException + */ + @Test + public void testExportXMLDepth() throws TopiaException { + + TopiaContext context = db.beginTransaction(); + + CompanyDAO companyDAO = TopiaTestDAOHelper.getCompanyDAO(context); + Company clCompany = companyDAO.findByName("CodeLutin"); + + EntityVisitor delegateVisitor = new ExportXMLVisitor(); + EntityVisitor visitor = new DepthEntityVisitor(delegateVisitor); + clCompany.accept(visitor); + + if (log.isInfoEnabled()) { + log.info("Export XML = \n" + delegateVisitor.toString()); + } + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/persistence/ExportXMLVisitor.java b/topia-persistence/src/test/java/org/nuiton/topia/persistence/ExportXMLVisitor.java new file mode 100644 index 0000000..32a377a --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/persistence/ExportXMLVisitor.java @@ -0,0 +1,106 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence; + + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Ignore; + +/** + * Visitor pour export xml. + * + * @author chatellier + * @version $Revision$ + * <p/> + * Last update : $Date$ + * By : $Author$ + */ +@Ignore +public class ExportXMLVisitor implements EntityVisitor { + + /** log. */ + private static Log log = LogFactory.getLog(ExportXMLVisitor.class); + + protected StringBuffer buffer; + + public ExportXMLVisitor() { + buffer = new StringBuffer(); + } + + @Override + public void start(TopiaEntity e) { + if (log.isDebugEnabled()) { + log.debug("start : " + e); + } + + buffer.append("<").append(e.getClass().getName()); + buffer.append(" topiaId=\"").append(e.getTopiaId()).append("\""); + buffer.append(" topiaCreateDate=\"").append(e.getTopiaCreateDate()).append("\""); + buffer.append(" topiaVersion=\"").append(e.getTopiaVersion()).append("\""); + buffer.append(">\n"); + } + + @Override + public void visit(TopiaEntity e, String name, Class<?> type, Object value) { + if (log.isDebugEnabled()) { + log.debug("visit : " + e); + } + + buffer.append("<").append(type.getName()).append(">").append(value).append("</").append(type.getName()).append(">\n"); + } + + @Override + public void visit(TopiaEntity e, String name, Class<?> collectionType, Class<?> type, + Object value) { + } + + @Override + public void visit(TopiaEntity e, String name, Class<?> collectionType, Class<?> type, int index, Object value) { + visit(e, name, type, value); + } + + @Override + public void end(TopiaEntity e) { + if (log.isDebugEnabled()) { + log.debug("end : " + e); + } + + buffer.append("</").append(e.getClass().getName()).append(">\n"); + } + + @Override + public String toString() { + String content = buffer.toString(); + return content; + } + + @Override + public void clear() { + // do nothing + } + + +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/persistence/NaturalIdTest.java b/topia-persistence/src/test/java/org/nuiton/topia/persistence/NaturalIdTest.java new file mode 100644 index 0000000..0b7268f --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/persistence/NaturalIdTest.java @@ -0,0 +1,177 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.PropertyValueException; +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaDatabase; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaTestDAOHelper; +import org.nuiton.topiatest.NaturalizedEntity; +import org.nuiton.topiatest.NaturalizedEntityDAO; + +/** + * NaturalIdTest + * <p/> + * Created: 18 févr. 2010 + * + * @author fdesbois + * @version $Revision$ + * <p/> + * Mise a jour: $Date$ + * par : $Author$ + */ +public class NaturalIdTest { + + private static final Log log = LogFactory.getLog(NaturalIdTest.class); + + @Rule + public final TopiaDatabase db = new TopiaDatabase(); + + @Test + public void testCreateSucessfull() throws Exception { + log.debug("Test naturalId : create succesfull"); + TopiaContext transaction = db.beginTransaction(); + + NaturalizedEntityDAO dao = + TopiaTestDAOHelper.getNaturalizedEntityDAO(transaction); + + // No exception will be thrown with the two properties + dao.createByNaturalId(5, "str"); + transaction.commitTransaction(); + + // No exception will only the need property + dao.createByNotNull(3); + transaction.commitTransaction(); + + // No exception will only the need property + dao.create(NaturalizedEntity.PROPERTY_NATURAL_ID_NOT_NULL, 3); + transaction.commitTransaction(); + } + + @Test + public void testCreateFailed() throws Exception { + log.debug("Test naturalId : create failed"); + TopiaContext transaction = db.beginTransaction(); + + NaturalizedEntityDAO dao = + TopiaTestDAOHelper.getNaturalizedEntityDAO(transaction); + + // Exception will be throw + try { + dao.create(); + transaction.commitTransaction(); + + // Note : this is possible to create an empty entity if the type + // is primitive like 'int' which have a default value of '0' + } catch (PropertyValueException eee) { + Assert.assertEquals("naturalIdNotNull", eee.getPropertyName()); + } + } + + @Test + public void testUpdateFailed() throws Exception { + log.debug("Test naturalId : update failed"); + + TopiaContext transaction = db.beginTransaction(); + + NaturalizedEntityDAO dao = + TopiaTestDAOHelper.getNaturalizedEntityDAO(transaction); + + NaturalizedEntity entity = + dao.createByNaturalId(5, "str"); + transaction.commitTransaction(); + + // Exception will be throw : not allowed to modify a naturalId property + try { + entity.setNaturalIdNotNull(8); + transaction.commitTransaction(); + } catch (TopiaException eee) { + Assert.assertEquals("org.hibernate.HibernateException", + eee.getCause().getClass().getName()); + } + } + + @Test + public void testFindByNaturalId() throws Exception { + log.debug("Test naturalId : findByNaturalId"); + TopiaContext transaction = db.beginTransaction(); + + NaturalizedEntityDAO dao = + TopiaTestDAOHelper.getNaturalizedEntityDAO(transaction); + + NaturalizedEntity entity = + dao.createByNaturalId(5, "str"); + transaction.commitTransaction(); + + NaturalizedEntity result = dao.findByNaturalId(5, "str"); + + Assert.assertEquals(entity, result); + } + + @Test + public void testExistNaturalId() throws Exception { + log.debug("Test naturalId : existNaturalId"); + TopiaContext transaction = db.beginTransaction(); + + NaturalizedEntityDAO dao = + TopiaTestDAOHelper.getNaturalizedEntityDAO(transaction); + + dao.createByNaturalId(5, "str"); + transaction.commitTransaction(); + + boolean result = dao.existByNaturalId(5, "str"); + + Assert.assertTrue(result); + + // not find with only one correct property + result = dao.existByNaturalId(8, "str"); + + Assert.assertFalse(result); + } + + @Test + public void testNaturalIdAreGeneralized() throws Exception { + + // test that natural ids are generalized + String[] generalizedNaturalizedNaturalIds = TopiaTestDAOHelper.TopiaTestEntityEnum.GeneralizedNaturalizedEntity.getNaturalIds(); + String[] naturalizedNaturalIds = TopiaTestDAOHelper.TopiaTestEntityEnum.NaturalizedEntity.getNaturalIds(); + Assert.assertArrayEquals(generalizedNaturalizedNaturalIds, naturalizedNaturalIds); + } + + @Test + public void testNotNullsAreGeneralized() throws Exception { + + // test that not nulls are generalized + String[] generalizedNaturalizedNotNulls = TopiaTestDAOHelper.TopiaTestEntityEnum.GeneralizedNaturalizedEntity.getNotNulls(); + String[] naturalizedNotNulls = TopiaTestDAOHelper.TopiaTestEntityEnum.NaturalizedEntity.getNotNulls(); + Assert.assertArrayEquals(generalizedNaturalizedNotNulls, naturalizedNotNulls); + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/persistence/TopiaContextFindTest.java b/topia-persistence/src/test/java/org/nuiton/topia/persistence/TopiaContextFindTest.java new file mode 100644 index 0000000..d326ac6 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/persistence/TopiaContextFindTest.java @@ -0,0 +1,162 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.persistence; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaDatabase; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaTestDAOHelper; +import org.nuiton.topiatest.Address; +import org.nuiton.topiatest.AddressDAO; +import org.nuiton.topiatest.Gender; +import org.nuiton.topiatest.Personne; +import org.nuiton.topiatest.PersonneDAO; + +import java.util.List; + +/** + * Tests the TopiaContext#find|findAll|findUnique methods + * + * @author Arnaud Thimel <thimel@codelutin.com> + */ +public class TopiaContextFindTest { + + @Rule + public final TopiaDatabase db = new TopiaDatabase(); + + protected TopiaContext context; + protected AddressDAO addressDAO; + protected PersonneDAO personneDAO; + protected Address address; + + @Before + public void createCompanies() throws TopiaException { + context = db.beginTransaction(); + personneDAO = TopiaTestDAOHelper.getPersonneDAO(context); + addressDAO = TopiaTestDAOHelper.getAddressDAO(context); + + address = addressDAO.create( + Address.PROPERTY_ADRESS, "17 rue de la Pote Gellée, 44200 NANTES"); + + personneDAO.create( + Personne.PROPERTY_NAME, "Arnaud", + Personne.PROPERTY_GENDER, Gender.MALE); + personneDAO.create( + Personne.PROPERTY_NAME, "Charlotte", + Personne.PROPERTY_GENDER, Gender.FEMALE); + personneDAO.create( + Personne.PROPERTY_NAME, "Hortense", + Personne.PROPERTY_GENDER, Gender.FEMALE); + context.commitTransaction(); + } + + @Test + public void testFindDAO() throws TopiaException { + Assert.assertEquals(3, personneDAO.count()); + + Assert.assertEquals(2, personneDAO.findAllByGender(Gender.FEMALE).size()); + Assert.assertNotNull(personneDAO.findByGender(Gender.FEMALE)); + Assert.assertNotNull(personneDAO.findByGender(Gender.MALE)); + Assert.assertNull(personneDAO.findByGender(null)); + + Assert.assertEquals(0, personneDAO.findAllByName("nobody").size()); + } + + @Test + public void testFindAll() throws TopiaException { + Assert.assertEquals(3, personneDAO.count()); + + String query = "from " + Personne.class.getName() + + " where " + Personne.PROPERTY_GENDER + "=:g"; + + List females = context.findAll(query, "g", Gender.FEMALE); + Assert.assertEquals(2, females.size()); + + List males = context.findAll(query, "g", Gender.MALE); + Assert.assertEquals(1, males.size()); + + List all = context.findAll("from " + Personne.class.getName()); + Assert.assertEquals(3, all.size()); + + List none = context.findAll("from " + Personne.class.getName() + + " where " + Personne.PROPERTY_NAME + "=:pax", "pax", "nobody"); + Assert.assertEquals(0, none.size()); + } + + @Test + public void testFind() throws TopiaException { + Assert.assertEquals(3, personneDAO.count()); + + String query = "from " + Personne.class.getName() + + " where " + Personne.PROPERTY_GENDER + "=:g"; + + List females = context.find(query, 0, 100, "g", Gender.FEMALE); + Assert.assertEquals(2, females.size()); + + females = context.find(query, 0, 0, "g", Gender.FEMALE); + Assert.assertEquals(1, females.size()); + Personne charlotte = (Personne)females.get(0); + + females = context.find(query, 1, 1, "g", Gender.FEMALE); + Assert.assertEquals(1, females.size()); + Personne hortense = (Personne)females.get(0); + + Assert.assertFalse(hortense.equals(charlotte)); + + // endIndex = -1 not supported in ToPIA 2.6, wait for 3.0 +// females = context.find(query, 0, -1, "g", Gender.FEMALE); +// Assert.assertEquals(2, females.size()); + } + + @Test + public void testFindUnique() throws TopiaException { + Assert.assertEquals(3, personneDAO.count()); + + String query = "from " + Personne.class.getName() + + " where " + Personne.PROPERTY_GENDER + "=:g"; + + Object male = context.findUnique(query, "g", Gender.MALE); + Assert.assertNotNull(male); + + Object none = context.findUnique("from " + Personne.class.getName() + + " where " + Personne.PROPERTY_NAME + "=:pax", "pax", "nobody"); + Assert.assertNull(none); + } + + @Test(expected = TopiaException.class) + public void testFindUniqueOutOfBounds() throws TopiaException { + Assert.assertEquals(3, personneDAO.count()); + + String query = "from " + Personne.class.getName() + + " where " + Personne.PROPERTY_GENDER + "=:g"; + + Object female = context.findUnique(query, "g", Gender.FEMALE); + Assert.assertNotNull(female); + } + +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/persistence/TopiaDAOTest.java b/topia-persistence/src/test/java/org/nuiton/topia/persistence/TopiaDAOTest.java new file mode 100644 index 0000000..0737ef5 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/persistence/TopiaDAOTest.java @@ -0,0 +1,201 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence; + +import com.google.common.collect.Lists; +import org.hamcrest.CoreMatchers; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaDatabase; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaTestDAOHelper; +import org.nuiton.topia.test.entities.Person; +import org.nuiton.topia.test.entities.PersonDAO; + +import java.util.List; + +/** + * Test on {@link TopiaDAO}. + * <p/> + * Last update : $Date$ + * By : $Author$ + * + * @author chatellier + * @version $Revision$ + */ +public class TopiaDAOTest { + + @Rule + public final TopiaDatabase db = new TopiaDatabase(); + + protected TopiaContext context; + + protected PersonDAO dao; + + @Before + public void setup() throws TopiaException { + + context = db.beginTransaction(); + + dao = TopiaTestDAOHelper.getPersonDAO(context); + } + + /** + * Test de creer une entité et de verifier qu'elle est + * présente dans la persistence au sein de la transaction. + * + * @throws Exception if any exception while test + */ + @Test + public void testCreateAndFindInTransaction() throws Exception { + + // appel 1 find all + createPerson("toto"); + List<Person> allPerson = dao.findAll(); + Assert.assertEquals(1, allPerson.size()); + context.commitTransaction(); + + // recherce la personne créée dans la même transaction + Person person2 = createPerson("titi"); + allPerson = dao.findAll(); + Assert.assertEquals(2, allPerson.size()); + Assert.assertThat(allPerson, CoreMatchers.hasItem(person2)); + + context.rollbackTransaction(); + + // meme test apres roolback + Person person3 = createPerson("tata"); + allPerson = dao.findAll(); + Assert.assertEquals(2, allPerson.size()); + Assert.assertThat(allPerson, CoreMatchers.hasItem(person3)); + + context.commitTransaction(); + } + + @Test + public void findAllLazyByQuery() throws TopiaException { + + Assert.assertEquals(dao.count(), 0); + + createPersons(101); + + Iterable<Person> allByLazy = dao.findAllLazyByQuery( + 100, + "FROM " + dao.getTopiaEntityEnum().getImplementationFQN() + " ORDER BY id"); + + List<Person> actual = Lists.newArrayList(); + + for (Person person : allByLazy) { + actual.add(person); + } + Assert.assertEquals(dao.count(), actual.size()); + + allByLazy = dao.findAllLazyByQuery( + 54, + "FROM " + dao.getTopiaEntityEnum().getImplementationFQN() + " ORDER BY id"); + + actual = Lists.newArrayList(); + + for (Person person : allByLazy) { + actual.add(person); + } + Assert.assertEquals(dao.count(), actual.size()); + + allByLazy = dao.findAllLazyByQuery( + 49, + "FROM " + dao.getTopiaEntityEnum().getImplementationFQN() + " ORDER BY id"); + + actual = Lists.newArrayList(); + + for (Person person : allByLazy) { + actual.add(person); + } + Assert.assertEquals(dao.count(), actual.size()); + + allByLazy = dao.findAllLazyByQuery( + 101, + "FROM " + dao.getTopiaEntityEnum().getImplementationFQN() + " ORDER BY id"); + + actual = Lists.newArrayList(); + + for (Person person : allByLazy) { + actual.add(person); + } + Assert.assertEquals(dao.count(), actual.size()); + + allByLazy = dao.findAllLazyByQuery( + 102, + "FROM " + dao.getTopiaEntityEnum().getImplementationFQN() + " ORDER BY id"); + + actual = Lists.newArrayList(); + + for (Person person : allByLazy) { + actual.add(person); + } + Assert.assertEquals(dao.count(), actual.size()); + } + + @Test + public void iterateOnTopiaDAO() throws TopiaException { + + createPersons(1999); + + List<Person> excepted = dao.findAll(); + + List<Person> actual = Lists.newArrayList(); + + for (Person person : dao) { + Assert.assertThat(excepted, CoreMatchers.hasItem(person)); + actual.add(person); + } + Assert.assertEquals(excepted.size(), actual.size()); + + dao.setBatchSize(54); + + actual = Lists.newArrayList(); + + for (Person person : dao) { + Assert.assertThat(excepted, CoreMatchers.hasItem(person)); + actual.add(person); + } + Assert.assertEquals(excepted.size(), actual.size()); + + } + + protected void createPersons(int number) throws TopiaException { + for (int i = 0; i < number; i++) { + createPerson("toto" + i); + } + + context.commitTransaction(); + } + + protected Person createPerson(String name) throws TopiaException { + return dao.create(Person.PROPERTY_NAME, name); + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/persistence/util/CollectorTest.java b/topia-persistence/src/test/java/org/nuiton/topia/persistence/util/CollectorTest.java new file mode 100644 index 0000000..7096c17 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/persistence/util/CollectorTest.java @@ -0,0 +1,123 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaTestDAOHelper; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topiatest.Company; +import org.nuiton.topiatest.CompanyImpl; +import org.nuiton.topiatest.Department; +import org.nuiton.topiatest.DepartmentImpl; +import org.nuiton.topiatest.EmployeImpl; + +/** @author tchemit <chemit@codelutin.com> */ +public class CollectorTest { + + + private static Log log = LogFactory.getLog(CollectorTest.class); + + + static TopiaEntityEnum[] contracts; + + @BeforeClass + public static void setUpClass() throws Exception { + contracts = TopiaTestDAOHelper.getContracts(); + } + + @AfterClass + public static void tearDownClass() throws Exception { + contracts = null; + } + + @Test + public void testCollector() throws Exception { + + Collector<Integer> detector = new Collector<Integer>(contracts) { + + int hits; + + @Override + protected void beforeAll(CollectorVisitor visitor, TopiaEntity... entities) { + super.beforeAll(visitor, entities); + hits = 0; + } + + @Override + protected Integer afterAll(CollectorVisitor visitor, TopiaEntity... entities) { + return hits; + } + + @Override + protected void onStarted(TopiaEntity e, boolean enter) { + super.onStarted(e, enter); + int level = stackSize(); + log.info(String.format("(%1$2d) %2$" + level * 2 + "s %3$s", level, ">>", getStack())); + + hits++; + } + + @Override + protected void onEnded(TopiaEntity e, boolean enter) { + super.onEnded(e, enter); + int level = stackSize() + 1; + log.info(String.format("(%1$2d) %2$" + level * 2 + "s %3$s", level, "<<", getStack())); + } + }; + + Company company = new CompanyImpl(); + EmployeImpl employe = new EmployeImpl(); + Department department = new DepartmentImpl(); + + detect(detector, 1, company); + + company.addEmploye(employe); + detect(detector, 2, company); + + company.addDepartment(department); + detect(detector, 3, company); + + company.removeEmploye(employe); + detect(detector, 2, company); + + company.removeDepartment(department); + detect(detector, 1, company); + } + + protected void detect(Collector<Integer> detector, + int expectedResult, + TopiaEntity... entities) throws TopiaException { + Integer result = detector.detect(entities); + Assert.assertNotNull(result); + Assert.assertEquals(expectedResult, result.intValue()); + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/persistence/util/EntityOperatorTest.java b/topia-persistence/src/test/java/org/nuiton/topia/persistence/util/EntityOperatorTest.java new file mode 100644 index 0000000..07d8c01 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/persistence/util/EntityOperatorTest.java @@ -0,0 +1,237 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence.util; + +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.nuiton.topia.TopiaTestDAOHelper; +import org.nuiton.topiatest.Company; +import org.nuiton.topiatest.CompanyImpl; +import org.nuiton.topiatest.Department; +import org.nuiton.topiatest.DepartmentImpl; +import org.nuiton.topiatest.Employe; +import org.nuiton.topiatest.EmployeImpl; +import org.nuiton.topiatest.Personne; +import org.nuiton.topiatest.PersonneImpl; + +import java.util.Collection; + +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; + +/** @author tchemit <chemit@codelutin.com> */ +public class EntityOperatorTest { + + public EntityOperatorTest() { + } + + static EntityOperator<Company> operationC; + + static EntityOperator<Employe> operationE; + + static EntityOperator<Personne> operationP; + + static EntityOperator<Department> operationD; + + Company c; + + Department d; + + Employe e; + + Personne p; + + @BeforeClass + public static void setUpClass() throws Exception { + operationC = TopiaTestDAOHelper.getOperator(Company.class); + operationE = TopiaTestDAOHelper.getOperator(Employe.class); + operationP = TopiaTestDAOHelper.getOperator(Personne.class); + operationD = TopiaTestDAOHelper.getOperator(Department.class); + } + + @AfterClass + public static void tearDownClass() throws Throwable { + operationC.finalize(); + operationE.finalize(); + operationP.finalize(); + operationD.finalize(); + } + + @Before + public void setUp() { + c = new CompanyImpl(); + d = new DepartmentImpl(); + e = new EmployeImpl(); + p = new PersonneImpl(); + } + + @After + public void tearDown() { + c = null; + d = null; + e = null; + p = null; + } + + /** Test of newOperator method, of class EntityOperator. */ + @Test + public void testGet() { + + Object actual; + String name; + + e.setName(name = "name"); + actual = operationE.get(Employe.PROPERTY_NAME, e); + assertEquals(name, actual); + + actual = operationP.get(Employe.PROPERTY_NAME, e); + assertEquals(name, actual); + + actual = operationD.get(Department.PROPERTY_COMPANY, d); + assertNull(actual); + + d.setCompany(c); + actual = operationD.get(Department.PROPERTY_COMPANY, d); + assertNotNull(actual); + assertEquals(c, actual); + + actual = operationC.get(Company.PROPERTY_DEPARTMENT, c); + assertNull(actual); + + c.addDepartment(d); + actual = operationC.get(Company.PROPERTY_DEPARTMENT, c); + assertNotNull(actual); + assertFalse(((Collection<?>) actual).isEmpty()); + } + + /** Test of set method, of class EntityOperator. */ + @Test + public void testSet() { + + String name; + + operationE.set(Employe.PROPERTY_NAME, e, name = "name"); + assertEquals(name, e.getName()); + + operationP.set(Employe.PROPERTY_NAME, e, name = "name2"); + assertEquals(name, e.getName()); + } + + /** Test of getChild method, of class EntityOperator. */ + @Test + public void testGetChild() { + Object actual; + String topiaId; + + topiaId = "0"; + + actual = operationC.get(Company.PROPERTY_DEPARTMENT, c); + assertNull(actual); + + actual = operationC.getChild(Company.PROPERTY_DEPARTMENT, c, topiaId); + assertNull(actual); + + c.addDepartment(d); + + actual = operationC.get(Company.PROPERTY_DEPARTMENT, c); + assertNotNull(actual); + assertFalse(((Collection<?>) actual).isEmpty()); + + actual = operationC.getChild(Company.PROPERTY_DEPARTMENT, c, topiaId); + assertNull(actual); + + d.setTopiaId(topiaId); + actual = operationC.getChild(Company.PROPERTY_DEPARTMENT, c, topiaId); + assertNotNull(actual); + assertEquals(d, actual); + + } + + /** Test of addChild method, of class EntityOperator. */ + @Test + public void testAddChild() { + + assertTrue(c.isDepartmentEmpty()); + + operationC.addChild(Company.PROPERTY_DEPARTMENT, c, d); + assertFalse(c.isDepartmentEmpty()); + assertEquals(d, c.getDepartment().iterator().next()); + } + + /** Test of isChildEmpty method, of class EntityOperator. */ + @Test + public void testIsChildEmpty() { + + assertTrue(c.isDepartmentEmpty()); + + boolean actual = operationC.isChildEmpty(Company.PROPERTY_DEPARTMENT, c); + assertTrue(actual); + + c.addDepartment(d); + + actual = operationC.isChildEmpty(Company.PROPERTY_DEPARTMENT, c); + assertFalse(actual); + } + + /** Test of sizeChild method, of class EntityOperator. */ + @Test + public void testChildSize() { + + assertTrue(c.isDepartmentEmpty()); + + int actual = operationC.sizeChild(Company.PROPERTY_DEPARTMENT, c); + assertEquals(0, actual); + + c.addDepartment(d); + + actual = operationC.sizeChild(Company.PROPERTY_DEPARTMENT, c); + assertEquals(1, actual); + c.clearDepartment(); + + actual = operationC.sizeChild(Company.PROPERTY_DEPARTMENT, c); + assertEquals(0, actual); + } + + /** Test of removeChild method, of class EntityOperator. */ + @Test + public void testRemoveChild() { + + assertTrue(c.isDepartmentEmpty()); + + c.addDepartment(d); + + assertFalse(c.isDepartmentEmpty()); + + operationC.removeChild(Company.PROPERTY_DEPARTMENT, c, d); + + assertTrue(c.isDepartmentEmpty()); + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/persistence/util/TopiaEntityBinderTest.java b/topia-persistence/src/test/java/org/nuiton/topia/persistence/util/TopiaEntityBinderTest.java new file mode 100644 index 0000000..662828b --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/persistence/util/TopiaEntityBinderTest.java @@ -0,0 +1,104 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence.util; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.nuiton.topia.TopiaTestDAOHelper; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topiatest.Company; +import org.nuiton.util.beans.Binder; +import org.nuiton.util.beans.BinderFactory; +import org.nuiton.util.beans.BinderModelBuilder; + +public class TopiaEntityBinderTest { + + static TopiaEntityEnum[] contracts; + + @BeforeClass + public static void setUpClass() throws Exception { + contracts = TopiaTestDAOHelper.getContracts(); + } + + public static class CompanyDTO { + + protected String name; + + protected int siret; + + protected String id; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public int getSiret() { + return siret; + } + + public void setSiret(int siret) { + this.siret = siret; + } + + public String getTopiaId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + } + + @Before + public void setUp() { + BinderFactory.clear(); + } + + @AfterClass + public static void afterClass() { + BinderFactory.clear(); + } + + @Test + public void testBinder() { + + BinderModelBuilder<Company, CompanyDTO> builder = BinderModelBuilder.newEmptyBuilder(Company.class, CompanyDTO.class); + builder.addSimpleProperties(Company.PROPERTY_NAME, Company.PROPERTY_SIRET); + builder.addProperties(TopiaEntity.TOPIA_ID, "id"); + BinderFactory.registerBinderModel(builder); + + Binder<Company, CompanyDTO> binder = + BinderFactory.newBinder(Company.class, CompanyDTO.class); + Assert.assertNotNull(binder); + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/persistence/util/TopiaEntityHelperTest.java b/topia-persistence/src/test/java/org/nuiton/topia/persistence/util/TopiaEntityHelperTest.java new file mode 100644 index 0000000..7e4a370 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/persistence/util/TopiaEntityHelperTest.java @@ -0,0 +1,278 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence.util; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaTestDAOHelper; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.TopiaId; +import org.nuiton.topiatest.Company; +import org.nuiton.topiatest.CompanyImpl; +import org.nuiton.topiatest.Department; +import org.nuiton.topiatest.DepartmentImpl; +import org.nuiton.topiatest.Employe; +import org.nuiton.topiatest.EmployeAbstract; +import org.nuiton.topiatest.EmployeImpl; +import org.nuiton.topiatest.Personne; +import org.nuiton.topiatest.PersonneAbstract; +import org.nuiton.topiatest.PersonneImpl; + +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +/** @author tchemit <chemit@codelutin.com> */ +public class TopiaEntityHelperTest { + + static TopiaEntityEnum[] contracts; + + final Set<Class<? extends TopiaEntity>> contractsClass; + + public TopiaEntityHelperTest() { + contractsClass = new HashSet<Class<? extends TopiaEntity>>(); + contractsClass.add(Company.class); + contractsClass.add(Employe.class); + contractsClass.add(Department.class); + } + + @BeforeClass + public static void setUpClass() throws Exception { + contracts = TopiaTestDAOHelper.getContracts(); + } + + @AfterClass + public static void tearDownClass() throws Exception { + contracts = null; + } + + /** Test of getContractClass method, of class TopiaEntityHelper. */ + @Test + public void testgetContract() { + + Class<?> result = TopiaEntityHelper.getContractClass(null, Company.class); + assertNull(result); + + Class<? extends TopiaEntity> expResult; + + expResult = Employe.class; + + getContractClass(expResult, EmployeImpl.class); + getContractClass(expResult, EmployeAbstract.class); + getContractClass(expResult, Employe.class); + + expResult = Personne.class; + + getContractClass(expResult, PersonneImpl.class); + getContractClass(expResult, PersonneAbstract.class); + getContractClass(expResult, Personne.class); + } + + /** Test of retainContracts method, of class TopiaEntityHelper. */ + @Test + public void testRetainContracts() { + Set<Class<? extends TopiaEntity>> classes = new HashSet<Class<? extends TopiaEntity>>(); + Set<Class<? extends TopiaEntity>> result = TopiaEntityHelper.retainContracts(contracts, classes); + + assertTrue(result.isEmpty()); + + classes.add(PersonneImpl.class); + result = TopiaEntityHelper.retainContracts(contracts, classes); + assertEquals(1, result.size()); + assertTrue(result.contains(Personne.class)); + + classes.add(PersonneAbstract.class); + result = TopiaEntityHelper.retainContracts(contracts, classes); + assertEquals(1, result.size()); + assertTrue(result.contains(Personne.class)); + + classes.add(Personne.class); + result = TopiaEntityHelper.retainContracts(contracts, classes); + assertEquals(1, result.size()); + assertTrue(result.contains(Personne.class)); + + classes.clear(); + classes.add(EmployeImpl.class); + result = TopiaEntityHelper.retainContracts(contracts, classes); + assertEquals(1, result.size()); + assertTrue(result.contains(Employe.class)); + + classes.add(PersonneImpl.class); + result = TopiaEntityHelper.retainContracts(contracts, classes); + assertEquals(2, result.size()); + assertTrue(result.contains(Personne.class)); + assertTrue(result.contains(Employe.class)); + + } + + /** + * Test of detectTypes method, of class TopiaEntityHelper. + * + * @throws TopiaException + */ + @Test + public void testDetectTypes() throws TopiaException { + Company company = new CompanyImpl(); + EmployeImpl employe = new EmployeImpl(); + Department departmnet = new DepartmentImpl(); + + detectTypes(new Class<?>[]{Company.class, Employe.class, Department.class}, company, employe, departmnet); + + company.addEmploye(employe); + detectTypes(new Class<?>[]{Company.class, Employe.class}, company); + + company.addDepartment(departmnet); + departmnet.setCompany(company); + detectTypes(new Class<?>[]{Company.class, Employe.class, Department.class}, company); + + company.removeEmploye(employe); + company.removeDepartment(departmnet); + detectTypes(new Class<?>[]{Company.class}, company); + } + + @Test + public void testDetector() throws Exception { + + Company company = new CompanyImpl(); + company.setTopiaId(TopiaId.create(Company.class)); + Employe employe = new EmployeImpl(); + employe.setTopiaId(TopiaId.create(Employe.class)); + Department department = new DepartmentImpl(); + department.setTopiaId(TopiaId.create(Department.class)); + + detectEntities(new Class<?>[]{Company.class, Employe.class, Department.class}, new int[]{1, 1, 1}, company, employe, department); + detectEntityIds(new Class<?>[]{Company.class, Employe.class, Department.class}, new int[]{1, 1, 1}, company, employe, department); + + company.addEmploye(employe); + detectEntities(new Class<?>[]{Company.class, Employe.class}, new int[]{1, 1, 1}, company); + detectEntityIds(new Class<?>[]{Company.class, Employe.class}, new int[]{1, 1, 1}, company); + + company.addDepartment(department); + department.setCompany(company); + + detectEntities(new Class<?>[]{Company.class, Employe.class, Department.class}, new int[]{1, 1, 1}, company); + detectEntityIds(new Class<?>[]{Company.class, Employe.class, Department.class}, new int[]{1, 1, 1}, company); + + company.removeEmploye(employe); + company.removeDepartment(department); + + + detectEntities(new Class<?>[]{Company.class}, new int[]{1}, company); + detectEntityIds(new Class<?>[]{Company.class}, new int[]{1}, company); + + detectEntities(new Class<?>[]{Company.class}, new int[]{2}, company, new CompanyImpl()); + detectEntityIds(new Class<?>[]{Company.class}, new int[]{2}, company, new CompanyImpl()); + + department.setCompany(company); + + detectEntities(new Class<?>[]{Company.class, Department.class}, new int[]{1, 1}, department); + detectEntityIds(new Class<?>[]{Company.class, Department.class}, new int[]{1, 1}, department); + + //TODO faire des tests avec des entites avec cycles + } + + protected void detectEntities(Class<?>[] expected, + int[] sizes, + TopiaEntity... data) throws TopiaException { + + Map<Class<? extends TopiaEntity>, List<TopiaEntity>> actual = null; + + try { + actual = TopiaEntityHelper.detectEntities(contracts, contractsClass, data); + + Assert.assertEquals(expected.length, actual.size()); + int index = 0; + for (Class<?> c : expected) { + List<TopiaEntity> value = actual.get(c); + int expectedSize = sizes[index++]; + Assert.assertEquals(expectedSize, value.size()); + } + } finally { + if (actual != null) { + actual.clear(); + } + } + } + + protected void detectEntityIds(Class<?>[] expected, + int[] sizes, + TopiaEntity... data) throws TopiaException { + + TopiaEntityIdsMap actual = null; + + try { + actual = TopiaEntityHelper.detectEntityIds(contracts, + contractsClass, + data + ); + + Assert.assertEquals(expected.length, actual.size()); + int index = 0; + for (Class<?> c : expected) { + List<String> value = actual.get(c); + int expectedSize = sizes[index++]; + Assert.assertEquals(expectedSize, value.size()); + } + } finally { + if (actual != null) { + actual.clear(); + } + } + } + + protected void detectTypes(Class<?>[] expected, TopiaEntity... data) throws TopiaException { + Set<String> fqns = new HashSet<String>(expected.length); + Set<Class<? extends TopiaEntity>> actual = null; + for (Class<?> c : expected) { + fqns.add(c.getName()); + } + try { + actual = TopiaEntityHelper.detectTypes(contracts, data); + Assert.assertEquals(expected.length, actual.size()); + for (Class<? extends TopiaEntity> c : actual) { + Assert.assertTrue(fqns.contains(c.getName())); + } + } finally { + fqns.clear(); + if (actual != null) { + actual.clear(); + } + } + } + + protected void getContractClass(Class<?> expected, Class<? extends TopiaEntity> klass) { + Class<?> result = TopiaEntityHelper.getContractClass(contracts, klass); + assertEquals(expected, result); + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/persistence/util/TopiaEntityRefTester.java b/topia-persistence/src/test/java/org/nuiton/topia/persistence/util/TopiaEntityRefTester.java new file mode 100644 index 0000000..8997cea --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/persistence/util/TopiaEntityRefTester.java @@ -0,0 +1,330 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence.util; + +import org.junit.After; +import org.junit.Assert; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.SortedMap; + +import static java.util.Map.Entry; + +/** + * A abstract class to help testing {@link TopiaEntityRef} as detectes types, or + * detects or references. + * <p/> + * An example of use if given in the test {@link TopiaEntityRefTesterTest}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.3.1 + */ +public abstract class TopiaEntityRefTester<T extends TopiaEntityEnum> { + + protected SortedMap<TopiaEntity, List<TopiaEntityRef>> detected; + + protected Iterator<Entry<TopiaEntity, List<TopiaEntityRef>>> itr; + + protected Entry<TopiaEntity, List<TopiaEntityRef>> entry; + + protected List<TopiaEntityRef> refs; + + protected T[] contracts; + + protected int index; + + protected T[] getContracts() { + if (contracts == null) { + contracts = getContracts0(); + } + return contracts; + } + + /** @return all the {@link TopiaEntityEnum} to be used */ + protected abstract T[] getContracts0(); + + @After + public void after() { + itr = null; + entry = null; + refs = null; + detected = null; + index = 0; + } + + /** + * Creates a new entity with the given {@code topiaId} (will use the {@link + * TopiaEntityEnum#getImplementation()} class). + * + * @param constant the constant defining the entity + * @param topiaId the topia to assign + * @param <T> the type of entity + * @return the new entity + * @throws IllegalAccessException if can no access entity constructor + * @throws InstantiationException if can no instanciate the entity + */ + protected <T extends TopiaEntity> T newEntity( + TopiaEntityEnum constant, String topiaId) throws + IllegalAccessException, + InstantiationException { + Class<? extends TopiaEntity> impl = constant.getImplementation(); + TopiaEntity topiaEntity = impl.newInstance(); + topiaEntity.setTopiaId(topiaId); + return (T) topiaEntity; + } + + /** + * Obtain the reference of an association for a given entity. + * <p/> + * Example, to obtain the Pet 'pudding' on a Person : + * <pre>pet[@topiaId='pudding']</pre> + * invoke + * <pre>getAssociationRef('pet',pet);</pre> + * + * @param associationName the name of the association + * @param e the required entity + * @return the reference of the association + */ + protected String getAssociationRef(String associationName, TopiaEntity e) { + return getAssociationRef(associationName, e.getTopiaId()); + } + + /** + * Obtain the reference of an association for a given id. + * <p/> + * Example to obtain the Pet 'pudding' on a Person : + * <pre>pet[@topiaId='pudding']</pre> + * invoke + * <pre>getAssociationRef('pet','pudding');</pre> + * + * @param associationName the name of the association + * @param e the id + * @return the reference of the association + */ + protected String getAssociationRef(String associationName, String e) { + String s = String.format( + TopiaEntityHelper.ASSOCIATION_PATTERN, + associationName, + e + ); + return s; + } + + /** + * Obtain the next entry from the iterator. + * <p/> + * As a side-effect, it will increment the state {@link #index}. + */ + protected void nextEntry() { + if (!itr.hasNext()) { + throw new IllegalStateException("no more entry to get..."); + } + entry = itr.next(); + index = 0; + } + + /** + * Detects the references from the given {@code entity} which have their + * topiaId in the given list of {@code ids}. + * <p/> + * As a side-effect, it will update the states {@code detected} and set the + * iterator {@code itr} to the first position on detected entries. + * + * @param entity the entity to seek + * @param nb the required number of entries + * @param ids the ids to seek + * @throws TopiaException if any pb while visiting entities + */ + protected void detectReferences( + TopiaEntity entity, int nb, String... ids) throws TopiaException { + detected = TopiaEntityHelper.detectReferences( + getContracts(), ids, entity); + assertDetected(nb); + + } + + /** + * Detects the references from the given {@code entity} which have their + * topiaId in the given list of {@code ids}. + * <p/> + * As a side-effect, it will update the states {@code detected} and set the + * iterator {@code itr} to the first position on detected entries. + * + * @param entity the entity to seek + * @param nb the required number of entries + * @param ids the ids to seek + * @throws TopiaException if any pb while visiting entities + */ + protected void detectReferences( + Collection<? extends TopiaEntity> entity, + int nb, String... ids) throws TopiaException { + detected = TopiaEntityHelper.detectReferences( + getContracts(), ids, entity); + assertDetected(nb); + } + + /** + * Detects the type of entities fro the given {@code data}. + * + * @param expected the array of expected types + * @param data the data to seek + * @throws TopiaException if any pb while visiting data + */ + protected void detectTypes(Class<?>[] expected, + TopiaEntity... data) throws TopiaException { + Set<String> fqns = new HashSet<String>(expected.length); + Set<Class<? extends TopiaEntity>> actual = null; + for (Class<?> c : expected) { + fqns.add(c.getName()); + } + try { + actual = TopiaEntityHelper.detectTypes(getContracts(), data); + Assert.assertEquals(expected.length, actual.size()); + for (Class<? extends TopiaEntity> c : actual) { + Assert.assertTrue(fqns.contains(c.getName())); + } + } finally { + fqns.clear(); + if (actual != null) { + actual.clear(); + } + } + } + + /** + * Asserts if the given entry definition is equals to the next entry + * references on internal state state {@code entry}. + * + * @param invoker the invoker of the reference + * @param invokerProperty the access path of the reference + * @param expected the expected topia entities path to access + * reference target + */ + protected void assertNextEntityRef(TopiaEntity invoker, + String invokerProperty, + TopiaEntity... expected) { + assertEntityRef(index, invoker, invokerProperty, expected); + index++; + } + + /** + * Asserts if the given entry definition (of an association) is equals to + * the next entry reference on internal state {@code entry}. + * + * @param invoker the invoker of the reference + * @param association the association name + * @param id the id of the association ( see {@link + * #getAssociationRef(String, TopiaEntity)} + * @param expected the expected topia entities path to access reference + * target + */ + protected void assertNextAssociationEntityRef(TopiaEntity invoker, + String association, + String id, + TopiaEntity... expected) { + String invokerProperty = getAssociationRef(association, id); + assertNextEntityRef(invoker, invokerProperty, expected); + } + + /** + * Asserts if the given entry definition is equals to the reference entry at + * position {@code index} on internal state {@code entry}. + * + * @param index the index of the reference to test in {@code + * entry} + * @param invoker the invoker of the reference + * @param invokerProperty the path of the reference + * @param expected th expected topia entities path to access + * reference target + */ + protected void assertEntityRef(int index, + TopiaEntity invoker, + String invokerProperty, + TopiaEntity... expected) { + Assert.assertNotNull(refs); + Assert.assertTrue("no index [" + index + "] in refs of size " + + refs.size(), index < refs.size()); + TopiaEntityRef actual = refs.get(index); + Assert.assertEquals(actual.getInvoker(), invoker); + Assert.assertEquals(actual.getInvokerProperty(), invokerProperty); + + TopiaEntity[] path = actual.getPath(); + Assert.assertEquals(expected.length, path.length); + for (int i = 0; i < expected.length; i++) { + Assert.assertEquals(expected[i].getTopiaId(), path[i].getTopiaId()); + } + } + + /** + * Asserts if the given {@code expected} entity is equals to the source of + * the internal state {@code entry}. + * + * @param expected the required source of the entry + * @param nbPath the expected number of references of the given entry + */ + protected void assertCurrentEntry(TopiaEntity expected, + int nbPath) { + assertEntry(expected, nbPath, entry); + } + + /** + * Asserts if the given {@code entry} has a good source and a good number of + * references. + * + * @param expected the expected entry source + * @param nbPath the expected number of reference of the entry + * @param entry the entry to test + */ + private void assertEntry(TopiaEntity expected, + int nbPath, + Entry<TopiaEntity, List<TopiaEntityRef>> entry) { + Assert.assertNotNull(entry); + Assert.assertEquals(expected, entry.getKey()); + refs = entry.getValue(); + Assert.assertEquals(nbPath, refs.size()); + } + + /** + * Asserts that the number of detected entries (store in internal state + * {@code detected}) is ok. + * <p/> + * As a side-effect, it will reset the internal state {@code itr} on the + * first entry of the {@code detected} list. + * + * @param size the expected number of detected entries + */ + protected void assertDetected(int size) { + Assert.assertNotNull(detected); + Assert.assertEquals(size, detected.size()); + itr = detected.entrySet().iterator(); + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/persistence/util/TopiaEntityRefTesterTest.java b/topia-persistence/src/test/java/org/nuiton/topia/persistence/util/TopiaEntityRefTesterTest.java new file mode 100644 index 0000000..db33ac1 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/persistence/util/TopiaEntityRefTesterTest.java @@ -0,0 +1,154 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.persistence.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Test; +import org.nuiton.topia.TopiaTestDAOHelper.TopiaTestEntityEnum; +import org.nuiton.topia.test.entities.Person; +import org.nuiton.topia.test.entities.Pet; +import org.nuiton.topia.test.entities.Race; + +/** + * Test the {@link TopiaEntityRefTester} on + * <p/> + * <ul> <li>{@link Pet}</li> <li>{@link Race}</li> <li>{@link Person}</li> + * </ul> + * + * @author tchemit <chemit@codelutin.com> + * @since 2.3.1 + */ +public class TopiaEntityRefTesterTest extends TopiaEntityRefTester<TopiaTestEntityEnum> { + + /** Logger */ + private static final Log log = + LogFactory.getLog(TopiaEntityRefTesterTest.class); + + private static final String PET1 = "pet1"; + + private static final String PET2 = "pet2"; + + private static final String RACE1 = "race1"; + + private static final String PERSON1 = "person1"; + + @Override + protected TopiaTestEntityEnum[] getContracts0() { + return new TopiaTestEntityEnum[]{ + TopiaTestEntityEnum.Pet, + TopiaTestEntityEnum.Person, + TopiaTestEntityEnum.Race, + }; + } + + @Test + public void testNoReferences() throws Exception { + + Pet pet = newEntity(TopiaTestEntityEnum.Pet, PET1); + + detectReferences(pet, 0); + + Race race = newEntity(TopiaTestEntityEnum.Race, RACE1); + + detectReferences(race, 0); + + Person person = newEntity(TopiaTestEntityEnum.Person, PERSON1); + + detectReferences(person, 0); + + } + + @Test + public void testReferences() throws Exception { + + Pet pet = newEntity(TopiaTestEntityEnum.Pet, PET1); + Race race = newEntity(TopiaTestEntityEnum.Race, RACE1); + pet.setRace(race); + Person person = newEntity(TopiaTestEntityEnum.Person, PERSON1); + + detectReferences(pet, 1, RACE1); + + nextEntry(); + assertCurrentEntry(race, 1); + assertNextEntityRef(pet, Pet.PROPERTY_RACE, pet, race); + + pet.setPerson(person); + + detectReferences(pet, 2, RACE1, PERSON1); + + nextEntry(); + assertCurrentEntry(person, 1); + assertNextEntityRef(pet, Pet.PROPERTY_PERSON, pet, person); + + nextEntry(); + assertCurrentEntry(race, 1); + assertNextEntityRef(pet, Pet.PROPERTY_RACE, pet, race); + + person.addPet(pet); + + detectReferences(person, 1, PET1); + + nextEntry(); + assertCurrentEntry(pet, 1); + assertNextAssociationEntityRef(person, Person.PROPERTY_PET, PET1, person, pet); + + Pet pet2 = newEntity(TopiaTestEntityEnum.Pet, PET2); + + person.addPet(pet2); + + detectReferences(person, 3, PET1, PET2, RACE1); + + nextEntry(); + assertCurrentEntry(pet, 1); + assertNextAssociationEntityRef(person, Person.PROPERTY_PET, PET1, person, pet); + + nextEntry(); + assertCurrentEntry(pet2, 1); + assertNextAssociationEntityRef(person, Person.PROPERTY_PET, PET2, person, pet2); + + nextEntry(); + assertCurrentEntry(race, 1); + assertNextEntityRef(pet, Pet.PROPERTY_RACE, person, pet, race); + + pet2.setRace(race); + + detectReferences(person, 3, PET1, PET2, RACE1); + + nextEntry(); + assertCurrentEntry(pet, 1); + assertNextAssociationEntityRef(person, Person.PROPERTY_PET, PET1, person, pet); + + nextEntry(); + assertCurrentEntry(pet2, 1); + assertNextAssociationEntityRef(person, Person.PROPERTY_PET, PET2, person, pet2); + + nextEntry(); + assertCurrentEntry(race, 2); + assertNextEntityRef(pet, Pet.PROPERTY_RACE, person, pet, race); + assertNextEntityRef(pet2, Pet.PROPERTY_RACE, person, pet2, race); + + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/test/ano1882/DAOAbstractTransformerTest.java b/topia-persistence/src/test/java/org/nuiton/topia/test/ano1882/DAOAbstractTransformerTest.java new file mode 100644 index 0000000..31a10ef --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/test/ano1882/DAOAbstractTransformerTest.java @@ -0,0 +1,56 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.test.ano1882; + +import org.junit.Rule; +import org.junit.Test; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaDatabase; +import org.nuiton.topia.TopiaTestDAOHelper; + +import java.util.Arrays; + +public class DAOAbstractTransformerTest { + + @Rule + public final TopiaDatabase db = new TopiaDatabase(); + + @Test + public void testAno1882() throws Exception { + TopiaContext transaction = db.beginTransaction(); + + FrenchCompanyDAO dao = TopiaTestDAOHelper.getFrenchCompanyDAO(transaction); + SIRETDAO siretDAO = TopiaTestDAOHelper.getSIRETDAO(transaction); + SIRET siret = siretDAO.create(); + FrenchCompany entity = + dao.create( + FrenchCompany.PROPERTY_S_IREN, null, + FrenchCompany.PROPERTY_SIREN2, null, + FrenchCompany.PROPERTY_S_IRET, Arrays.asList(siret), + FrenchCompany.PROPERTY_SIRET2, null); + transaction.commitTransaction(); + dao.delete(entity); + transaction.commitTransaction(); + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/test/ano1991/TopiaQueryTest.java b/topia-persistence/src/test/java/org/nuiton/topia/test/ano1991/TopiaQueryTest.java new file mode 100644 index 0000000..ba60766 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/test/ano1991/TopiaQueryTest.java @@ -0,0 +1,130 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.test.ano1991; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaDatabase; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaTestDAOHelper; +import org.nuiton.topia.framework.TopiaQuery; +import org.nuiton.topia.test.entities.Person; +import org.nuiton.topia.test.entities.PersonDAO; +import org.nuiton.topia.test.entities.Pet; +import org.nuiton.topia.test.entities.PetDAO; + +import java.util.Arrays; +import java.util.List; + +/** + * Prevent regression of http://nuiton.org/issues/1991. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.8 + */ +@Deprecated +public class TopiaQueryTest { + + @Rule + public final TopiaDatabase db = new TopiaDatabase(); + + @Test + public void testInOperatorForIds() throws TopiaException { + + TopiaContext context = db.beginTransaction(); + + PersonDAO personDAO = TopiaTestDAOHelper.getPersonDAO(context); + + // appel 1 find all + Person toto = personDAO.create(Person.PROPERTY_NAME, "toto"); + Person titi = personDAO.create(Person.PROPERTY_NAME, "titi"); + + TopiaQuery query; + + query = personDAO.createQuery().addWhere( + Person.PROPERTY_NAME, TopiaQuery.Op.IN, new String[]{"toto"} + ); + + List<Person> result; + result = personDAO.findAllByQuery(query); + Assert.assertNotNull(result); + Assert.assertEquals(1, result.size()); + Assert.assertEquals(toto, result.get(0)); + + query = personDAO.createQuery().addWhere( + Person.PROPERTY_NAME, TopiaQuery.Op.NOT_IN, new String[]{"toto"} + ); + + result = personDAO.findAllByQuery(query); + Assert.assertNotNull(result); + Assert.assertEquals(1, result.size()); + Assert.assertEquals(titi, result.get(0)); + } + + @Test + public void testInOperatorForEntities() throws TopiaException { + + TopiaContext context = db.beginTransaction(); + + PetDAO petDAO = TopiaTestDAOHelper.getPetDAO(context); + PersonDAO personDAO = TopiaTestDAOHelper.getPersonDAO(context); + + Pet bernard = petDAO.create(Pet.PROPERTY_NAME, "bernard"); + Pet bianca = petDAO.create(Pet.PROPERTY_NAME, "bianca"); + Pet minou = petDAO.create(Pet.PROPERTY_NAME, "minou"); + Person toto = personDAO.create(Person.PROPERTY_NAME, "toto"); + toto.addPet(bernard); + + Person titi = personDAO.create(Person.PROPERTY_NAME, "titi"); + titi.addPet(bianca); + + Person tutu = personDAO.create(Person.PROPERTY_NAME, "tutu"); + tutu.addPet(minou); + + TopiaQuery query; + + query = petDAO.createQuery("p").addWhere( + "p." + Pet.PROPERTY_PERSON, TopiaQuery.Op.IN, Arrays.asList(toto, titi) + ); + + List<Pet> result; + result = petDAO.findAllByQuery(query); + Assert.assertNotNull(result); + Assert.assertEquals(2, result.size()); + Assert.assertTrue(result.contains(bernard)); + Assert.assertTrue(result.contains(bianca)); + + query = petDAO.createQuery("p").addWhere( + "p." + Pet.PROPERTY_PERSON, TopiaQuery.Op.NOT_IN, Arrays.asList(toto, titi) + ); + + result = petDAO.findAllByQuery(query); + Assert.assertNotNull(result); + Assert.assertEquals(1, result.size()); + Assert.assertTrue(result.contains(minou)); + } + +} diff --git a/topia-persistence/src/test/java/org/nuiton/topia/test/evo1912/EntityDTOTransformerTest.java b/topia-persistence/src/test/java/org/nuiton/topia/test/evo1912/EntityDTOTransformerTest.java new file mode 100644 index 0000000..ab7e4e7 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topia/test/evo1912/EntityDTOTransformerTest.java @@ -0,0 +1,45 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.test.evo1912; + +import org.junit.Assert; +import org.junit.Test; +import org.nuiton.topiatest.CompanyDTO; + +/** @author ymartel <martel@codelutin.com> */ +public class EntityDTOTransformerTest { + + @Test + public void testEvo1912() throws Exception { + // simply test the getter/setter on id property : with the tagValue, they should be generated + CompanyDTO companyDTO = new CompanyDTO(); + String originalId = companyDTO.getTopiaId(); + Assert.assertNull(originalId); + String wantedId = "mycompany"; + companyDTO.setTopiaId(wantedId); + String updatedId = companyDTO.getTopiaId(); + Assert.assertEquals(wantedId, updatedId); + } + +} diff --git a/topia-persistence/src/test/java/org/nuiton/topiatest/EnumTest.java b/topia-persistence/src/test/java/org/nuiton/topiatest/EnumTest.java new file mode 100644 index 0000000..9735ac2 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topiatest/EnumTest.java @@ -0,0 +1,74 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topiatest; + +import org.junit.Assert; +import org.junit.Rule; +import org.junit.Test; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaDatabase; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaTestDAOHelper; + +/** + * Test the support of possibility to have an attribute of type enumeration + * in a entity + */ +public class EnumTest { + + @Rule + public final TopiaDatabase db = new TopiaDatabase(); + + /** + * Create an entity having two field of type enumeration. One is stored + * using ordinal, the other using the name. + * <p/> + * The test check that values are stored, and that find methods works + * + * @throws TopiaException if any exception with db + */ + @Test + public void storeEntityWithEnumValue() throws TopiaException { + TopiaContext transaction = db.beginTransaction(); + + PersonneDAO dao = TopiaTestDAOHelper.getPersonneDAO(transaction); + Personne personne = new PersonneImpl(); + personne.setGender(Gender.FEMALE); + personne.setOtherGender(Gender.MALE); + dao.create(personne); + String topiaId = personne.getTopiaId(); + transaction.commitTransaction(); + transaction.closeContext(); + + transaction = db.beginTransaction(); + dao = TopiaTestDAOHelper.getPersonneDAO(transaction); + dao.findByTopiaId(topiaId); + Assert.assertEquals(Gender.FEMALE, personne.getGender()); + Assert.assertEquals(Gender.MALE, personne.getOtherGender()); + + Assert.assertNotNull(dao.findByGender(Gender.FEMALE)); + Assert.assertNotNull(dao.findByOtherGender(Gender.MALE)); + transaction.closeContext(); + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topiatest/ExtraDAOEntityDAOImpl.java b/topia-persistence/src/test/java/org/nuiton/topiatest/ExtraDAOEntityDAOImpl.java new file mode 100644 index 0000000..f1ac535 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topiatest/ExtraDAOEntityDAOImpl.java @@ -0,0 +1,39 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topiatest; + +import org.nuiton.topia.TopiaException; + +/** + * Created: 26 mai 2010 + * + * @author fdesbois <fdesbois@codelutin.com> + * @version $Id$ + */ +public class ExtraDAOEntityDAOImpl<E extends ExtraDAOEntity> extends ExtraDAOEntityDAOAbstract<E> { + + @Override + public void extra() throws TopiaException { + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topiatest/Gender.java b/topia-persistence/src/test/java/org/nuiton/topiatest/Gender.java new file mode 100644 index 0000000..74cb19f --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topiatest/Gender.java @@ -0,0 +1,28 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topiatest; + +public enum Gender { + MALE, FEMALE +} diff --git a/topia-persistence/src/test/java/org/nuiton/topiatest/Title.java b/topia-persistence/src/test/java/org/nuiton/topiatest/Title.java new file mode 100644 index 0000000..015a075 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topiatest/Title.java @@ -0,0 +1,36 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topiatest; + +/** + * To test http://nuiton.org/issues/1732. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.6.2 + */ +public enum Title { + Mr, + Madam, + Other +} diff --git a/topia-persistence/src/test/java/org/nuiton/topiatest/beangen/RoueImpl.java b/topia-persistence/src/test/java/org/nuiton/topiatest/beangen/RoueImpl.java new file mode 100644 index 0000000..681cc31 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topiatest/beangen/RoueImpl.java @@ -0,0 +1,52 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topiatest.beangen; + +import org.nuiton.topiatest.Product; + +/** + * RoueImpl + * + * Created: 14 janv. 2010 + * + * @author fdesbois + * @version $Revision$ + * + * Mise a jour: $Date$ + * par : $Author$ + */ +public class RoueImpl extends Roue { + + @Override + public void mount() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Product getModel(String id) { + throw new UnsupportedOperationException("Not supported yet."); + } + +} diff --git a/topia-persistence/src/test/java/org/nuiton/topiatest/deletetest/Contact2DAOImpl.java b/topia-persistence/src/test/java/org/nuiton/topiatest/deletetest/Contact2DAOImpl.java new file mode 100644 index 0000000..e731c09 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topiatest/deletetest/Contact2DAOImpl.java @@ -0,0 +1,49 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topiatest.deletetest; + + +import java.util.Set; +import java.util.TreeSet; +import org.nuiton.topia.TopiaException; +import org.nuiton.topiatest.Company; +import org.nuiton.topiatest.Employe; + +/** + * + * @author desbois + */ +public class Contact2DAOImpl<E extends Contact2> extends Contact2DAOAbstract<E> { + + @Override + public Set<Contact2> findAllByCompany(Company company) throws TopiaException { + Set<Contact2> contacts = new TreeSet<Contact2>(); + for (Employe e : company.getEmploye()) { + contacts.addAll(e.getContacts()); + } + return contacts; + } + +} diff --git a/topia-persistence/src/test/java/org/nuiton/topiatest/deletetest/DeleteEntityTest.java b/topia-persistence/src/test/java/org/nuiton/topiatest/deletetest/DeleteEntityTest.java new file mode 100644 index 0000000..7f6c5ec --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topiatest/deletetest/DeleteEntityTest.java @@ -0,0 +1,178 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + + +/** + * DeleteEntityTest.java + * + * Created: 4 juin 2009 + * + * @author Florian Desbois <fdesbois@codelutin.com> + * @version $Revision$ + * + * Mise a jour: $Date$ + * par : $Author$ + */ + +package org.nuiton.topiatest.deletetest; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.Rule; +import org.junit.Test; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaDatabase; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaTestDAOHelper; +import org.nuiton.topia.generator.DAOAbstractTransformer; +import org.nuiton.topiatest.Personne; +import org.nuiton.topiatest.PersonneDAO; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +/** + * Deleting tests with DAO and Entities generated with ToPIA (diagram + * delete-test in topiatest.zargo). Different case of deleting, with inheritance + * or NMultiplicity relationship between two entities, or both. Initiate from an + * issue with DAOAbstractGenerator delete method generation. Tests with H2 + * Database. Configuration in src/test/resources/TopiaContextImpl.properties + */ +public class DeleteEntityTest { + + private static final Log log = LogFactory.getLog(DeleteEntityTest.class); + + @Rule + public final TopiaDatabase db = new TopiaDatabase(); + + /** + * Test for deleting entities with inheritance. Delete from the DAO linked + * with the subclass entity and from the DAO linked with the superclass + * entity. In the test model, the two entities have NMultiplicity + * relationship without association class entity. + * + * @throws TopiaException if any exception while manipulating db + */ + @Test + public void testDeleteEntityWithInheritance() throws TopiaException { + log.debug("START TEST : testDeleteEntityWithInheritance"); + + TopiaContext transaction = db.beginTransaction(); + + log.debug("DAO : PersonneDAO"); + PersonneDAO dao = TopiaTestDAOHelper.getPersonneDAO(transaction); + + log.debug("CREATE PERSONNE : Bob Marley"); + Personne personne = dao.create(Personne.PROPERTY_NAME, "Bob Marley"); + transaction.commitTransaction(); + String idPersonne = personne.getTopiaId(); + assertNotNull(idPersonne); + log.debug("ENTITY PERSONNE SAVED !"); + + log.debug("DELETE PERSONNE"); + dao.delete(personne); + transaction.commitTransaction(); + Personne res = dao.findByTopiaId(idPersonne); + assertNull(res); + log.debug("ENTITY PERSONNE DELETED !"); + + log.debug("CREATE PERSONNE : Ziggy Marley"); + Personne personne2 = dao.create(Personne.PROPERTY_NAME, "Ziggy Marley"); + transaction.commitTransaction(); + String idPersonne2 = personne2.getTopiaId(); + assertNotNull(idPersonne2); + log.debug("ENTITY PERSONNE SAVED !"); + + log.debug("DAO parent (abstract) : PartyDAO"); + Party2DAO dao2 = TopiaTestDAOHelper.getParty2DAO(transaction); + + log.debug("DELETE PERSONNE with PartyDAO"); + dao2.delete(personne2); + transaction.commitTransaction(); + Party2 res2 = dao2.findByTopiaId(idPersonne2); + assertNull(res2); + log.debug("ENTITY PERSONNE DELETED !"); + + + } + + /** + * Test for deleting entities with NMultiplicity relation without + * association class entity. Test DAO generation for deleting references + * between two entities with NMultiplicity relation. In the test model, the + * two entities have both inheritance. + * + * @throws TopiaException if any exception while manipulating db + * @see DAOAbstractTransformer + */ + @Test + public void testDeleteEntityWithManyToManyRelation() throws TopiaException { + log.debug("START TEST : testDeleteEntityWithManyToManyRelation"); + + TopiaContext transaction = db.beginTransaction(); + + PersonneDAO dao = TopiaTestDAOHelper.getPersonneDAO(transaction); + + log.debug("CREATE PERSONNE : Bob Marley"); + Personne personne = dao.create(Personne.PROPERTY_NAME, "Bob Marley"); + transaction.commitTransaction(); + String idPersonne = personne.getTopiaId(); + assertNotNull(idPersonne); + log.debug("ENTITY PERSONNE SAVED !"); + + Contact2DAO contactDAO = TopiaTestDAOHelper.getContact2DAO(transaction); + + log.debug("CREATE CONTACT : jaja@codelutin.com"); + Contact2 contact = contactDAO.create(Contact2.PROPERTY_CONTACT_VALUE, "jaja@codelutin.com"); + transaction.commitTransaction(); + String idContact = contact.getTopiaId(); + assertNotNull(idContact); + log.debug("ENTITY CONTACT SAVED !"); + + log.debug("ADD CONTACT TO PERSONNE"); + personne.addContacts(contact); + transaction.commitTransaction(); + assertEquals(1, personne.getContacts().size()); + log.debug("CONTACT ADDED !"); + + log.debug("DELETE PERSONNE"); + dao.delete(personne); + transaction.commitTransaction(); + Personne res = dao.findByTopiaId(idPersonne); + assertNull(res); + log.debug("ENTITY PERSONNE DELETED !"); + + assertEquals(0, contact.getParty2().size()); + + log.debug("DELETE CONTACT"); + contactDAO.delete(contact); + transaction.commitTransaction(); + Contact2 res2 = contactDAO.findByTopiaId(idContact); + assertNull(res2); + log.debug("ENTITY PERSONNE DELETED !"); + + } + +} diff --git a/topia-persistence/src/test/java/org/nuiton/topiatest/persistence/Entity1.java b/topia-persistence/src/test/java/org/nuiton/topiatest/persistence/Entity1.java new file mode 100644 index 0000000..23e0af8 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topiatest/persistence/Entity1.java @@ -0,0 +1,48 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topiatest.persistence; + +import org.nuiton.topia.persistence.TopiaEntity; + +/** + * Created: 11 mai 2010 + * + * @author fdesbois <fdesbois@codelutin.com> + * @version $Id$ + */ +public interface Entity1 extends TopiaEntity { + + String ATTR_1 = "attr1"; + + String ATTR_2 = "attr2"; + + String getAttr1(); + + void setAttr1(String attr1); + + String getAttr2(); + + void setAttr2(String attr2); + +} diff --git a/topia-persistence/src/test/java/org/nuiton/topiatest/persistence/Entity1Abstract.java b/topia-persistence/src/test/java/org/nuiton/topiatest/persistence/Entity1Abstract.java new file mode 100644 index 0000000..9dfe501 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topiatest/persistence/Entity1Abstract.java @@ -0,0 +1,82 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topiatest.persistence; + +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.persistence.EntityVisitor; +import org.nuiton.topia.persistence.TopiaEntityAbstract; + +/** + * Created: 11 mai 2010 + * + * @author fdesbois <fdesbois@codelutin.com> + * @version $Id$ + */ +public abstract class Entity1Abstract extends TopiaEntityAbstract + implements Entity1 { + + protected String attr1; + + protected String attr2; + + @Override + public String getAttr1() { + fireOnPreRead(ATTR_1, attr1); + String result = attr1; + fireOnPostRead(ATTR_1, attr1); + return result; + } + + @Override + public void setAttr1(String attr1) { + String _oldValue = this.attr1; + fireOnPreWrite(ATTR_1, _oldValue, attr1); + this.attr1 = attr1; + fireOnPostWrite(ATTR_1, _oldValue, attr1); + } + + @Override + public String getAttr2() { + fireOnPreRead(ATTR_2, attr2); + String result = attr2; + fireOnPostRead(ATTR_2, attr2); + return result; + } + + @Override + public void setAttr2(String attr2) { + String _oldValue = this.attr2; + fireOnPreWrite(ATTR_2, _oldValue, attr2); + this.attr2 = attr2; + fireOnPostWrite(ATTR_2, _oldValue, attr2); + } + + @Override + public void accept(EntityVisitor visitor) throws TopiaException { + visitor.start(this); + visitor.visit(this, ATTR_1, String.class, attr1); + visitor.visit(this, ATTR_2, String.class, attr2); + visitor.end(this); + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topiatest/persistence/Entity1Impl.hbm.xml b/topia-persistence/src/test/java/org/nuiton/topiatest/persistence/Entity1Impl.hbm.xml new file mode 100644 index 0000000..e90d72e --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topiatest/persistence/Entity1Impl.hbm.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Persistence + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!--<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">--> +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.nuiton.topiatest"> + <class name="org.nuiton.topiatest.persistence.Entity1Impl" table="entity1" node="org.nuiton.topiatest.persistence.Entity1Impl" abstract="false" proxy="org.nuiton.topiatest.persistence.Entity1" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="timestamp" node="@topiaCreateDate"/> + <property name="attr1" type="java.lang.String" access="field" column="attr1" node="attr1"/> + <property name="attr2" type="java.lang.String" access="field" column="attr2" node="attr2"/> + </class> +</hibernate-mapping> diff --git a/topia-persistence/src/test/java/org/nuiton/topiatest/persistence/Entity1Impl.java b/topia-persistence/src/test/java/org/nuiton/topiatest/persistence/Entity1Impl.java new file mode 100644 index 0000000..fef9809 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topiatest/persistence/Entity1Impl.java @@ -0,0 +1,36 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topiatest.persistence; + +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.persistence.EntityVisitor; + +/** + * Created: 11 mai 2010 + * + * @author fdesbois <fdesbois@codelutin.com> + * @version $Id$ + */ +public class Entity1Impl extends Entity1Abstract { +} diff --git a/topia-persistence/src/test/java/org/nuiton/topiatest/persistence/package-info.java b/topia-persistence/src/test/java/org/nuiton/topiatest/persistence/package-info.java new file mode 100644 index 0000000..22b48c5 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topiatest/persistence/package-info.java @@ -0,0 +1,27 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +/** + * Contains persistent entities for Tests. + */ +package org.nuiton.topiatest.persistence; diff --git a/topia-persistence/src/test/java/org/nuiton/topiatest/service/FakeService.java b/topia-persistence/src/test/java/org/nuiton/topiatest/service/FakeService.java new file mode 100644 index 0000000..9f87dc3 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topiatest/service/FakeService.java @@ -0,0 +1,63 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topiatest.service; + +import org.nuiton.topia.framework.TopiaContextImpl; +import org.nuiton.topia.framework.TopiaContextImplTest; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.framework.TopiaService; + +/** + * FakeService which implements {@link TopiaService} to test existing service + * from {@link TopiaContextImplTest#testGetServices()}. This fake service + * doesn't contains property SERVICE_NAME used by {@link + * TopiaContextImpl#serviceEnabled(Class)}. + * <p/> + * Created: 10 mai 2010 + * + * @author fdesbois <fdesbois@codelutin.com> + * @version $Id$ + */ +public class FakeService implements TopiaService { + + @Override + public String getServiceName() { + return "fake"; + } + + @Override + public Class<?>[] getPersistenceClasses() { + return new Class<?>[0]; + } + + @Override + public boolean preInit(TopiaContextImplementor context) { + return true; + } + + @Override + public boolean postInit(TopiaContextImplementor context) { + return true; + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topiatest/service/TestService.java b/topia-persistence/src/test/java/org/nuiton/topiatest/service/TestService.java new file mode 100644 index 0000000..a6e8710 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topiatest/service/TestService.java @@ -0,0 +1,66 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topiatest.service; + +import org.junit.Ignore; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.framework.TopiaContextImplTest; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.framework.TopiaService; + +/** + * TestService which implements {@link TopiaService} to test loading from {@link + * TopiaContextImplTest#testLoadServices()}. + * <p/> + * Created: 10 mai 2010 + * + * @author fdesbois <fdesbois@codelutin.com> + * @version $Id$ + */ +@Ignore +public class TestService implements TopiaService { + + /** Needed field to use {@link TopiaContext#serviceEnabled(Class)} * */ + public static final String SERVICE_NAME = "test"; + + @Override + public String getServiceName() { + return SERVICE_NAME; + } + + @Override + public Class<?>[] getPersistenceClasses() { + return new Class<?>[0]; + } + + @Override + public boolean preInit(TopiaContextImplementor context) { + return true; + } + + @Override + public boolean postInit(TopiaContextImplementor context) { + return true; + } +} diff --git a/topia-persistence/src/test/java/org/nuiton/topiatest/service/package-info.java b/topia-persistence/src/test/java/org/nuiton/topiatest/service/package-info.java new file mode 100644 index 0000000..4e93df3 --- /dev/null +++ b/topia-persistence/src/test/java/org/nuiton/topiatest/service/package-info.java @@ -0,0 +1,28 @@ +/* + * #%L + * ToPIA :: Persistence + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +/** + * This package contains classes which implements {@link + * org.nuiton.topia.framework.TopiaService} to test services API. + */ +package org.nuiton.topiatest.service; diff --git a/topia-persistence/src/test/resources/TopiaConnectionProviderHardcoded.properties b/topia-persistence/src/test/resources/TopiaConnectionProviderHardcoded.properties new file mode 100644 index 0000000..32b0e32 --- /dev/null +++ b/topia-persistence/src/test/resources/TopiaConnectionProviderHardcoded.properties @@ -0,0 +1,34 @@ +### +# #%L +# ToPIA :: Persistence +# $Id$ +# $HeadURL$ +# %% +# Copyright (C) 2004 - 2014 CodeLutin +# %% +# 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 General Lesser Public License for more details. +# +# You should have received a copy of the GNU General Lesser Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/lgpl-3.0.html>. +# #L% +### +# Proprietes par defaut pour une base de donnees de type H2 +hibernate.hbm2ddl.auto=update +hibernate.show_sql=false + +hibernate.dialect=org.hibernate.dialect.H2Dialect +hibernate.connection.username=sa +hibernate.connection.password= +hibernate.connection.driver_class=org.h2.Driver +hibernate.connection.provider_class=org.nuiton.topia.framework.TopiaConnectionProviderHardCoded +# tchemit 2010-11-28 : comment this line, each test must define his own db path +#hibernate.connection.url=jdbc:h2:file:target/surefire-workdir/h2data diff --git a/topia-persistence/src/test/resources/TopiaContextImpl.properties b/topia-persistence/src/test/resources/TopiaContextImpl.properties new file mode 100644 index 0000000..b9835d4 --- /dev/null +++ b/topia-persistence/src/test/resources/TopiaContextImpl.properties @@ -0,0 +1,33 @@ +### +# #%L +# ToPIA :: Persistence +# $Id$ +# $HeadURL$ +# %% +# Copyright (C) 2004 - 2014 CodeLutin +# %% +# 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 General Lesser Public License for more details. +# +# You should have received a copy of the GNU General Lesser Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/lgpl-3.0.html>. +# #L% +### +# Proprietes par defaut pour une base de donnees de type H2 +hibernate.hbm2ddl.auto=update +hibernate.show_sql=false + +hibernate.dialect=org.hibernate.dialect.H2Dialect +hibernate.connection.username=sa +hibernate.connection.password= +hibernate.connection.driver_class=org.h2.Driver +# tchemit 2010-11-28 : comment this line, each test must define his own db path +#hibernate.connection.url=jdbc:h2:file:target/surefire-workdir/h2data diff --git a/topia-persistence/src/test/resources/log4j.properties b/topia-persistence/src/test/resources/log4j.properties new file mode 100644 index 0000000..a08e331 --- /dev/null +++ b/topia-persistence/src/test/resources/log4j.properties @@ -0,0 +1,35 @@ +### +# #%L +# ToPIA :: Persistence +# $Id$ +# $HeadURL$ +# %% +# Copyright (C) 2004 - 2014 CodeLutin +# %% +# 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 General Lesser Public License for more details. +# +# You should have received a copy of the GNU General Lesser Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/lgpl-3.0.html>. +# #L% +### +#\u00a0This log is used to display trace in generation + +# Global logging configuration +log4j.rootLogger=WARN, stdout +# Console output... +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) %M - %m%n +# package level +log4j.logger.org.nuiton.topia=INFO +#log4j.logger.org.nuiton.topiatest=DEBUG +log4j.logger.org.nuiton.topia.persistence.util=INFO diff --git a/topia-persistence/src/test/xmi/topiatest.properties b/topia-persistence/src/test/xmi/topiatest.properties new file mode 100644 index 0000000..3f5f80e --- /dev/null +++ b/topia-persistence/src/test/xmi/topiatest.properties @@ -0,0 +1,43 @@ +### +# #%L +# ToPIA :: Persistence +# $Id$ +# $HeadURL$ +# %% +# Copyright (C) 2004 - 2014 CodeLutin +# %% +# 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 General Lesser Public License for more details. +# +# You should have received a copy of the GNU General Lesser Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/lgpl-3.0.html>. +# #L% +### +model.tagvalue.i18n=topia.test.common. +model.tagvalue.generateOperatorForDAOHelper=true +model.tagvalue.constantPrefix=PROPERTY_ +model.tagvalue.generateDTOTopiaId=true + +# Do not use this tag value (deprecated since 2.5) +#model.tagvalue.useLegacyDAO=true +# Replaced by this one, which will allow us to switch to any dao implementation... +#model.tagvalue.daoImplementation=org.nuiton.topia.persistence.TopiaDAOLegacy + +#org.nuiton.topiatest.Company.class.tagvalue.naturalIdMutable=false +#org.nuiton.topiatest.Company.attribute.siret.tagvalue.naturalId=true +#org.nuiton.topiatest.Company.attribute.name.tagvalue.naturalId=true +#org.nuiton.topiatest.Company.attribute.name.tagvalue.notNull=false + +org.nuiton.topiatest.NaturalizedEntity.attribute.naturalIdNotNull.tagvalue.naturalId=true +org.nuiton.topiatest.NaturalizedEntity.attribute.naturalIdNull.tagvalue.naturalId=true +org.nuiton.topiatest.NaturalizedEntity.attribute.naturalIdNull.tagvalue.notNull=false + +org.nuiton.topiatest.Personne.attribute.otherGender.tagValue.useEnumerationName=true diff --git a/topia-persistence/src/test/xmi/topiatest.zargo b/topia-persistence/src/test/xmi/topiatest.zargo new file mode 100644 index 0000000..3d815e2 Binary files /dev/null and b/topia-persistence/src/test/xmi/topiatest.zargo differ diff --git a/topia-service-migration/LICENSE.txt b/topia-service-migration/LICENSE.txt new file mode 100644 index 0000000..cca7fc2 --- /dev/null +++ b/topia-service-migration/LICENSE.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/topia-service-migration/README.txt b/topia-service-migration/README.txt new file mode 100644 index 0000000..d2e50d3 --- /dev/null +++ b/topia-service-migration/README.txt @@ -0,0 +1,2 @@ +To deploy new version of pom: mvn deploy +To install localy: mvn install diff --git a/topia-service-migration/changelog.txt b/topia-service-migration/changelog.txt new file mode 100644 index 0000000..d152780 --- /dev/null +++ b/topia-service-migration/changelog.txt @@ -0,0 +1,63 @@ +2.2.1 chemit 20090903 + + * [FIX #38] manual migration service does not work on windows OS + * [FEATURE #40] clean manual migration service + * [FEATURE #42] add i18n in migration service + +-- chemit -- Thu, 03 Sep 2009 18:51:54 +0200 + +2.2.0 + * integrate in ToPIA project as org.nuiton.topia:topia-service-migration + * introduce a simpler migration service ManualMigrationEngine + - do only manual sql migration (using ToPIA persistence api) + - use only one callback handler of type ManualMigrationCallback + - use a ToPIA TMSVersion entity in package org.nuiton.migration + +1.1.0 + * migrate to nuiton + +1.0.3 chemit 20090511 + * bump versions (lutinproject, lutinutil, topia, maven-license-switcher-plugin) + * use doxia-modules-jrst instead of maven-jrst-plugin + * improve download section on site + +1.0.2 chemit 20090220 +* 20090220 [chemit] - use lutinproject 3.4 + +1.0.1 chemit 20081215 +* 20081215 [chemit] - new release for isis-fish :) follow release of topia + +1.0 chemit 20081210 + * 20081205 [chemit] - improve poms, use lutinproject 3.2, migrate tests to JUnit4 + +0.9.1 + * [chatellier] Correction d'un bug de recherche de mapping sous windows (\) + +0.9 + * <chemit> use lutinproject 3.0 + clean pom + use topia 2.0.27 + * <poussin> modif dans service de migration pour permettre a l'app de + faire la migration via le callback + +0.8 + + * 20071120 [chatellier] update topia-service site + * 20071115 [chatellier] apply hibernate code style to open/close session and + transaction + * 20071115 [chatellier] refractoring separate specifique topia service + and real migration code + * 20071115 [chatellier] add schema existance detection support + and dont migrate if there is no table + * 20071111 [chatellier] add support for callback handler + * 20071111 [chatellier] use cascade to calculate dependencies order + * 20071109 [chatellier] change schema creation process to + correct duplicate foreign key creation + * 20071109 [chatellier] add inherit support to calculate dependancies + +0.6 ??? ??? + + * 20070426 [chatellier] use lutinutil.Resources.getUrls() to get mappings + * 20070426 [chatellier] change finder migration to use Class.forName() + * 20070420 [chatellier] remove the 'V' letter in old mapping directories name + * 20070420 [chatellier] add support to look for hibernate mapping in a tree + structure of directories + * 20070418 [chatellier] add migration service \ No newline at end of file diff --git a/topia-service-migration/pom.xml b/topia-service-migration/pom.xml new file mode 100644 index 0000000..d0d1254 --- /dev/null +++ b/topia-service-migration/pom.xml @@ -0,0 +1,131 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Migration + + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2010 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.nuiton</groupId> + <artifactId>topia</artifactId> + <version>2.9.3-SNAPSHOT</version> + </parent> + + <groupId>org.nuiton.topia</groupId> + <artifactId>topia-service-migration</artifactId> + + <name>ToPIA :: Service Migration</name> + <description>Hibernate based migration service</description> + + <dependencies> + + <!-- Sibling dependencies --> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>topia-persistence</artifactId> + <version>${project.version}</version> + <scope>compile</scope> + </dependency> + + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>topia-persistence</artifactId> + <version>${project.version}</version> + <scope>test</scope> + <classifier>tests</classifier> + </dependency> + + <dependency> + <groupId>org.nuiton</groupId> + <artifactId>nuiton-utils</artifactId> + </dependency> + + <dependency> + <groupId>org.nuiton.i18n</groupId> + <artifactId>nuiton-i18n</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + </dependency> + + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + </dependency> + + <dependency> + <groupId>org.hibernate</groupId> + <artifactId>hibernate-core</artifactId> + </dependency> + + <!-- Depencies for test--> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + + </dependencies> + + <build> + <resources> + + <resource> + <directory>${project.basedir}/src/main/resources</directory> + <includes> + <include>**/*.hbm.xml</include> + <include>**/*.properties</include> + </includes> + </resource> + + </resources> + <plugins> + + <plugin> + <groupId>org.nuiton.i18n</groupId> + <artifactId>i18n-maven-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>parserJava</goal> + <goal>gen</goal> + </goals> + </execution> + </executions> + </plugin> + + </plugins> + </build> + +</project> diff --git a/topia-service-migration/src/license/THIRD-PARTY.properties b/topia-service-migration/src/license/THIRD-PARTY.properties new file mode 100644 index 0000000..456402d --- /dev/null +++ b/topia-service-migration/src/license/THIRD-PARTY.properties @@ -0,0 +1,49 @@ +### +# #%L +# ToPIA :: Service Migration +# $Id$ +# $HeadURL$ +# %% +# Copyright (C) 2004 - 2014 CodeLutin +# %% +# 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 General Lesser Public License for more details. +# +# You should have received a copy of the GNU General Lesser Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/lgpl-3.0.html>. +# #L% +### +# Generated by org.codehaus.mojo.license.AddThirdPartyMojo +#------------------------------------------------------------------------------- +# Already used licenses in project : +# - Apache License 2.0 +# - BSD License +# - COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 +# - Common Public License Version 1.0 +# - GNU Lesser General Public License, version 2.1 +# - GNU Library or Lesser General Public License +# - Indiana University Extreme! Lab Software License, vesion 1.1.1 +# - Lesser General Public License (LGPL) v 3.0 +# - Lesser General Public License (LPGL) +# - Lesser General Public License (LPGL) v 2.1 +# - MIT License +# - MPL 1.1 +# - New BSD License +# - The Apache Software License, Version 2.0 +# - The H2 License, Version 1.0 +# - license.txt +#------------------------------------------------------------------------------- +# Please fill the missing licenses for dependencies : +# +# +#Fri Mar 15 12:42:01 CET 2013 +commons-primitives--commons-primitives--1.0=The Apache Software License, Version 2.0 +dom4j--dom4j--1.6.1=BSD License diff --git a/topia-service-migration/src/main/java/org/nuiton/topia/migration/AbstractTopiaMigrationCallback.java b/topia-service-migration/src/main/java/org/nuiton/topia/migration/AbstractTopiaMigrationCallback.java new file mode 100644 index 0000000..52ab25f --- /dev/null +++ b/topia-service-migration/src/main/java/org/nuiton/topia/migration/AbstractTopiaMigrationCallback.java @@ -0,0 +1,225 @@ +/* + * #%L + * ToPIA :: Service Migration + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.migration; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.jdbc.Work; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.util.StringUtil; +import org.nuiton.util.version.Version; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.List; + +import static org.nuiton.i18n.I18n.t; + +/** + * Abstract migration callback. + * + * @author tchemit <chemit@codelutin.com> + * @version $Id$ + * @since 2.5 + */ +public abstract class AbstractTopiaMigrationCallback { + + /** Logger */ + static private Log log = LogFactory.getLog(AbstractTopiaMigrationCallback.class); + + /** @return the available versions from the call back */ + public abstract Version[] getAvailableVersions(); + + /** @return the current application version (says the version to use) */ + public abstract Version getApplicationVersion(); + + /** + * Hook to ask user if migration can be performed. + * + * @param dbVersion the actual db version + * @param versions the versions to update + * @return {@code false} if migration is canceled, {@code true} otherwise. + */ + public abstract boolean askUser(Version dbVersion, + List<Version> versions); + + protected abstract void migrateForVersion(Version version, + TopiaContextImplementor tx, + boolean showSql, + boolean showProgression) throws Exception; + + /** + * Tentative de migration depuis la version de la base version la version + * souhaitee. + * <p/> + * On applique toutes les migrations de version indiquee dans le parametre + * <code>version</code>. + * <p/> + * Pour chaque version, on cherche la methode migrateTo_XXX ou XXX est la + * version transforme en identifiant java via la methode + * {@link Version#getValidName()} et on l'execute. + * <p/> + * Note: pour chaque version a appliquer, on ouvre une nouvelle transaction. + * + * @param ctxt topia context de la transaction en cours + * @param dbVersion database version + * @param showSql drapeau pour afficher les requete sql + * @param showProgression drapeau pour afficher la progression + * @param versions all versions knwon by service @return migration a + * ggrement + * @return {@code true} si la migration est accepté, {@code false} autrement. + */ + public boolean doMigration(TopiaContext ctxt, + Version dbVersion, + boolean showSql, + boolean showProgression, + List<Version> versions) { + + boolean doMigrate = askUser(dbVersion, versions); + if (!doMigrate) { + // l'utilisateur a refuse la migration + return false; + } + TopiaContextImplementor tx; + + for (Version v : versions) { + // ouverture d'une connexion direct JDBC sur la base + try { + + tx = (TopiaContextImplementor) ctxt.beginTransaction(); + + try { + + log.info(t("topia.migration.start.migrate", v)); + + migrateForVersion(v, tx, showSql, showProgression); + + // commit des modifs + tx.commitTransaction(); + + } catch (Exception eee) { + // en cas d'erreur + log.error("Migration impossible de la base", eee); + // rollback du travail en cours + tx.rollbackTransaction(); + // propagation de l'erreur + throw eee; + } finally { + // close database connexion + if (tx != null) { + tx.closeContext(); + } + } + + } catch (Exception eee) { + log.error("Error lors de la tentative de migration", eee); + doMigrate = false; + // toute erreur arrête la mgration + break; + } + } + return doMigrate; + } + + public void executeSQL(TopiaContextImplementor tx, String... sqls) + throws TopiaException { + executeSQL(tx, false, false, sqls); + } + + /** + * Executes the given {@code sqls} requests. + * + * @param tx the session + * @param showSql flag to see sql requests + * @param showProgression flag to see progession on console + * @param sqls requests to execute + * @throws TopiaException if any pb + * @since 2.3.0 + */ + public void executeSQL(TopiaContextImplementor tx, + final boolean showSql, + final boolean showProgression, + final String... sqls) throws TopiaException { + + if (log.isInfoEnabled()) { + + log.info(t("topia.migration.start.sqls", sqls.length)); + } + if (showSql) { + StringBuilder buffer = new StringBuilder(); + for (String s : sqls) { + buffer.append(s).append("\n"); + } + log.info("SQL TO EXECUTE :\n" + + "--------------------------------------------------------------------------------\n" + + "--------------------------------------------------------------------------------\n" + + buffer.toString() + + "--------------------------------------------------------------------------------\n" + + "--------------------------------------------------------------------------------\n" + ); + } + tx.getHibernate().doWork(new Work() { + + @Override + public void execute(Connection connection) throws SQLException { + int index = 0; + int max = sqls.length; + for (String sql : sqls) { + index++; + long t0 = System.nanoTime(); + if (log.isInfoEnabled()) { + String message = ""; + + if (showProgression) { + message = t("topia.migration.start.sql", index, max); + } + if (showSql) { + message += "\n" + sql; + } + if (showProgression || showSql) { + + log.info(message); + } + } + PreparedStatement sta = connection.prepareStatement(sql); + try { + sta.executeUpdate(); + } finally { + sta.close(); + } + if (log.isDebugEnabled()) { + String message; + message = t("topia.migration.end.sql", index, max, StringUtil.convertTime(System.nanoTime() - t0)); + log.debug(message); + } + } + } + }); + + } + +} diff --git a/topia-service-migration/src/main/java/org/nuiton/topia/migration/MigrationServiceException.java b/topia-service-migration/src/main/java/org/nuiton/topia/migration/MigrationServiceException.java new file mode 100644 index 0000000..609ffa8 --- /dev/null +++ b/topia-service-migration/src/main/java/org/nuiton/topia/migration/MigrationServiceException.java @@ -0,0 +1,75 @@ +/* + * #%L + * ToPIA :: Service Migration + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.migration; + +/** + * TopiaMigrationServiceException.java + * + * @author Chatellier Eric + * @author Chevallereau Benjamin + * @author Eon Sébastien + * @author Trève Vincent + * @version $Revision$ + * + * Last update : $Date$ + */ +public class MigrationServiceException extends Exception { + + /** + * Version + */ + private static final long serialVersionUID = -8900901171551405745L; + + /** + * Constructeur par defaut + */ + public MigrationServiceException() { + } + + /** + * Constructeur avec message + * @param message Le message d'erreur + */ + public MigrationServiceException(String message) { + super(message); + } + + /** + * Constructeur avec message et exception + * @param message Le message d'erreur + * @param exception l'exception + */ + public MigrationServiceException(String message, Throwable exception) { + super(message, exception); + } + + /** + * Constructeur avec exception + * @param exception l'exception + */ + public MigrationServiceException(Throwable exception) { + super(exception); + } +} diff --git a/topia-service-migration/src/main/java/org/nuiton/topia/migration/TopiaMigrationCallback.java b/topia-service-migration/src/main/java/org/nuiton/topia/migration/TopiaMigrationCallback.java new file mode 100644 index 0000000..847c5c2 --- /dev/null +++ b/topia-service-migration/src/main/java/org/nuiton/topia/migration/TopiaMigrationCallback.java @@ -0,0 +1,58 @@ +/* + * #%L + * ToPIA :: Service Migration + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.migration; + +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.util.Version; + +import java.lang.reflect.Method; + +/** + * Default migration call back to use. + * <p/> + * Replace deprecated implementation {@code ManualMigrationCallback}. + * + * @author tchemit <chemit@codelutin.com> + * @version $Id$ + * @since 2.3.4 + * @deprecated since 2.5, use now the {@link TopiaMigrationCallbackByClass} or {@link TopiaMigrationCallbackByMethod}. + * <b>will not be replaced and remove before version {@code 2.6}</b>. + */ +@Deprecated +public abstract class TopiaMigrationCallback extends TopiaMigrationCallbackByMethod { + + @Deprecated + protected Method getMigrationMethod(Version version) throws NoSuchMethodException { + + String methodName = "migrateTo_" + version.getValidName(); + + Method m = getClass().getMethod(methodName, + TopiaContextImplementor.class, + boolean.class, + boolean.class + ); + return m; + } +} diff --git a/topia-service-migration/src/main/java/org/nuiton/topia/migration/TopiaMigrationCallbackByClass.java b/topia-service-migration/src/main/java/org/nuiton/topia/migration/TopiaMigrationCallbackByClass.java new file mode 100644 index 0000000..52d3de2 --- /dev/null +++ b/topia-service-migration/src/main/java/org/nuiton/topia/migration/TopiaMigrationCallbackByClass.java @@ -0,0 +1,162 @@ +/* + * #%L + * ToPIA :: Service Migration + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.migration; + +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.util.ObjectUtil; +import org.nuiton.util.version.Version; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +/** + * Migration callback which use a different class for each version to migrate. + * <p/> + * You must fill in the constructor the mapping for each version of + * {@link #getAvailableVersions()} a matching migrator for version which + * extends {@link MigrationCallBackForVersion}. + * <p/> + * Use the callback when you have a lot of version to migrate and the + * {@link TopiaMigrationCallbackByMethod} begins to be messy. + * + * @author tchemit <chemit@codelutin.com> + * @version $Id$ + * @since 2.5 + */ +public abstract class TopiaMigrationCallbackByClass extends AbstractTopiaMigrationCallback { + + protected MigrationCallBackForVersionResolver callBackResolver; + + protected TopiaMigrationCallbackByClass(MigrationCallBackForVersionResolver callBackResolver) { + + this.callBackResolver = callBackResolver; + + // check for each version of migration we have a migrator + for (Version version : getAvailableVersions()) { + Class<? extends MigrationCallBackForVersion> callBack = this.callBackResolver.getCallBack(version); + if (callBack == null) { + throw new IllegalStateException("It misses a migration class for version " + version); + } + } + } + + @Override + protected void migrateForVersion(Version version, + TopiaContextImplementor tx, + boolean showSql, + boolean showProgression) throws Exception { + + Class<? extends MigrationCallBackForVersion> migratorClass = callBackResolver.getCallBack(version); + + MigrationCallBackForVersion migrator = ObjectUtil.newInstance(migratorClass, Arrays.asList(this), true); + + String[] queries = migrator.prepareMigration(tx, showSql, showProgression); + + executeSQL(tx, showSql, showProgression, queries); + + } + + /** + * Call back for a given version. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5 + */ + public abstract static class MigrationCallBackForVersion { + + protected final Version version; + + protected final TopiaMigrationCallbackByClass callBack; + + public MigrationCallBackForVersion(Version version, + TopiaMigrationCallbackByClass callBack) { + this.version = version; + this.callBack = callBack; + + } + + protected String[] prepareMigration(TopiaContextImplementor tx, + boolean showSql, + boolean showProgression) throws TopiaException { + + List<String> queries = new ArrayList<String>(); + + prepareMigrationScript(tx, queries, showSql, showProgression); + + return queries.toArray(new String[queries.size()]); + } + + protected abstract void prepareMigrationScript(TopiaContextImplementor tx, + List<String> queries, + boolean showSql, + boolean showProgression) throws TopiaException; + + public void executeSQL(TopiaContextImplementor tx, + String... sqls) throws TopiaException { + callBack.executeSQL(tx, sqls); + } + + public void executeSQL(TopiaContextImplementor tx, + boolean showSql, + boolean showProgression, + String... sqls) throws TopiaException { + callBack.executeSQL(tx, showSql, showProgression, sqls); + } + + } + + /** + * Resolver to obtain the correct migration class for a given version. + * + * @since 2.5 + */ + public interface MigrationCallBackForVersionResolver { + + Class<? extends MigrationCallBackForVersion> getCallBack(Version version); + + } + + /** + * A simple call back resolver via a constant map. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5 + */ + public static class MigrationCallBackForVersionResolverByMap implements MigrationCallBackForVersionResolver { + + protected final Map<Version, Class<? extends MigrationCallBackForVersion>> versionMigrationMapping; + + public MigrationCallBackForVersionResolverByMap(Map<Version, Class<? extends MigrationCallBackForVersion>> versionMigrationMapping) { + this.versionMigrationMapping = versionMigrationMapping; + } + + @Override + public Class<? extends MigrationCallBackForVersion> getCallBack(Version version) { + return versionMigrationMapping.get(version); + } + } +} diff --git a/topia-service-migration/src/main/java/org/nuiton/topia/migration/TopiaMigrationCallbackByClassNG.java b/topia-service-migration/src/main/java/org/nuiton/topia/migration/TopiaMigrationCallbackByClassNG.java new file mode 100644 index 0000000..714d5b5 --- /dev/null +++ b/topia-service-migration/src/main/java/org/nuiton/topia/migration/TopiaMigrationCallbackByClassNG.java @@ -0,0 +1,191 @@ +/* + * #%L + * ToPIA :: Service Migration + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin, Tony chemit + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.migration; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.util.version.Version; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.Set; +import java.util.TreeMap; + +/** + * Migration callback which use a different class for each version to migrate. + * <p/> + * You must fill in the constructor the mapping for each version of + * {@link #getAvailableVersions()} a matching migrator for version which + * extends {@link MigrationCallBackForVersion}. + * <p/> + * Use the callback when you have a lot of version to migrate and the + * {@link TopiaMigrationCallbackByMethod} begins to be messy. + * + * @author tchemit <chemit@codelutin.com> + * @version $Id$ + * @since 2.9.11 + */ +public abstract class TopiaMigrationCallbackByClassNG extends AbstractTopiaMigrationCallback { + + /** Logger. */ + private static final Log log = + LogFactory.getLog(TopiaMigrationCallbackByClassNG.class); + + protected MigrationCallBackForVersionResolver callBackResolver; + + protected TopiaMigrationCallbackByClassNG(MigrationCallBackForVersionResolver callBackResolver) { + + this.callBackResolver = callBackResolver; + } + + @Override + public Version[] getAvailableVersions() { + Set<Version> allVersions = callBackResolver.getAllVersions(); + return allVersions.toArray(new Version[allVersions.size()]); + } + + @Override + protected void migrateForVersion(Version version, + TopiaContextImplementor tx, + boolean showSql, + boolean showProgression) throws Exception { + + MigrationCallBackForVersion migrator = callBackResolver.getCallBack(version); + + migrator.setCallBack(this); + + String[] queries = migrator.prepareMigration(tx, showSql, showProgression); + + executeSQL(tx, showSql, showProgression, queries); + + } + + /** + * Call back for a given version. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.5 + */ + public abstract static class MigrationCallBackForVersion { + + protected TopiaMigrationCallbackByClassNG callBack; + + public abstract Version getVersion(); + + public void setCallBack(TopiaMigrationCallbackByClassNG callBack) { + this.callBack = callBack; + } + + protected String[] prepareMigration(TopiaContextImplementor tx, + boolean showSql, + boolean showProgression) throws TopiaException { + + List<String> queries = new ArrayList<String>(); + + prepareMigrationScript(tx, queries, showSql, showProgression); + + return queries.toArray(new String[queries.size()]); + } + + protected abstract void prepareMigrationScript(TopiaContextImplementor tx, + List<String> queries, + boolean showSql, + boolean showProgression) throws TopiaException; + + public void executeSQL(TopiaContextImplementor tx, + String... sqls) throws TopiaException { + callBack.executeSQL(tx, sqls); + } + + public void executeSQL(TopiaContextImplementor tx, + boolean showSql, + boolean showProgression, + String... sqls) throws TopiaException { + callBack.executeSQL(tx, showSql, showProgression, sqls); + } + + } + + /** + * Resolver to obtain the correct migration class for a given version. + * + * @since 2.6.11 + */ + public interface MigrationCallBackForVersionResolver { + + /** + * Returns all detected versions. + * + * @return all detected versions. + */ + Set<Version> getAllVersions(); + + /** + * for a given version, returns his migration callback. + * + * @param version the version to migrate + * @return the migration call for the given version, or {@code null} + * if no such migration callback exists for the version + */ + MigrationCallBackForVersion getCallBack(Version version); + } + + /** + * A simple call back resolver via a service loader. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.9.11 + */ + public static class MigrationCallBackForVersionResolverByServiceLoader implements MigrationCallBackForVersionResolver { + + protected final Map<Version, MigrationCallBackForVersion> versionMigrationMapping; + + public MigrationCallBackForVersionResolverByServiceLoader() { + this.versionMigrationMapping = new TreeMap<Version, MigrationCallBackForVersion>(); + ServiceLoader<MigrationCallBackForVersion> load = ServiceLoader.load(MigrationCallBackForVersion.class); + for (MigrationCallBackForVersion callBackForVersion : load) { + Version version = callBackForVersion.getVersion(); + if (log.isInfoEnabled()) { + log.info("Detects migration version " + version + " [" + + callBackForVersion + "]"); + } + versionMigrationMapping.put(version, callBackForVersion); + } + } + + @Override + public MigrationCallBackForVersion getCallBack(Version version) { + return versionMigrationMapping.get(version); + } + + @Override + public Set<Version> getAllVersions() { + return versionMigrationMapping.keySet(); + } + } +} diff --git a/topia-service-migration/src/main/java/org/nuiton/topia/migration/TopiaMigrationCallbackByMethod.java b/topia-service-migration/src/main/java/org/nuiton/topia/migration/TopiaMigrationCallbackByMethod.java new file mode 100644 index 0000000..beebc40 --- /dev/null +++ b/topia-service-migration/src/main/java/org/nuiton/topia/migration/TopiaMigrationCallbackByMethod.java @@ -0,0 +1,76 @@ +/* + * #%L + * ToPIA :: Service Migration + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.migration; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.util.version.Version; + +import java.lang.reflect.Method; + +/** + * Migration callback base on methods. + * <p/> + * The callback defines for each version of {@link #getAvailableVersions()} + * a method named {@code migrate_on_XXX} where {@code XXX} is the version with + * all dots replaces by underscores. + * <p/> + * Replace deprecated implementation {@code TopiaMigrationCallBack}. + * + * @author tchemit <chemit@codelutin.com> + * @version $Id$ + * @since 2.5 + */ +public abstract class TopiaMigrationCallbackByMethod extends AbstractTopiaMigrationCallback { + + /** Logger */ + static private Log log = LogFactory.getLog(TopiaMigrationCallbackByMethod.class); + + @Override + protected void migrateForVersion(Version version, + TopiaContextImplementor tx, + boolean showSql, + boolean showProgression) throws Exception { + + String methodName = "migrateTo_" + version.getValidName(); + + Method m = getClass().getMethod(methodName, + TopiaContextImplementor.class, + boolean.class, + boolean.class + ); + + m.setAccessible(true); + + if (log.isDebugEnabled()) { + log.debug("launch method " + m.getName()); + } + + m.invoke(this, tx, showSql, showProgression); + + } + + +} diff --git a/topia-service-migration/src/main/java/org/nuiton/topia/migration/TopiaMigrationEngine.java b/topia-service-migration/src/main/java/org/nuiton/topia/migration/TopiaMigrationEngine.java new file mode 100644 index 0000000..d2f69ee --- /dev/null +++ b/topia-service-migration/src/main/java/org/nuiton/topia/migration/TopiaMigrationEngine.java @@ -0,0 +1,728 @@ +/* + * #%L + * ToPIA :: Service Migration + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.migration; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.cfg.Configuration; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaNotFoundException; +import org.nuiton.topia.TopiaRuntimeException; +import org.nuiton.topia.event.TopiaContextAdapter; +import org.nuiton.topia.event.TopiaContextEvent; +import org.nuiton.topia.event.TopiaContextListener; +import org.nuiton.topia.event.TopiaTransactionEvent; +import org.nuiton.topia.event.TopiaTransactionVetoable; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.framework.TopiaUtil; +import org.nuiton.topia.migration.mappings.TMSVersion; +import org.nuiton.topia.migration.mappings.TMSVersionDAO; +import org.nuiton.util.version.Version; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Properties; +import java.util.Set; +import java.util.SortedSet; +import java.util.TreeSet; + +import static org.nuiton.i18n.I18n.t; + +/** + * Le moteur de migration proposé par topia. Il est basé sur un {@link AbstractTopiaMigrationCallback} + * qui donne la version de l'application, les version de mises à jour disponibles. + * <p/> + * Le call back offre aussi les commandes sql à passer pour chaque version de mise à jour. + * <p/> + * FIXME Finir cette documentation + * + * @author tchemit + * @version $Id$ + * @since 2.3.4 + */ +public class TopiaMigrationEngine implements TopiaMigrationService { + + /** logger */ + private final static Log log = LogFactory.getLog(TopiaMigrationEngine.class); + + /** Configuration hibernate ne mappant que l'entite version (initialise en pre-init) */ + protected Configuration versionConfiguration; + + /** Un drapeau pour savoir si la table version existe en base (initialise en pre-init) */ + protected boolean versionTableExist; + + /** Configuration hibernate ne mappant que l'entite version de l'ancien systeme de migration (initialise en pre-init) */ + protected Configuration legacyVersionConfiguration; + + /** Un drapeau pour savoir si la table version (de l'ancien service Manual) existe en base (initialise en pre-init) */ + protected boolean legacyVersionTableExist; + + /** Version courante de la base (initialise en pre-init) */ + protected Version dbVersion; + + /** Drapeau pour savoir si la base est versionnée ou non */ + protected boolean dbNotVersioned; + + /** + * A flag to know if none of the dealed entities tables exists in db. + * + * @since 2.5.3 + */ + protected boolean dbEmpty; + + /** Un drapeau pour effectuer la migration au demarrage (initialise en pre-init) */ + protected boolean migrateOnInit; + + /** CallbackHandler list (initialise en pre-init) */ + protected AbstractTopiaMigrationCallback callback; + + /** topia root context (initialise en pre-init) */ + protected TopiaContextImplementor rootContext; + + /** Un drapeau pour savoir si le service a bien ete initialise (i.e a bien fini la methode preInit) */ + protected boolean init; + + /** + * A flag to check if version was detected in database. + * <p/> + * This flag is set to {@code true} at the end of method {@link #detectDbVersion()}. + */ + protected boolean versionDetected; + + /** Un drapeau pour afficher les requetes sql executees */ + protected boolean showSql; + + /** Un drapeau pour afficher la progression des requetes sql executees */ + protected boolean showProgression; + + /** delegate context listener. */ + protected final TopiaContextListener contextListener; + + /** delgate transaction listener */ + protected final TopiaTransactionVetoable transactionVetoable; + + public TopiaMigrationEngine() { + + transactionVetoable = new TopiaTransactionVetoable() { + @Override + public void beginTransaction(TopiaTransactionEvent event) { + + TopiaContextImplementor context = + (TopiaContextImplementor) event.getSource(); + + // add topia context listener + context.addTopiaContextListener(contextListener); + + } + }; + contextListener = new TopiaContextAdapter() { + + @Override + public void postCreateSchema(TopiaContextEvent event) { + if (log.isDebugEnabled()) { + log.debug("postCreateSchema event called : will save version in database"); + } + saveApplicationVersion(); + } + + @Override + public void postUpdateSchema(TopiaContextEvent event) { + if (log.isDebugEnabled()) { + log.debug("postUpdateSchema event called : will save version in database"); + } + saveApplicationVersion(); + } + + @Override + public void postRestoreSchema(TopiaContextEvent event) { + if (log.isDebugEnabled()) { + log.debug("postRestoreSchema event detected, redo, schema migration"); + } + if (migrateOnInit) { + // do automatic migration + if (log.isDebugEnabled()) { + log.debug("Starts Migrate from postRestoreSchema..."); + } + try { + doMigrateSchema(); + } catch (Exception e) { + if (log.isErrorEnabled()) { + log.error("postRestoreSchema schema migration failed for reason " + e.getMessage(), e); + } + } + } + } + }; + } + + //-------------------------------------------------------------------------- + //-- TopiaService implementation + //-------------------------------------------------------------------------- + + @Override + public Class<?>[] getPersistenceClasses() { + return new Class<?>[]{TMSVersion.class}; + } + + @Override + public String getServiceName() { + return SERVICE_NAME; + } + + @Override + public boolean preInit(TopiaContextImplementor context) { + rootContext = context; + + Properties config = context.getConfig(); + + String callbackStr = getSafeParameter(config, MIGRATION_CALLBACK); + if (log.isDebugEnabled()) { + log.debug("Use callback - " + callbackStr); + } + + migrateOnInit = Boolean.valueOf(config.getProperty(MIGRATION_MIGRATE_ON_INIT, String.valueOf(Boolean.TRUE))); + if (log.isDebugEnabled()) { + log.debug("Migrate on init - " + migrateOnInit); + } + + showSql = Boolean.valueOf(config.getProperty(MIGRATION_SHOW_SQL, String.valueOf(Boolean.FALSE))); + if (log.isDebugEnabled()) { + log.debug("Show sql - " + showSql); + } + + showProgression = Boolean.valueOf(config.getProperty(MIGRATION_SHOW_PROGRESSION, String.valueOf(Boolean.FALSE))); + if (log.isDebugEnabled()) { + log.debug("Show progression - " + showProgression); + } + // enregistrement du callback + try { + Class<?> clazz = Class.forName(callbackStr); + callback = (AbstractTopiaMigrationCallback) clazz.newInstance(); + } catch (Exception e) { + log.error("Could not instanciate CallbackHandler [" + callbackStr + "]", e); + } + + // creation de la configuration hibernate ne concernant que l'entite Version + // afin de pouvoir creer la table via un schemaExport si necessaire + + Configuration configuration = new Configuration(); + for (Class<?> aClass : getPersistenceClasses()) { + configuration.addClass(aClass); + } + + versionConfiguration = createHibernateConfiguration(configuration); + + init = true; + + // add topia context listener + context.addTopiaContextListener(contextListener); + context.addTopiaTransactionVetoable(transactionVetoable); + + if (log.isDebugEnabled()) { + log.debug("Service [" + this + "] is init."); + } + + if (migrateOnInit) { + // do automatic migration + try { + + if (log.isDebugEnabled()) { + log.debug("Starts Migrate from preInit..."); + } + doMigrateSchema(); + + } catch (MigrationServiceException e) { + throw new TopiaRuntimeException("Can't migrate schema for reason " + e.getMessage(), e); + } + } else { + if (log.isDebugEnabled()) { + log.debug("Service [" + this + "] skip migration on init as required"); + } + } + return true; + } + + @Override + public boolean postInit(TopiaContextImplementor context) { + // nothing to do in post-init + return true; + } + + public void doMigrateSchema() throws MigrationServiceException { + // migration + boolean complete = migrateSchema(); + if (!complete) { + if (log.isErrorEnabled()) { + log.error(t("topia.migration.migration.incomplete")); + } + throw new TopiaRuntimeException(t("topia.migration.migration.incomplete")); + } + } + + //-------------------------------------------------------------------------- + //-- TopiaMigrationService implementation + //-------------------------------------------------------------------------- + + @Override + public boolean migrateSchema() throws MigrationServiceException { + + checkInit(); + + detectDbVersion(); + + Version version = callback.getApplicationVersion(); + + log.info(t("topia.migration.start.migration", + version.getVersion(), + dbVersion.getVersion()) + ); + + if (log.isDebugEnabled()) { + log.debug("Migrate schema to version = " + dbVersion); + log.debug("is db not versionned ? = " + dbNotVersioned); + log.debug("is db empty ? = " + dbEmpty); + log.debug("TMSVersion exists = " + versionTableExist); + } + + if (dbEmpty) { + + // db is empty (no table, no migration to apply) + return true; + } + + if (versionTableExist && dbVersion.equals(version)) { + if (log.isInfoEnabled()) { + log.info(t("topia.migration.skip.migration.db.is.up.to.date")); + } + // la base est a jour + return true; + } + + // Aucune version existante, la base de données est vierge + if (versionTableExist && dbNotVersioned && migrateOnInit) { + log.info(t("topia.migration.skip.migration.db.is.empty")); + // la base est vierge, aucune migration nécessaire + // mise à jour de la table tmsversion + saveApplicationVersion(); + return true; + } + + if (legacyVersionTableExist && dbVersion.equals(version)) { + + // on a trouvee une table depreciee tmsVersion avec la bonne version de base + // il suffit donc d'enregister la version dans la nouvelle table + if (log.isInfoEnabled()) { + log.info(t("topia.migration.skip.migration.db.is.up.to.date")); + } + // la base est a jour mais il faut migrer la table + saveApplicationVersion(); + return true; + } + + SortedSet<Version> allVersions = new TreeSet<Version>(); + allVersions.addAll(Arrays.asList(callback.getAvailableVersions())); + if (log.isInfoEnabled()) { + log.info(t("topia.migration.available.versions", allVersions)); + } + + // tell if migration is needed + boolean needToMigrate = false; + + if (dbVersion.before(version)) { + + // on filtre les versions a appliquer + List<Version> versionsToApply = filterVersions(allVersions, + dbVersion, + version, + false, + true); + + if (versionsToApply.isEmpty()) { + if (log.isInfoEnabled()) { + log.info(t("topia.migration.skip.migration.no.version.to.apply")); + } + } else { + if (log.isInfoEnabled()) { + log.info(t("topia.migration.migrate.versions", versionsToApply)); + } + + // perform the migration + needToMigrate = callback.doMigration(rootContext, + dbVersion, + showSql, + showProgression, + versionsToApply); + + if (log.isDebugEnabled()) { + log.debug("Handler choose : " + needToMigrate); + } + if (!needToMigrate) { + // l'utilisateur a annule la migration + return false; + } + } + } + + // on sauvegarde la version si necessaire (base non versionnee ou migration realisee) + if (!versionTableExist || needToMigrate) { + + if (log.isDebugEnabled()) { + log.debug("Set application version in database to " + version); + } + + // put version in database and create table if required + saveApplicationVersion(); + } + + // return success flag + // - no migration needed + // - or migration needed and accepted + return true; + } + + //-------------------------------------------------------------------------- + //-- Internal methods + //-------------------------------------------------------------------------- + + /** + * Enregistre la version donnee en base avec creation de la table + * si elle n'existe pas. + */ + protected void saveApplicationVersion() { + + checkInit(); + + Version version = callback.getApplicationVersion(); + + detectDbVersion(); + + if (log.isDebugEnabled()) { + log.debug("Save version = " + version); + log.debug("Table exists = " + versionTableExist); + log.debug("Detected version = " + dbVersion); + } + + try { + + boolean createTable = !versionTableExist; + // update version even if database has not been migrated + // only case that database doesn't exist match this + if (createTable) { + // si la base n'etait pas versionnee, la table version n'existe pas + // creation + if (log.isDebugEnabled()) { + log.debug("Adding tms_version table"); + } + + // creer le schema en base + // dans la configuration versionConfiguration, il n'y a que la table version + TMSVersionDAO.createTable(versionConfiguration); + + if (log.isDebugEnabled()) { + log.debug("Table for " + TMSVersion.class.getSimpleName() + " created"); + } + } + + // Set new version in database + TopiaContext tx = rootContext.beginTransaction(); + try { + + // delete all previous data in table + TMSVersionDAO.deleteAll(tx); + + if (log.isInfoEnabled()) { + log.info(t("topia.migration.saving.db.version", version)); + } + + // create new version and store it in table + TMSVersion tmsVersion = + TMSVersionDAO.create(tx, version.getVersion()); + if (log.isDebugEnabled()) { + log.debug("Created version : " + tmsVersion.getVersion()); + } + + tx.commitTransaction(); + } catch (TopiaException e) { + if (tx != null) { + tx.rollbackTransaction(); + } + throw e; + } finally { + if (tx != null) { + tx.closeContext(); + } + } + + if (legacyVersionTableExist) { + + if (log.isDebugEnabled()) { + log.debug("Will drop legacy tmsVersion table"); + } + // on supprime l'ancienne table + TMSVersionDAO.dropTable(legacyVersionConfiguration); + } + } catch (TopiaException e) { + throw new TopiaRuntimeException(e); + } + + // on change les etats internes du service + // ainsi cela empechera le redeclanchement de la migration + // suite a une creation de schema + versionTableExist = true; + dbVersion = version; + } + + /** + * Recupere depuis la base les états internes du service : + * <p/> + * <ul> + * <li>{@link #versionTableExist}</li> + * <li>{@link #dbVersion}</li> + * <li>{@link #dbEmpty}</li> + * </ul> + */ + protected void detectDbVersion() { + + if (versionDetected) { + + // this method was already invoked + if (log.isDebugEnabled()) { + log.debug("version was already detected : " + dbVersion); + } + return; + } + + // compute dbempty field value + dbEmpty = detectDbEmpty(); + + if (log.isDebugEnabled()) { + log.debug("Db is empty : " + dbEmpty); + } + + + Version v = null; + try { + + // on detecte si la table de versionning existe + versionTableExist = + TopiaUtil.isSchemaExist(versionConfiguration, + TMSVersion.class.getName()); + + // check if at least one class exists in db + + + if (log.isDebugEnabled()) { + log.debug("Table " + TMSVersionDAO.TABLE_NAME + " exist = " + versionTableExist); + } + + if (versionTableExist) { + + // recuperation de la version de la base + v = getVersion(versionTableExist, TMSVersionDAO.TABLE_NAME); + + if (log.isWarnEnabled()) { + if (v == null) { + log.warn("Version not found on table " + TMSVersionDAO.TABLE_NAME); + } + } + return; + } + + // try with legacy table tmsVersion + Configuration conf = new Configuration(); + conf.addXML(TMSVersionDAO.LEGACY_MAPPING); + + legacyVersionConfiguration = createHibernateConfiguration(conf); + legacyVersionTableExist = + TopiaUtil.isSchemaExist(legacyVersionConfiguration, + TMSVersion.class.getName()); + + if (legacyVersionTableExist) { + + if (log.isDebugEnabled()) { + log.debug("Legacy : detected " + TMSVersionDAO.LEGACY_TABLE_NAME + " table"); + } + + // recuperation de la version de la base + v = getVersion(legacyVersionTableExist, TMSVersionDAO.LEGACY_TABLE_NAME); + + if (v != null) { + + if (log.isDebugEnabled()) { + log.debug("Legacy : " + t("topia.migration.detected.db.version", v)); + } + } + } + } finally { + + if (v == null) { + // la base dans ce cas n'est pas versionee. + // On dit que la version de la base est 0 + // et les schema de cette version 0 doivent + // etre detenu en local + v = Version.VZERO; + dbNotVersioned = true; + log.info(t("topia.migration.db.not.versionned")); + } else { + log.info(t("topia.migration.detected.db.version", v)); + } + dbVersion = v; + versionDetected = true; + } + } + + /** + * Detects if there is some schema existing for at least one of the dealed + * entity of the underlying db context. + * + * @return {@code true} if there is no schema for any of the dealed entities, + * {@code false} otherwise. + * @since 2.5.3 + */ + protected boolean detectDbEmpty() { + + + try { + boolean result; + // get db real hibernate configuration + Configuration rootConfiguration = + rootContext.getHibernateConfiguration(); + + result = TopiaUtil.isSchemaEmpty(rootConfiguration); + return result; + } catch (TopiaNotFoundException e) { + throw new RuntimeException(e); + } + + } + + protected Version getVersion(boolean versionTableExist, String tableName) { + if (!versionTableExist) { + + // table does not exist, version is null + return null; + } + try { + TopiaContext tx = rootContext.beginTransaction(); + try { + Version v = TMSVersionDAO.getVersion(tx, tableName); + return v; + } finally { + if (tx != null) { + tx.closeContext(); + } + } + } catch (TopiaException e) { + throw new TopiaRuntimeException("Can't obtain dbVersion for reason " + e.getMessage(), e); + } + } + + protected String getSafeParameter(Properties config, String key) { + String value = config.getProperty(key, null); + if (StringUtils.isEmpty(value)) { + throw new IllegalStateException("'" + key + "' not set."); + } + return value; + } + + protected void checkInit() { + if (!init) { + throw new IllegalStateException("le service n'est pas initialisé!"); + } + } + + /** + * Creates the hibernate configuration to be used by the service. + * + * @param configuration the incoming hibernate configuration + * @return the complete configuration usable by the service + * @deprecated since 2.5.3, prefer to use the method {@link #createHibernateConfiguration(Configuration)} + */ + @Deprecated + protected Configuration creaHibernateConfiguration(Configuration configuration) { + return createHibernateConfiguration(configuration); + } + + /** + * Creates the hibernate configuration to be used by the service. + * + * @param configuration the incoming hibernate configuration + * @return the complete configuration usable by the service + * @since 2.5.3 + */ + protected Configuration createHibernateConfiguration(Configuration configuration) { + Properties config = rootContext.getConfig(); + + Properties prop = new Properties(); + prop.putAll(configuration.getProperties()); + prop.putAll(config); + + configuration.setProperties(prop); + configuration.buildMappings(); + return configuration; + + } + + + /** + * Filter versions. + * + * @param versions versions to filter + * @param min min version to accept + * @param max max version to accept + * @param includeMin flag to include min version + * @param includeMax flag to include max version + * @return versions between min and max + */ + protected List<Version> filterVersions(Set<Version> versions, + Version min, + Version max, + boolean includeMin, + boolean includeMax) { + List<Version> toApply = new ArrayList<Version>(); + for (Version v : versions) { + int t; + if (min != null) { + t = v.compareTo(min); + if (t < 0 || t == 0 && !includeMin) { + // version trop ancienne + continue; + } + } + if (max != null) { + t = v.compareTo(max); + if (t > 0 || t == 0 && !includeMax) { + // version trop recente + continue; + } + } + toApply.add(v); + } + return toApply; + } +} diff --git a/topia-service-migration/src/main/java/org/nuiton/topia/migration/TopiaMigrationService.java b/topia-service-migration/src/main/java/org/nuiton/topia/migration/TopiaMigrationService.java new file mode 100644 index 0000000..24d104a --- /dev/null +++ b/topia-service-migration/src/main/java/org/nuiton/topia/migration/TopiaMigrationService.java @@ -0,0 +1,60 @@ +/* + * #%L + * ToPIA :: Service Migration + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.migration; + +import org.nuiton.topia.framework.TopiaService; + +/** + * TopiaMigrationService.java + * + * @author Chatellier Eric + * @author Chevallereau Benjamin + * @author Eon Sébastien + * @author Trève Vincent + * @author tchemit <chemit@codelutin.com> + * @version $Id$ + */ +public interface TopiaMigrationService extends TopiaService { + + /** Nom du service */ + String SERVICE_NAME = "migration"; + + /** Nom du service topia */ + String TOPIA_SERVICE_NAME = "topia.service.migration"; + + /** Pour spécifier dans la configuration le callback a utiliser */ + String MIGRATION_CALLBACK = "topia.service.migration.callback"; + + /** Un drapeau pour indiquer si on doit lancer le service au demarrage */ + String MIGRATION_MIGRATE_ON_INIT = "topia.service.migration.no.migrate.on.init"; + + /** Pour afficher les requetes sql executees */ + String MIGRATION_SHOW_SQL = "topia.service.migration.showSql"; + + /** Pour afficher la progression des requetes sql executees */ + String MIGRATION_SHOW_PROGRESSION = "topia.service.migration.showProgression"; + + boolean migrateSchema() throws MigrationServiceException; +} diff --git a/topia-service-migration/src/main/java/org/nuiton/topia/migration/mappings/TMSVersion.java b/topia-service-migration/src/main/java/org/nuiton/topia/migration/mappings/TMSVersion.java new file mode 100644 index 0000000..3c16a34 --- /dev/null +++ b/topia-service-migration/src/main/java/org/nuiton/topia/migration/mappings/TMSVersion.java @@ -0,0 +1,81 @@ +/* + * #%L + * ToPIA :: Service Migration + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.migration.mappings; + +import org.apache.commons.lang3.StringUtils; +import org.nuiton.util.version.Version; +import org.nuiton.util.version.Versions; + +import java.io.Serializable; + +/** + * TMSVersion.java + * + * @author Chatellier Eric + * @author Chevallereau Benjamin + * @author Eon Sébastien + * @author Trève Vincent + * @author tchemit <chemit@codelutin.com> + * @since 1.0 + */ +public class TMSVersion implements Serializable { + + private static final long serialVersionUID = 1L; + + public static final String PROPERTY_VERSION = "version"; + + public static TMSVersion valueOf(Version version) { + return new TMSVersion(version.toString()); + } + + public static TMSVersion valueOf(String version) { + return new TMSVersion(version); + } + + /** La version. */ + private String version; + + public TMSVersion() { + } + + public TMSVersion(String version) { + if (StringUtils.isEmpty(version)) { + throw new IllegalArgumentException("version parameter can not be null nor empty."); + } + this.version = version; + } + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public Version toVersion() { + return StringUtils.isEmpty(version) ? null : Versions.valueOf(version); + } +} diff --git a/topia-service-migration/src/main/java/org/nuiton/topia/migration/mappings/TMSVersionDAO.java b/topia-service-migration/src/main/java/org/nuiton/topia/migration/mappings/TMSVersionDAO.java new file mode 100644 index 0000000..3e0d2ca --- /dev/null +++ b/topia-service-migration/src/main/java/org/nuiton/topia/migration/mappings/TMSVersionDAO.java @@ -0,0 +1,193 @@ +/* + * #%L + * ToPIA :: Service Migration + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.migration.mappings; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.hibernate.Criteria; +import org.hibernate.HibernateException; +import org.hibernate.Session; +import org.hibernate.cfg.Configuration; +import org.hibernate.jdbc.Work; +import org.hibernate.tool.hbm2ddl.SchemaExport; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaRuntimeException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.framework.TopiaUtil; +import org.nuiton.util.version.Version; +import org.nuiton.util.version.Versions; + +import java.sql.Connection; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; + +/** + * TMSVersion DAO helper. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.3.4 + */ +public class TMSVersionDAO { + + /** logger */ + private final static Log log = LogFactory.getLog(TMSVersionDAO.class); + + public static final String LEGACY_TABLE_NAME = "tmsVersion"; + + public static final String TABLE_NAME = "tms_version"; + + public static TMSVersion get(TopiaContext tx) throws TopiaException { + + try { + Session session = ((TopiaContextImplementor) tx).getHibernate(); + Criteria criteria = session.createCriteria(TMSVersion.class); + List<?> list = criteria.list(); + TMSVersion result = list.isEmpty() ? null : (TMSVersion) list.get(0); + return result; + } catch (HibernateException e) { + throw new TopiaException("Could not obtain version", e); + } + } + + public static void createTable(Configuration configuration) { + // creer le schema en base + // dans la configuration il n'y a que la table version + SchemaExport schemaExport = new SchemaExport(configuration); + schemaExport.create(log.isDebugEnabled(), true); + } + + public static void dropTable(Configuration configuration) { + // supprimer le schema en base + // dans la configuration il n'y a que la table version + SchemaExport schemaExport = new SchemaExport(configuration); + schemaExport.drop(log.isDebugEnabled(), true); + } + + public static TMSVersion create(TopiaContext tx, String version) throws TopiaException { + + try { + Session session = ((TopiaContextImplementor) tx).getHibernate(); + + TMSVersion result = TMSVersion.valueOf(version); + // save entity + session.save(result); + return result; + } catch (HibernateException e) { + throw new TopiaException("Could not create version " + version, e); + } + } + + public static void update(TopiaContext tx, TMSVersion version) throws TopiaException { + try { + Session session = ((TopiaContextImplementor) tx).getHibernate(); + session.saveOrUpdate(version); + tx.commitTransaction(); + } catch (HibernateException e) { + throw new TopiaException("Could not update version " + version, e); + } + } + + public static void deleteAll(TopiaContext tx) throws TopiaException { + try { + Session session = ((TopiaContextImplementor) tx).getHibernate(); + Criteria criteria = session.createCriteria(TMSVersion.class); + List<?> list = criteria.list(); + for (Object o : list) { + session.delete(o); + } + } catch (HibernateException e) { + throw new TopiaException("Could not delete all versions", e); + } + } + + public static final String LEGACY_MAPPING = + "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" + + "<!DOCTYPE hibernate-mapping PUBLIC \"-//Hibernate/Hibernate Mapping DTD 3.0//EN\" \"classpath://org/hibernate/hibernate-mapping-3.0.dtd\">\n" + + "<hibernate-mapping>\n" + + " <class name=\"" + TMSVersion.class.getName() + "\" table=\"" + LEGACY_TABLE_NAME + "\">\n" + + " <id column=\"" + TMSVersion.PROPERTY_VERSION + "\" name=\"" + TMSVersion.PROPERTY_VERSION + "\"/>\n" + + " </class>\n" + + "</hibernate-mapping>"; + + public static Version getVersion(TopiaContext tx, String tableName) { + try { + TopiaContextImplementor txImpl = (TopiaContextImplementor) tx; + Configuration hibernateConfiguration = txImpl.getHibernateConfiguration(); + + // Get schema name + String schemaName = TopiaUtil.getSchemaName(hibernateConfiguration); + + GetVersionWork work = new GetVersionWork(schemaName, tableName); + txImpl.getHibernate().doWork(work); + Version v = work.getVersion(); + return v; + } catch (TopiaException e) { + throw new TopiaRuntimeException("Can't obtain dbVersion for reason " + e.getMessage(), e); + } + } + + public static class GetVersionWork implements Work { + + protected Version version; + + private final String tableName; + private final String schemaName; + + public GetVersionWork(String schemaName, String tableName) { + this.tableName = tableName; + this.schemaName = schemaName; + } + + @Override + public void execute(Connection connection) throws SQLException { + + String fullTableName = schemaName == null ? + tableName : schemaName + "." + tableName; + + + PreparedStatement st = connection.prepareStatement("select " + TMSVersion.PROPERTY_VERSION + " from " + fullTableName + ";"); + try { + ResultSet set = st.executeQuery(); + + if (set.next()) { + version = Versions.valueOf(set.getString(1)); + } + } catch (SQLException eee) { + if (log.isErrorEnabled()) { + log.error("Could not find version", eee); + } + version = null; + } finally { + st.close(); + } + } + + public Version getVersion() { + return version; + } + } +} diff --git a/topia-service-migration/src/main/resources/i18n/topia-service-migration_en_GB.properties b/topia-service-migration/src/main/resources/i18n/topia-service-migration_en_GB.properties new file mode 100644 index 0000000..5e06f2a --- /dev/null +++ b/topia-service-migration/src/main/resources/i18n/topia-service-migration_en_GB.properties @@ -0,0 +1,14 @@ +topia.migration.available.versions=Available versions \: %1$s +topia.migration.db.not.versionned=Database version not found, so database schema is considered as V0 +topia.migration.detected.db.version=detected database version \: %1$s +topia.migration.end.sql=Request [%1$-4s/%2$-4s] executed in %3$s. +topia.migration.migrate.versions=Versions to apply \: %1$s +topia.migration.migration.incomplete=Database migration not succesfully ended \! +topia.migration.saving.db.version=Saving new database version \: %1$s +topia.migration.skip.migration.db.is.empty=Database is empty, no migration needed. +topia.migration.skip.migration.db.is.up.to.date=Database is up to date, no migration needed. +topia.migration.skip.migration.no.version.to.apply=No version to apply, no migration needed. +topia.migration.start.migrate=Start migration to version %1$s +topia.migration.start.migration=Starting Topia Migration Service - Application version \: %1$s, Database version \: %2$s +topia.migration.start.sql=Executing request [%1$-4s/%2$-4s] +topia.migration.start.sqls=Will execute %1$s requests... diff --git a/topia-service-migration/src/main/resources/i18n/topia-service-migration_es_ES.properties b/topia-service-migration/src/main/resources/i18n/topia-service-migration_es_ES.properties new file mode 100644 index 0000000..edd110d --- /dev/null +++ b/topia-service-migration/src/main/resources/i18n/topia-service-migration_es_ES.properties @@ -0,0 +1,14 @@ +topia.migration.available.versions=Versiones detectadas \: %1$s +topia.migration.db.not.versionned=La base de datos no tiene versión, se considera como la versión 0. +topia.migration.detected.db.version=Versión de la base \: %1$s +topia.migration.end.sql=Consulta [%1$-4s/%2$-4s] ejecutada en %3$s. +topia.migration.migrate.versions=Actualización para instalar \: %1$s +topia.migration.migration.incomplete=\\u00a1La migración de la base de datos ha fallado o se ha cancelado\! +topia.migration.saving.db.version=Copia de seguridad de la nueva versión de la base \: %1$s +topia.migration.skip.migration.db.is.empty=Base de datos vacía, no es necesaria la migración. +topia.migration.skip.migration.db.is.up.to.date=La base está actualizada, no es necesaria ninguna migración. +topia.migration.skip.migration.no.version.to.apply=No hay versión a aplicar, no es necesaria ninguna migración. +topia.migration.start.migrate=Inicio de la actualización hacia la versión %1$s +topia.migration.start.migration=Inicio del servicio de actualización - versión de la aplicación \: %1$s, versión de la base \: %2$s +topia.migration.start.sql=Ejecución de la consulta [%1$-4s/%2$-4s] +topia.migration.start.sqls=Se va a ejecutar %1$s consulta(s)... diff --git a/topia-service-migration/src/main/resources/i18n/topia-service-migration_fr_FR.properties b/topia-service-migration/src/main/resources/i18n/topia-service-migration_fr_FR.properties new file mode 100644 index 0000000..294063a --- /dev/null +++ b/topia-service-migration/src/main/resources/i18n/topia-service-migration_fr_FR.properties @@ -0,0 +1,14 @@ +topia.migration.available.versions=Versions détectées \: %1$s +topia.migration.db.not.versionned=La base de données n'est pas versionnée, elle sera considérée en version 0. +topia.migration.detected.db.version=Version de base détectée \: %1$s +topia.migration.end.sql=Requête [%1$-4s/%2$-4s] exécutée en %3$s. +topia.migration.migrate.versions=Mises à jour à installer \: %1$s +topia.migration.migration.incomplete=La migration de la base s'est mal déroulée ou a été annulée\! +topia.migration.saving.db.version=Sauvegarde de la nouvelle version de la base \: %1$s +topia.migration.skip.migration.db.is.empty=La base de données est vierge, aucune migration nécessaire. +topia.migration.skip.migration.db.is.up.to.date=La base est à jour, aucune migration nécessaire. +topia.migration.skip.migration.no.version.to.apply=Aucune version à appliquer, aucune migration nécessaire. +topia.migration.start.migrate=Démarrage de la mise à jour vers la version %1$s +topia.migration.start.migration=Démarrage du service de mise à jour - version de l'application \: %1$s, version de la base \: %2$s +topia.migration.start.sql=Execution de la requête [%1$-4s/%2$-4s] +topia.migration.start.sqls=Va executer %1$s requête(s)... diff --git a/topia-service-migration/src/main/resources/org/nuiton/topia/migration/mappings/TMSVersion.hbm.xml b/topia-service-migration/src/main/resources/org/nuiton/topia/migration/mappings/TMSVersion.hbm.xml new file mode 100644 index 0000000..9c95ab1 --- /dev/null +++ b/topia-service-migration/src/main/resources/org/nuiton/topia/migration/mappings/TMSVersion.hbm.xml @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Migration + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "classpath://org/hibernate/hibernate-mapping-3.0.dtd"> +<hibernate-mapping> + <class name="org.nuiton.topia.migration.mappings.TMSVersion" table="tms_version"> + <id column="version" name="version"/> + </class> +</hibernate-mapping> diff --git a/topia-service-migration/src/main/xmi/MigrationService.zargo b/topia-service-migration/src/main/xmi/MigrationService.zargo new file mode 100644 index 0000000..1d2d7a6 Binary files /dev/null and b/topia-service-migration/src/main/xmi/MigrationService.zargo differ diff --git a/topia-service-migration/src/site/rst/index.rst b/topia-service-migration/src/site/rst/index.rst new file mode 100644 index 0000000..2e1b658 --- /dev/null +++ b/topia-service-migration/src/site/rst/index.rst @@ -0,0 +1,235 @@ +.. - +.. * #%L +.. * ToPIA :: Service Migration +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +======================= +ToPIA Migration Service +======================= + +ToPIA Migration Service est un module ToPIA chargé d'effectuer la migration +d'une base de données existante sans perte de données. + +Configuration +------------- +Ce service doit disposer de quelques proprietés de configuration pour effectuer +la migration d'une base de données. + +Ces propriétés sont fournies au service via un TopiaContext et font donc partie +de la configuration de l'application. + + +Configuration de la base de données +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + hibernate.dialect=org.hibernate.dialect.H2Dialect + hibernate.connection.username=sa + hibernate.connection.password= + hibernate.connection.driver_class=org.h2.Driver + + topia.persistence.directories=directory1,directory2 + topia.persistence.classes=classImpl1,classImpl2 + +Ces informations servent à créer une configuration hibernate (qui contient les +informations de connexion et les mappings de l'application). + +Les lignes commencant par "hibernate" sont spécifiques à hibernate et au type de +base de données utilisé. Les lignes suivantes sont spécifiques à ToPIA mais +contiennent les mappings indispensable pour créer le schéma de la base de +données après migration. + + +Configuration des anciens mappings +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +La configuration doit contenir ces propriétés : + +:: + + topia.service.migration.mappingsdir=oldmappings + topia.service.migration.modelnames=model1,model2,model3 + +qui spécifie le répertoire de recherche des anciens mappings pour les différents +modèles. + +Ce dossier contient ensuite un sous-dossier par modèle comportant chacun un +sous-dossier par version par version (nommé X, *X* étant la version), avec pour +chaque dossier, l'ensemble des mappings hibernate de cette version. + +Exemple : + +:: + + oldmappings/ + model1/ + 1/ + Class1.hbm.xml + Class2.hbm.xml + Class3.hbm.xml + 2/ + Class1.hbm.xml + Class2New.hbm.xml + Class3.hbm.xml + 2.2/ + Class2.hbm.xml + Class3.hbm.xml + Class4New.hbm.xml + + +Configuration de la version +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +La configuration doit contenir une propriété : + +:: + + topia.service.migration.version=3.5.1 (exemple) + +Cette propriété renseigne la version *courante* de l'application. +Le modèle de classes doit contenir un tag "version" indiquant la version +*courante* du modèle. + +Lors de la migration ces deux versions seront comparées pour déterminer +les mappings à utiliser. + + +Configuration du callback +~~~~~~~~~~~~~~~~~~~~~~~~~ +Il est possible de définir une classe de type MigrationCallbackHandler, pour +interagir, par exemple, avec l'utilisateur et lui demander s'il faut migrer la +base de données. + +Ces *callback* doivent implémenter MigrationCallbackHandler et se trouver dans +la configuration: + +:: + + topia.service.migration.callbackhandlers=org.nuiton.test.MyCallbackHandler + + +Configuration du service +~~~~~~~~~~~~~~~~~~~~~~~~ +Enfin pour utiliser le service, il faut l'activer. La configuration doit +contenir la propriétés suivante : + +:: + + topia.service.migration=org.nuiton.topia.migration.TopiaMigrationServiceImpl + + +Utilisation +----------- +Ce module étant un service ToPIA, il doit être activé pour pouvoir s'exécuter. + +Il commence par se connecter au SGBD, vérifie si les versions diffèrent, et +effectue la migration si besoin. + +Dans le cas où la version ne peut pas être deterninée, il considère que le +schema en base est en version V0 (les mppings de cette version doivent être +fournit). Dans ce cas, il effectue en plus une détection des tables pour +savoir si le schéma existe deja. S'il n'existe pas, il ne tente donc pas +d'effetuer une migration. + +Classes de migration +~~~~~~~~~~~~~~~~~~~~ +Pour savoir comment migrer les données, le développeur utilisant le module de +migration doit produire des classes Java de migration (une par classe +nécéssitant une modification et par version). + +Ces classes doivent : + +- hériter de la classe ``AbstractMigration`` ou de l'interface ``Migration`` +- se trouver dans un sous package des classes d'implémentation référencées + par les mappings. Ce package doit se nommer VnVm où: + + - *n* est la version de départ de migration + - *m* la version d'arrivée + +- respecter une convention de nommage de la forme ``MigrationClass`` où: + + - *Class* est le nom de l'entité devant être migrée + + Exemple, pour migrer une *Personne* d'une version 2 à 2.1 le nom de la classe + sera : ``V2V2_1.MigratePersonne.java`` + + Note: les "." étant interdits dans le nom de fichier, ils sont remplacés par + le caractère "_". + +Ensuite, chaque classe doit : + +- implémenter au moins la méthode ``migrate(MapAdapter, MapHelper)`` + cette méthode prend une MapAdapter en paramètre, pour modifier un tuple et un + MapHelper pouvant servir à retrouver des informations sur le reste des tuples + de la base +- surcharger (si besoin) la méthode ``public ProxyClass migrateFrom()`` dans le + cas où les tuples à modifier proviennent d'une classe différente de la classe + courante. + +Exemple : + +Ici, la modification porte sur la transformation de l'attribut +``timestampNaissance`` de la classe ``domaine.Personne`` en une nouvelle table +``domaine.Naissance``, effectée dans le changement de la version 1 à la version +2 : + +:: + + // migrateFrom() + public ProxyClass migrateFrom() { + return new SimpleProxyClass("domaine.Personne"); + } + + // migrate(MapAdapter, MapHelper) + public void migrate(MapAdapter map, MapHelper helper) { + + // map d'entree vide, conversion du timestamp + // en jour, mois, annee + try { + Long timestamp = (Long) map.getOldValue("timestampNaissance"); + Calendar c = Calendar.getInstance(); + c.setTimeInMillis(timestamp); + map.setValue("jour", c.get(Calendar.DAY_OF_MONTH)); + map.setValue("mois", c.get(Calendar.MONTH)); + map.setValue("annee", c.get(Calendar.YEAR)); + } catch (ExceptionAttributeUndefined e) { + e.printStackTrace(); + } + } + +Donc cette classe : + +- doit se nommer ``MigrateNaissance.java`` et se trouver dans le package + ``domaine.V1V2`` +- redéfinit la methode migrateFrom() pour indiquer que les donnees proviennent de + ``domaine.Personne`` +- implémente ``migrate()`` pour produire des tuples de type ``domaine.Naissance`` + à partir de tuples ``domaine.Personne`` + +Création de schéma +~~~~~~~~~~~~~~~~~~ +Dans le cas où l'application est ammenée à créer un schéma de base de données, +elle doit en informer le module de migration pour que celui-ci renseigne +la version du schéma créé. + +Le module s'enregistre automatiquement aupres de ToPIA pour savoir quand +celui-ci a créé un nouveau schéma. Il renseigne donc automatiquement la version +par la suite. diff --git a/topia-service-migration/src/site/rst/manualMigration.rst b/topia-service-migration/src/site/rst/manualMigration.rst new file mode 100644 index 0000000..b2ebab6 --- /dev/null +++ b/topia-service-migration/src/site/rst/manualMigration.rst @@ -0,0 +1,215 @@ +.. - +.. * #%L +.. * ToPIA :: Service Migration +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +======================================== +ToPIA Migration Service (manual version) +======================================== + +ToPIA Migration Service est un module ToPIA chargé d'effectuer la migration +d'une base de données existante sans perte de données. + +Cette page décrit un second service de migration où c'est l'utilisateur qui +indique les scripts sql à effectuer pour chaque changement de version. + +Ce service est plus léger que le service automatique et a quelques restrictions : + +- un seul modèle à migrer +- un callback unique est requis (celui écrit par l'utilisateur) + +Configuration +------------- +Ce service doit disposer de quelques proprietés de configuration pour effectuer +la migration d'une base de données. + +Ces propriétés sont fournies au service via un TopiaContext et font donc partie +de la configuration de l'application. + + +Configuration de la base de données +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +:: + + hibernate.dialect=org.hibernate.dialect.H2Dialect + hibernate.connection.username=sa + hibernate.connection.password= + hibernate.connection.driver_class=org.h2.Driver + + topia.persistence.directories=directory1,directory2 + topia.persistence.classes=classImpl1,classImpl2 + +Ces informations servent à créer une configuration hibernate (qui contient les +informations de connexion et les mappings de l'application). + +Les lignes commencant par "hibernate" sont spécifiques à hibernate et au type de +base de données utilisé. Les lignes suivantes sont spécifiques à ToPIA mais +contiennent les mappings indispensable pour créer le schéma de la base de +données après migration. + + +Configuration des anciens mappings +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +La configuration doit contenir ces propriétés : + +:: + + topia.service.migration.mappingsdir=oldmappings + topia.service.migration.modelname=model + +qui spécifie le répertoire de recherche des anciens mappings pour l'unique +modèle. + +Ce dossier contient ensuite un sous-dossier pour le modèle comportant chacun un +sous-dossier par version par version (nommé X, *X* étant la version), avec pour +chaque dossier, l'ensemble des mappings hibernate de cette version. + +Exemple : + +:: + + oldmappings/ + model1/ + 1/ + Class1.hbm.xml + Class2.hbm.xml + Class3.hbm.xml + 2/ + Class1.hbm.xml + Class2New.hbm.xml + Class3.hbm.xml + 2.2/ + Class2.hbm.xml + Class3.hbm.xml + Class4New.hbm.xml + + +Configuration de la version +~~~~~~~~~~~~~~~~~~~~~~~~~~~ +La configuration doit contenir une propriété : + +:: + + topia.service.migration.version=3.5.1 (exemple) + +Cette propriété renseigne la version *courante* de l'application. +Le modèle de classes doit contenir un tag "version" indiquant la version +*courante* du modèle. + +Lors de la migration ces deux versions seront comparées pour déterminer +les mappings à utiliser. + +Plus précisement, on cherche à partir des anciens mappings quelles versions +peuvent être appliquées entre la version de la base et la version de +l'application. + +Configuration du callback +~~~~~~~~~~~~~~~~~~~~~~~~~ +On doit définir une classe de type ManualMigrationCallback, pour donner les +les actions à réaliser pour chaque migration de version. +Dans ce callback, l'utilisateur doit indiquer s'il faut migrer la +base de données. + +Ce *callback* doit implémenter ManualMigrationCallback et se trouver dans +la configuration: + +:: + + topia.service.migration.callbackhandler=org.nuiton.test.MyMigrationCallback + + +Configuration du service +~~~~~~~~~~~~~~~~~~~~~~~~ +Enfin pour utiliser le service, il faut l'activer. La configuration doit +contenir la propriétés suivante : + +:: + + topia.service.migration=org.nuiton.topia.migration.ManualMigrationEngine + + +Contrôler la migration +~~~~~~~~~~~~~~~~~~~~~~ + +Il est possible de ne pas déclancher automatiquement le service de migration +au démarrage du service, cela peut être utile lorsque l'on veut effectuer des +tâches sur la base avant d'effectuer une éventuelle migration. + +Pour bloquer la migration au démarrage, on ajoute dans la configuration de ToPIA + +:: + + topia.service.migration.migrate.on.init=false + + +Il est ensuite possible de récupérer le service de migration et d'effectuer +la migration à partir d'un topiaContext : + +:: + + ManualMigrationEngine service = (ManualMigrationEngine) ctxt.getService(TopiaMigrationService.class); + service.doMigrateSchema(); + +Utilisation +----------- +Ce module étant un service ToPIA, il doit être activé pour pouvoir s'exécuter. + +Il commence par se connecter au SGBD, vérifie si les versions diffèrent, et +effectue la migration si besoin. + +Dans le cas où la version ne peut pas être deterninée, il considère que le +schema en base est en version V0 (les mppings de cette version doivent être +fournit). Dans ce cas, il effectue en plus une détection des tables pour +savoir si le schéma existe deja. S'il n'existe pas, il ne tente donc pas +d'effetuer une migration. + +Callback de migration +~~~~~~~~~~~~~~~~~~~~~ +Pour savoir comment migrer les données, le développeur doit écrire une méthode +par version de base, donc on doit avoir autant de méthodes que de versions +détectées dans les ancines mappings sauf pour la version V0. + +Ces méthodes sont de la forme + +:: + + migrateTo_XXX + +où XXX est la version tranformée en identifiat java valide. + +Exemple : + +:: + + migrateTo_1_0_0 (pour la version 1.0.0) + +Création de schéma +~~~~~~~~~~~~~~~~~~ +Dans le cas où l'application est ammenée à créer un schéma de base de données, +ou à mettre à jour le schéma, elle doit en informer le module de migration +pour que celui-ci renseigne la version du schéma créé. + +Le module s'enregistre automatiquement aupres de ToPIA pour savoir quand +celui-ci a créé un nouveau schéma. Il renseigne donc automatiquement la version +par la suite. + diff --git a/topia-service-migration/src/site/site.xml b/topia-service-migration/src/site/site.xml new file mode 100644 index 0000000..35a5328 --- /dev/null +++ b/topia-service-migration/src/site/site.xml @@ -0,0 +1,61 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Migration + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<project name="${project.name}"> + + <bannerLeft> + <name>${project.name}</name> + <href>index.html</href> + </bannerLeft> + + <body> + + <breadcrumbs> + <item name="${project.name}" href="index.html"/> + </breadcrumbs> + + <menu ref="parent"/> + + <menu name="Utilisateur" inherit="top"> + <item name="migration service" href="/index.html"/> + <item name="Manual migration service" href="/manualMigration.html"/> + </menu> + + <menu name="Téléchargement"> + <item + href="${repository.home.url}/org/nuiton/topia/${project.artifactId}/${project.version}/${project.build.finalName}.jar" + name="Librairie (jar)"/> + <item + href="${repository.home.url}/org/nuiton/topia/${project.artifactId}/${project.version}/${project.build.finalName}-javadoc.jar" + name="Javadoc (jar)"/> + <item + href="${repository.home.url}/org/nuiton/topia/${project.artifactId}/${project.version}/${project.build.finalName}-sources.jar" + name="Sources (jar)"/> + </menu> + + <menu ref="reports"/> + + </body> +</project> diff --git a/topia-service-migration/src/test/resources/ConfigurationAdapterTest-hibernate.cfg.xml b/topia-service-migration/src/test/resources/ConfigurationAdapterTest-hibernate.cfg.xml new file mode 100644 index 0000000..a382a84 --- /dev/null +++ b/topia-service-migration/src/test/resources/ConfigurationAdapterTest-hibernate.cfg.xml @@ -0,0 +1,42 @@ +<?xml version='1.0' encoding='utf-8'?> +<!-- + #%L + ToPIA :: Service Migration + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-configuration + PUBLIC "-//Hibernate/Hibernate Configuration DTD//EN" + "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> + +<hibernate-configuration> + + <session-factory> + + <!--property name="connection.datasource">java:comp/env/atelier-content-lmfr</property--> + <property name="show_sql">false</property> + <property name="dialect">org.hibernate.dialect.H2Dialect</property> + + <!-- Mapping files --> + + </session-factory> + +</hibernate-configuration> diff --git a/topia-service-replication/LICENSE.txt b/topia-service-replication/LICENSE.txt new file mode 100644 index 0000000..cca7fc2 --- /dev/null +++ b/topia-service-replication/LICENSE.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/topia-service-replication/README.txt b/topia-service-replication/README.txt new file mode 100644 index 0000000..d2e50d3 --- /dev/null +++ b/topia-service-replication/README.txt @@ -0,0 +1,2 @@ +To deploy new version of pom: mvn deploy +To install localy: mvn install diff --git a/topia-service-replication/changelog.txt b/topia-service-replication/changelog.txt new file mode 100644 index 0000000..ab64e83 --- /dev/null +++ b/topia-service-replication/changelog.txt @@ -0,0 +1,2 @@ +2.2.0 + * initial version \ No newline at end of file diff --git a/topia-service-replication/pom.xml b/topia-service-replication/pom.xml new file mode 100644 index 0000000..9c1f6ee --- /dev/null +++ b/topia-service-replication/pom.xml @@ -0,0 +1,199 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Replication + + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2010 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.nuiton</groupId> + <artifactId>topia</artifactId> + <version>2.9.3-SNAPSHOT</version> + </parent> + + <groupId>org.nuiton.topia</groupId> + <artifactId>topia-service-replication</artifactId> + + <name>ToPIA :: Service Replication</name> + <description>Hibernate based replication service</description> + + <dependencies> + + <!-- Sibling dependencies --> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>topia-persistence</artifactId> + <version>${project.version}</version> + <scope>compile</scope> + </dependency> + + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>topia-persistence</artifactId> + <version>${project.version}</version> + <scope>test</scope> + <classifier>tests</classifier> + </dependency> + + <dependency> + <groupId>org.nuiton.i18n</groupId> + <artifactId>nuiton-i18n</artifactId> + </dependency> + + <dependency> + <groupId>commons-collections</groupId> + <artifactId>commons-collections</artifactId> + </dependency> + + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + </dependency> + + <dependency> + <groupId>org.hibernate</groupId> + <artifactId>hibernate-core</artifactId> + </dependency> + + <!-- Depencies for test--> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.nuiton.i18n</groupId> + <artifactId>i18n-maven-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>parserJava</goal> + <goal>gen</goal> + </goals> + </execution> + </executions> + </plugin> + + <plugin> + <artifactId>maven-jar-plugin</artifactId> + <executions> + <execution> + <id>attach-test</id> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> + + <profiles> + <!-- perform only on a release stage when using the maven-release-plugin --> + <profile> + <id>release-profile</id> + <activation> + <property> + <name>performRelease</name> + <value>true</value> + </property> + </activation> + + <build> + <plugins> + + <!-- always compute tests source jar --> + <plugin> + <artifactId>maven-source-plugin</artifactId> + <executions> + <execution> + <id>attach-test-sources</id> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> + + <!-- always compute tests source jar --> + <plugin> + <artifactId>maven-javadoc-plugin</artifactId> + <executions> + <execution> + <id>attach-test-javadoc</id> + <goals> + <goal>test-jar</goal> + </goals> + </execution> + </executions> + </plugin> + + </plugins> + </build> + </profile> + + <!-- reporting at release time --> + <profile> + <id>reporting</id> + <activation> + <property> + <name>performRelease</name> + <value>true</value> + </property> + </activation> + + <reporting> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>cobertura-maven-plugin</artifactId> + <version>2.4</version> + </plugin> + </plugins> + </reporting> + + </profile> + </profiles> +</project> diff --git a/topia-service-replication/src/license/THIRD-PARTY.properties b/topia-service-replication/src/license/THIRD-PARTY.properties new file mode 100644 index 0000000..7ca75c1 --- /dev/null +++ b/topia-service-replication/src/license/THIRD-PARTY.properties @@ -0,0 +1,49 @@ +### +# #%L +# ToPIA :: Service Replication +# $Id$ +# $HeadURL$ +# %% +# Copyright (C) 2004 - 2014 CodeLutin +# %% +# 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 General Lesser Public License for more details. +# +# You should have received a copy of the GNU General Lesser Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/lgpl-3.0.html>. +# #L% +### +# Generated by org.codehaus.mojo.license.AddThirdPartyMojo +#------------------------------------------------------------------------------- +# Already used licenses in project : +# - Apache License 2.0 +# - BSD License +# - COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 +# - Common Public License Version 1.0 +# - GNU Lesser General Public License, version 2.1 +# - GNU Library or Lesser General Public License +# - Indiana University Extreme! Lab Software License, vesion 1.1.1 +# - Lesser General Public License (LGPL) v 3.0 +# - Lesser General Public License (LPGL) +# - Lesser General Public License (LPGL) v 2.1 +# - MIT License +# - MPL 1.1 +# - New BSD License +# - The Apache Software License, Version 2.0 +# - The H2 License, Version 1.0 +# - license.txt +#------------------------------------------------------------------------------- +# Please fill the missing licenses for dependencies : +# +# +#Fri Mar 15 12:41:29 CET 2013 +commons-primitives--commons-primitives--1.0=The Apache Software License, Version 2.0 +dom4j--dom4j--1.6.1=BSD License diff --git a/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationContext.java b/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationContext.java new file mode 100644 index 0000000..26437d2 --- /dev/null +++ b/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationContext.java @@ -0,0 +1,276 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.replication; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.util.TopiaEntityHelper; +import org.nuiton.topia.persistence.util.TopiaEntityIdsMap; +import org.nuiton.topia.replication.model.ReplicationModel; +import org.nuiton.topia.replication.model.ReplicationNode; +import org.nuiton.topia.replication.model.ReplicationOperationDef; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Defines the context used to replicate. + * <p/> + * It contains : + * <ul> + * <li>the {@link #replicationModel}</li> + * <li>data sources involved ([@link #sourceTx}, {@link #targetTx})</li> + * <li>universe of data to replicate {@link #data}</li> + * <li>nodes that has been treated successfully {@link #treated}</li> + * </ul> + * + * @author tchemit <chemit@codelutin.com> + * @since 2.4.3 + */ +public class TopiaReplicationContext { + + /** Logger */ + private static final Log log = + LogFactory.getLog(TopiaReplicationContext.class); + + /** Replication model. */ + protected final ReplicationModel replicationModel; + + /** + * root context of incoming data source (says where to obtain entities + * to replicate). + */ + protected final TopiaContext sourceTx; + + /** + * root context of outcmoing data source (says where to replicate + * entities). + */ + protected final TopiaContext targetTx; + + /** dictionnary of data to replicate. */ + protected TopiaEntityIdsMap data; + + /** + * List of nodes that has been replicated with success (used to + * rollback them if something was wrong). + */ + protected List<ReplicationNode> treated; + + protected TopiaReplicationOperationProvider operationProvider; + + public TopiaReplicationContext(TopiaReplicationOperationProvider operationProvider, + ReplicationModel replicationModel, + TopiaContext sourceTx, + TopiaContext targetTx) { + this.operationProvider = operationProvider; + this.replicationModel = replicationModel; + this.sourceTx = sourceTx; + this.targetTx = targetTx; + } + + public TopiaReplicationOperation getOperation(ReplicationOperationDef operationDef) { + TopiaReplicationOperation operation = + operationProvider.getOperation(operationDef); + return operation; + } + + public TopiaReplicationOperationUndoable getUndoableOperation(ReplicationOperationDef operationDef) throws IllegalArgumentException { + TopiaReplicationOperationUndoable operation = + operationProvider.getUndoableOperation(operationDef); + return operation; + } + + public ReplicationModel getReplicationModel() { + return replicationModel; + } + + public void addTreatedNode(ReplicationNode node) { + getTreated().add(node); + } + + public ReplicationNode[] getReverseTreated() { + + List<ReplicationNode> result = + new ArrayList<ReplicationNode>(getTreated()); + + // reverse nodes treated + Collections.reverse(result); + + return result.toArray(new ReplicationNode[result.size()]); + } + + + /** + * Init the replication context. + * <p/> + * Comptute the universe to replicate and store it in {@link #data}. + * + * @throws TopiaException for any error when initializing the replication context + */ + public void init() throws TopiaException { + + TopiaContextImplementor srcCtxt = newSourceTx(); + try { + + // obtain all data to replicate + data = getIds(srcCtxt); + + // adjust operation according to this data (some operations can be + // skipped if no data is associated to the given nodes) + // will also sort for each node his operations (according to + // their respective phase) + getReplicationModel().adjustOperations(data); + + } finally { + close(srcCtxt, true); + } + } + + public List<String> getEntityIds(Class<? extends TopiaEntity> type) throws TopiaException { + + List<String> nodeEntityIds = data.get(type); + return nodeEntityIds; + } + + public List<String> getEntityIds(ReplicationNode node) throws TopiaException { + + List<String> nodeEntityIds = getEntityIds(node.getEntityType()); + return nodeEntityIds; + } + + public List<? extends TopiaEntity> getEntities(TopiaContextImplementor tx, + ReplicationNode node) throws TopiaException { + + List<String> nodeEntityIds = getEntityIds(node); + + if (nodeEntityIds == null) { + + // no ids found for this node + return Collections.emptyList(); + } + + if (log.isDebugEnabled()) { + log.debug("Will load " + nodeEntityIds.size() + " entities"); + } + + List<? extends TopiaEntity> nodeEntities = + TopiaEntityHelper.getEntitiesList( + tx, + nodeEntityIds.toArray(new String[nodeEntityIds.size()]) + ); + return nodeEntities; + + } + + public TopiaContextImplementor newSourceTx() throws TopiaException { + return (TopiaContextImplementor) sourceTx.beginTransaction(); + } + + public TopiaContextImplementor newTargetTx() throws TopiaException { + return (TopiaContextImplementor) targetTx.beginTransaction(); + } + + public static void close(TopiaContext tx, boolean rollback) throws TopiaException { + + try { + if (rollback) { + tx.rollbackTransaction(); + } + } finally { + tx.closeContext(); + } + } + + public void clear() { + if (data != null) { + data.clear(); + } + if (treated != null) { + treated.clear(); + } + } + + protected TopiaEntityIdsMap getIds(TopiaContextImplementor srcCtxt) throws TopiaException { + + TopiaEntityIdsMap data; + + // on recupere les objets a repliquer par type + if (replicationModel.isReplicateAll()) { + + // on recupere pour chaque type tous les ids des entites a repliquer + data = new TopiaEntityIdsMap(); + for (TopiaEntityEnum e : replicationModel.getContracts()) { + List<String> ids = srcCtxt.getDAO(e.getContract()).findAllIds(); + data.put(e.getContract(), ids); + } + } else { + + // on recupere les entites specifies a repliquer + TopiaEntity[] entities = TopiaEntityHelper.getEntities( + srcCtxt, + replicationModel.getTopiaIds() + ); + + // on calcule toutes les ids des entites a repliquer + data = TopiaEntityHelper.detectEntityIds( + replicationModel.getContracts(), + replicationModel.getTypes(), + entities + ); + } + return data; + } + + protected List<ReplicationNode> getTreated() { + if (treated == null) { + treated = new ArrayList<ReplicationNode>(); + } + return treated; + } + + protected TopiaContext getSourceTx() { + return sourceTx; + } + + protected TopiaContext getTargetTx() { + return targetTx; + } + + protected TopiaEntityIdsMap getData() { + return data; + } + + @Override + protected void finalize() throws Throwable { + super.finalize(); + clear(); + } +} diff --git a/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationModelBuilder.java b/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationModelBuilder.java new file mode 100644 index 0000000..496d37b --- /dev/null +++ b/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationModelBuilder.java @@ -0,0 +1,303 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.replication; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.util.TopiaEntityHelper; +import org.nuiton.topia.replication.model.ReplicationModel; +import org.nuiton.topia.replication.model.ReplicationNode; +import org.nuiton.topia.replication.model.ReplicationOperationPhase; + +import java.util.Arrays; +import java.util.Set; + +import static org.nuiton.i18n.I18n.t; + +/** + * Builder of {@link ReplicationModel}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.4.3 + */ +public class TopiaReplicationModelBuilder { + + /** Logger */ + private static final Log log = + LogFactory.getLog(TopiaReplicationModelBuilder.class); + + /** Provider of {@link TopiaReplicationOperation}. */ + protected TopiaReplicationOperationProvider operationProvider; + + public TopiaReplicationOperationProvider getOperationProvider() { + if (operationProvider == null) { + operationProvider = new TopiaReplicationOperationProvider(); + } + return operationProvider; + } + + /** + * Prepare le modele de replication pour les entites données dans + * {@code topiaIds} et de leur couverture. + * <p/> + * Le paramètre {@code computeOrder} détermine si on doit calculer l'ordre + * de replication des données (valeur à {@code true}), sinon (valeur + * à {@code false}) on utilise l'ordre induit par le paramètre + * {@code contracts}. + * + * @param context le context de la base source (peut être utilisé + * pour calculer l'ordre) + * @param contracts les contrats des types a repliquer + * @param computeOrder drapeau positionné à {@code true} si on doit calculer + * l'ordre de réplication, {@code false} si on utilise + * l'ordre induit par le paramètre {@code contracts}. + * @param topiaIds les ids des entités à répliquer + * @return le modele pour la replication + * @throws TopiaException pour toute erreur rencontree + */ + public ReplicationModel prepare(TopiaContext context, + TopiaEntityEnum[] contracts, + boolean computeOrder, + String... topiaIds) throws TopiaException { + ReplicationModel model = + createModel(context, contracts, computeOrder, topiaIds); + initModel(model, computeOrder); + return model; + } + + /** + * Prepare le modele de replication pour toutes les entites des types + * donnes. + * <p/> + * La méthode calcule l'ordre de replication des données. + * <p/> + * Actuellement, on n'est pas capable de calculer l'ordre si le graphe des + * entités contient des cycles. + * <p/> + * TODO : faire en sorte de pouvoir gérer les cycles. + * + * @param contracts les contrats des types a repliquer + * @return le modele pour la replication + * @throws TopiaException pour toute erreur rencontree + */ + public ReplicationModel prepareForAll(TopiaEntityEnum[] contracts) throws TopiaException { + ReplicationModel model = createModelForAll(contracts); + initModel(model, true); + return model; + } + + public void createOperation(ReplicationModel model, + TopiaEntityEnum type, + ReplicationOperationPhase phase, + Class<? extends TopiaReplicationOperation> operationClass, + Object... parameters) { + + TopiaEntityHelper.checkNotNull("createOperation", "model", model); + TopiaEntityHelper.checkNotNull("createOperation", "type", type); + TopiaEntityHelper.checkNotNull("createOperation", "phase", phase); + TopiaEntityHelper.checkNotNull("createOperation", "operationClass", operationClass); + + TopiaReplicationOperation operation = + getOperationProvider().getOperation(operationClass); + + if (operation == null) { + throw new IllegalArgumentException( + t("topia.replication.error.unkown.operation", + operationClass.getSimpleName(), + Arrays.toString(getOperationProvider().getOperations())) + ); + } + + ReplicationNode node = model.getNode(type); + if (node == null) { + throw new IllegalArgumentException( + t("topia.replication.error.unkown.owner.node", + type, + operationClass.getSimpleName(), + model.getNodes()) + ); + } + operation.register(model, node, phase, parameters); + } + + /** + * Ajouter une nouvelle operation pre-replication, sur un type de donnee. + * + * @param model le modele de replication + * @param type le type du noeud de replication + * @param operationClass l'implantation de l'operation + * @param parameters les parametres supplementaires pour l'operation + */ + public void addBeforeOperation(ReplicationModel model, + TopiaEntityEnum type, + Class<? extends TopiaReplicationOperation> operationClass, + Object... parameters) { + createOperation( + model, + type, + ReplicationOperationPhase.before, + operationClass, + parameters + ); + } + + /** + * Ajouter une nouvelle operation post-replication, sur un type de donnee. + * + * @param model le modele de replication + * @param type le type du noeud de replication + * @param operationClass l'implantation de l'operation + * @param parameters les parametres supplementaires pour l'operation + */ + public void addAfterOperation(ReplicationModel model, + TopiaEntityEnum type, + Class<? extends TopiaReplicationOperation> operationClass, + Object... parameters) { + createOperation( + model, + type, + ReplicationOperationPhase.after, + operationClass, + parameters + ); + } + + /** + * Instantie un nouveau modèle de réplication pour les entités données par + * leur ids. + * <p/> + * L'ordre de réplication dépend du paramètre {@code computeOrder}. Si + * celui-ci vaut {@code true}, on calcule l'ordre de réplication, sinon on + * utilise l'ordre induit par les {@code contracts}. + * + * @param context le context Topia pour récupérer certainnes + * informations de la base source si nécessaire. + * @param contracts les types d'entités + * @param computeOrder drapeau pour calculer l'ordre de réplication + * (valeur à {@code true}), sinon on utilise l'ordre + * induit par les {@code contracts}. + * @param topiaIds les ids à dupliquer + * @return le modèle crée mais non initialisé. + * @throws TopiaException pour toute erreur lors de la création du modèle + */ + public ReplicationModel createModel(TopiaContext context, + TopiaEntityEnum[] contracts, + boolean computeOrder, + String... topiaIds) + throws TopiaException { + + ReplicationModel model; + + if (computeOrder) { + // determines types to use + Set<Class<? extends TopiaEntity>> detectTypes = + detectTypes(context, contracts, topiaIds); + model = new ReplicationModel(contracts, detectTypes, topiaIds); + } else { + model = new ReplicationModel(contracts, false, topiaIds); + + } + return model; + } + + /** + * Instantie un nouveau modèle de réplication en respectant l'ordre induit + * par les {@code contracts}. + * + * @param contracts les types d'entités + * @param topiaIds les ids à dupliquer + * @return le modèle crée mais non initialisé. + * @throws TopiaException pour toute erreur lors de la création du modèle + * @deprecated since 2.4.3, prefer use method {@link #createModel(TopiaContext, TopiaEntityEnum[], boolean, String...)} + */ + @Deprecated + public ReplicationModel createModelWithComputedOrder(TopiaEntityEnum[] contracts, + String... topiaIds) + throws TopiaException { + + ReplicationModel model; + + + model = new ReplicationModel(contracts, false, topiaIds); + return model; + } + + /** + * Instantie un nouveau modèle de réplication pour toutes les entitées. + * <p/> + * Ici, l'ordre est toujours calculé. + * + * @param contracts les types d'entités + * @return le modèle crée mais non initialisé. + * @throws TopiaException pour toute erreur lors de la création du modèle + */ + public ReplicationModel createModelForAll(TopiaEntityEnum[] contracts) + throws TopiaException { + ReplicationModel model = new ReplicationModel(contracts, true); + return model; + } + + public ReplicationModel initModel(ReplicationModel model, + boolean computeOrder) throws TopiaException { + TopiaEntityHelper.checkNotNull("initModel", "model", model); + + model.detectAssociations(); + model.detectDirectDependencies(); + if (computeOrder) { + model.detectShell(); + model.detectDependencies(); + } + model.detectObjectsToDettach(); + model.detectOperations(); + return model; + } + + protected Set<Class<? extends TopiaEntity>> detectTypes(TopiaContext context, + TopiaEntityEnum[] contracts, + String... ids) throws TopiaException { + + TopiaContext ctxt = context.beginTransaction(); + try { + TopiaEntity[] entities = TopiaEntityHelper.getEntities(ctxt, ids); + + // on detecte tous les types connus pour les entites données + Set<Class<? extends TopiaEntity>> types = + TopiaEntityHelper.detectTypes(contracts, entities); + + if (log.isDebugEnabled()) { + log.debug("for type : " + entities.getClass()); + for (Class<? extends TopiaEntity> k : types) { + log.debug(k); + } + } + return types; + } finally { + ctxt.closeContext(); + } + } +} diff --git a/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperation.java b/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperation.java new file mode 100644 index 0000000..357cb42 --- /dev/null +++ b/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperation.java @@ -0,0 +1,101 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication; + +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.replication.model.ReplicationModel; +import org.nuiton.topia.replication.model.ReplicationNode; +import org.nuiton.topia.replication.model.ReplicationOperationDef; +import org.nuiton.topia.replication.model.ReplicationOperationPhase; + +import java.util.List; +import java.util.ServiceLoader; + +/** + * Le contrat d'une operation a effectuer lors de la replication. + * <p/> + * Le module propose des operations de base dans le paquetage + * {@code org.nuiton.topia.replication.operation}. + * <p/> + * <p/> + * Pour definir une nouvelle implantation d'operation, il faut l'enregister + * en tant que service (au sens de la classe {@link ServiceLoader}, + * c'est à dire ajouter dans un fichier (du class-path) + * {@code META-INF/services/org.nuiton.topia.replication.TopiaReplicationOperation} + * <p/> + * une ligne avec le nom qualifie de votre implantation. + * <p/> + * Lors du chargement du service, on detecte toutes les operations disponibles. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + */ +public interface TopiaReplicationOperation { + + /** + * Creer et enregister une operation utilisateur apres la creation du + * modele via la methode + * <p/> + * {@link TopiaReplicationService#prepare(TopiaEntityEnum[], boolean, String...)}. + * + * @param model le modele de replication + * @param ownerNode le noeud proprietaire de l'operation + * @param phase la phase ou positionner l'operation + * @param parameters les parametres de l'operation + * @throws UnsupportedOperationException if can not register this operation + * (says when operation is only internal) + * @see ReplicationModel + * @see ReplicationOperationPhase + * @deprecated since 2.5.2, there is no difference between a user operation and an internal one. This method will be + * removed in version 2.6 and never replaced + */ + @Deprecated + void register(ReplicationModel model, + ReplicationNode ownerNode, + ReplicationOperationPhase phase, + Object... parameters) + throws UnsupportedOperationException; + + /** + * Execute l'operation avec le parametrage donnee. + * <p/> + * Note : le commit sur le context cible doit etre geree dans la methode. + * + * @param replicationContext le contexte de replication + * @param operationDef la definition de l'operation a realiser + * @param srcCtxt le context source + * @param dstCtxt le context destination + * @param entities la liste des entités à traiter + * @throws Exception pour toute erreur + */ + void run(TopiaReplicationContext replicationContext, + ReplicationOperationDef operationDef, + TopiaContextImplementor srcCtxt, + TopiaContextImplementor dstCtxt, + List<? extends TopiaEntity> entities) throws Exception; + +} diff --git a/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationProvider.java b/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationProvider.java new file mode 100644 index 0000000..863d456 --- /dev/null +++ b/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationProvider.java @@ -0,0 +1,127 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.replication; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.persistence.util.TopiaEntityHelper; +import org.nuiton.topia.replication.model.ReplicationOperationDef; + +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; + +/** + * Provider of {@link TopiaReplicationOperation}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.4.3 + */ +public class TopiaReplicationOperationProvider { + + /** Logger */ + private static final Log log = + LogFactory.getLog(TopiaReplicationOperationProvider.class); + + /** + * All available operations detected via a {@link ServiceLoader} on + * contract {@link TopiaReplicationOperation}. + */ + protected TopiaReplicationOperation[] operations; + + /** + * Obtains all {@link TopiaReplicationOperation} available + * via {@link ServiceLoader}. + * <p/> + * If {@link #operations} is null, then load operations, otherwise just + * return the already computed result. + * + * @return the array of all available operations + */ + public TopiaReplicationOperation[] getOperations() { + if (operations == null) { + // chargement des operations disponibles une seule fois + + ServiceLoader<TopiaReplicationOperation> loader = + ServiceLoader.load(TopiaReplicationOperation.class); + + List<TopiaReplicationOperation> operations = + new ArrayList<TopiaReplicationOperation>(); + + for (TopiaReplicationOperation op : loader) { + if (log.isDebugEnabled()) { + log.debug("detected operation " + op); + } + operations.add(op); + } + this.operations = operations.toArray( + new TopiaReplicationOperation[operations.size()]); + } + return operations; + } + + /** + * Obtains the instanciated (and initialized) operation of the given type. + * + * @param operationClass type of searched operation + * @return the found operation, or {@code null} if not found. + */ + public TopiaReplicationOperation getOperation( + Class<? extends TopiaReplicationOperation> operationClass) { + + TopiaEntityHelper.checkNotNull("getOperation", "operationClass", + operationClass); + TopiaReplicationOperation result = null; + + for (TopiaReplicationOperation op : getOperations()) { + if (operationClass.isAssignableFrom(op.getClass())) { + result = op; + break; + } + } + return result; + } + + /** + * Obtains the instanciated (and initialized) operation of the given + * operation definition. + * + * @param operationDef operation definition of searched operation + * @return the found operation, or {@code null} if not found. + */ + public TopiaReplicationOperation getOperation(ReplicationOperationDef operationDef) { + TopiaEntityHelper.checkNotNull("getOperation", "operationDef", + operationDef); + return getOperation(operationDef.getOperationClass()); + } + + public TopiaReplicationOperationUndoable getUndoableOperation(ReplicationOperationDef operationDef) throws IllegalArgumentException { + TopiaReplicationOperation operation = getOperation(operationDef); + if (!(operation instanceof TopiaReplicationOperationUndoable)) { + throw new IllegalArgumentException( + "the operation " + operation + " is not undoable"); + } + return (TopiaReplicationOperationUndoable) operation; + } +} diff --git a/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationUndoable.java b/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationUndoable.java new file mode 100644 index 0000000..fcd0921 --- /dev/null +++ b/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationOperationUndoable.java @@ -0,0 +1,55 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication; + +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.replication.model.ReplicationOperationDef; + +/** + * Le contrat d'une operation {@link TopiaReplicationOperation} qui peut être + * rollbacker lorsque la replication a échouée. + * <p/> + * + * @author tchemit <chemit@codelutin.com> + * @since 2.4.3 + */ +public interface TopiaReplicationOperationUndoable extends TopiaReplicationOperation { + + /** + * Execute l'operation inverse avec le parametrage donnee + * (pour annuler l'opération). + * <p/> + * Note : le commit sur le context cible doit etre geree dans la methode. + * + * @param operationDef la definition de l'operation a realiser + * @param replicationContext le context de replication + * @param dstCtxt le context destination + * @throws Exception pour toute erreur + */ + void rollback(ReplicationOperationDef operationDef, + TopiaReplicationContext replicationContext, + TopiaContextImplementor dstCtxt + ) throws Exception; +} diff --git a/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationService.java b/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationService.java new file mode 100644 index 0000000..aca4e72 --- /dev/null +++ b/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationService.java @@ -0,0 +1,125 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ +package org.nuiton.topia.replication; + +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaService; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.replication.model.ReplicationModel; + +/** + * User visible interface for replication engin + * <p/> + * To use replicator you must have properties defined in config file: + * <li>topia.replication.engin=[class used to indexation] <li>possible specific + * property for index engin used + * <p/> + * The replication is always done in two phases : + * <p/> + * <li> - prepare the replication model - ({@link #prepare(TopiaEntityEnum[], boolean, String...)} - ({@link #prepareForAll(TopiaEntityEnum[])} + * <p/> + * <li> - lanch replication {@link #doReplicate(ReplicationModel, + * TopiaContext)} + * + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + */ +public interface TopiaReplicationService extends TopiaService { + + /** + * Nom du service + */ + String SERVICE_NAME = "replication"; + + /** + * Nom du service topia + */ + String TOPIA_SERVICE_NAME = "topia.service.replication"; + + /** + * Obtains the {@code model builder} use to creat the replication's model. + * + * @return the model builder + * @since 2.4.3 + */ + TopiaReplicationModelBuilder getModelBuilder(); + + /** + * Prepare le modele de replication pour les entites dans les topiaIds sont + * donnes. + * + * @param contracts les contrats a repliquer + * @param computeOrder drapeau positionné à {@code true} si on doit calculer + * l'ordre des entités à repliquer, sinon on utilise + * l'ordre induit par les {@code contracts}. + * @param topiaIds la liste des ids d'entites a repliquer + * @return le model de replication initialise + * @throws TopiaException pour toute erreur recontree + */ + ReplicationModel prepare(TopiaEntityEnum[] contracts, + boolean computeOrder, + String... topiaIds) throws TopiaException; + + /** + * Prepare le modele de replication pour toutes les entites des types + * donnes. + * <p/> + * La méthode calcule l'ordre de replication des données. + * <p/> + * Actuellement, on n'est pas capable de calculer l'ordre si le graphe des + * entités contient des cycles. + * <p/> + * TODO : faire en sorte de pouvoir gérer les cycles. + * + * @param contracts les contrats des types a repliquer + * @return le modele pour la replication + * @throws TopiaException pour toute erreur rencontree + */ + ReplicationModel prepareForAll(TopiaEntityEnum[] contracts) throws TopiaException; + + /** + * Lance l'operation de replication a partir du context source vers le + * context de destination sur les entites donnees et en utilisant le modele + * de replication precedemment construit via la methode {@link + * #prepare(TopiaEntityEnum[], boolean, String...)} ou {@link + * #prepareForAll(TopiaEntityEnum[])}. + * + * @param model le modele de replication + * @param dstCtxt le context sur la source de donnees ou repliquer + * @throws Exception pour toute erreur pendant la replication + */ + void doReplicate(ReplicationModel model, + TopiaContext dstCtxt) throws Exception; + + /** + * Pour revenir en arrière lorsque la réplication a échouée . + * + * @param replicationContext the replication's context used to start replication. + * @throws Exception pour toute erreur pendant la replication + */ + void doRollback(TopiaReplicationContext replicationContext) throws Exception; +} + + diff --git a/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationServiceImpl.java b/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationServiceImpl.java new file mode 100644 index 0000000..b4729bd --- /dev/null +++ b/topia-service-replication/src/main/java/org/nuiton/topia/replication/TopiaReplicationServiceImpl.java @@ -0,0 +1,329 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.util.TopiaEntityHelper; +import org.nuiton.topia.replication.model.ReplicationModel; +import org.nuiton.topia.replication.model.ReplicationNode; +import org.nuiton.topia.replication.model.ReplicationOperationDef; + +import java.util.List; + +/** + * Implantation du service de replication. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + */ +public class TopiaReplicationServiceImpl implements TopiaReplicationService { + + /** Logger */ + private static final Log log = + LogFactory.getLog(TopiaReplicationServiceImpl.class); + + /** le contexte sur la base source de la replication */ + protected TopiaContextImplementor context; + + /** + * le constructeur de modèle de réplication. + * + * @since 2.4.3 + */ + protected TopiaReplicationModelBuilder modelBuilder; + + //-------------------------------------------------------------------------- + //-- TopiaService implementation ------------------------------------------- + //-------------------------------------------------------------------------- + + @Override + public String getServiceName() { + return SERVICE_NAME; + } + + @Override + public Class<?>[] getPersistenceClasses() { + // pas de classes persistentes pour ce service + return null; + } + + @Override + public boolean preInit(TopiaContextImplementor context) { + // nothing to init + return true; + } + + @Override + public boolean postInit(TopiaContextImplementor context) { + // set the incoming root context from topia + this.context = context; + //TODO avoir un objet pour lire les contrainte de resolution de cycle + //TODO sur les dependances (par exemple, une dependance marquee comme + // non nulle : ne peut jamais etre dettache, alors que dans le cas + // contraire on peut dettacher la dependance + // Cela permet de traiter plus de cas... + + //Properties prop = context.getConfig(); + return true; + } + + //-------------------------------------------------------------------------- + //-- TopiaReplicationService implementation -------------------------------- + //-------------------------------------------------------------------------- + + @Override + public ReplicationModel prepare(TopiaEntityEnum[] contracts, + boolean computeOrder, + String... topiaIds) throws TopiaException { + ReplicationModel model = + getModelBuilder().prepare(context, contracts, computeOrder, topiaIds); + return model; + } + + @Override + public ReplicationModel prepareForAll(TopiaEntityEnum[] contracts) throws TopiaException { + ReplicationModel model = getModelBuilder().prepareForAll(contracts); + return model; + } + + + @Override + public TopiaReplicationModelBuilder getModelBuilder() { + if (modelBuilder == null) { + modelBuilder = new TopiaReplicationModelBuilder(); + } + return modelBuilder; + } + + @Override + public void doReplicate(ReplicationModel model, + TopiaContext targetTx) throws Exception { + + TopiaEntityHelper.checkNotNull("doReplicate", "model", model); + TopiaEntityHelper.checkNotNull("doReplicate", "dstCtxt", targetTx); + + // create a replication context + TopiaReplicationContext replicationContext = + new TopiaReplicationContext(getModelBuilder().getOperationProvider(), + model, + context, + targetTx); + + // init replication context + replicationContext.init(); + + ReplicationNode currentNode = null; + try { + + for (ReplicationNode node : model.getOrder()) { + + currentNode = node; + + // start replication of current node + doReplicateNode(replicationContext, node); + + // node was sucessfull replicated, mark it + replicationContext.addTreatedNode(node); + } + } catch (Exception e) { + + if (log.isErrorEnabled()) { + log.error("Could not replicate node " + currentNode, e); + } + // an error occurs, rollback all sucessfull replicated nodes. + doRollback(replicationContext); + + // then throw the original error + throw e; + } finally { + replicationContext.clear(); + } + } + + @Override + public void doRollback(TopiaReplicationContext replicationContext) throws Exception { + + TopiaEntityHelper.checkNotNull("doRollback", "replicationContext", replicationContext); + + ReplicationNode[] treated = replicationContext.getReverseTreated(); + + if (treated.length == 0) { + + // aucun noeud de réplication commités + return; + } + + log.info("Will rollback " + treated.length + " nodes..."); + + for (ReplicationNode node : treated) { + + try { + doRollbackNode(replicationContext, node); + } catch (Exception e) { + + log.error("Could not rollback node " + node, e); + + //tchemit 2010-08-17 perharps we should not throw the exception + // and try to rollback other nodes ? + throw e; + } + } + } + + public void doReplicateNode(TopiaReplicationContext replicationContext, + ReplicationNode node) throws Exception { + + ReplicationOperationDef[] defs = node.getOperations(); + + if (defs.length == 0) { + log.info("skip node " + node + " - no operation detected."); + return; + } + + // destination transaction will be opened at the last moment to avoid + // to open it if something was wrong before... + TopiaContextImplementor dstCtxt = null; + + // open transaction on source db (to obtain entities to treate). + // this transaction must stay open the time of all operations of the + // node in order to make lazy association loaded by hibernate + TopiaContextImplementor srcCtxt = replicationContext.newSourceTx(); + + try { + log.info("start replication for " + node + " : " + defs.length + + " operation(s)"); + + // get from source db the entities to treate for this node + List<? extends TopiaEntity> entities = + replicationContext.getEntities(srcCtxt, node); + + if (log.isInfoEnabled()) { + log.info("will replicate on " + entities.size() + + " entity(ies)"); + } + if (log.isDebugEnabled()) { + for (TopiaEntity entity : entities) { + log.debug(entity.getTopiaId()); + } + } + + // open transaction on target db (the transaction is common to all + // operations of the node). Each operation must do his own commit + // if needed + dstCtxt = replicationContext.newTargetTx(); + + for (ReplicationOperationDef def : defs) { + + log.info("start operation " + def); + + TopiaReplicationOperation operation = + replicationContext.getOperation(def); + + if (!node.equals(def.getNode())) { + + // when a node operation is reattached to a later node... + entities = + replicationContext.getEntities(srcCtxt, def.getNode()); + } + + operation.run(replicationContext, + def, + srcCtxt, + dstCtxt, + entities + ); + } + + } finally { + + try { + // on rollback le context source (car on a peut-etre modifie + // des associations ou des dependances mais on ne veut rien + // retenir au niveau d'hibernate, sinon on s'expose a des erreurs + // lorsque l'on veut recharger des objets dans le context...) + TopiaReplicationContext.close(srcCtxt, true); + } finally { + if (dstCtxt != null) { + // on ferme la transaction sur la base destination + // on rollback toujours la transaction, ainsi si une erreur + // est survenue la session reste dans un état cohérent. + // De plus si les actions n'ont pas fait de commit, la + // session reste aussi dans un état incohérent. Le commit + // reste toujours à la charge de l'opération + TopiaReplicationContext.close(dstCtxt, true); + } + } + } + } + + public void doRollbackNode(TopiaReplicationContext replicationContext, + ReplicationNode node) throws Exception { + + // recuperation des operations reversibles + ReplicationOperationDef[] defs = node.getUndoableOperations(); + + if (defs.length == 0) { + + // pas d'operation reversible, donc rien a faire sur ce noeud. + log.info("skip node " + node + " - no reversible operation detected."); + return; + } + + log.info("start rollback for " + node + " : " + defs.length + + " reversible operation(s)"); + + // open transaction on target db + TopiaContextImplementor dstCtxt = replicationContext.newTargetTx(); + + try { + + for (ReplicationOperationDef def : defs) { + + log.info("start rollback operation " + def); + + TopiaReplicationOperationUndoable operation = + replicationContext.getUndoableOperation(def); + operation.rollback(def, replicationContext, dstCtxt); + } + } finally { + if (dstCtxt != null) { + // on ferme la transaction sur la base destination + // on rollback toujours la transaction, ainsi si une erreur + // est survenue la session reste dans un état cohérent. + // De plus si les actions n'ont pas fait de commit, la + // session reste aussi dans un état incohérent. Le commit + // reste toujours à la charge de l'opération + TopiaReplicationContext.close(dstCtxt, true); + } + } + } + +} diff --git a/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationLink.java b/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationLink.java new file mode 100644 index 0000000..1d26336 --- /dev/null +++ b/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationLink.java @@ -0,0 +1,116 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication.model; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * Pour definir un lien entre deux entites (deux noeuds de replication). + * + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + */ +public class ReplicationLink { + + /** le noeud source du lien */ + protected final ReplicationNode source; + + /** le noeud destination du lien */ + protected final ReplicationNode target; + + /** + * liste des noeuds requis. + * <p/> + * TODO tchemit 2010-08-14 Expliquer à quoi ça sert vraiment... + */ + protected final Set<ReplicationNode> requires; + + /** nom du lien */ + protected final String name; + + /** drapeau positionné à {@code true} lorsque le lien est une association. */ + protected final boolean association; + + public ReplicationLink(ReplicationNode source, + ReplicationNode target, + String name, + boolean association) { + this.source = source; + this.target = target; + this.name = name; + this.association = association; + Set<ReplicationNode> tmpSet = new HashSet<ReplicationNode>(); + tmpSet.add(source); + tmpSet.addAll(source.getAssociations().values()); + tmpSet.addAll(source.getDependencies().values()); + tmpSet.remove(target); + requires = Collections.unmodifiableSet(tmpSet); + } + + public String getName() { + return name; + } + + public ReplicationNode getSource() { + return source; + } + + public ReplicationNode getTarget() { + return target; + } + + public boolean isAssociation() { + return association; + } + + /** + * Teste si on peut reattacher le lien en connaissant l'univers des + * noeuds disponibles. + * <p/> + * On teste si toutes les pre-requis sont disponibles. + * <p/> + * Si oui, on peut reattacher. + * + * @param universe l'univers des noeuds disponibles + * @param currentNode le noeud qui vient d'etre replique + * @return {@code true} si on peut reattacher ce lien + */ + public boolean canReattach(Set<ReplicationNode> universe, + ReplicationNode currentNode) { + boolean result = universe.containsAll(requires); + if (result) { + result = currentNode.equals(target) || universe.contains(target); + } + return result; + } + + @Override + public String toString() { + return "<source:" + source + ", target:" + target + ", name:" + name + + ", association:" + association + ">"; + } +} diff --git a/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationModel.java b/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationModel.java new file mode 100644 index 0000000..e4e7a34 --- /dev/null +++ b/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationModel.java @@ -0,0 +1,669 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication.model; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.util.EntityOperator; +import org.nuiton.topia.persistence.util.TopiaEntityHelper; +import org.nuiton.topia.persistence.util.TopiaEntityIdsMap; +import org.nuiton.topia.replication.TopiaReplicationOperation; +import org.nuiton.topia.replication.operation.AttachLink; +import org.nuiton.topia.replication.operation.DettachAssociation; +import org.nuiton.topia.replication.operation.Duplicate; +import org.nuiton.topia.replication.operation.LoadLink; + +import java.util.ArrayList; +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.Map.Entry; +import java.util.Set; + +/** + * Model of replication. + * <p/> + * the object contains the required {@link #nodes} to replicate : + * <ul> + * <li>all entities for the given {@code nodes} if flag {@link #replicateAll} is + * setted to {@code true}</li> + * <li>the entities given by the field {@link #topiaIds}</li> + * </ul> + * + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + */ +public class ReplicationModel { + + /** Logger */ + private static final Log log = LogFactory.getLog(ReplicationModel.class); + + /** l'ensemble des contrats d'entites a repliquer */ + protected final TopiaEntityEnum[] contracts; + + /** les ids des entites a repliquer (non utilise en mode replicateAll) */ + protected final String[] topiaIds; + + /** le dictionnaire des noeuds a repliquer associes a leur type */ + protected final Map<TopiaEntityEnum, ReplicationNode> nodes; + + /** la liste des noeuds a repliquer (dans l'ordre de replication) */ + protected final List<ReplicationNode> order; + + /** + * un drapeau pour savoir si on effectue une replication de toutes les + * donnees des contrats. + */ + protected final boolean replicateAll; + + public ReplicationModel(TopiaEntityEnum[] contracts, + Set<Class<? extends TopiaEntity>> types, + String... topiaIds) { + this.contracts = contracts; + this.topiaIds = topiaIds; + replicateAll = false; + order = new ArrayList<ReplicationNode>(); + Map<TopiaEntityEnum, ReplicationNode> tmpNodes = + new HashMap<TopiaEntityEnum, ReplicationNode>(); + for (Class<? extends TopiaEntity> k : types) { + TopiaEntityEnum e = getContract(k); + ReplicationNode replicationNode = new ReplicationNode(e); + tmpNodes.put(e, replicationNode); + } + nodes = Collections.unmodifiableMap(tmpNodes); + } + + public ReplicationModel(TopiaEntityEnum[] contracts, + boolean replicateAll, + String... topiaIds) { + this.contracts = contracts; + this.topiaIds = topiaIds; + this.replicateAll = replicateAll; + order = new ArrayList<ReplicationNode>(); + Map<TopiaEntityEnum, ReplicationNode> tmpNodes = + new HashMap<TopiaEntityEnum, ReplicationNode>(); + for (TopiaEntityEnum e : contracts) { + ReplicationNode replicationNode = new ReplicationNode(e); + tmpNodes.put(e, replicationNode); + if (!replicateAll) { + // mode restreint : l'ordre est induit par l'ordre sur les + // contrats passes + order.add(replicationNode); + } + } + nodes = Collections.unmodifiableMap(tmpNodes); + } + + @SuppressWarnings({"unchecked"}) + public TopiaEntityEnum getContract(Class<?> type) { + TopiaEntityEnum e = null; + if (TopiaEntity.class.isAssignableFrom(type)) { + e = TopiaEntityHelper.getEntityEnum( + (Class<? extends TopiaEntity>) type, contracts); + } + return e; + } + + public Collection<ReplicationNode> getNodes() { + return nodes.values(); + } + + public Set<Class<? extends TopiaEntity>> getTypes() { + Set<Class<? extends TopiaEntity>> result = + new HashSet<Class<? extends TopiaEntity>>(); + for (TopiaEntityEnum e : nodes.keySet()) { + result.add(e.getContract()); + } + return result; + } + + public ReplicationNode getNode(TopiaEntityEnum contract) { + return nodes.get(contract); + } + + public void addDependency(List<ReplicationNode> nodes) { + if (log.isDebugEnabled()) { + log.debug("Try to add nodes : " + nodes + " in universe : " + order); + } + for (ReplicationNode node : nodes) { + if (order.contains(node)) { + // can not be done + throw new IllegalStateException( + "Node " + node + " is already registred : " + order); + } + order.add(node); + } + } + + public TopiaEntityEnum[] getContracts() { + return contracts; + } + + public String[] getTopiaIds() { + return topiaIds; + } + + public List<ReplicationNode> getOrder() { + return order; + } + + public boolean isReplicateAll() { + return replicateAll; + } + + @SuppressWarnings("unchecked") + public ReplicationNode getNode(String propertyName, Class<?> propertyType) { + if (TopiaEntity.class.isAssignableFrom(propertyType)) { + Class<? extends TopiaEntity> t = + (Class<? extends TopiaEntity>) propertyType; + TopiaEntityEnum e = getContract(t); + if (nodes.containsKey(e)) { + ReplicationNode dep = getNode(e); + return dep; + } + } + return null; + } + + public void detectAssociations(TopiaEntityEnum... filter) throws TopiaException { + for (TopiaEntityEnum type : nodes.keySet()) { + ReplicationNode node = getNode(type); + EntityOperator<? super TopiaEntity> operator = node.getOperator(); + List<String> associationProperties = + operator.getAssociationProperties(); + if (!associationProperties.isEmpty()) { + + for (String p : associationProperties) { + ReplicationNode dep = getNode( + p, + operator.getAssociationPropertyType(p) + ); + if (dep != null) { + if (log.isDebugEnabled()) { + log.debug("from type - " + + type.getContract().getSimpleName() + + " [" + p + ":" + dep + "]"); + } + node.addAssociation(p, dep); + } + } + } + } + } + + public void detectDirectDependencies() throws TopiaException { + for (TopiaEntityEnum type : nodes.keySet()) { + ReplicationNode node = getNode(type); + EntityOperator<? super TopiaEntity> operator = node.getOperator(); + List<String> properties = operator.getProperties(); + if (!properties.isEmpty()) { + for (String p : properties) { + ReplicationNode dep = + getNode(p, operator.getPropertyType(p)); + if (dep != null) { + if (log.isDebugEnabled()) { + log.debug("from type - " + + type.getContract().getSimpleName() + + " [" + p + ":" + dep + "]"); + } + node.addDependency(p, dep); + } + } + } + } + } + + public void detectDependencies() throws TopiaException { + Set<ReplicationNode> toResolved = + new HashSet<ReplicationNode>(nodes.values()); + Set<ReplicationNode> resolved = new HashSet<ReplicationNode>(); + List<Set<ReplicationNode>> levels = + new ArrayList<Set<ReplicationNode>>(); + + // premiere passe pour detecter les niveaux de replications + // on ne regarde que les dependences directes et pas les associations + // si A -> B alors B doit etre dans un niveau inferieure (i.e replique + // avant). + //TODO Il faut pouvoir gerer les cycles (pour cela on doit avoir + //TODO un dictionnaire pour les compositions qui peuvent etre nulle + //TODO ainsi on doit etre capable de ne pas tenir compte d'une composition + //TODO si c'est nullable, sinon cela veut dire obligatoirement + //TODO que B doit etre replique avant A... + while (!toResolved.isEmpty()) { + Set<ReplicationNode> level = new HashSet<ReplicationNode>(); + for (ReplicationNode node : toResolved) { + if (node.hasDependency()) { + for (ReplicationNode n : node.getDependencies().values()) { + if (!resolved.contains(n)) { + level.add(n); + } + } + } + } + Set<ReplicationNode> safeLevel = new HashSet<ReplicationNode>(); + if (level.isEmpty()) { + safeLevel.addAll(toResolved); + } else { + // des depedences trouvees + for (ReplicationNode n : level) { + //TODO il faut verifier que le type n'est pas une dependence de level + safeLevel.add(n); + } + if (safeLevel.isEmpty()) { + // on a detecte un cycle sur les dependences, on ne peut rien faire pour le moment + throw new IllegalStateException( + "un cycle dans les dependences a été détecté, " + + "l\'algorithme necessite plus de donnes... " + + "\n niveau courant : " + level + "\n resolus : " + + getOrder()); + } + } + if (log.isDebugEnabled()) { + log.debug("level [" + levels.size() + "] resolved : " + + safeLevel); + } + toResolved.removeAll(safeLevel); + resolved.addAll(safeLevel); + levels.add(safeLevel); + level.clear(); + } + + // seconde passe : on recherche le meilleur ordre possible pour + // les types de chaque niveau + // on se base desormais que sur les associations + // si A -*> B alors on va essayer de repliquer B avant A + // si on detecte un cycle alors on ne peut pas imposer un ordre optimal + // et on devra dettacher B de A pendant la replication puis reattacher B + // a A apres replication de A et B + // l'ordre optimal permet de ne pas effectuer + // calcul des ordres de replications pour les ensembles de chaque niveau + // pour trouver le bon ordre, on travaille sur les couvertures + // des noeuds. + HashSet<ReplicationNode> done = new HashSet<ReplicationNode>(); + for (Set<ReplicationNode> level : levels) { + detectDependenciesOrder(level, done); + } + } + + public void detectDependenciesOrder(Set<ReplicationNode> safeLevel, + Set<ReplicationNode> doned) { + if (log.isDebugEnabled()) { + log.debug("will detect " + safeLevel); + } + Map<ReplicationNode, Set<ReplicationNode>> dico = + new HashMap<ReplicationNode, Set<ReplicationNode>>(); + for (ReplicationNode n : safeLevel) { + Set<ReplicationNode> shell = + new HashSet<ReplicationNode>(n.getShell()); + shell.retainAll(safeLevel); + if (log.isDebugEnabled()) { + log.debug("shell to use for " + n + " : " + shell); + } + dico.put(n, shell); + } + List<Set<ReplicationNode>> levels = + new ArrayList<Set<ReplicationNode>>(); + while (!dico.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("level [" + levels.size() + "] on " + safeLevel); + for (Entry<ReplicationNode, Set<ReplicationNode>> entry : + dico.entrySet()) { + log.debug("node " + entry.getKey() + " : " + + entry.getValue()); + } + } + + // detection des noeud libres + Set<ReplicationNode> free = new HashSet<ReplicationNode>(); + for (Entry<ReplicationNode, Set<ReplicationNode>> e : + dico.entrySet()) { + if (e.getValue().isEmpty()) { + free.add(e.getKey()); + } + } + + if (free.isEmpty()) { + // un cycle a ete detectee + // on ne peut plus rien predire pour cet ensemble + if (log.isWarnEnabled()) { + log.warn("level [" + levels.size() + + "] cycle detecte : " + dico.keySet()); + } + throw new IllegalStateException( + "un cycle n'a pas pu etre resoud entre l'ensemble " + + dico.keySet()); + } + + log.info("there is some free node(s) to resolve : " + free); + for (Entry<ReplicationNode, Set<ReplicationNode>> e : + dico.entrySet()) { + Set<ReplicationNode> list = e.getValue(); + list.removeAll(free); + } + for (ReplicationNode n : free) { + dico.remove(n); + } + if (log.isDebugEnabled()) { + log.debug("level [" + levels.size() + "] resolved : " + free); + } + levels.add(free); + doned.addAll(free); + + if (dico.isEmpty()) { + // ordre optimal trouve + break; + } + } + for (Set<ReplicationNode> nodesForLevel : levels) { + addDependency(new ArrayList<ReplicationNode>(nodesForLevel)); + } + dico.clear(); + levels.clear(); + } + + public void detectObjectsToDettach() { + Set<ReplicationNode> universe = new HashSet<ReplicationNode>(); + for (ReplicationNode node : getOrder()) { + // on detecte si le node a des associations + // sortants de l'universe deja replique + // si oui, alors on marque l'association pour un dettachement + if (node.hasAssociation()) { + for (Entry<String, ReplicationNode> e : + node.getAssociations().entrySet()) { + ReplicationNode nodeDst = e.getValue(); + if (!universe.contains(nodeDst)) { + if (log.isDebugEnabled()) { + log.debug("association to dettach " + e.getKey() + + " for " + node); + } + // association sortant + node.addAssociationToDettach(e.getKey()); + } + } + } + // on fait de meme pour les dependences directes + //TODO la resolution des conflits sur dependences n'est pas encore + //TODO en place + if (node.hasDependency()) { + for (Entry<String, ReplicationNode> e : + node.getDependencies().entrySet()) { + ReplicationNode nodeDst = e.getValue(); + if (!universe.contains(nodeDst)) { + if (log.isDebugEnabled()) { + log.debug("dependency to dettach " + e.getKey() + + " for " + node); + } + // association sortant + node.addDependencyToDettach(e.getKey()); + } + } + } + universe.add(node); + } + } + + public void detectOperations() { + Set<ReplicationNode> universe = new HashSet<ReplicationNode>(); + Set<ReplicationLink> links = new HashSet<ReplicationLink>(); + Set<ReplicationLink> linksToLoad = new HashSet<ReplicationLink>(); + + // premiere passe pour recuperer toutes les associations + + for (ReplicationNode node : order) { + if (node.hasAssociation()) { + for (Entry<String, ReplicationNode> entry : + node.getAssociations().entrySet()) { + String name = entry.getKey(); + // dans tous les cas, on ajoute un link d'association a reattacher + ReplicationNode target = node.getAssociations().get(name); + ReplicationLink link = + new ReplicationLink(node, target, name, true); + if (nodes.containsValue(target)) { + // on a trouve une association que l'on doit gerer + links.add(link); + if (log.isDebugEnabled()) { + log.debug("link to treate : " + link); + } + } else { + } + } + List<String> associationProperties = + node.getOperator().getAssociationProperties(); + for (String name : associationProperties) { + Class<?> associationPropertyType = node.getOperator().getAssociationPropertyType(name); + TopiaEntityEnum contract = getContract(associationPropertyType); + if (contract == null || !nodes.containsKey(contract)) { +// if (!TopiaEntity.class.isAssignableFrom(associationPropertyType) || +// !nodes.containsKey(associationPropertyType)) { + + ReplicationLink link = + new ReplicationLink(node, null, name, true); + + linksToLoad.add(link); + if (log.isDebugEnabled()) { + log.debug("link to load before replication : " + + link); + } + } + } + } + } + + // deuxieme passe pour detecter les operations a realiser + for (ReplicationNode node : order) { + log.debug("------------------------------- for node " + node); + // on detecte si le node a des associations + // sortants de l'universe deja replique + // si oui, alors on marque l'association pour un dettachement + if (node.hasAssociationsToDettach()) { + Set<String> names = node.getAssociationsToDettach(); + // operations de dettachement d'association + for (String name : names) { + addPreOperation(node, node, DettachAssociation.class, name); + } + } + Set<ReplicationLink> tmpLinks = new HashSet<ReplicationLink>(); + + // recherche des associations a charger avant replication + + for (ReplicationLink link : linksToLoad) { + if (node.equals(link.getSource())) { + tmpLinks.add(link); + } + } + if (!tmpLinks.isEmpty()) { + // on a des associations a charger avant replication + for (ReplicationLink link : tmpLinks) { +// addPreOperation(node, node, LoadLink.class, link); + addPreOperation(node, link.getSource(), LoadLink.class, link); + } + linksToLoad.removeAll(links); + tmpLinks.clear(); + } + + // operation de duplication + addDuplicateOperation(node, node, Duplicate.class); + + universe.add(node); + + // operations de reattachement + for (ReplicationLink link : links) { + if (link.canReattach(universe, node)) { + // lien reattachable + tmpLinks.add(link); + } + } + if (!tmpLinks.isEmpty()) { + // on a trouve des liens a reattacher + for (ReplicationLink link : tmpLinks) { +// addPostOperation(node, node, AttachLink.class, link); + addPostOperation(node, link.getTarget(), AttachLink.class, link); + } + // ces liens ne sont plus a traiter + links.removeAll(tmpLinks); + } + } + } + + // List<ReplicationOperationDef> realOperations = new ArrayList<ReplicationOperationDef>(); + public void adjustOperations(TopiaEntityIdsMap data) { + for (TopiaEntityEnum e : getContracts()) { + List<String> ids = data.get(e.getContract()); + ReplicationNode node = getNode(e); + if (node == null) { + // le noeud n'est pas connu (ce n'est pas normal!) + continue; + } + List<ReplicationOperationDef> realOperations = + new ArrayList<ReplicationOperationDef>(); + if (CollectionUtils.isEmpty(ids)) { + + if (log.isInfoEnabled()) { + log.info("skip operations on node " + node + + " (no data associated)"); + } + + // must only keep operation that are not directly linked with the node + // at the moment only attachLink is in that case + ReplicationOperationDef[] operations = node.getOperations(); + for (ReplicationOperationDef op : operations) { + + if (!node.equals(op.getNode())) { + + // keep this operation + realOperations.add(op); + if (log.isInfoEnabled()) { + log.info(" keep " + op); + } + } else { + if (log.isInfoEnabled()) { + log.info(" skip " + op); + } + } + } + node.setOperations(realOperations); + } + + // always sort operations on the phase + node.sortOperations(); + +// if (CollectionUtils.isEmpty(ids)) { +// ReplicationOperationDef[] operations = node.getOperations(); +// log.info("skip operations on node " + node + +// " (no data associated)"); +// for (ReplicationOperationDef op : operations) { +// log.info(" skip " + op); +// } +// node.clearOperations(); +// } else { +// node.sortOperations(); +// } + } + } + + public void detectShell() { + for (ReplicationNode n : nodes.values()) { + Set<ReplicationNode> shell = new HashSet<ReplicationNode>(); + getShell(n, shell); + shell.remove(n); + n.setShell(shell); + } + } + + protected void getShell(ReplicationNode node, + Set<ReplicationNode> explored) { + if (!explored.contains(node)) { + explored.add(node); + } + if (node.hasAssociation()) { + for (ReplicationNode n : node.getAssociations().values()) { + if (!explored.contains(n)) { + getShell(n, explored); + } + } + } + if (node.hasDependency()) { + for (ReplicationNode n : node.getDependencies().values()) { + if (!explored.contains(n)) { + getShell(n, explored); + } + } + } + } + + protected void addPreOperation(ReplicationNode ownerNode, + ReplicationNode node, + Class<? extends TopiaReplicationOperation> operationClass, + Object... params) { + addOperation(ownerNode, + node, + ReplicationOperationPhase.before, + operationClass, + params + ); + } + + protected void addDuplicateOperation(ReplicationNode ownerNode, + ReplicationNode node, + Class<? extends TopiaReplicationOperation> operationClass, + Object... params) { + addOperation(ownerNode, + node, + ReplicationOperationPhase.duplicate, + operationClass, + params + ); + } + + protected void addPostOperation(ReplicationNode ownerNode, + ReplicationNode node, + Class<? extends TopiaReplicationOperation> operationClass, + Object... params) { + addOperation(ownerNode, + node, + ReplicationOperationPhase.after, + operationClass, + params + ); + } + + protected void addOperation(ReplicationNode ownerNode, + ReplicationNode node, + ReplicationOperationPhase phase, + Class<? extends TopiaReplicationOperation> operationClass, + Object... params) { + + ReplicationOperationDef op; + op = new ReplicationOperationDef(phase, operationClass, node, params); + ownerNode.addOperation(op); + } +} diff --git a/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationNode.java b/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationNode.java new file mode 100644 index 0000000..94a67a4 --- /dev/null +++ b/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationNode.java @@ -0,0 +1,267 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication.model; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.util.EntityOperator; +import org.nuiton.topia.persistence.util.EntityOperatorStore; +import org.nuiton.topia.replication.TopiaReplicationOperationUndoable; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Model of a replication's node. + * <p/> + * The invariant of a replication's node is his {@link #contract}, means the + * type of entity to replicate. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + */ +public class ReplicationNode { + + /** Logger */ + private static final Log log = LogFactory.getLog(ReplicationNode.class); + + /** contract of entity of the node. */ + protected final TopiaEntityEnum contract; + + /** entity operator. */ + protected final EntityOperator<? super TopiaEntity> operator; + + /** shell of the node. */ + protected Set<ReplicationNode> shell; + + /** + * dictionnary of associations defined on the node (keys are association + * name, and values are target node). + */ + protected final Map<String, ReplicationNode> associations; + + /** names of association to dettach while replication. */ + protected final Set<String> associationsToDettach; + + /** + * compositions defined on the node (keys are association name, + * and values are target node). + */ + protected final Map<String, ReplicationNode> dependencies; + + /** names of dependency to dettach while replication. */ + protected final Set<String> dependenciesToDettach; + + /** operations to fire when replication pass on this node. */ + protected final List<ReplicationOperationDef> operations; + + public ReplicationNode(TopiaEntityEnum contract) { + this.contract = contract; + operator = EntityOperatorStore.<TopiaEntity>getOperator(contract); + associations = new HashMap<String, ReplicationNode>(); + dependencies = new HashMap<String, ReplicationNode>(); + shell = new HashSet<ReplicationNode>(); + associationsToDettach = new HashSet<String>(); + dependenciesToDettach = new HashSet<String>(); + operations = new ArrayList<ReplicationOperationDef>(); + if (log.isTraceEnabled()) { + log.trace("new node : " + this); + } + } + + public void addAssociation(String name, ReplicationNode node) { + associations.put(name, node); + } + + public void addOperation(int index, ReplicationOperationDef op) { + operations.add(index, op); + if (log.isDebugEnabled()) { + log.debug(op + " to node " + this); + } + } + + public void addOperation(ReplicationOperationDef op) { + operations.add(op); + if (log.isDebugEnabled()) { + log.debug(op + " to node " + this); + } + } + + public void setOperations(List<ReplicationOperationDef> operations) { + clearOperations(); + this.operations.addAll(operations); + } + + public ReplicationOperationDef[] getOperations() { + return operations.toArray( + new ReplicationOperationDef[operations.size()]); + } + + public ReplicationOperationDef[] getUndoableOperations() { + List<ReplicationOperationDef> result = + new ArrayList<ReplicationOperationDef>(); + for (ReplicationOperationDef operation : operations) { + if (TopiaReplicationOperationUndoable.class.isAssignableFrom( + operation.getOperationClass())) { + result.add(operation); + } + } + return result.toArray(new ReplicationOperationDef[result.size()]); + } + + public boolean hasAssociation() { + return !associations.isEmpty(); + } + + public boolean hasAssociationsToDettach() { + return !associationsToDettach.isEmpty(); + } + + public String[] getAssociationsDettached(ReplicationNode node) { + Set<String> result = new HashSet<String>(); + for (String name : associationsToDettach) { + ReplicationNode get = associations.get(name); + if (node.equals(get)) { + result.add(name); + } + } + return result.toArray(new String[result.size()]); + } + + public String[] getDependenciesDettached(ReplicationNode node) { + Set<String> result = new HashSet<String>(); + for (String name : dependenciesToDettach) { + ReplicationNode get = dependencies.get(name); + if (node.equals(get)) { + result.add(name); + } + } + return result.toArray(new String[result.size()]); + } + + public boolean hasDependenciesToDettach() { + return !dependenciesToDettach.isEmpty(); + } + + public boolean hasDependency() { + return !dependencies.isEmpty(); + } + + public void addDependency(String name, ReplicationNode node) { + dependencies.put(name, node); + } + + public void addAssociationToDettach(String key) { + associationsToDettach.add(key); + } + + public void addDependencyToDettach(String key) { + dependenciesToDettach.add(key); + } + + public Map<String, ReplicationNode> getAssociations() { + return associations; + } + + public Set<String> getAssociationsToDettach() { + return associationsToDettach; + } + + public Set<String> getDependenciesToDettach() { + return dependenciesToDettach; + } + + public TopiaEntityEnum getContract() { + return contract; + } + + public Class<? extends TopiaEntity> getEntityType() { + return contract.getContract(); + } + + public EntityOperator<? super TopiaEntity> getOperator() { + return operator; + } + + public Map<String, ReplicationNode> getDependencies() { + return dependencies; + } + + public Set<ReplicationNode> getShell() { + return shell; + } + + public void setShell(Set<ReplicationNode> shell) { + this.shell = shell; + } + + /** + * sort operation by their phase. + * + * @see ReplicationOperationPhase + */ + public void sortOperations() { + Collections.sort(operations); + } + + /** + * Remove all operation of the node (for example when no data is associated + * with the type of the node, then no needed operations). + */ + public void clearOperations() { + operations.clear(); + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + ReplicationNode other = (ReplicationNode) obj; + return contract == other.contract; + } + + @Override + public int hashCode() { + int hash = 7; + hash = 37 * hash + contract.hashCode(); + return hash; + } + + @Override + public String toString() { + return contract.toString(); + } +} diff --git a/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationOperationDef.java b/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationOperationDef.java new file mode 100644 index 0000000..c3f991c --- /dev/null +++ b/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationOperationDef.java @@ -0,0 +1,131 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication.model; + +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.replication.TopiaReplicationOperation; + +import java.util.Arrays; + +/** + * Definition of a concrete operation to execute. + * <p/> + * A such operation involves : + * <ul> + * <li>the replication node</li> + * <li>the replication phase</li> + * <li>the replication operation type</li> + * <li>the replication operation arguments</li> + * </ul> + * <p/> + * This definition is detected when building replication model. + * <p/> + * Then when starting replication, based on this definition, we can instanciate + * the operation to execute. + * <p/> + * <b>Note:</b> Such objects can be comparable via their {@link #phase}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + */ +public class ReplicationOperationDef implements Comparable<ReplicationOperationDef> { + + protected final ReplicationOperationPhase phase; + + protected final Class<? extends TopiaReplicationOperation> operationClass; + + protected final ReplicationNode node; + + protected final Object[] parameters; + + public ReplicationOperationDef( + ReplicationOperationPhase phase, + Class<? extends TopiaReplicationOperation> operation, + ReplicationNode node, + Object... parameters) { + operationClass = operation; + this.phase = phase; + this.node = node; + this.parameters = parameters; + } + + public ReplicationNode getNode() { + return node; + } + + public Object[] getParameters() { + return parameters; + } + + public Class<? extends TopiaReplicationOperation> getOperationClass() { + return operationClass; + } + + public ReplicationOperationPhase getPhase() { + return phase; + } + + public TopiaEntityEnum getContract() { + return node.getContract(); + } + + public Class<? extends TopiaEntity> getEntityType() { + return node.getEntityType(); + } + + @Override + public String toString() { + return " <" + operationClass.getSimpleName() + " on " + node + + (parameters.length == 0 ? "" : + ", params:" + Arrays.toString(parameters)) + ">"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + ReplicationOperationDef that = (ReplicationOperationDef) o; + + if (!node.equals(that.node)) return false; + if (!operationClass.equals(that.operationClass)) return false; + return phase == that.phase; + } + + @Override + public int hashCode() { + int result = phase.hashCode(); + result = 31 * result + operationClass.hashCode(); + result = 31 * result + node.hashCode(); + return result; + } + + @Override + public int compareTo(ReplicationOperationDef o) { + int result = getPhase().compareTo(o.getPhase()); + return result; + } +} diff --git a/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationOperationPhase.java b/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationOperationPhase.java new file mode 100644 index 0000000..de869dd --- /dev/null +++ b/topia-service-replication/src/main/java/org/nuiton/topia/replication/model/ReplicationOperationPhase.java @@ -0,0 +1,49 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication.model; + +import org.nuiton.topia.replication.operation.Duplicate; + +/** + * Une enumeration pour definir quand appliquer une operation. + * <p/> + * L'ordre induit par cette enumeration sera utilisé pour trier les operations + * a realiser sur chaque noeud de replication. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + */ +public enum ReplicationOperationPhase { + + /** a appliquer avant la duplicate d'un noeud */ + before, + /** + * pour dupliquer un noeud (cette phase ne doit etre utilise que sur l'operation + * de type {@link Duplicate} + */ + duplicate, + /** a appliquer apres avoir duplique le noeud */ + after +} diff --git a/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/AttachAssociation.java b/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/AttachAssociation.java new file mode 100644 index 0000000..41f0265 --- /dev/null +++ b/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/AttachAssociation.java @@ -0,0 +1,267 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication.operation; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.util.EntityOperator; +import org.nuiton.topia.persistence.util.TopiaEntityHelper; +import org.nuiton.topia.replication.TopiaReplicationContext; +import org.nuiton.topia.replication.TopiaReplicationOperation; +import org.nuiton.topia.replication.model.ReplicationModel; +import org.nuiton.topia.replication.model.ReplicationNode; +import org.nuiton.topia.replication.model.ReplicationOperationDef; +import org.nuiton.topia.replication.model.ReplicationOperationPhase; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static org.nuiton.i18n.I18n.t; + +/** + * Pour attacher une association. + * <p/> + * L'opération requière 2 ou 3 paramètres : + * <ul> + * <li>{@code parameters[0]} : le nom de l'association à traiter</li> + * <li>{@code parameters[1]} : un drapeau pour savoir si on est sur le reverse + * de l'association</li> + * <li>{@code parameters[2]} : le noeud source de l'association + * (uniquement utilisé si on est sur le reverse d'une association)</li> + * </ul> + * <p/> + * Deux cas peuvent se produire : + * <p/> + * - le noeud de l'operation est la source de l'association, dans ce cas la + * {@code entities} contient les entites sources de l'association et on + * retrouve les entites associes a partir du type de l'association + * <p/> + * Ce premier cas est verifie quand {@code reverse} (le second parametre) est + * à {@code false}. + * <p/> + * - le noeud de l'operation est la cible de l'association, dans ce cas la + * {@code entities} contient les entities associées (cibles) de + * l'assocation et on retrouve les entities a partir d'un troisieme parametre + * qui donne le node source de l'association. + * <p/> + * Note : cette operation est interne, et n'est pas creable par l'utilisateur + * via la methode {@link #register(ReplicationModel, ReplicationNode, + * ReplicationOperationPhase, Object...)}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + * @deprecated since 2.5.2, this operation will be removed in version 2.6 and + * will not be replaced : prefer use the {@link AttachLink} instead. + */ +@Deprecated +public class AttachAssociation implements TopiaReplicationOperation { + + /** Logger */ + private static final Log log = LogFactory.getLog(AttachAssociation.class); + + @Override + public void register(ReplicationModel model, + ReplicationNode ownerNode, + ReplicationOperationPhase phase, + Object... parameters) { + +// throw new UnsupportedOperationException( +// _("topia.replication.error.operation.uncreatable", getClass())); + } + + @Override + public void run(TopiaReplicationContext replicationContext, + ReplicationOperationDef operationDef, + TopiaContextImplementor srcCtxt, + TopiaContextImplementor dstCtxt, + List<? extends TopiaEntity> entities) throws TopiaException { + + String name = (String) operationDef.getParameters()[0]; + Boolean reverse = (Boolean) operationDef.getParameters()[1]; + + if (log.isDebugEnabled()) { + log.debug("currentNode : " + operationDef.getNode() + + ", association name : " + name + " reverse ? " + reverse); + } + + ReplicationNode ownerNode; + ReplicationNode cibleNode; + + EntityOperator<? super TopiaEntity> ownerOperator; + + // contient la liste des ids d'association autorisees ici + List<String> associationsId; + + // contient la liste des ids des entite source de l'association + List<String> ownerIds; + if (reverse) { + + // on est en mode reverse, i.e : + // - le noeud courant est la cible + // - le noeud cible est passe en parametre + // - nodeEntities contient les entities sources + + ownerNode = (ReplicationNode) operationDef.getParameters()[2]; + ownerOperator = ownerNode.getOperator(); + + cibleNode = operationDef.getNode(); + + ownerIds = TopiaEntityHelper.getTopiaIdList(entities); + associationsId = replicationContext.getEntityIds(cibleNode); + + } else { + + // on est en mode non reverse, i.e : + // - le noeud courant est la source + // - le noeud cible est deduit de l'association sur le noeud source + // - nodeEntities contient les entities cibles + + ownerNode = operationDef.getNode(); + ownerOperator = ownerNode.getOperator(); + + cibleNode = ownerNode.getAssociations().get(name); + + ownerIds = replicationContext.getEntityIds(cibleNode); + associationsId = TopiaEntityHelper.getTopiaIdList(entities); + } + + if (ownerIds == null || ownerIds.isEmpty()) { + // pas de donnees a traiter + log.info(t("topia.replication.attachAssociation.nothing.to.do", + ownerOperator)); + return; + } + + // contient la liste des entites sources de l'association + List<? extends TopiaEntity> ownerEntities; + + // on recharge obligatoirement les donnees sources car elles ont pu etre + // modifiees (dettachement d'association ou autres) + // ils nous faut les entites telles qu'elles sont en base source + + ownerEntities = TopiaEntityHelper.getEntitiesList( + srcCtxt, + ownerIds.toArray(new String[ownerIds.size()]) + ); + + boolean shouldCommit = false; + + if (log.isInfoEnabled()) { + log.info("ownerNode : " + ownerNode + " , targetNode : " + + cibleNode + ", association : " + name); + } + if (log.isDebugEnabled()) { + log.debug("owner ids : " + ownerIds); + log.debug("association ids : " + associationsId); + } + + for (TopiaEntity src : ownerEntities) { + + // les association cibles connues pour l'entite sur la base source + Collection<?> targetEntities = + (Collection<?>) ownerOperator.get(name, src); + + if (targetEntities == null || targetEntities.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("no association '" + name + "' attached to " + + src); + } + // pas de donnees dans l'association + continue; + } + if (log.isDebugEnabled()) { + log.debug("will try to attach " + targetEntities.size() + + " association(s) '" + name + "' to " + src); + } + + // l'entite repliquee a laquelle on veut attacher l'association + + TopiaEntity dst = dstCtxt.findByTopiaId(src.getTopiaId()); + + // les association cibles connues pour l'entite sur la base + // destination + Collection<?> dstTargetEntities = (Collection<?>) + ownerOperator.get(name, dst); + + // les ids des entities deja associees + List<String> dstTargetAssociationsId = + dstTargetEntities == null ? + Collections.<String>emptyList() : + TopiaEntityHelper.getTopiaIdList( + (Collection<? extends TopiaEntity>) dstTargetEntities); + + boolean shouldUpdate = false; + for (Object a : targetEntities) { + + TopiaEntity assosiationSrc = (TopiaEntity) a; + + // on verifie que l'association doit etre rattachee + if (associationsId.contains(assosiationSrc.getTopiaId())) { + if (dstTargetAssociationsId.contains( + assosiationSrc.getTopiaId())) { + // deja attache + if (log.isDebugEnabled()) { + log.debug("already attached association '" + name + + "' : " + assosiationSrc); + } + continue; + + } + + // la donnees doit etre attachee + + TopiaEntity assosiationDst = + dstCtxt.findByTopiaId(assosiationSrc.getTopiaId()); + ownerOperator.addChild(name, dst, assosiationDst); + if (log.isDebugEnabled()) { + log.debug("will attach association '" + name + "' : " + + assosiationDst); + } + shouldUpdate = true; + } + + } + + if (shouldUpdate) { + if (log.isTraceEnabled()) { + log.trace("will update " + dst.getTopiaId()); + } + //FIXME: on ne peut pas updater l'objet car l'objet peut rentre + // en conflit dans la session hibernate + // cela fonctionne sans faire d'update (heureusement...) + //dst.update(); + shouldCommit = true; + } + } + + if (shouldCommit) { + dstCtxt.commitTransaction(); + } + } +} diff --git a/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/AttachLink.java b/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/AttachLink.java new file mode 100644 index 0000000..b9a6032 --- /dev/null +++ b/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/AttachLink.java @@ -0,0 +1,244 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication.operation; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.util.EntityOperator; +import org.nuiton.topia.persistence.util.TopiaEntityHelper; +import org.nuiton.topia.replication.TopiaReplicationContext; +import org.nuiton.topia.replication.TopiaReplicationOperation; +import org.nuiton.topia.replication.model.ReplicationLink; +import org.nuiton.topia.replication.model.ReplicationModel; +import org.nuiton.topia.replication.model.ReplicationNode; +import org.nuiton.topia.replication.model.ReplicationOperationDef; +import org.nuiton.topia.replication.model.ReplicationOperationPhase; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import static org.nuiton.i18n.I18n.t; + +/** + * Pour attacher une association. + * <p/> + * Deux cas peuvent se produire : + * <p/> + * - le noeud de l'operation est la source de l'association, dans ce cas la + * <code>nodeEntities</code> contient les entites sources de l'association et on + * retrouve les entites associes a partir du type de l'association + * <p/> + * Ce premier cas est verifie quand reverse (le econd parametre) est a false + * <p/> + * - le noeud de l'operation est la cible de l'association, dans ce cas la + * <code>nodeEntities</code> contient les entities associes (cibles) de + * l'assocation et on retrouve les entities a partir d'un troisieme parametre + * qui donne le node source des entities sources. + * <p/> + * Note : cette operation est interne, et n'est pas creable par l'utilisateur + * via la methode {@link #register(ReplicationModel, ReplicationNode, + * ReplicationOperationPhase, Object...)}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + */ +public class AttachLink implements TopiaReplicationOperation { + + /** Logger */ + private static final Log log = LogFactory.getLog(AttachLink.class); + + @Override + public void register(ReplicationModel model, + ReplicationNode ownerNode, + ReplicationOperationPhase phase, + Object... parameters) { + +// throw new UnsupportedOperationException( +// _("topia.replication.error.operation.uncreatable", getClass())); + } + + @Override + public void run(TopiaReplicationContext replicationContext, + ReplicationOperationDef operationDef, + TopiaContextImplementor srcCtxt, + TopiaContextImplementor dstCtxt, + List<? extends TopiaEntity> nodeEntities + ) throws TopiaException { + + ReplicationLink link = + (ReplicationLink) operationDef.getParameters()[0]; + + String name = link.getName(); + + if (log.isDebugEnabled()) { + log.debug("currentNode : " + operationDef.getNode() + + " , link to attach : " + link); + } + + ReplicationNode ownerNode = link.getSource(); + ReplicationNode cibleNode = link.getTarget(); + + EntityOperator<? super TopiaEntity> ownerOperator = + ownerNode.getOperator(); + + // contient la liste des ids d'association autorisees ici + List<String> associationIds = null; + + // contient la liste des ids des entite source de l'association + List<String> ownerIds = null; + + if (ownerNode.equals(operationDef.getNode())) { + ownerIds = TopiaEntityHelper.getTopiaIdList(nodeEntities); + } + + if (cibleNode.equals(operationDef.getNode())) { + associationIds = TopiaEntityHelper.getTopiaIdList(nodeEntities); + } + + if (ownerIds == null) { + ownerIds = replicationContext.getEntityIds(ownerNode); + } + + if (associationIds == null) { + associationIds = replicationContext.getEntityIds(cibleNode); + } + + if (ownerIds == null || ownerIds.isEmpty()) { + // pas de donnees a traiter + log.info(t("topia.replication.attachAssociation.nothing.to.do", + ownerOperator)); + return; + } + + // on recharge obligatoirement les donnees sources car elles ont pu etre + // modifiees (dettachement d'association ou autres) + // ils nous faut les entites telles qu'elles sont en base source + + // contient la liste des entites sources de l'association + List<? extends TopiaEntity> ownerEntities = + TopiaEntityHelper.getEntitiesList( + srcCtxt, + ownerIds.toArray(new String[ownerIds.size()]) + ); + + boolean shouldCommit = false; + + + if (log.isInfoEnabled()) { + log.info("ownerNode : " + ownerNode + " , targetNode : " + + cibleNode + ", association : " + name); + } + if (log.isDebugEnabled()) { + log.debug("owner ids : " + ownerIds); + log.debug("association ids : " + associationIds); + } + + for (TopiaEntity src : ownerEntities) { + + // les association cibles connues pour l'entite sur la base source + Collection<?> targetEntities = (Collection<?>) + ownerOperator.get(name, src); + + if (targetEntities == null || targetEntities.isEmpty()) { + if (log.isDebugEnabled()) { + log.debug("no association '" + name + "' attached to " + + src); + } + // pas de donnees dans l'association + continue; + } + if (log.isDebugEnabled()) { + log.debug("will try to attach " + targetEntities.size() + + " association(s) '" + name + "' to " + src); + } + + // l'entite repliquee a laquelle on veut attacher l'association + + TopiaEntity dst = dstCtxt.findByTopiaId(src.getTopiaId()); + + // les association cibles connues pour l'entite sur la base + // destination + Collection<?> dstTargetEntities = (Collection<?>) + ownerOperator.get(name, dst); + + // les ids des entities deja associees + List<String> dstTargetAssociationsId = + dstTargetEntities == null ? + Collections.<String>emptyList() : + TopiaEntityHelper.getTopiaIdList( + (Collection<? extends TopiaEntity>) dstTargetEntities); + boolean shouldUpdate = false; + for (Object a : targetEntities) { + + TopiaEntity assosiationSrc = (TopiaEntity) a; + + // on verifie que l'association doit etre rattachee + if (associationIds.contains(assosiationSrc.getTopiaId())) { + if (dstTargetAssociationsId.contains( + assosiationSrc.getTopiaId())) { + // deja attache + if (log.isDebugEnabled()) { + log.debug("already attached association '" + + name + "' : " + assosiationSrc); + } + continue; + + } + + // la donnees doit etre attachee + + TopiaEntity assosiationDst = + dstCtxt.findByTopiaId(assosiationSrc.getTopiaId()); + ownerOperator.addChild(name, dst, assosiationDst); + if (log.isDebugEnabled()) { + log.debug("will attach association '" + name + + "' : " + assosiationDst); + } + shouldUpdate = true; + } + + } + + if (shouldUpdate) { + if (log.isTraceEnabled()) { + log.trace("will update " + dst.getTopiaId()); + } + //FIXME: on ne peut pas updater l'objet car l'objet peut rentre + // en conflit dans la session hibernate + // cela fonctionne sans faire d'update (heureusement...) + //dst.update(); + shouldCommit = true; + } + } + + if (shouldCommit) { + dstCtxt.commitTransaction(); + } + } +} diff --git a/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/DettachAssociation.java b/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/DettachAssociation.java new file mode 100644 index 0000000..181d105 --- /dev/null +++ b/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/DettachAssociation.java @@ -0,0 +1,95 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication.operation; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.util.EntityOperator; +import org.nuiton.topia.replication.TopiaReplicationContext; +import org.nuiton.topia.replication.TopiaReplicationOperation; +import org.nuiton.topia.replication.model.ReplicationModel; +import org.nuiton.topia.replication.model.ReplicationNode; +import org.nuiton.topia.replication.model.ReplicationOperationDef; +import org.nuiton.topia.replication.model.ReplicationOperationPhase; + +import java.util.List; + +/** + * Pour dettacher une association. + * <p/> + * Note : cette operation est interne, et n'est pas creable par + * l'utilisateur via la methode + * {@link #register(ReplicationModel, ReplicationNode, ReplicationOperationPhase, Object...)}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + */ +public class DettachAssociation implements TopiaReplicationOperation { + + /** + * Logger + */ + private static final Log log = LogFactory.getLog(DettachAssociation.class); + + @Override + public void register(ReplicationModel model, + ReplicationNode ownerNode, + ReplicationOperationPhase phase, + Object... parameters) { + +// throw new UnsupportedOperationException( +// _("topia.replication.error.operation.uncreatable", getClass())); + } + + @Override + public void run(TopiaReplicationContext replicationContext, + ReplicationOperationDef operationDef, + TopiaContextImplementor srcCtxt, + TopiaContextImplementor dstCtxt, + List<? extends TopiaEntity> entities + ) throws TopiaException { + + String name = (String) operationDef.getParameters()[0]; + + EntityOperator<? super TopiaEntity> operator; + operator = operationDef.getNode().getOperator(); + + // dettach les associations + for (TopiaEntity e : entities) { + int size = operator.sizeChild(name, e); + + if (size > 0) { + if (log.isDebugEnabled()) { + log.debug("will dettach " + size + " association(s) '" + + name + "' from " + e); + } + operator.setNull(name, e); + } + } + } +} diff --git a/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/Duplicate.java b/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/Duplicate.java new file mode 100644 index 0000000..a1367e6 --- /dev/null +++ b/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/Duplicate.java @@ -0,0 +1,128 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication.operation; + +import org.apache.commons.collections.CollectionUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaDAO; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.replication.TopiaReplicationContext; +import org.nuiton.topia.replication.TopiaReplicationOperationUndoable; +import org.nuiton.topia.replication.model.ReplicationModel; +import org.nuiton.topia.replication.model.ReplicationNode; +import org.nuiton.topia.replication.model.ReplicationOperationDef; +import org.nuiton.topia.replication.model.ReplicationOperationPhase; + +import java.util.List; + +/** + * L'operation de duplication d'un noeud de replication. + * <p/> + * Note : cette operation est interne, et n'est pas creable par + * l'utilisateur via la methode + * {@link #register(ReplicationModel, ReplicationNode, ReplicationOperationPhase, Object...)}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + */ +public class Duplicate implements TopiaReplicationOperationUndoable { + + + /** + * Logger + */ + private static final Log log = + LogFactory.getLog(Duplicate.class); + + @Override + public void register(ReplicationModel model, + ReplicationNode ownerNode, + ReplicationOperationPhase phase, + Object... parameters) throws UnsupportedOperationException { +// throw new UnsupportedOperationException( +// _("topia.replication.error.operation.uncreatable", getClass())); + } + + @Override + public void run(TopiaReplicationContext replicationContext, + ReplicationOperationDef operationDef, + TopiaContextImplementor srcCtxt, + TopiaContextImplementor dstCtxt, + List<? extends TopiaEntity> entities) throws TopiaException { + + // fix http://nuiton.org/issues/1547 + //FIXME tchemit-2011-06-03 : while using hibernate 3.5.6, while duplicating entities we can have some associations in double sessions + //FIXME This hack works but it should be better to resolve the bug :( perharps this is not possible + //FIXME since we can not have a fresh empty hibernate session... + srcCtxt.getHibernate().clear(); + + // replication des donnees vers la destination + srcCtxt.replicateEntities(dstCtxt, entities); + + // sauvegarde de la destination + dstCtxt.commitTransaction(); + } + + + @Override + public void rollback(ReplicationOperationDef operationDef, + TopiaReplicationContext replicationContext, + TopiaContextImplementor dstCtxt) throws Exception { + + List<String> ids = + replicationContext.getEntityIds(operationDef.getNode()); + + if (CollectionUtils.isEmpty(ids)) { + + // rien a supprimer + return; + } + + Class<? extends TopiaEntity> entityClass = operationDef.getEntityType(); + TopiaDAO<TopiaEntity> dao = + (TopiaDAO<TopiaEntity>) dstCtxt.getDAO(entityClass); + + List<String> allIds = dao.findAllIds(); + try { + for (String id : ids) { + if (allIds.contains(id)) { + + // on peut supprimer cette entité + log.info("Will delete " + id); + TopiaEntity entity = dao.findByTopiaId(id); + dao.delete(entity); + } + } + } finally { + allIds.clear(); + + // commit des suppressions + dstCtxt.commitTransaction(); + } + } +} diff --git a/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/LoadLink.java b/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/LoadLink.java new file mode 100644 index 0000000..4b285f2 --- /dev/null +++ b/topia-service-replication/src/main/java/org/nuiton/topia/replication/operation/LoadLink.java @@ -0,0 +1,108 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication.operation; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.util.EntityOperator; +import org.nuiton.topia.replication.TopiaReplicationContext; +import org.nuiton.topia.replication.TopiaReplicationOperation; +import org.nuiton.topia.replication.model.ReplicationLink; +import org.nuiton.topia.replication.model.ReplicationModel; +import org.nuiton.topia.replication.model.ReplicationNode; +import org.nuiton.topia.replication.model.ReplicationOperationDef; +import org.nuiton.topia.replication.model.ReplicationOperationPhase; + +import java.util.List; + +import static org.nuiton.i18n.I18n.t; + +/** + * Pour charger une association sur le noeud de l'operation. + * <p/> + * Note : cette operation est interne, et n'est pas creable par + * l'utilisateur via la methode + * {@link #register(ReplicationModel, ReplicationNode, ReplicationOperationPhase, Object...)}. + * + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + */ +public class LoadLink implements TopiaReplicationOperation { + + /** Logger */ + private static final Log log = LogFactory.getLog(LoadLink.class); + + @Override + public void register(ReplicationModel model, + ReplicationNode ownerNode, + ReplicationOperationPhase phase, + Object... parameters) { + +// throw new UnsupportedOperationException( +// _("topia.replication.error.operation.uncreatable", getClass())); + } + + @Override + public void run(TopiaReplicationContext replicationContext, + ReplicationOperationDef operationDef, + TopiaContextImplementor srcCtxt, + TopiaContextImplementor dstCtxt, + List<? extends TopiaEntity> nodeEntities + ) throws TopiaException { + + ReplicationLink link = (ReplicationLink) operationDef.getParameters()[0]; + + String name = link.getName(); + + ReplicationNode ownerNode = link.getSource(); + + if (!ownerNode.equals(operationDef.getNode())) { + throw new IllegalStateException( + t("topia.replication.error.operation.loadLink.illegalSource", + operationDef.getNode(), ownerNode)); + } + + if (log.isDebugEnabled()) { + log.debug("currentNode : " + operationDef.getNode() + + " , link to load : " + link); + } + + EntityOperator<? super TopiaEntity> ownerOperator = + ownerNode.getOperator(); + + for (TopiaEntity src : nodeEntities) { + + // on se contente de charger l'association + int size = ownerOperator.sizeChild(name, src); + if (log.isDebugEnabled()) { + log.debug("load association '" + name + "' on " + src + + " : " + size); + } + } + } +} diff --git a/topia-service-replication/src/main/resources/META-INF/services/org.nuiton.topia.replication.TopiaReplicationOperation b/topia-service-replication/src/main/resources/META-INF/services/org.nuiton.topia.replication.TopiaReplicationOperation new file mode 100644 index 0000000..8b0cb1b --- /dev/null +++ b/topia-service-replication/src/main/resources/META-INF/services/org.nuiton.topia.replication.TopiaReplicationOperation @@ -0,0 +1,8 @@ +# +# la liste des operations disponibles pour le moteur de replication. +# +org.nuiton.topia.replication.operation.AttachLink +org.nuiton.topia.replication.operation.LoadLink +org.nuiton.topia.replication.operation.AttachAssociation +org.nuiton.topia.replication.operation.DettachAssociation +org.nuiton.topia.replication.operation.Duplicate diff --git a/topia-service-replication/src/main/resources/i18n/topia-service-replication_en_GB.properties b/topia-service-replication/src/main/resources/i18n/topia-service-replication_en_GB.properties new file mode 100644 index 0000000..0e0126e --- /dev/null +++ b/topia-service-replication/src/main/resources/i18n/topia-service-replication_en_GB.properties @@ -0,0 +1,5 @@ +topia.replication.attachAssociation.nothing.to.do=Nothing to attach... +topia.replication.error.operation.loadLink.illegalSource=The source node of loading association requires the node %1$s, but was %2$s. +topia.replication.error.operation.uncreatable=The operation %1$s is internal and can not be added using the api. +topia.replication.error.unkown.operation=The operation %1$s is unknown, known operations are \: %2$s +topia.replication.error.unkown.owner.node=The target node (of type %1$s) for operation %2$s is unknown, known nodes are \: %3$s diff --git a/topia-service-replication/src/main/resources/i18n/topia-service-replication_es_ES.properties b/topia-service-replication/src/main/resources/i18n/topia-service-replication_es_ES.properties new file mode 100644 index 0000000..9e6fff6 --- /dev/null +++ b/topia-service-replication/src/main/resources/i18n/topia-service-replication_es_ES.properties @@ -0,0 +1,5 @@ +topia.replication.attachAssociation.nothing.to.do=Rien a attacher... +topia.replication.error.operation.loadLink.illegalSource=Le noeud source de l'opération de chargement d'association attendait un noeud %1$s mais a trouvé le noeud %2$s. +topia.replication.error.operation.uncreatable=La operación %1$s es interna y no puede ser creada por el API para añadir la operación. +topia.replication.error.unkown.operation=La operación %1$s es desconocida, operaciones conocidas \: %2$s +topia.replication.error.unkown.owner.node=El nodo rattachement (de tipo %1$s) para la operación %2$s no ha sido encontrado, nodos conocidos \: %3$s diff --git a/topia-service-replication/src/main/resources/i18n/topia-service-replication_fr_FR.properties b/topia-service-replication/src/main/resources/i18n/topia-service-replication_fr_FR.properties new file mode 100644 index 0000000..3812fb2 --- /dev/null +++ b/topia-service-replication/src/main/resources/i18n/topia-service-replication_fr_FR.properties @@ -0,0 +1,5 @@ +topia.replication.attachAssociation.nothing.to.do=Rien à attacher... +topia.replication.error.operation.loadLink.illegalSource=Le noeud source de l'opération de chargement d'association attendait un noeud %1$s mais a trouvé le noeud %2$s. +topia.replication.error.operation.uncreatable=L'operation %1$s est interne et ne peut pas être créée par l'api d'ajout d'opération. +topia.replication.error.unkown.operation=L'opération %1$s est inconnue, opérations connues \: %2$s +topia.replication.error.unkown.owner.node=Le noeud de rattachement (de type %1$s) pour l'opération %2$s n'a pas été trouvé, noeuds connus \: %3$s diff --git a/topia-service-replication/src/site/resources/dependencies.html b/topia-service-replication/src/site/resources/dependencies.html new file mode 100644 index 0000000..748e895 --- /dev/null +++ b/topia-service-replication/src/site/resources/dependencies.html @@ -0,0 +1,1434 @@ +<!-- + #%L + ToPIA :: Service Replication + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + + + + + + + + + + + +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>ToPIA - Replication service - Dépendances du projet</title> + <style type="text/css" media="all"> + @import url("./css/maven-base.css"); + @import url("./css/maven-theme.css"); + @import url("./css/lutin.css"); + @import url("./css/site.css"); + </style> + <link rel="stylesheet" href="./css/print.css" type="text/css" media="print" /> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + </head> + <body class="composite"> + <div id="banner"> + <a href="index.html" id="bannerLeft" > + + ToPIA - Replication service + + </a> + <a href="http://www.codelutin.com/" id="bannerRight" > + + <img src="http://www.codelutin.com/images/lutinorange-codelutin.png" alt="$alt" /> + + </a> + <div class="clear"> + <hr/> + </div> + </div> + <div id="breadcrumbs"> + + + + + + + + + <div class="xleft"> + Dernière publication: 09/06/2009 + | Version: 2.2.0-rc-2-SNAPSHOT + </div> + <div class="xright"> <a href="http://www.chorem.org" class="externalLink">Chorem</a> + | + <a href="http://www.nuiton.org" class="externalLink">Nuiton</a> + | + <a href="http://labs.libre-entreprise.org/" class="externalLink">Labs</a> + | + <a href="http://www.codelutin.com/" class="externalLink">CodeLutin</a> + | + <a href="http://maven.nuiton.org/release" class="externalLink">dépôt maven</a> + + + + + + + + + </div> + <div class="clear"> + <hr/> + </div> + </div> + <div id="leftColumn"> + <div id="navcolumn"> + + + + + + + + + <h5>Projet parent</h5> + <ul> + + <li class="none"> + <a href="../index.html">ToPIA - Tools for Portable and Independent Architecture</a> + </li> + </ul> + <h5>Utilisateur</h5> + <ul> + + <li class="none"> + <a href="index.html">Accueil</a> + </li> + </ul> + <h5>Téléchargement</h5> + <ul> + + <li class="none"> + <a href="http://maven.nuiton.org/release/org/nuiton/topia/topia-service-replication/2..." class="externalLink">Librairie (jar)</a> + </li> + + <li class="none"> + <a href="http://maven.nuiton.org/release/org/nuiton/topia/topia-service-replication/2..." class="externalLink">Javadoc (jar)</a> + </li> + + <li class="none"> + <a href="http://maven.nuiton.org/release/org/nuiton/topia/topia-service-replication/2..." class="externalLink">Sources (jar)</a> + </li> + </ul> + <h5>Documentation sur le projet</h5> + <ul> + + + + + + + + + + + + + + + + <li class="expanded"> + <a href="project-info.html">Info Projet</a> + <ul> + + <li class="none"> + <a href="index.html">Bienvenue dans le projet</a> + </li> + + <li class="none"> + <a href="issue-tracking.html">Contrôle des livraisons</a> + </li> + + <li class="none"> + <strong>Dépendances</strong> + </li> + + <li class="none"> + <a href="source-repository.html">Dépôt de sources</a> + </li> + + <li class="none"> + <a href="dependency-management.html">Gestion des dépendances</a> + </li> + + <li class="none"> + <a href="plugin-management.html">Gestion des plugins</a> + </li> + + <li class="none"> + <a href="integration.html">Intégration continue</a> + </li> + + <li class="none"> + <a href="license.html">Licence du projet</a> + </li> + + <li class="none"> + <a href="mail-lists.html">Listes de diffusion</a> + </li> + + <li class="none"> + <a href="team-list.html">Membres de ce projet</a> + </li> + + <li class="none"> + <a href="plugins.html">Plugins du projet</a> + </li> + + <li class="none"> + <a href="project-summary.html">Résumé du projet</a> + </li> + </ul> + </li> + + + + + + + + + + + + + + + + <li class="collapsed"> + <a href="project-reports.html">Rapports Projet</a> + </li> + </ul> + + + + + + + + + </div> + + + <!-- Search Google --> + <div id="Google-search"> + <form method="get" action="http://www.google.fr/custom" target="_top"> + <input type="hidden" name="client" value="pub-5067547915349883"></input> + <input type="hidden" name="forid" value="1"></input> + <input type="hidden" name="ie" value="ISO-8859-15"></input> + <input type="hidden" name="oe" value="ISO-8859-15"></input> + <input type="hidden" name="cof" value="GALT:#008000;GL:1;DIV:#336699;VLC:663399;AH:center;BGC:FFFFFF;LBGC:336699;ALC:0000FF;LC:0000FF;T:000000;GFNT:0000FF;GIMP:0000FF;LH:50;LW:210;L:http://www.codelutin.com/images/ + <input type="hidden" name="hl" value="fr"></input> + <table bgcolor="#eeeeee"> + <tr> + <td valign="top" align="right"> + <label for="sbi" style="display: none">Entrez les termes que vous recherchez.</label> + <input type="text" name="q" size="10" maxlength="255" value="" id="sbi"></input> + <label for="sbb" style="display: none">Envoyer un formulaire de recherche</label> + </td> + </tr> + <tr> + <td valign="top" align="right"> + <input type="submit" name="sa" value="Google" id="sbb"></input> + </td> + </tr> + </table> + </form> + </div> + <!-- Search Google --> + + <!-- paypal --> + <div id="paypal"> + <!-- + | FIXME probleme de recherche de lutin-site-skin_XX.properties, dans doute pas dans le classpath :(, mais ou mettre la dependance :( - test dans le plugin declaration, dans dep du projet, marche pas + | <a href=" # $ i18n.getString( "maven-lutin-skin", fr, "whymakedonate.url" )"> $ i18n.getString( "maven-lutin-skin", fr, "whymakedonate" )</a> + +--> + <a href="http://www.codelutin.com/whymakedonate/en">Why make donate</a> + <form action="https://www.paypal.com/cgi-bin/webscr" method="post"> + <input type="hidden" name="cmd" value="_xclick"/> + <input type="hidden" name="business" value="paypal@codelutin.com"/> + <input type="hidden" name="item_name" value="topia-service-replication"/> + <input type="hidden" name="item_number" value="Don"/> + <input type="hidden" name="no_shipping" value="2"/> + <input type="hidden" name="no_note" value="1"/> + <input type="hidden" name="currency_code" value="EUR"/> + <input type="hidden" name="tax" value="0"/> + <input type="hidden" name="lc" value="FR"/> + <input type="hidden" name="bn" value="PP-DonationsBF"/> + <input type="image" src="images/paypal.gif" border="0" name="submit" alt="Effectuez vos dons via PayPal"/> + </form> + </div> + <div id="Google-AdSense"> + <script type="text/javascript"> + <!-- + google_ad_client = "pub-5067547915349883"; + google_ad_width = 120; + google_ad_height = 240; + google_ad_format = "120x240_as"; + google_ad_type = "text"; + google_ad_channel = ""; + //--> + </script> + <script type="text/javascript" + src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> + </script> + + <script src="http://www.google-analytics.com/urchin.js" type="text/javascript"> + </script> + <script type="text/javascript"> + <!-- + _uacct = "UA-1064192-2"; + _udn="labs.libre-entreprise.org"; + urchinTracker(); + // --> + </script> + <div id="googleAnalytics"> + <a class="tiny" href="http://www.codelutin.com/google">google analytics</a> + </div> + </div> + </div> + <div id="bodyColumn"> + <div id="contentBox"> + <a name="Dépendances_du_projet"></a><div class="section"><h2>Dépendances du projet</h2> +<a name="compile"></a><div class="section"><h3>compile</h3> +<p>Ce qui suit est la liste des dépendances de portée compile pour ce projet. Ces dépendances sont requises pour compiler et exécuter l´application :</p> +<table class="bodyTable"><tr class="a"><th>GroupId</th> +<th>ArtifactId</th> +<th>Version</th> +<th>Type</th> +</tr> +<tr class="b"><td>org.nuiton</td> +<td><a href="http://maven-site.nuiton.org/nuiton-utils" class="externalLink">nuiton-utils</a></td> +<td>1.1.0-rc-1</td> +<td>jar</td> +</tr> +<tr class="a"><td>org.nuiton.topia</td> +<td><a href="http://maven-site.nuiton.org/topia/topia-persistence" class="externalLink">topia-persistence</a></td> +<td>2.2.0-rc-2-SNAPSHOT</td> +<td>jar</td> +</tr> +</table> +</div> +<a name="test"></a><div class="section"><h3>test</h3> +<p>Ce qui suit est la liste des dépendances de portée test pour ce projet. Ces dépendances sont requises seulement pour compiler et exécuter les tests unitaires de l´application :</p> +<table class="bodyTable"><tr class="b"><th>GroupId</th> +<th>ArtifactId</th> +<th>Version</th> +<th>Classifieur</th> +<th>Type</th> +</tr> +<tr class="a"><td>com.h2database</td> +<td><a href="http://www.h2database.com" class="externalLink">h2</a></td> +<td>1.1.113</td> +<td>-</td> +<td>jar</td> +</tr> +<tr class="b"><td>commons-lang</td> +<td><a href="http://commons.apache.org/lang/" class="externalLink">commons-lang</a></td> +<td>2.4</td> +<td>-</td> +<td>jar</td> +</tr> +<tr class="a"><td>junit</td> +<td><a href="http://junit.org" class="externalLink">junit</a></td> +<td>4.6</td> +<td>-</td> +<td>jar</td> +</tr> +<tr class="b"><td>org.nuiton.topia</td> +<td><a href="http://maven-site.nuiton.org/topia/topia-persistence" class="externalLink">topia-persistence</a></td> +<td>2.2.0-rc-2-SNAPSHOT</td> +<td>tests</td> +<td>jar</td> +</tr> +</table> +</div> +</div> +<a name="Dépendances_transitives_du_projet"></a><div class="section"><h2>Dépendances transitives du projet</h2> +<p>Ce qui suit est la liste des dépendances transitives pour ce projet. Les dépendances transitives sont les dépendances des dépendances du projet :</p> +<a name="compile"></a><div class="section"><h3>compile</h3> +<p>Ce qui suit est la liste des dépendances de portée compile pour ce projet. Ces dépendances sont requises pour compiler et exécuter l´application :</p> +<table class="bodyTable"><tr class="a"><th>GroupId</th> +<th>ArtifactId</th> +<th>Version</th> +<th>Type</th> +</tr> +<tr class="b"><td>antlr</td> +<td><a href="http://www.antlr.org/" class="externalLink">antlr</a></td> +<td>2.7.6</td> +<td>jar</td> +</tr> +<tr class="a"><td>commons-beanutils</td> +<td><a href="http://commons.apache.org/beanutils/" class="externalLink">commons-beanutils</a></td> +<td>1.8.0</td> +<td>jar</td> +</tr> +<tr class="b"><td>commons-collections</td> +<td><a href="http://commons.apache.org/collections/" class="externalLink">commons-collections</a></td> +<td>3.2.1</td> +<td>jar</td> +</tr> +<tr class="a"><td>commons-logging</td> +<td><a href="http://commons.apache.org/logging" class="externalLink">commons-logging</a></td> +<td>1.1.1</td> +<td>jar</td> +</tr> +<tr class="b"><td>commons-primitives</td> +<td>commons-primitives</td> +<td>1.0</td> +<td>jar</td> +</tr> +<tr class="a"><td>dom4j</td> +<td><a href="http://dom4j.org" class="externalLink">dom4j</a></td> +<td>1.6.1</td> +<td>jar</td> +</tr> +<tr class="b"><td>javax.transaction</td> +<td><a href="http://java.sun.com/products/jta" class="externalLink">jta</a></td> +<td>1.1</td> +<td>jar</td> +</tr> +<tr class="a"><td>log4j</td> +<td><a href="http://logging.apache.org/log4j/docs/" class="externalLink">log4j</a></td> +<td>1.2.14</td> +<td>jar</td> +</tr> +<tr class="b"><td>org.hibernate</td> +<td><a href="http://hibernate.org/hibernate-core" class="externalLink">hibernate-core</a></td> +<td>3.3.1.GA</td> +<td>jar</td> +</tr> +<tr class="a"><td>org.nuiton</td> +<td><a href="http://maven-site.nuiton.org/nuiton-i18n-api" class="externalLink">nuiton-i18n-api</a></td> +<td>1.0.0-rc-1</td> +<td>jar</td> +</tr> +<tr class="b"><td>org.slf4j</td> +<td><a href="http://www.slf4j.org" class="externalLink">slf4j-api</a></td> +<td>1.5.2</td> +<td>jar</td> +</tr> +<tr class="a"><td>xml-apis</td> +<td><a href="http://xml.apache.org/commons/#external" class="externalLink">xml-apis</a></td> +<td>1.0.b2</td> +<td>jar</td> +</tr> +</table> +</div> +<a name="runtime"></a><div class="section"><h3>runtime</h3> +<p>Ce qui suit est la liste des dépendances de portée runtime pour ce projet. Ces dépendances sont requises pour exécuter l´application :</p> +<table class="bodyTable"><tr class="b"><th>GroupId</th> +<th>ArtifactId</th> +<th>Version</th> +<th>Type</th> +</tr> +<tr class="a"><td>javassist</td> +<td><a href="http://www.jboss.org/products/javassist" class="externalLink">javassist</a></td> +<td>3.4.GA</td> +<td>jar</td> +</tr> +<tr class="b"><td>net.sf.ehcache</td> +<td><a href="http://ehcache.sf.net" class="externalLink">ehcache</a></td> +<td>1.2.3</td> +<td>jar</td> +</tr> +<tr class="a"><td>org.hibernate</td> +<td><a href="http://hibernate.org/hibernate-ehcache" class="externalLink">hibernate-ehcache</a></td> +<td>3.3.1.GA</td> +<td>jar</td> +</tr> +<tr class="b"><td>org.slf4j</td> +<td><a href="http://www.slf4j.org" class="externalLink">slf4j-log4j12</a></td> +<td>1.5.2</td> +<td>jar</td> +</tr> +</table> +</div> +</div> +<a name="Graphe_des_dépendances_du_projet"></a><div class="section"><h2>Graphe des dépendances du projet</h2> +<script language="javascript" type="text/javascript"> + function toggleDependencyDetail( divId, imgId ) + { + var div = document.getElementById( divId ); + var img = document.getElementById( imgId ); + if( div.style.display == '' ) + { + div.style.display = 'none'; + img.src='./images/icon_info_sml.gif'; + } + else + { + div.style.display = ''; + img.src='./images/close.gif'; + } + } +</script> +<a name="Arbre_des_dépendances"></a><div class="section"><h3>Arbre des dépendances</h3> +<ul><li>org.nuiton.topia:topia-service-replication:jar:2.2.0-rc-2-SNAPSHOT <img id="_361856635" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_332873203', '_361856635' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_332873203" style="display:none"><table class="bodyTable"><tr class="a"><th>ToPIA - Replication service</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Hibernate based replication service</p> +<p><b>URL: </b><a href="http://maven-site.nuiton.org/topia/topia-service-replication" class="externalLink">http://maven-site.nuiton.org/topia/topia-service-replication</a></p> +<p><b>Licence du projet: </b><a href="http://www.gnu.org/licenses/lgpl.txt" class="externalLink">LGPL</a></p> +</td> +</tr> +</table> +</div><ul><li>org.nuiton.topia:topia-persistence:jar:2.2.0-rc-2-SNAPSHOT (compile) <img id="_2131659052" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_1799153893', '_2131659052' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_1799153893" style="display:none"><table class="bodyTable"><tr class="a"><th>ToPIA - Persistence</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Hibernate based persistence module</p> +<p><b>URL: </b><a href="http://maven-site.nuiton.org/topia/topia-persistence" class="externalLink">http://maven-site.nuiton.org/topia/topia-persistence</a></p> +<p><b>Licence du projet: </b><a href="http://www.gnu.org/licenses/lgpl.txt" class="externalLink">LGPL</a></p> +</td> +</tr> +</table> +</div><ul><li>org.nuiton:nuiton-utils:jar:1.1.0-rc-1 (compile) <img id="_172528118" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_345116793', '_172528118' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_345116793" style="display:none"><table class="bodyTable"><tr class="a"><th>Nuiton utilities library</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Library of usefull class to be used in any project.</p> +<p><b>URL: </b><a href="http://maven-site.nuiton.org/nuiton-utils" class="externalLink">http://maven-site.nuiton.org/nuiton-utils</a></p> +<p><b>Licence du projet: </b><a href="http://www.gnu.org/licenses/lgpl.txt" class="externalLink">LGPL</a></p> +</td> +</tr> +</table> +</div></li> +<li>commons-collections:commons-collections:jar:3.2.1 (compile) <img id="_1797506881" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_2058865305', '_1797506881' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_2058865305" style="display:none"><table class="bodyTable"><tr class="a"><th>Commons Collections</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Types that extend and augment the Java Collections Framework.</p> +<p><b>URL: </b><a href="http://commons.apache.org/collections/" class="externalLink">http://commons.apache.org/collections/</a></p> +<p><b>Licence du projet: </b><a href="http://www.apache.org/licenses/LICENSE-2.0.txt" class="externalLink">The Apache Software License, Version 2.0</a></p> +</td> +</tr> +</table> +</div></li> +<li>org.hibernate:hibernate-core:jar:3.3.1.GA (compile) <img id="_466605881" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_2105074675', '_466605881' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_2105074675" style="display:none"><table class="bodyTable"><tr class="a"><th>Hibernate Core</th> +</tr> +<tr class="b"><td><p><b>Description: </b>The core functionality of Hibernate</p> +<p><b>URL: </b><a href="http://hibernate.org/hibernate-core" class="externalLink">http://hibernate.org/hibernate-core</a></p> +<p><b>Licence du projet: </b><a href="http://www.gnu.org/licenses/lgpl-2.1.html" class="externalLink">GNU Lesser General Public License</a></p> +</td> +</tr> +</table> +</div><ul><li>antlr:antlr:jar:2.7.6 (compile) <img id="_106049578" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_99657057', '_106049578' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_99657057" style="display:none"><table class="bodyTable"><tr class="a"><th>AntLR</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Il n´y a aucune description actuellement liée à ce projet.</p> +<p><b>URL: </b><a href="http://www.antlr.org/" class="externalLink">http://www.antlr.org/</a></p> +<p><b>Licence du projet: </b>Aucune licence n´est définie pour ce projet.</p> +</td> +</tr> +</table> +</div></li> +<li>commons-collections:commons-collections:jar:3.2.1 (compile) <img id="_1564790785" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_709372533', '_1564790785' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_709372533" style="display:none"><table class="bodyTable"><tr class="a"><th>Commons Collections</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Types that extend and augment the Java Collections Framework.</p> +<p><b>URL: </b><a href="http://commons.apache.org/collections/" class="externalLink">http://commons.apache.org/collections/</a></p> +<p><b>Licence du projet: </b><a href="http://www.apache.org/licenses/LICENSE-2.0.txt" class="externalLink">The Apache Software License, Version 2.0</a></p> +</td> +</tr> +</table> +</div></li> +<li>dom4j:dom4j:jar:1.6.1 (compile) <img id="_624125485" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_1427869017', '_624125485' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_1427869017" style="display:none"><table class="bodyTable"><tr class="a"><th>dom4j</th> +</tr> +<tr class="b"><td><p><b>Description: </b>dom4j: the flexible XML framework for Java</p> +<p><b>URL: </b><a href="http://dom4j.org" class="externalLink">http://dom4j.org</a></p> +<p><b>Licence du projet: </b>Aucune licence n´est définie pour ce projet.</p> +</td> +</tr> +</table> +</div><ul><li>xml-apis:xml-apis:jar:1.0.b2 (compile) <img id="_32950924" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_5964266', '_32950924' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_5964266" style="display:none"><table class="bodyTable"><tr class="a"><th>XML Commons External Components XML APIs</th> +</tr> +<tr class="b"><td><p><b>Description: </b>xml-commons provides an Apache-hosted set of DOM, SAX, and + JAXP interfaces for use in other xml-based projects. Our hope is that we + can standardize on both a common version and packaging scheme for these + critical XML standards interfaces to make the lives of both our developers + and users easier. The External Components portion of xml-commons contains + interfaces that are defined by external standards organizations. For DOM, + that's the W3C; for SAX it's David Megginson and sax.sourceforge.net; for + JAXP it's Sun.</p> +<p><b>URL: </b><a href="http://xml.apache.org/commons/#external" class="externalLink">http://xml.apache.org/commons/#external</a></p> +<p><b>Licence du projet: </b><a href="http://www.apache.org/licenses/LICENSE-2.0.txt" class="externalLink">The Apache Software License, Version 2.0</a></p> +</td> +</tr> +</table> +</div></li> +</ul> +</li> +<li>javax.transaction:jta:jar:1.1 (compile) <img id="_1179369495" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_300249109', '_1179369495' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_300249109" style="display:none"><table class="bodyTable"><tr class="a"><th>Java Transaction API</th> +</tr> +<tr class="b"><td><p><b>Description: </b>The javax.transaction package. It is appropriate for inclusion in a classpath, and may be added to a Java 2 installation.</p> +<p><b>URL: </b><a href="http://java.sun.com/products/jta" class="externalLink">http://java.sun.com/products/jta</a></p> +<p><b>Licence du projet: </b>Aucune licence n´est définie pour ce projet.</p> +</td> +</tr> +</table> +</div></li> +<li>org.slf4j:slf4j-api:jar:1.5.2 (compile) <img id="_1719747487" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_1149625196', '_1719747487' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_1149625196" style="display:none"><table class="bodyTable"><tr class="a"><th>SLF4J API Module</th> +</tr> +<tr class="b"><td><p><b>Description: </b>The slf4j API</p> +<p><b>URL: </b><a href="http://www.slf4j.org" class="externalLink">http://www.slf4j.org</a></p> +<p><b>Licence du projet: </b>Aucune licence n´est définie pour ce projet.</p> +</td> +</tr> +</table> +</div></li> +</ul> +</li> +<li>org.hibernate:hibernate-ehcache:jar:3.3.1.GA (runtime) <img id="_207564523" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_1669070803', '_207564523' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_1669070803" style="display:none"><table class="bodyTable"><tr class="a"><th>Hibernate Ehcache Integration</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Integration of Hibernate with Ehcache</p> +<p><b>URL: </b><a href="http://hibernate.org/hibernate-ehcache" class="externalLink">http://hibernate.org/hibernate-ehcache</a></p> +<p><b>Licence du projet: </b><a href="http://www.gnu.org/licenses/lgpl-2.1.html" class="externalLink">GNU Lesser General Public License</a></p> +</td> +</tr> +</table> +</div><ul><li>org.hibernate:hibernate-core:jar:3.3.1.GA (compile) <img id="_1944781312" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_1766447944', '_1944781312' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_1766447944" style="display:none"><table class="bodyTable"><tr class="a"><th>Hibernate Core</th> +</tr> +<tr class="b"><td><p><b>Description: </b>The core functionality of Hibernate</p> +<p><b>URL: </b><a href="http://hibernate.org/hibernate-core" class="externalLink">http://hibernate.org/hibernate-core</a></p> +<p><b>Licence du projet: </b><a href="http://www.gnu.org/licenses/lgpl-2.1.html" class="externalLink">GNU Lesser General Public License</a></p> +</td> +</tr> +</table> +</div></li> +<li>net.sf.ehcache:ehcache:jar:1.2.3 (runtime) <img id="_178841574" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_1940554778', '_178841574' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_1940554778" style="display:none"><table class="bodyTable"><tr class="a"><th>ehcache</th> +</tr> +<tr class="b"><td><p><b>Description: </b>ehcache is a pure Java, in-process cache with the following features: + + 1. Fast. + 2. Simple. + 3. Multiple eviction policies: LRU, LFU and FIFO. + 4. Caches can be in memory or on disk. + 5. Disk Stores can be persistent between VM restarts. + 6. Distributed caching using multicast and RMI, with a pluggable API. + 7. Cache and CacheManager listeners + 8. Supports multiple Caches per CacheManager, and multiple CacheManagers per application. + 9. Acts as a pluggable cache for Hibernate 3.1, 3 and 2.1. + 10. Small foot print. Both in terms of size and memory requirements. + 11. Minimal dependencies apart from J2SE. + 12. Fully documented. See the online Documentation and the online JavaDoc. + 13. Comprehensive Test Coverage. See the clover test report. + 14. Available under the Apache 1.1 license. EHCache's copyright and licensing has been reviewed and approved by the Apache Software Foundation, making EHCache suitable for use in Apache projects. + 15. Production tested. EHCache is used on a large and very busy eCommerce site. + 16. Web caching, pull-through caches and other common caching implementations are provided in the ehcache-constructs module.</p> +<p><b>URL: </b><a href="http://ehcache.sf.net" class="externalLink">http://ehcache.sf.net</a></p> +<p><b>Licence du projet: </b><a href="http://ehcache.sourceforge.net/LICENSE.txt" class="externalLink">The Apache Software License, Version 2.0</a></p> +</td> +</tr> +</table> +</div><ul><li>commons-collections:commons-collections:jar:3.2.1 (compile) <img id="_220346089" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_1012024174', '_220346089' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_1012024174" style="display:none"><table class="bodyTable"><tr class="a"><th>Commons Collections</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Types that extend and augment the Java Collections Framework.</p> +<p><b>URL: </b><a href="http://commons.apache.org/collections/" class="externalLink">http://commons.apache.org/collections/</a></p> +<p><b>Licence du projet: </b><a href="http://www.apache.org/licenses/LICENSE-2.0.txt" class="externalLink">The Apache Software License, Version 2.0</a></p> +</td> +</tr> +</table> +</div></li> +</ul> +</li> +<li>org.slf4j:slf4j-api:jar:1.5.2 (runtime) <img id="_1584299707" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_1004717710', '_1584299707' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_1004717710" style="display:none"><table class="bodyTable"><tr class="a"><th>SLF4J API Module</th> +</tr> +<tr class="b"><td><p><b>Description: </b>The slf4j API</p> +<p><b>URL: </b><a href="http://www.slf4j.org" class="externalLink">http://www.slf4j.org</a></p> +<p><b>Licence du projet: </b>Aucune licence n´est définie pour ce projet.</p> +</td> +</tr> +</table> +</div></li> +</ul> +</li> +<li>javassist:javassist:jar:3.4.GA (runtime) <img id="_466120759" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_1868187669', '_466120759' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_1868187669" style="display:none"><table class="bodyTable"><tr class="a"><th>Javassist</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Simple Java bytecode manipulation</p> +<p><b>URL: </b><a href="http://www.jboss.org/products/javassist" class="externalLink">http://www.jboss.org/products/javassist</a></p> +<p><b>Licence du projet: </b><a href="http://repository.jboss.com/licenses/lgpl.txt" class="externalLink">lgpl</a></p> +</td> +</tr> +</table> +</div></li> +<li>org.slf4j:slf4j-log4j12:jar:1.5.2 (runtime) <img id="_1836545258" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_818662745', '_1836545258' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_818662745" style="display:none"><table class="bodyTable"><tr class="a"><th>SLF4J LOG4J-12 Binding</th> +</tr> +<tr class="b"><td><p><b>Description: </b>The slf4j log4j-12 binding</p> +<p><b>URL: </b><a href="http://www.slf4j.org" class="externalLink">http://www.slf4j.org</a></p> +<p><b>Licence du projet: </b>Aucune licence n´est définie pour ce projet.</p> +</td> +</tr> +</table> +</div><ul><li>org.slf4j:slf4j-api:jar:1.5.2 (runtime) <img id="_1428616332" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_419843451', '_1428616332' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_419843451" style="display:none"><table class="bodyTable"><tr class="a"><th>SLF4J API Module</th> +</tr> +<tr class="b"><td><p><b>Description: </b>The slf4j API</p> +<p><b>URL: </b><a href="http://www.slf4j.org" class="externalLink">http://www.slf4j.org</a></p> +<p><b>Licence du projet: </b>Aucune licence n´est définie pour ce projet.</p> +</td> +</tr> +</table> +</div></li> +<li>log4j:log4j:jar:1.2.14 (runtime) <img id="_1450521592" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_28372781', '_1450521592' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_28372781" style="display:none"><table class="bodyTable"><tr class="a"><th>Log4j</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Log4j</p> +<p><b>URL: </b><a href="http://logging.apache.org/log4j/docs/" class="externalLink">http://logging.apache.org/log4j/docs/</a></p> +<p><b>Licence du projet: </b><a href="http://www.apache.org/licenses/LICENSE-2.0.txt" class="externalLink">The Apache Software License, Version 2.0</a></p> +</td> +</tr> +</table> +</div></li> +</ul> +</li> +</ul> +</li> +<li>org.nuiton.topia:topia-persistence:jar:tests:2.2.0-rc-2-SNAPSHOT (test) <img id="_525949532" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_820849945', '_525949532' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_820849945" style="display:none"><table class="bodyTable"><tr class="a"><th>ToPIA - Persistence</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Hibernate based persistence module</p> +<p><b>URL: </b><a href="http://maven-site.nuiton.org/topia/topia-persistence" class="externalLink">http://maven-site.nuiton.org/topia/topia-persistence</a></p> +<p><b>Licence du projet: </b><a href="http://www.gnu.org/licenses/lgpl.txt" class="externalLink">LGPL</a></p> +</td> +</tr> +</table> +</div><ul><li>org.nuiton:nuiton-utils:jar:1.1.0-rc-1 (compile) <img id="_837761545" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_2013940788', '_837761545' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_2013940788" style="display:none"><table class="bodyTable"><tr class="a"><th>Nuiton utilities library</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Library of usefull class to be used in any project.</p> +<p><b>URL: </b><a href="http://maven-site.nuiton.org/nuiton-utils" class="externalLink">http://maven-site.nuiton.org/nuiton-utils</a></p> +<p><b>Licence du projet: </b><a href="http://www.gnu.org/licenses/lgpl.txt" class="externalLink">LGPL</a></p> +</td> +</tr> +</table> +</div></li> +<li>commons-collections:commons-collections:jar:3.2.1 (compile) <img id="_762254222" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_743799223', '_762254222' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_743799223" style="display:none"><table class="bodyTable"><tr class="a"><th>Commons Collections</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Types that extend and augment the Java Collections Framework.</p> +<p><b>URL: </b><a href="http://commons.apache.org/collections/" class="externalLink">http://commons.apache.org/collections/</a></p> +<p><b>Licence du projet: </b><a href="http://www.apache.org/licenses/LICENSE-2.0.txt" class="externalLink">The Apache Software License, Version 2.0</a></p> +</td> +</tr> +</table> +</div></li> +<li>org.hibernate:hibernate-core:jar:3.3.1.GA (compile) <img id="_978173335" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_1531565256', '_978173335' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_1531565256" style="display:none"><table class="bodyTable"><tr class="a"><th>Hibernate Core</th> +</tr> +<tr class="b"><td><p><b>Description: </b>The core functionality of Hibernate</p> +<p><b>URL: </b><a href="http://hibernate.org/hibernate-core" class="externalLink">http://hibernate.org/hibernate-core</a></p> +<p><b>Licence du projet: </b><a href="http://www.gnu.org/licenses/lgpl-2.1.html" class="externalLink">GNU Lesser General Public License</a></p> +</td> +</tr> +</table> +</div></li> +<li>org.hibernate:hibernate-ehcache:jar:3.3.1.GA (runtime) <img id="_2115119983" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_1210100860', '_2115119983' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_1210100860" style="display:none"><table class="bodyTable"><tr class="a"><th>Hibernate Ehcache Integration</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Integration of Hibernate with Ehcache</p> +<p><b>URL: </b><a href="http://hibernate.org/hibernate-ehcache" class="externalLink">http://hibernate.org/hibernate-ehcache</a></p> +<p><b>Licence du projet: </b><a href="http://www.gnu.org/licenses/lgpl-2.1.html" class="externalLink">GNU Lesser General Public License</a></p> +</td> +</tr> +</table> +</div></li> +<li>javassist:javassist:jar:3.4.GA (runtime) <img id="_317289033" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_527275658', '_317289033' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_527275658" style="display:none"><table class="bodyTable"><tr class="a"><th>Javassist</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Simple Java bytecode manipulation</p> +<p><b>URL: </b><a href="http://www.jboss.org/products/javassist" class="externalLink">http://www.jboss.org/products/javassist</a></p> +<p><b>Licence du projet: </b><a href="http://repository.jboss.com/licenses/lgpl.txt" class="externalLink">lgpl</a></p> +</td> +</tr> +</table> +</div></li> +<li>org.slf4j:slf4j-log4j12:jar:1.5.2 (runtime) <img id="_1440074860" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_2137677481', '_1440074860' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_2137677481" style="display:none"><table class="bodyTable"><tr class="a"><th>SLF4J LOG4J-12 Binding</th> +</tr> +<tr class="b"><td><p><b>Description: </b>The slf4j log4j-12 binding</p> +<p><b>URL: </b><a href="http://www.slf4j.org" class="externalLink">http://www.slf4j.org</a></p> +<p><b>Licence du projet: </b>Aucune licence n´est définie pour ce projet.</p> +</td> +</tr> +</table> +</div></li> +</ul> +</li> +<li>org.nuiton:nuiton-utils:jar:1.1.0-rc-1 (compile) <img id="_774684808" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_1874145603', '_774684808' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_1874145603" style="display:none"><table class="bodyTable"><tr class="a"><th>Nuiton utilities library</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Library of usefull class to be used in any project.</p> +<p><b>URL: </b><a href="http://maven-site.nuiton.org/nuiton-utils" class="externalLink">http://maven-site.nuiton.org/nuiton-utils</a></p> +<p><b>Licence du projet: </b><a href="http://www.gnu.org/licenses/lgpl.txt" class="externalLink">LGPL</a></p> +</td> +</tr> +</table> +</div><ul><li>log4j:log4j:jar:1.2.14 (compile) <img id="_656987672" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_823139270', '_656987672' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_823139270" style="display:none"><table class="bodyTable"><tr class="a"><th>Log4j</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Log4j</p> +<p><b>URL: </b><a href="http://logging.apache.org/log4j/docs/" class="externalLink">http://logging.apache.org/log4j/docs/</a></p> +<p><b>Licence du projet: </b><a href="http://www.apache.org/licenses/LICENSE-2.0.txt" class="externalLink">The Apache Software License, Version 2.0</a></p> +</td> +</tr> +</table> +</div></li> +<li>commons-logging:commons-logging:jar:1.1.1 (compile) <img id="_1657699797" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_82471895', '_1657699797' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_82471895" style="display:none"><table class="bodyTable"><tr class="a"><th>Commons Logging</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Commons Logging is a thin adapter allowing configurable bridging to other, + well known logging systems.</p> +<p><b>URL: </b><a href="http://commons.apache.org/logging" class="externalLink">http://commons.apache.org/logging</a></p> +<p><b>Licence du projet: </b><a href="http://www.apache.org/licenses/LICENSE-2.0.txt" class="externalLink">The Apache Software License, Version 2.0</a></p> +</td> +</tr> +</table> +</div></li> +<li>commons-primitives:commons-primitives:jar:1.0 (compile) <img id="_185855807" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_1802375087', '_185855807' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_1802375087" style="display:none"><table class="bodyTable"><tr class="a"><th>Unnamed - commons-primitives:commons-primitives:jar:1.0</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Il n´y a aucune description actuellement liée à ce projet.</p> +<p><b>Licence du projet: </b>Aucune licence n´est définie pour ce projet.</p> +</td> +</tr> +</table> +</div></li> +<li>commons-collections:commons-collections:jar:3.2.1 (compile) <img id="_108515244" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_813329649', '_108515244' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_813329649" style="display:none"><table class="bodyTable"><tr class="a"><th>Commons Collections</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Types that extend and augment the Java Collections Framework.</p> +<p><b>URL: </b><a href="http://commons.apache.org/collections/" class="externalLink">http://commons.apache.org/collections/</a></p> +<p><b>Licence du projet: </b><a href="http://www.apache.org/licenses/LICENSE-2.0.txt" class="externalLink">The Apache Software License, Version 2.0</a></p> +</td> +</tr> +</table> +</div></li> +<li>commons-beanutils:commons-beanutils:jar:1.8.0 (compile) <img id="_1625158460" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_713824759', '_1625158460' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_713824759" style="display:none"><table class="bodyTable"><tr class="a"><th>Commons BeanUtils</th> +</tr> +<tr class="b"><td><p><b>Description: </b>BeanUtils provides an easy-to-use but flexible wrapper around reflection and introspection.</p> +<p><b>URL: </b><a href="http://commons.apache.org/beanutils/" class="externalLink">http://commons.apache.org/beanutils/</a></p> +<p><b>Licence du projet: </b><a href="http://www.apache.org/licenses/LICENSE-2.0.txt" class="externalLink">The Apache Software License, Version 2.0</a></p> +</td> +</tr> +</table> +</div><ul><li>commons-logging:commons-logging:jar:1.1.1 (compile) <img id="_1919242539" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_1684491794', '_1919242539' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_1684491794" style="display:none"><table class="bodyTable"><tr class="a"><th>Commons Logging</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Commons Logging is a thin adapter allowing configurable bridging to other, + well known logging systems.</p> +<p><b>URL: </b><a href="http://commons.apache.org/logging" class="externalLink">http://commons.apache.org/logging</a></p> +<p><b>Licence du projet: </b><a href="http://www.apache.org/licenses/LICENSE-2.0.txt" class="externalLink">The Apache Software License, Version 2.0</a></p> +</td> +</tr> +</table> +</div></li> +</ul> +</li> +<li>org.nuiton:nuiton-i18n-api:jar:1.0.0-rc-1 (compile) <img id="_2220293" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_1974473650', '_2220293' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_1974473650" style="display:none"><table class="bodyTable"><tr class="a"><th>Nuiton i18n api</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Api of i18n system</p> +<p><b>URL: </b><a href="http://maven-site.nuiton.org/nuiton-i18n-api" class="externalLink">http://maven-site.nuiton.org/nuiton-i18n-api</a></p> +<p><b>Licence du projet: </b><a href="http://www.gnu.org/licenses/lgpl.txt" class="externalLink">LGPL</a></p> +</td> +</tr> +</table> +</div><ul><li>commons-logging:commons-logging:jar:1.1.1 (compile) <img id="_246156384" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_566055785', '_246156384' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_566055785" style="display:none"><table class="bodyTable"><tr class="a"><th>Commons Logging</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Commons Logging is a thin adapter allowing configurable bridging to other, + well known logging systems.</p> +<p><b>URL: </b><a href="http://commons.apache.org/logging" class="externalLink">http://commons.apache.org/logging</a></p> +<p><b>Licence du projet: </b><a href="http://www.apache.org/licenses/LICENSE-2.0.txt" class="externalLink">The Apache Software License, Version 2.0</a></p> +</td> +</tr> +</table> +</div></li> +<li>commons-beanutils:commons-beanutils:jar:1.8.0 (compile) <img id="_1048354434" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_468243529', '_1048354434' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_468243529" style="display:none"><table class="bodyTable"><tr class="a"><th>Commons BeanUtils</th> +</tr> +<tr class="b"><td><p><b>Description: </b>BeanUtils provides an easy-to-use but flexible wrapper around reflection and introspection.</p> +<p><b>URL: </b><a href="http://commons.apache.org/beanutils/" class="externalLink">http://commons.apache.org/beanutils/</a></p> +<p><b>Licence du projet: </b><a href="http://www.apache.org/licenses/LICENSE-2.0.txt" class="externalLink">The Apache Software License, Version 2.0</a></p> +</td> +</tr> +</table> +</div></li> +</ul> +</li> +</ul> +</li> +<li>com.h2database:h2:jar:1.1.113 (test) <img id="_2086971951" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_868581929', '_2086971951' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_868581929" style="display:none"><table class="bodyTable"><tr class="a"><th>H2 Database Engine</th> +</tr> +<tr class="b"><td><p><b>Description: </b>H2 Database Engine</p> +<p><b>URL: </b><a href="http://www.h2database.com" class="externalLink">http://www.h2database.com</a></p> +<p><b>Licence du projet: </b><a href="http://h2database.com/html/license.html" class="externalLink">The H2 License, Version 1.0</a></p> +</td> +</tr> +</table> +</div></li> +<li>commons-lang:commons-lang:jar:2.4 (test) <img id="_1422056950" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_1200970692', '_1422056950' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_1200970692" style="display:none"><table class="bodyTable"><tr class="a"><th>Commons Lang</th> +</tr> +<tr class="b"><td><p><b>Description: </b>Commons Lang, a package of Java utility classes for the + classes that are in java.lang's hierarchy, or are considered to be so + standard as to justify existence in java.lang.</p> +<p><b>URL: </b><a href="http://commons.apache.org/lang/" class="externalLink">http://commons.apache.org/lang/</a></p> +<p><b>Licence du projet: </b><a href="http://www.apache.org/licenses/LICENSE-2.0.txt" class="externalLink">The Apache Software License, Version 2.0</a></p> +</td> +</tr> +</table> +</div></li> +<li>junit:junit:jar:4.6 (test) <img id="_1585890069" src="./images/icon_info_sml.gif" alt="Information" onclick="toggleDependencyDetail( '_1088426452', '_1585890069' );" style="cursor: pointer;vertical-align:text-bottom;"></img><div id="_1088426452" style="display:none"><table class="bodyTable"><tr class="a"><th>JUnit</th> +</tr> +<tr class="b"><td><p><b>Description: </b>JUnit is a regression testing framework written by Erich Gamma and Kent Beck. It is used by the developer who implements unit tests in Java.</p> +<p><b>URL: </b><a href="http://junit.org" class="externalLink">http://junit.org</a></p> +<p><b>Licence du projet: </b><a href="http://www.opensource.org/licenses/cpl1.0.txt" class="externalLink">Common Public License Version 1.0</a></p> +</td> +</tr> +</table> +</div></li> +</ul> +</li> +</ul> +</div> +</div> +<a name="Licences"></a><div class="section"><h2>Licences</h2> +<p><b>lgpl: </b>Javassist</p> +<p><b>Inconnu: </b>AntLR, Java Transaction API, SLF4J API Module, SLF4J LOG4J-12 Binding, Unnamed - commons-primitives:commons-primitives:jar:1.0, dom4j</p> +<p><b>LGPL: </b>Nuiton i18n api, Nuiton utilities library, ToPIA - Persistence, ToPIA - Replication service</p> +<p><b>The H2 License, Version 1.0: </b>H2 Database Engine</p> +<p><b>Common Public License Version 1.0: </b>JUnit</p> +<p><b>GNU Lesser General Public License: </b>Hibernate Core, Hibernate Ehcache Integration</p> +<p><b>The Apache Software License, Version 2.0: </b>Commons BeanUtils, Commons Collections, Commons Lang, Commons Logging, Log4j, XML Commons External Components XML APIs, ehcache</p> +</div> +<a name="Détails_du_fichier_de_dépendances"></a><div class="section"><h2>Détails du fichier de dépendances</h2> +<table class="bodyTable"><tbody><tr class="a"><th align="left">Nom du fichier</th> +<th align="right">Taille</th> +<th align="right">Entrées</th> +<th align="right">Classes</th> +<th align="right">Packages</th> +<th align="center">Révision du JDK</th> +<th align="center">Debug</th> +</tr> +<tr class="b"><td align="left">antlr-2.7.6.jar</td> +<td align="right">433,04 ko</td> +<td align="right">226</td> +<td align="right">224</td> +<td align="right">12</td> +<td align="center">1.2</td> +<td align="center">debug</td> +</tr> +<tr class="a"><td align="left">h2-1.1.113.jar</td> +<td align="right">1,10 Mo</td> +<td align="right">458</td> +<td align="right">455</td> +<td align="right">29</td> +<td align="center">1.4</td> +<td align="center">debug</td> +</tr> +<tr class="b"><td align="left">commons-beanutils-1.8.0.jar</td> +<td align="right">225,90 ko</td> +<td align="right">155</td> +<td align="right">137</td> +<td align="right">6</td> +<td align="center">1.3</td> +<td align="center">debug</td> +</tr> +<tr class="a"><td align="left">commons-collections-3.2.1.jar</td> +<td align="right">561,90 ko</td> +<td align="right">482</td> +<td align="right">458</td> +<td align="right">12</td> +<td align="center">1.2</td> +<td align="center">debug</td> +</tr> +<tr class="b"><td align="left">commons-lang-2.4.jar</td> +<td align="right">255,67 ko</td> +<td align="right">148</td> +<td align="right">127</td> +<td align="right">9</td> +<td align="center">1.2</td> +<td align="center">debug</td> +</tr> +<tr class="a"><td align="left">commons-logging-1.1.1.jar</td> +<td align="right">59,26 ko</td> +<td align="right">42</td> +<td align="right">28</td> +<td align="right">2</td> +<td align="center">1.1</td> +<td align="center">debug</td> +</tr> +<tr class="b"><td align="left">commons-primitives-1.0.jar</td> +<td align="right">254,07 ko</td> +<td align="right">271</td> +<td align="right">259</td> +<td align="right">4</td> +<td align="center">1.1</td> +<td align="center">debug</td> +</tr> +<tr class="a"><td align="left">dom4j-1.6.1.jar</td> +<td align="right">306,54 ko</td> +<td align="right">208</td> +<td align="right">190</td> +<td align="right">14</td> +<td align="center">1.3</td> +<td align="center">debug</td> +</tr> +<tr class="b"><td align="left">javassist-3.4.GA.jar</td> +<td align="right">459,97 ko</td> +<td align="right">279</td> +<td align="right">262</td> +<td align="right">15</td> +<td align="center">1.2</td> +<td align="center">debug</td> +</tr> +<tr class="a"><td align="left">jta-1.1.jar</td> +<td align="right">14,72 ko</td> +<td align="right">24</td> +<td align="right">18</td> +<td align="right">2</td> +<td align="center">1.3</td> +<td align="center">debug</td> +</tr> +<tr class="b"><td align="left">junit-4.6.jar</td> +<td align="right">213,11 ko</td> +<td align="right">239</td> +<td align="right">204</td> +<td align="right">28</td> +<td align="center">1.5</td> +<td align="center">debug</td> +</tr> +<tr class="a"><td align="left">log4j-1.2.14.jar</td> +<td align="right">358,83 ko</td> +<td align="right">288</td> +<td align="right">256</td> +<td align="right">19</td> +<td align="center">1.1</td> +<td align="center">debug</td> +</tr> +<tr class="b"><td align="left">ehcache-1.2.3.jar</td> +<td align="right">203,17 ko</td> +<td align="right">137</td> +<td align="right">117</td> +<td align="right">13</td> +<td align="center">1.3</td> +<td align="center">debug</td> +</tr> +<tr class="a"><td align="left">hibernate-core-3.3.1.GA.jar</td> +<td align="right">2,17 Mo</td> +<td align="right">1 482</td> +<td align="right">1 386</td> +<td align="right">81</td> +<td align="center">1.4</td> +<td align="center">debug</td> +</tr> +<tr class="b"><td align="left">hibernate-ehcache-3.3.1.GA.jar</td> +<td align="right">6,98 ko</td> +<td align="right">12</td> +<td align="right">2</td> +<td align="right">1</td> +<td align="center">1.4</td> +<td align="center">debug</td> +</tr> +<tr class="a"><td align="left">nuiton-i18n-api-1.0.0-rc-1.jar</td> +<td align="right">41,09 ko</td> +<td align="right">29</td> +<td align="right">15</td> +<td align="right">3</td> +<td align="center">1.6</td> +<td align="center">debug</td> +</tr> +<tr class="b"><td align="left">nuiton-utils-1.1.0-rc-1.jar</td> +<td align="right">191,83 ko</td> +<td align="right">146</td> +<td align="right">126</td> +<td align="right">4</td> +<td align="center">1.6</td> +<td align="center">debug</td> +</tr> +<tr class="a"><td align="left">topia-persistence-2.2.0-rc-2-SNAPSHOT-tests.jar</td> +<td align="right">122,27 ko</td> +<td align="right">138</td> +<td align="right">103</td> +<td align="right">9</td> +<td align="center">1.6</td> +<td align="center">debug</td> +</tr> +<tr class="b"><td align="left">topia-persistence-2.2.0-rc-2-SNAPSHOT.jar</td> +<td align="right">191,25 ko</td> +<td align="right">86</td> +<td align="right">71</td> +<td align="right">6</td> +<td align="center">1.6</td> +<td align="center">debug</td> +</tr> +<tr class="a"><td align="left">slf4j-api-1.5.2.jar</td> +<td align="right">16,98 ko</td> +<td align="right">29</td> +<td align="right">18</td> +<td align="right">3</td> +<td align="center">1.3</td> +<td align="center">debug</td> +</tr> +<tr class="b"><td align="left">slf4j-log4j12-1.5.2.jar</td> +<td align="right">9,28 ko</td> +<td align="right">16</td> +<td align="right">6</td> +<td align="right">1</td> +<td align="center">1.3</td> +<td align="center">debug</td> +</tr> +<tr class="a"><td align="left">xml-apis-1.0.b2.jar</td> +<td align="right">106,76 ko</td> +<td align="right">217</td> +<td align="right">184</td> +<td align="right">17</td> +<td align="center">1.2</td> +<td align="center">release</td> +</tr> +<tr class="b"><th align="left">Total</th> +<th align="right">Taille</th> +<th align="right">Entrées</th> +<th align="right">Classes</th> +<th align="right">Packages</th> +<th align="center">Révision du JDK</th> +<th align="center">Debug</th> +</tr> +<tr class="a"><td align="right">22</td> +<td align="right">7,21 Mo</td> +<td align="right">5 112</td> +<td align="right">4 646</td> +<td align="right">290</td> +<td align="center">1.6</td> +<td align="right">21</td> +</tr> +<tr class="b"><td align="right">compile: 14</td> +<td align="right">compile: 4,87 Mo</td> +<td align="right">compile: 3 685</td> +<td align="right">compile: 3 370</td> +<td align="right">compile: 185</td> +<td align="center">-</td> +<td align="right">compile: 13</td> +</tr> +<tr class="a"><td align="right">test: 4</td> +<td align="right">test: 1,68 Mo</td> +<td align="right">test: 983</td> +<td align="right">test: 889</td> +<td align="right">test: 75</td> +<td align="center">-</td> +<td align="right">test: 4</td> +</tr> +<tr class="b"><td align="right">runtime: 4</td> +<td align="right">runtime: 679,40 ko</td> +<td align="right">runtime: 444</td> +<td align="right">runtime: 387</td> +<td align="right">runtime: 30</td> +<td align="center">-</td> +<td align="right">runtime: 4</td> +</tr> +</tbody> +</table> +</div> +<a name="Localisation_du_référentiel_des_dépendances"></a><div class="section"><h2>Localisation du référentiel des dépendances</h2> +<table class="bodyTable"><tbody><tr class="a"><th align="left">Référentiel ID</th> +<th align="left">URL</th> +<th align="center">Release</th> +<th align="center">Snapshot</th> +</tr> +<tr class="b"><td align="left">apache-snapshot-repository</td> +<td align="left"><a href="http://people.apache.org/repo/m2-snapshot-repository" class="externalLink">http://people.apache.org/repo/m2-snapshot-repository</a></td> +<td align="center">Oui</td> +<td align="center">Oui</td> +</tr> +<tr class="a"><td align="left">apache.snapshots</td> +<td align="left"><a href="http://people.apache.org/repo/m2-snapshot-repository" class="externalLink">http://people.apache.org/repo/m2-snapshot-repository</a></td> +<td align="center">-</td> +<td align="center">Oui</td> +</tr> +<tr class="b"><td align="left">nuiton.snapshot</td> +<td align="left"><a href="http://maven.nuiton.org/snapshot" class="externalLink">http://maven.nuiton.org/snapshot</a></td> +<td align="center">-</td> +<td align="center">Oui</td> +</tr> +<tr class="a"><td align="left">nuiton.release</td> +<td align="left"><a href="http://maven.nuiton.org/release" class="externalLink">http://maven.nuiton.org/release</a></td> +<td align="center">Oui</td> +<td align="center">-</td> +</tr> +<tr class="b"><td align="left">codehaus-repository</td> +<td align="left"><a href="http://repository.codehaus.org" class="externalLink">http://repository.codehaus.org</a></td> +<td align="center">Oui</td> +<td align="center">Oui</td> +</tr> +<tr class="a"><td align="left">central</td> +<td align="left"><a href="http://repo1.maven.org/maven2" class="externalLink">http://repo1.maven.org/maven2</a></td> +<td align="center">Oui</td> +<td align="center">-</td> +</tr> +</tbody> +</table> +<p>Localisation du référentiel pour chacune des dépendances.</p> +<table class="bodyTable"><tbody><tr class="b"><th align="left">Artefact</th> +<th align="center">apache-snapshot-repository</th> +<th align="center">apache.snapshots</th> +<th align="center">nuiton.snapshot</th> +<th align="center">nuiton.release</th> +<th align="center">codehaus-repository</th> +<th align="center">central</th> +</tr> +<tr class="a"><td align="left">antlr:antlr:jar:2.7.6</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://repo1.maven.org/maven2/antlr/antlr/2.7.6/antlr-2.7.6.jar" class="externalLink"><img alt="Found at http://repo1.maven.org/maven2" src="images/icon_success_sml.gif" /></a></td> +</tr> +<tr class="b"><td align="left">com.h2database:h2:jar:1.1.113</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://repo1.maven.org/maven2/com/h2database/h2/1.1.113/h2-1.1.113.jar" class="externalLink"><img alt="Found at http://repo1.maven.org/maven2" src="images/icon_success_sml.gif" /></a></td> +</tr> +<tr class="a"><td align="left">commons-beanutils:commons-beanutils:jar:1.8.0</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://repo1.maven.org/maven2/commons-beanutils/commons-beanutils/1.8.0/comm..." class="externalLink"><img alt="Found at http://repo1.maven.org/maven2" src="images/icon_success_sml.gif" /></a></td> +</tr> +<tr class="b"><td align="left">commons-collections:commons-collections:jar:3.2.1</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://repo1.maven.org/maven2/commons-collections/commons-collections/3.2.1/..." class="externalLink"><img alt="Found at http://repo1.maven.org/maven2" src="images/icon_success_sml.gif" /></a></td> +</tr> +<tr class="a"><td align="left">commons-lang:commons-lang:jar:2.4</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://repo1.maven.org/maven2/commons-lang/commons-lang/2.4/commons-lang-2.4..." class="externalLink"><img alt="Found at http://repo1.maven.org/maven2" src="images/icon_success_sml.gif" /></a></td> +</tr> +<tr class="b"><td align="left">commons-logging:commons-logging:jar:1.1.1</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://repo1.maven.org/maven2/commons-logging/commons-logging/1.1.1/commons-..." class="externalLink"><img alt="Found at http://repo1.maven.org/maven2" src="images/icon_success_sml.gif" /></a></td> +</tr> +<tr class="a"><td align="left">commons-primitives:commons-primitives:jar:1.0</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://repo1.maven.org/maven2/commons-primitives/commons-primitives/1.0/comm..." class="externalLink"><img alt="Found at http://repo1.maven.org/maven2" src="images/icon_success_sml.gif" /></a></td> +</tr> +<tr class="b"><td align="left">dom4j:dom4j:jar:1.6.1</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://repo1.maven.org/maven2/dom4j/dom4j/1.6.1/dom4j-1.6.1.jar" class="externalLink"><img alt="Found at http://repo1.maven.org/maven2" src="images/icon_success_sml.gif" /></a></td> +</tr> +<tr class="a"><td align="left">javassist:javassist:jar:3.4.GA</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://repo1.maven.org/maven2/javassist/javassist/3.4.GA/javassist-3.4.GA.ja..." class="externalLink"><img alt="Found at http://repo1.maven.org/maven2" src="images/icon_success_sml.gif" /></a></td> +</tr> +<tr class="b"><td align="left">javax.transaction:jta:jar:1.1</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://repo1.maven.org/maven2/javax/transaction/jta/1.1/jta-1.1.jar" class="externalLink"><img alt="Found at http://repo1.maven.org/maven2" src="images/icon_success_sml.gif" /></a></td> +</tr> +<tr class="a"><td align="left">junit:junit:jar:4.6</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://repo1.maven.org/maven2/junit/junit/4.6/junit-4.6.jar" class="externalLink"><img alt="Found at http://repo1.maven.org/maven2" src="images/icon_success_sml.gif" /></a></td> +</tr> +<tr class="b"><td align="left">log4j:log4j:jar:1.2.14</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://repo1.maven.org/maven2/log4j/log4j/1.2.14/log4j-1.2.14.jar" class="externalLink"><img alt="Found at http://repo1.maven.org/maven2" src="images/icon_success_sml.gif" /></a></td> +</tr> +<tr class="a"><td align="left">net.sf.ehcache:ehcache:jar:1.2.3</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://repo1.maven.org/maven2/net/sf/ehcache/ehcache/1.2.3/ehcache-1.2.3.jar" class="externalLink"><img alt="Found at http://repo1.maven.org/maven2" src="images/icon_success_sml.gif" /></a></td> +</tr> +<tr class="b"><td align="left">org.hibernate:hibernate-core:jar:3.3.1.GA</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://repo1.maven.org/maven2/org/hibernate/hibernate-core/3.3.1.GA/hibernat..." class="externalLink"><img alt="Found at http://repo1.maven.org/maven2" src="images/icon_success_sml.gif" /></a></td> +</tr> +<tr class="a"><td align="left">org.hibernate:hibernate-ehcache:jar:3.3.1.GA</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://repo1.maven.org/maven2/org/hibernate/hibernate-ehcache/3.3.1.GA/hiber..." class="externalLink"><img alt="Found at http://repo1.maven.org/maven2" src="images/icon_success_sml.gif" /></a></td> +</tr> +<tr class="b"><td align="left">org.nuiton:nuiton-i18n-api:jar:1.0.0-rc-1</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://maven.nuiton.org/release/org/nuiton/nuiton-i18n-api/1.0.0-rc-1/nuiton..." class="externalLink"><img alt="Found at http://maven.nuiton.org/release" src="images/icon_success_sml.gif" /></a></td> +<td align="center">-</td> +<td align="center">-</td> +</tr> +<tr class="a"><td align="left">org.nuiton:nuiton-utils:jar:1.1.0-rc-1</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://maven.nuiton.org/release/org/nuiton/nuiton-utils/1.1.0-rc-1/nuiton-ut..." class="externalLink"><img alt="Found at http://maven.nuiton.org/release" src="images/icon_success_sml.gif" /></a></td> +<td align="center">-</td> +<td align="center">-</td> +</tr> +<tr class="b"><td align="left">org.nuiton.topia:topia-persistence:jar:tests:2.2.0-rc-2-SNAPSHOT</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://maven.nuiton.org/snapshot/org/nuiton/topia/topia-persistence/2.2.0-rc..." class="externalLink"><img alt="Found at http://maven.nuiton.org/snapshot" src="images/icon_success_sml.gif" /></a></td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +</tr> +<tr class="a"><td align="left">org.nuiton.topia:topia-persistence:jar:2.2.0-rc-2-SNAPSHOT</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://maven.nuiton.org/snapshot/org/nuiton/topia/topia-persistence/2.2.0-rc..." class="externalLink"><img alt="Found at http://maven.nuiton.org/snapshot" src="images/icon_success_sml.gif" /></a></td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +</tr> +<tr class="b"><td align="left">org.slf4j:slf4j-api:jar:1.5.2</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://repo1.maven.org/maven2/org/slf4j/slf4j-api/1.5.2/slf4j-api-1.5.2.jar" class="externalLink"><img alt="Found at http://repo1.maven.org/maven2" src="images/icon_success_sml.gif" /></a></td> +</tr> +<tr class="a"><td align="left">org.slf4j:slf4j-log4j12:jar:1.5.2</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://repo1.maven.org/maven2/org/slf4j/slf4j-log4j12/1.5.2/slf4j-log4j12-1...." class="externalLink"><img alt="Found at http://repo1.maven.org/maven2" src="images/icon_success_sml.gif" /></a></td> +</tr> +<tr class="b"><td align="left">xml-apis:xml-apis:jar:1.0.b2</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center">-</td> +<td align="center"><a href="http://repo1.maven.org/maven2/xml-apis/xml-apis/1.0.b2/xml-apis-1.0.b2.jar" class="externalLink"><img alt="Found at http://repo1.maven.org/maven2" src="images/icon_success_sml.gif" /></a></td> +</tr> +<tr class="a"><th align="left">Total</th> +<th align="center">apache-snapshot-repository</th> +<th align="center">apache.snapshots</th> +<th align="center">nuiton.snapshot</th> +<th align="center">nuiton.release</th> +<th align="center">codehaus-repository</th> +<th align="center">central</th> +</tr> +<tr class="b"><td align="left">22 (compile: 14, test: 4, runtime: 4)</td> +<td align="center">0</td> +<td align="center">0</td> +<td align="center">2</td> +<td align="center">2</td> +<td align="center">0</td> +<td align="center">18</td> +</tr> +</tbody> +</table> +</div> + + </div> + </div> + <div id="poweredBy"> + + + + <a href="http://maven.apache.org" title="Maven" class="poweredBy"> + <img class="poweredBy" + alt="Maven" + src="http://maven-site.nuiton.org/jrst/images/logos/maven-feather.png" + width="90" + height="30" + /> + </a> + + + + <a href="http://maven-site.nuiton.org/jrst/" title="JRst" class="poweredBy"> + <img class="poweredBy" + alt="JRst" + src="http://maven-site.nuiton.org/jrst/images/jrst-logo.png" + width="90" + height="30" + /> + </a> + + + + <a href="http://docutils.sourceforge.net/rst.html" title="ReStructuredText" class="poweredBy"> + <img class="poweredBy" + alt="ReStructuredText" + src="http://maven-site.nuiton.org/jrst/images/restructuredtext-logo.png" + width="90" + height="30" + /> + </a> + </div> + <div class="clear"> + <hr/> + </div> + <div id="footer"> + <div class="xright"> + © + 2004-2009 + + CodeLutin + + + + + + + + + </div> + <div class="clear"> + <hr/> + </div> + </div> + </body> +</html> diff --git a/topia-service-replication/src/site/resources/dependency-management.html b/topia-service-replication/src/site/resources/dependency-management.html new file mode 100644 index 0000000..3709562 --- /dev/null +++ b/topia-service-replication/src/site/resources/dependency-management.html @@ -0,0 +1,511 @@ +<!-- + #%L + ToPIA :: Service Replication + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> + + + + + + + + + + + +<html xmlns="http://www.w3.org/1999/xhtml"> + <head> + <title>ToPIA - Replication service - Gestion des dépendances du project</title> + <style type="text/css" media="all"> + @import url("./css/maven-base.css"); + @import url("./css/maven-theme.css"); + @import url("./css/lutin.css"); + @import url("./css/site.css"); + </style> + <link rel="stylesheet" href="./css/print.css" type="text/css" media="print" /> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> + </head> + <body class="composite"> + <div id="banner"> + <a href="index.html" id="bannerLeft" > + + ToPIA - Replication service + + </a> + <a href="http://www.codelutin.com/" id="bannerRight" > + + <img src="http://www.codelutin.com/images/lutinorange-codelutin.png" alt="$alt" /> + + </a> + <div class="clear"> + <hr/> + </div> + </div> + <div id="breadcrumbs"> + + + + + + + + + <div class="xleft"> + Dernière publication: 09/06/2009 + | Version: 2.2.0-rc-2-SNAPSHOT + </div> + <div class="xright"> <a href="http://www.chorem.org" class="externalLink">Chorem</a> + | + <a href="http://www.nuiton.org" class="externalLink">Nuiton</a> + | + <a href="http://labs.libre-entreprise.org/" class="externalLink">Labs</a> + | + <a href="http://www.codelutin.com/" class="externalLink">CodeLutin</a> + | + <a href="http://maven.nuiton.org/release" class="externalLink">dépôt maven</a> + + + + + + + + + </div> + <div class="clear"> + <hr/> + </div> + </div> + <div id="leftColumn"> + <div id="navcolumn"> + + + + + + + + + <h5>Projet parent</h5> + <ul> + + <li class="none"> + <a href="../index.html">ToPIA - Tools for Portable and Independent Architecture</a> + </li> + </ul> + <h5>Utilisateur</h5> + <ul> + + <li class="none"> + <a href="index.html">Accueil</a> + </li> + </ul> + <h5>Téléchargement</h5> + <ul> + + <li class="none"> + <a href="http://maven.nuiton.org/release/org/nuiton/topia/topia-service-replication/2..." class="externalLink">Librairie (jar)</a> + </li> + + <li class="none"> + <a href="http://maven.nuiton.org/release/org/nuiton/topia/topia-service-replication/2..." class="externalLink">Javadoc (jar)</a> + </li> + + <li class="none"> + <a href="http://maven.nuiton.org/release/org/nuiton/topia/topia-service-replication/2..." class="externalLink">Sources (jar)</a> + </li> + </ul> + <h5>Documentation sur le projet</h5> + <ul> + + + + + + + + + + + + + + + + <li class="expanded"> + <a href="project-info.html">Info Projet</a> + <ul> + + <li class="none"> + <a href="index.html">Bienvenue dans le projet</a> + </li> + + <li class="none"> + <a href="issue-tracking.html">Contrôle des livraisons</a> + </li> + + <li class="none"> + <a href="dependencies.html">Dépendances</a> + </li> + + <li class="none"> + <a href="source-repository.html">Dépôt de sources</a> + </li> + + <li class="none"> + <strong>Gestion des dépendances</strong> + </li> + + <li class="none"> + <a href="plugin-management.html">Gestion des plugins</a> + </li> + + <li class="none"> + <a href="integration.html">Intégration continue</a> + </li> + + <li class="none"> + <a href="license.html">Licence du projet</a> + </li> + + <li class="none"> + <a href="mail-lists.html">Listes de diffusion</a> + </li> + + <li class="none"> + <a href="team-list.html">Membres de ce projet</a> + </li> + + <li class="none"> + <a href="plugins.html">Plugins du projet</a> + </li> + + <li class="none"> + <a href="project-summary.html">Résumé du projet</a> + </li> + </ul> + </li> + + + + + + + + + + + + + + + + <li class="collapsed"> + <a href="project-reports.html">Rapports Projet</a> + </li> + </ul> + + + + + + + + + </div> + + + <!-- Search Google --> + <div id="Google-search"> + <form method="get" action="http://www.google.fr/custom" target="_top"> + <input type="hidden" name="client" value="pub-5067547915349883"></input> + <input type="hidden" name="forid" value="1"></input> + <input type="hidden" name="ie" value="ISO-8859-15"></input> + <input type="hidden" name="oe" value="ISO-8859-15"></input> + <input type="hidden" name="cof" value="GALT:#008000;GL:1;DIV:#336699;VLC:663399;AH:center;BGC:FFFFFF;LBGC:336699;ALC:0000FF;LC:0000FF;T:000000;GFNT:0000FF;GIMP:0000FF;LH:50;LW:210;L:http://www.codelutin.com/images/ + <input type="hidden" name="hl" value="fr"></input> + <table bgcolor="#eeeeee"> + <tr> + <td valign="top" align="right"> + <label for="sbi" style="display: none">Entrez les termes que vous recherchez.</label> + <input type="text" name="q" size="10" maxlength="255" value="" id="sbi"></input> + <label for="sbb" style="display: none">Envoyer un formulaire de recherche</label> + </td> + </tr> + <tr> + <td valign="top" align="right"> + <input type="submit" name="sa" value="Google" id="sbb"></input> + </td> + </tr> + </table> + </form> + </div> + <!-- Search Google --> + + <!-- paypal --> + <div id="paypal"> + <!-- + | FIXME probleme de recherche de lutin-site-skin_XX.properties, dans doute pas dans le classpath :(, mais ou mettre la dependance :( - test dans le plugin declaration, dans dep du projet, marche pas + | <a href=" # $ i18n.getString( "maven-lutin-skin", fr, "whymakedonate.url" )"> $ i18n.getString( "maven-lutin-skin", fr, "whymakedonate" )</a> + +--> + <a href="http://www.codelutin.com/whymakedonate/en">Why make donate</a> + <form action="https://www.paypal.com/cgi-bin/webscr" method="post"> + <input type="hidden" name="cmd" value="_xclick"/> + <input type="hidden" name="business" value="paypal@codelutin.com"/> + <input type="hidden" name="item_name" value="topia-service-replication"/> + <input type="hidden" name="item_number" value="Don"/> + <input type="hidden" name="no_shipping" value="2"/> + <input type="hidden" name="no_note" value="1"/> + <input type="hidden" name="currency_code" value="EUR"/> + <input type="hidden" name="tax" value="0"/> + <input type="hidden" name="lc" value="FR"/> + <input type="hidden" name="bn" value="PP-DonationsBF"/> + <input type="image" src="images/paypal.gif" border="0" name="submit" alt="Effectuez vos dons via PayPal"/> + </form> + </div> + <div id="Google-AdSense"> + <script type="text/javascript"> + <!-- + google_ad_client = "pub-5067547915349883"; + google_ad_width = 120; + google_ad_height = 240; + google_ad_format = "120x240_as"; + google_ad_type = "text"; + google_ad_channel = ""; + //--> + </script> + <script type="text/javascript" + src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> + </script> + + <script src="http://www.google-analytics.com/urchin.js" type="text/javascript"> + </script> + <script type="text/javascript"> + <!-- + _uacct = "UA-1064192-2"; + _udn="labs.libre-entreprise.org"; + urchinTracker(); + // --> + </script> + <div id="googleAnalytics"> + <a class="tiny" href="http://www.codelutin.com/google">google analytics</a> + </div> + </div> + </div> + <div id="bodyColumn"> + <div id="contentBox"> + <div class="section"><h2>Gestion des dépendances du project</h2> +<div class="section"><h3>compile</h3> +<p>Ce qui suit est la liste des dépendances définies dans dependencyManagement de portée compile pour ce projet. Ces dépendances sont requises pour compiler et exécuter l´application :</p> +<table class="bodyTable"><tr class="a"><th>GroupId</th> +<th>ArtifactId</th> +<th>Version</th> +<th>Type</th> +</tr> +<tr class="b"><td>asm</td> +<td><a href="http://asm.objectweb.org/" class="externalLink">asm</a></td> +<td>1.5.3</td> +<td>jar</td> +</tr> +<tr class="a"><td>commons-collections</td> +<td><a href="http://commons.apache.org/collections/" class="externalLink">commons-collections</a></td> +<td>3.2.1</td> +<td>jar</td> +</tr> +<tr class="b"><td>commons-lang</td> +<td><a href="http://commons.apache.org/lang/" class="externalLink">commons-lang</a></td> +<td>2.4</td> +<td>jar</td> +</tr> +<tr class="a"><td>org.apache.lucene</td> +<td><a href="http://lucene.apache.org/java/lucene-core" class="externalLink">lucene-core</a></td> +<td>2.4.1</td> +<td>jar</td> +</tr> +<tr class="b"><td>org.apache.tapestry</td> +<td><a href="http://tapestry.apache.org/tapestry5.1/tapestry-core" class="externalLink">tapestry-core</a></td> +<td>5.1.0.5</td> +<td>jar</td> +</tr> +<tr class="a"><td>org.apache.xmlrpc</td> +<td><a href="http://ws.apache.org/xmlrpc/xmlrpc-client" class="externalLink">xmlrpc-client</a></td> +<td>3.1.2</td> +<td>jar</td> +</tr> +<tr class="b"><td>org.apache.xmlrpc</td> +<td><a href="http://ws.apache.org/xmlrpc/xmlrpc-server" class="externalLink">xmlrpc-server</a></td> +<td>3.1.2</td> +<td>jar</td> +</tr> +<tr class="a"><td>org.codehaus.xfire</td> +<td>xfire-java5</td> +<td>1.2.6</td> +<td>jar</td> +</tr> +<tr class="b"><td>org.hibernate</td> +<td><a href="http://hibernate.org/hibernate-core" class="externalLink">hibernate-core</a></td> +<td>3.3.1.GA</td> +<td>jar</td> +</tr> +<tr class="a"><td>org.nuiton</td> +<td><a href="http://maven-site.nuiton.org/nuiton-utils" class="externalLink">nuiton-utils</a></td> +<td>1.1.0-rc-1</td> +<td>jar</td> +</tr> +<tr class="b"><td>xom</td> +<td><a href="http://www.xom.nu" class="externalLink">xom</a></td> +<td>1.1</td> +<td>jar</td> +</tr> +</table> +</div> +<div class="section"><h3>runtime</h3> +<p>Ce qui suit est la liste des dépendances définies dans dependencyManagement de portée runtime pour ce projet. Ces dépendances sont requises pour exécuter l´application :</p> +<table class="bodyTable"><tr class="a"><th>GroupId</th> +<th>ArtifactId</th> +<th>Version</th> +<th>Type</th> +</tr> +<tr class="b"><td>commons-httpclient</td> +<td><a href="http://jakarta.apache.org/httpcomponents/httpclient-3.x/" class="externalLink">commons-httpclient</a></td> +<td>3.1</td> +<td>jar</td> +</tr> +<tr class="a"><td>javassist</td> +<td><a href="http://www.jboss.org/products/javassist" class="externalLink">javassist</a></td> +<td>3.4.GA</td> +<td>jar</td> +</tr> +<tr class="b"><td>org.hibernate</td> +<td><a href="http://hibernate.org/hibernate-ehcache" class="externalLink">hibernate-ehcache</a></td> +<td>3.3.1.GA</td> +<td>jar</td> +</tr> +<tr class="a"><td>org.slf4j</td> +<td><a href="http://www.slf4j.org" class="externalLink">slf4j-log4j12</a></td> +<td>1.5.2</td> +<td>jar</td> +</tr> +</table> +</div> +<div class="section"><h3>test</h3> +<p>Ce qui suit est la liste des dépendances définies dans dependencyManagement de portée test pour ce projet. Ces dépendances sont requises seulement pour compiler et exécuter les tests unitaires de l´application :</p> +<table class="bodyTable"><tr class="b"><th>GroupId</th> +<th>ArtifactId</th> +<th>Version</th> +<th>Type</th> +</tr> +<tr class="a"><td>com.h2database</td> +<td><a href="http://www.h2database.com" class="externalLink">h2</a></td> +<td>1.1.113</td> +<td>jar</td> +</tr> +<tr class="b"><td>jetty</td> +<td><a href="http://jetty.mortbay.org/" class="externalLink">jetty</a></td> +<td>5.1.10</td> +<td>jar</td> +</tr> +<tr class="a"><td>junit</td> +<td><a href="http://junit.org" class="externalLink">junit</a></td> +<td>4.6</td> +<td>jar</td> +</tr> +</table> +</div> +<div class="section"><h3>provided</h3> +<p>Ce qui suit est la liste des dépendances définies dans dependencyManagement de portée provided pour ce projet. Ces dépendances sont requises pour compiler l´application, mais devraient être fournies par défaut lors de l´utilisation de cette librairie :</p> +<table class="bodyTable"><tr class="b"><th>GroupId</th> +<th>ArtifactId</th> +<th>Version</th> +<th>Type</th> +</tr> +<tr class="a"><td>org.nuiton</td> +<td><a href="http://maven-site.nuiton.org/eugene" class="externalLink">eugene</a></td> +<td>1.0.0-rc-2-SNAPSHOT</td> +<td>jar</td> +</tr> +</table> +</div> +</div> + + </div> + </div> + <div id="poweredBy"> + + + + <a href="http://maven.apache.org" title="Maven" class="poweredBy"> + <img class="poweredBy" + alt="Maven" + src="http://maven-site.nuiton.org/jrst/images/logos/maven-feather.png" + width="90" + height="30" + /> + </a> + + + + <a href="http://maven-site.nuiton.org/jrst/" title="JRst" class="poweredBy"> + <img class="poweredBy" + alt="JRst" + src="http://maven-site.nuiton.org/jrst/images/jrst-logo.png" + width="90" + height="30" + /> + </a> + + + + <a href="http://docutils.sourceforge.net/rst.html" title="ReStructuredText" class="poweredBy"> + <img class="poweredBy" + alt="ReStructuredText" + src="http://maven-site.nuiton.org/jrst/images/restructuredtext-logo.png" + width="90" + height="30" + /> + </a> + </div> + <div class="clear"> + <hr/> + </div> + <div id="footer"> + <div class="xright"> + © + 2004-2009 + + CodeLutin + + + + + + + + + </div> + <div class="clear"> + <hr/> + </div> + </div> + </body> +</html> diff --git a/topia-service-replication/src/site/rst/index.rst b/topia-service-replication/src/site/rst/index.rst new file mode 100644 index 0000000..e029b01 --- /dev/null +++ b/topia-service-replication/src/site/rst/index.rst @@ -0,0 +1,31 @@ +.. - +.. * #%L +.. * ToPIA :: Service Replication +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +========================= +ToPIA Replication Service +========================= + +Permet de répliquer des grappes d'objets plus facilement. + +Documentation à faire. diff --git a/topia-service-replication/src/site/site.xml b/topia-service-replication/src/site/site.xml new file mode 100644 index 0000000..ad4c611 --- /dev/null +++ b/topia-service-replication/src/site/site.xml @@ -0,0 +1,58 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Replication + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<project name="${project.name}"> + + <bannerLeft> + <name>${project.name}</name> + <href>index.html</href> + </bannerLeft> + + <body> + + <breadcrumbs> + <item name="${project.name}" href="index.html"/> + </breadcrumbs> + + <menu ref="parent"/> + + <menu name="Utilisateur" inherit="top"/> + + <menu name="Téléchargement"> + <item + href="${repository.home.url}/org/nuiton/topia/${project.artifactId}/${project.version}/${project.build.finalName}.jar" + name="Librairie (jar)"/> + <item + href="${repository.home.url}/org/nuiton/topia/${project.artifactId}/${project.version}/${project.build.finalName}-javadoc.jar" + name="Javadoc (jar)"/> + <item + href="${repository.home.url}/org/nuiton/topia/${project.artifactId}/${project.version}/${project.build.finalName}-sources.jar" + name="Sources (jar)"/> + </menu> + + <menu ref="reports"/> + + </body> +</project> diff --git a/topia-service-replication/src/test/java/org/nuiton/topia/replication/AbstractTopiaReplicationServiceTest.java b/topia-service-replication/src/test/java/org/nuiton/topia/replication/AbstractTopiaReplicationServiceTest.java new file mode 100644 index 0000000..f4538c9 --- /dev/null +++ b/topia-service-replication/src/test/java/org/nuiton/topia/replication/AbstractTopiaReplicationServiceTest.java @@ -0,0 +1,694 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication; + +import org.apache.commons.logging.Log; +import org.junit.Assert; +import org.nuiton.i18n.I18n; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaDAO; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.util.EntityOperator; +import org.nuiton.topia.persistence.util.EntityOperatorStore; +import org.nuiton.topia.persistence.util.TopiaEntityHelper; +import org.nuiton.topia.replication.model.ReplicationModel; +import org.nuiton.topia.replication.model.ReplicationNode; +import org.nuiton.topia.replication.model.ReplicationOperationDef; + +import java.io.File; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Set; + +/** + * TopiaReplicationServiceImplTest. + * <p/> + * Created: 07 jun. 09 17:14:22 + * + * @author tchemit <chemit@codelutin.com> + * @version $Id$ + * @since 2.2.0 + */ +public abstract class AbstractTopiaReplicationServiceTest extends Assert { + + static protected TopiaContext context; + + static protected TopiaContext ctxt; + + protected TopiaContextImplementor dstCtxt; + + protected TopiaReplicationService service; + + protected ReplicationModel model; + + static protected boolean init; + + static private Long testsTimeStamp; + + static private File testsBasedir; + + static private final String TEST_BASEDIR = "target%1$ssurefire-tests%1$s%2$td_%2$tm_%2$tY%1$s%2$tH_%2$tM_%2$tS"; + + public static void after() throws Exception { + if (context != null && !context.isClosed()) { + try { + context.closeContext(); + } catch (TopiaException e) { + // cela peut arriver si on demande la fermeture dans un thread + // ailleurs... + } + } + init = false; + } + + public void setUp() throws Exception { + + if (!init) { + + I18n.setDefaultLocale(Locale.FRANCE); + + try { + context = createDb("source"); + } catch (Exception e) { + getLog().error("could not create db source.", e); + throw e; + } + init = true; + } + + ctxt = context.beginTransaction(); + + service = context.getService(TopiaReplicationService.class); + } + + public void tearDown() throws Exception { + if (ctxt != null) { + ctxt.rollbackTransaction(); + ctxt.closeContext(); + ctxt = null; + } + service = null; + } + + + protected TopiaReplicationModelBuilder getModelBuilder() { + return service.getModelBuilder(); + } + + protected abstract TopiaContext createDb2(String name) throws Exception; + + protected abstract TopiaContext createDb(String name) throws Exception; + + protected TopiaContext createReplicateDb(Object contract) throws Exception { + TopiaContext rootCtxt = createDb2(contract.toString() + dbCounter++); + return rootCtxt; + } + + protected abstract TopiaEntityEnum[] getContracts(); + + protected abstract Log getLog(); + + protected <E extends TopiaEntity> E update(E e) throws TopiaException { + return (E) ctxt.findByTopiaId(e.getTopiaId()); + } + + /** + * Test of detectTypes method, of class ReplicationServiceImplementor. + * + * @throws Exception if any error + */ + public void testDetectTypes() throws Exception { + } + + /** + * Test of getOperation method, of class ReplicationServiceImplementor. + * + * @throws Exception if any error + */ + public void testGetOperation() throws Exception { + } + + /** + * Test of detectAssociations method, of class ReplicationModel. + * + * @throws Exception if any error + */ + public void testDetectAssociations() throws Exception { + } + + /** + * Test of detectDirectDependencies method, of class ReplicationModel. + * + * @throws Exception if any error + */ + public void testDetectDirectDependencies() throws Exception { + } + + /** + * Test of detectShell method, of class ReplicationModel. + * + * @throws Exception if any error + */ + public void testDetectShell() throws Exception { + } + + /** + * Test of detectDependencies method, of class ReplicationModel. + * + * @throws Exception if any error + */ + public void testDetectDependencies() throws Exception { + } + + /** + * Test of detectObjectsToDettach method, of class ReplicationModel. + * + * @throws Exception if any error + */ + public void testDetectObjectsToDettach() throws Exception { + } + + /** + * Test of detectOperations method, of class ReplicationModel. + * + * @throws Exception if any error + */ + public void testDetectOperations() throws Exception { + } + + /** + * Test of doReplicate method, of class ReplicationService. + * + * @throws Exception if any error + */ + public void testDoReplicate() throws Exception { + } + + protected void detectTypes(TopiaEntity entity, Object... expectedCouple) throws TopiaException { + + Set<?> detectTypes; + + detectTypes = service.getModelBuilder().detectTypes(context, getContracts(), entity.getTopiaId()); + assertEquals("expected types : " + + Arrays.toString(expectedCouple) + + " but was " + detectTypes, + expectedCouple.length, detectTypes.size()); + for (Object o : expectedCouple) { + assertTrue(detectTypes.contains(o)); + } + } + + protected void getOperation(Class<? extends TopiaReplicationOperation> operationClass, boolean shouldExist) throws TopiaException { + TopiaReplicationOperation operation = getModelBuilder().getOperationProvider().getOperation(operationClass); + assertEquals(shouldExist, operation != null); + } + + protected void detectAssociations(TopiaEntity entity, + Object... expectedCouple) + throws TopiaException { + + createModel(entity); + model.detectAssociations(); + + assertEquals(0, expectedCouple.length % 2); + + for (int i = 0, j = expectedCouple.length / 2; i < j; i++) { + TopiaEntityEnum src = (TopiaEntityEnum) expectedCouple[2 * i]; + String name = (String) expectedCouple[2 * i + 1]; + ReplicationNode nodeSrc = model.getNode(src); + assertNotNull("association " + name + " not found", nodeSrc); + assertTrue(nodeSrc.hasAssociation()); + assertTrue(nodeSrc.getAssociations().containsKey(name)); + } + } + + protected void detectDirectDependencies(TopiaEntity entity, + Object... expectedCouple) + throws TopiaException { + + createModel(entity); + model.detectDirectDependencies(); + + assertEquals(0, expectedCouple.length % 2); + + for (int i = 0, j = expectedCouple.length / 2; i < j; i++) { + TopiaEntityEnum src = (TopiaEntityEnum) expectedCouple[2 * i]; + String name = (String) expectedCouple[2 * i + 1]; + ReplicationNode nodeSrc = model.getNode(src); + assertTrue(nodeSrc + " should have dependency but was not!", nodeSrc.hasDependency()); + assertTrue(nodeSrc + " should contain dependency " + name + "but was not! (" + nodeSrc.getDependencies() + ")", nodeSrc.getDependencies().containsKey(name)); + } + } + + protected void detectShell(TopiaEntity entity, + TopiaEntityEnum... expected) throws + TopiaException { + Set<ReplicationNode> shell; + + createModel(entity); + model.detectAssociations(); + model.detectDirectDependencies(); + model.detectShell(); + + TopiaEntityEnum c = TopiaEntityHelper.getEntityEnum( + entity.getClass(), getContracts()); + assertNotNull(c); + shell = model.getNode(c).getShell(); + assertEquals( + "expected shell : " + Arrays.toString(expected) + ", but was " + + shell, expected.length, shell.size()); + + for (int i = 0, j = expected.length; i < j; i++) { + TopiaEntityEnum type = expected[i]; + ReplicationNode node = model.getNode(type); + assertTrue(shell.contains(node)); + assertEquals(type, node.getContract()); + } + } + + protected void detectDependencies( + TopiaEntity entity, + TopiaEntityEnum[]... expected) throws TopiaException { + + createModel(entity); + model.detectAssociations(); + model.detectDirectDependencies(); + model.detectShell(); + model.detectDependencies(); + List<ReplicationNode> dependencies = model.getOrder(); + + int i = 0; + for (ReplicationNode level : dependencies) { + getLog().info("level " + level + " = " + level); + } + +// assertEquals("expected " + expected.length + " levels but had " + dependencies.size(), expected.length, dependencies.size()); +// +// Iterator<List<ReplicationNode>> order = dependencies.iterator(); +// if (entity != null) { +// getLog().info("for " + entity.getTopiaId()); +// } +// int index = 0; +// for (TopiaEntityEnum[] expectedLevel : expected) { +// +// List<ReplicationNode> next = order.next(); +// getLog().info("level " + (index++) + " : " + next); +// for (TopiaEntityEnum ee : expectedLevel) { +// ReplicationNode expectedNode = model.getNode(ee); +// +// assertTrue("should have contains node " + expectedNode, next.contains(expectedNode)); +// } +// +// } + } + + protected void detectObjectsToDettach(TopiaEntity entity, Object... expected) throws TopiaException { + + assertEquals(0, expected.length % 2); + + createModel(entity); + model.detectAssociations(); + model.detectDirectDependencies(); + model.detectShell(); + model.detectDependencies(); + model.detectObjectsToDettach(); + Set<ReplicationNode> nodes = new HashSet<ReplicationNode>(); + + for (int i = 0, j = expected.length / 2; i < j; i++) { + TopiaEntityEnum e = (TopiaEntityEnum) expected[2 * i]; + ReplicationNode node = model.getNode(e); + String[] ids = (String[]) expected[2 * i + 1]; + assertEquals(ids.length > 0, node.hasAssociationsToDettach()); + for (String id : ids) { + assertTrue(node.getAssociationsToDettach().contains(id)); + } + nodes.add(node); + } + + for (ReplicationNode node : model.getNodes()) { + if (!nodes.contains(node)) { + // on verifie bien qu'il n' y a pas d'associations dettachee + assertFalse(node.hasAssociationsToDettach()); + } + } + + } + + protected void detectOperations(TopiaEntity entity, Object... expected) throws TopiaException { + + assertEquals(0, expected.length % 2); + + if (entity == null) { + prepareModel(); + } else { + prepareModel(entity.getTopiaId()); + } +// createModel(entity); +// model.detectAssociations(); +// model.detectDirectDependencies(); +// model.detectShell(); +// model.detectDependencies(); +// model.detectObjectsToDettach(); +// model.detectOperations(); + + if (getLog().isInfoEnabled()) { + getLog().info("=========================================================================="); + if (entity == null) { + + getLog().info("resume of operations for all "); + } else { + getLog().info("resume of operations for entity " + entity.getTopiaId()); + } + + for (ReplicationNode node : model.getOrder()) { + ReplicationOperationDef[] operations = node.getOperations(); + for (ReplicationOperationDef op : operations) { + getLog().info("[" + node + "] : operation " + op); + } + } + getLog().info("=========================================================================="); + } + } + + private static int dbCounter; + + protected void doReplicate(TopiaEntityEnum contract, + TopiaEntity... entity) throws Exception { + + TopiaContext rootCtxt = createReplicateDb("doReplicate_" + contract); + + List<String> ids = TopiaEntityHelper.getTopiaIdList(Arrays.asList(entity)); + getLog().info("entity " + ids); + + prepareModel(ids.toArray(new String[ids.size()])); + + dstCtxt = (TopiaContextImplementor) rootCtxt; + + service.doReplicate(model, dstCtxt); + + //dstCtxt.closeContext(); + + if (entity.length == 0) { + + return; + } + dstCtxt = (TopiaContextImplementor) rootCtxt.beginTransaction(); + + for (TopiaEntity e : entity) { + TopiaEntity actual = dstCtxt.findByTopiaId(e.getTopiaId()); + assertNotNull(actual); + assertEquals(e, actual); + } + + dstCtxt.closeContext(); + + dstCtxt = (TopiaContextImplementor) rootCtxt; + } + + protected void doReplicateAll() throws Exception { + + TopiaContext rootCtxt = createReplicateDb("doReplicateAll"); + + prepareModelAll(); + + dstCtxt = (TopiaContextImplementor) rootCtxt; + + service.doReplicate(model, dstCtxt); + + TopiaContextImplementor ctxt2 = (TopiaContextImplementor) ctxt; + dstCtxt = (TopiaContextImplementor) rootCtxt.beginTransaction(); + + assertDbEquals(model.getContracts(), (TopiaContextImplementor) ctxt, ctxt2); + + dstCtxt.closeContext(); + + dstCtxt = (TopiaContextImplementor) rootCtxt; + } + + protected void doReplicateWithComputedOrder(TopiaEntity... entity) throws Exception { + + TopiaContext rootCtxt = createReplicateDb("doReplicateWithComputedOrder"); + + List<String> ids = TopiaEntityHelper.getTopiaIdList(Arrays.asList(entity)); + + prepareModelWithComputedOrder(ids.toArray(new String[ids.size()])); + + dstCtxt = (TopiaContextImplementor) rootCtxt; + + service.doReplicate(model, dstCtxt); + + getLog().info("replication is done for " + Arrays.toString(entity) + ", will verify data..."); + + TopiaContextImplementor ctxt2 = (TopiaContextImplementor) ctxt; + dstCtxt = (TopiaContextImplementor) rootCtxt.beginTransaction(); + + assertDbEquals(model.getContracts(), (TopiaContextImplementor) ctxt, ctxt2); + + dstCtxt.closeContext(); + + dstCtxt = (TopiaContextImplementor) rootCtxt; + } + + protected void assertDbEquals(TopiaEntityEnum[] contracts, + TopiaContextImplementor ctxt, + TopiaContextImplementor ctxt2) throws TopiaException { + Set<String> ids = new HashSet<String>(); + + if (getLog().isInfoEnabled()) { + getLog().info("will verify db for contracts " + Arrays.toString(contracts)); + } + for (TopiaEntityEnum c : contracts) { + if (getLog().isDebugEnabled()) { + getLog().debug("verify for contract " + c); + } + TopiaDAO<? extends TopiaEntity> daoSrc = ctxt.getDAO(c.getContract()); + TopiaDAO<? extends TopiaEntity> daoDst = ctxt2.getDAO(c.getContract()); + long nbSrc = daoSrc.count(); + long nbDst = daoDst.count(); + assertEquals("le nombres d'entites de type " + c + " devrait etre " + nbSrc + " mais est " + nbDst, nbSrc, nbDst); + List<String> idsSrc = daoSrc.findAllIds(); + List<String> idsDst = daoDst.findAllIds(); + Collections.sort(idsSrc); + Collections.sort(idsDst); + assertEquals(idsSrc, idsDst); + for (String id : idsSrc) { + if (getLog().isDebugEnabled()) { + getLog().debug("verify for entity " + id); + } + TopiaEntity eSrc = daoSrc.findByTopiaId(id); + TopiaEntity eDst = daoDst.findByTopiaId(id); + assertEquals(eSrc, eDst); + assertEntityEquals(eSrc, eDst, ids); + } + } + } + + protected void assertEntityEquals(TopiaEntity expected, + TopiaEntity actual, + Set<String> treated) { + if (treated == null) { + treated = new HashSet<String>(); + } + if (treated.contains(actual.getTopiaId())) { + return; + } + if (getLog().isDebugEnabled()) { + getLog().debug(expected); + } + assertEquals(actual.getTopiaId(), expected.getTopiaId()); + treated.add(actual.getTopiaId()); + if (getLog().isDebugEnabled()) { + getLog().debug("expected : " + expected + " / actual " + actual); + } + TopiaEntityEnum contract = TopiaEntityHelper.getEntityEnum(expected.getClass(), getContracts()); + if (contract == null) { + // this type of entity in not dealed here... + getLog().debug("untested property type " + expected.getClass()); + return; + } + Assert.assertNotNull( + "contract not found for " + expected.getClass() + " in " + + Arrays.toString(getContracts()), contract); + EntityOperator<TopiaEntity> operator = EntityOperatorStore.getOperator(contract); + List<String> associationProperties = operator.getAssociationProperties(); + for (String name : associationProperties) { + if (getLog().isDebugEnabled()) { + getLog().debug("association " + name); + } + if (operator.isChildEmpty(name, expected)) { + assertTrue("l'association " + name + " devrait etre vide mais possede " + operator.sizeChild(name, actual) + " entrees", operator.isChildEmpty(name, actual)); + } else { + assertFalse("l'association " + name + " devrait posseder " + operator.isChildEmpty(name, expected) + " mais est vide", operator.isChildEmpty(name, actual)); + + } + assertEquals(operator.isChildEmpty(name, actual), operator.isChildEmpty(name, expected)); + + Class<?> type = operator.getAssociationPropertyType(name); + Collection<?> src = (Collection<?>) operator.get(name, expected); + Collection<?> dst = (Collection<?>) operator.get(name, actual); +// assertEquals(src, dst); + Iterator<?> itrSrc = src.iterator(); + Iterator<?> itrDst = dst.iterator(); + while (itrSrc.hasNext()) { + if (TopiaEntity.class.isAssignableFrom(type)) { + assertEntityEquals((TopiaEntity) itrSrc.next(), (TopiaEntity) itrDst.next(), treated); + } else { + assertEquals(itrSrc.next(), itrDst.next()); + } + } + } + + for (String name : operator.getProperties()) { + if (getLog().isDebugEnabled()) { + getLog().debug("dependency " + name); + } + if (associationProperties.contains(name)) { + // deja traite au dessus + continue; + } + Class<?> type = operator.getPropertyType(name); + Object src = operator.get(name, expected); + Object dst = operator.get(name, actual); + assertFalse(src == null && dst != null); + assertFalse(src != null && dst == null); + if (src == null) { + continue; + } + if (TopiaEntity.class.isAssignableFrom(type)) { + assertEntityEquals((TopiaEntity) src, (TopiaEntity) dst, treated); + } else { + assertEquals(src, dst); + } + } + } + + @Deprecated + protected void createUnsupportedBeforeOperation(TopiaEntityEnum contract, + TopiaEntity entity, + Class<? extends TopiaReplicationOperation> operationClass, + Object... parameters) throws Exception { + + getLog().info("entity " + entity.getTopiaId()); + prepareModel(entity.getTopiaId()); + + getModelBuilder().addBeforeOperation(model, contract, operationClass, parameters); + // on ne doit pas avoir le droit de creer cette operation + fail(); + } + + @Deprecated + protected void createUnsupportedAfterOperation( + TopiaEntityEnum contract, + TopiaEntity entity, + Class<? extends TopiaReplicationOperation> operationClass, + Object... parameters) throws Exception { + + getLog().info("entity " + entity.getTopiaId()); + prepareModel(entity.getTopiaId()); +// model = service.createModel(getContracts()); +// model.detectDirectDependencies(); + getModelBuilder().addAfterOperation(model, contract, operationClass, parameters); + // on ne doit pas avoir le droit de creer cette operation + fail(); + } + + protected void createSupportedBeforeOperation(TopiaEntityEnum contract, + TopiaEntity entity, + Class<? extends TopiaReplicationOperation> operationClass, + Object... parameters) throws Exception { + + getLog().info("entity " + entity.getTopiaId()); + prepareModel(entity.getTopiaId()); + + getModelBuilder().addBeforeOperation(model, contract, operationClass, parameters); + // on doit avoir le droit de creer cette operation + Assert.assertTrue(true); + } + + protected void createSupportedAfterOperation( + TopiaEntityEnum contract, + TopiaEntity entity, + Class<? extends TopiaReplicationOperation> operationClass, + Object... parameters) throws Exception { + + getLog().info("entity " + entity.getTopiaId()); + prepareModel(entity.getTopiaId()); +// model = service.createModel(getContracts()); +// model.detectDirectDependencies(); + getModelBuilder().addAfterOperation(model, contract, operationClass, parameters); + // on doit avoir le droit de creer cette operation + Assert.assertTrue(true); + } + + protected Long getTestsTimeStamp() { + if (testsTimeStamp == null) { + testsTimeStamp = System.currentTimeMillis(); + getLog().info("tests timestamp : " + testsTimeStamp); + } + return testsTimeStamp; + } + + protected File getTestDir(Class<?> testClass) { + if (testsBasedir == null) { + String tmp = System.getProperty("basedir"); + if (tmp == null) { + tmp = new File("").getAbsolutePath(); + } + String name = String.format(TEST_BASEDIR, File.separator, new Date(getTestsTimeStamp())); + testsBasedir = new File(new File(tmp), name); + getLog().info("tests basedir : " + testsBasedir); + } + return new File(testsBasedir, testClass.getSimpleName()); + } + + protected void createModel(TopiaEntity entity) throws TopiaException { + model = getModelBuilder().createModel(context, + getContracts(), + true, + entity.getTopiaId() + ); + } + + protected void prepareModel(String... ids) throws TopiaException { + model = service.prepare(getContracts(), true, ids); + } + + protected void prepareModelAll() throws TopiaException { + model = service.prepareForAll(getContracts()); + } + + protected void prepareModelWithComputedOrder(String... ids) throws TopiaException { + model = service.prepare(getContracts(), false, ids); + } +} diff --git a/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationOperationTest.java b/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationOperationTest.java new file mode 100644 index 0000000..64f116e --- /dev/null +++ b/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationOperationTest.java @@ -0,0 +1,367 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.*; +import org.nuiton.topia.TestHelper; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaContextFactory; +import org.nuiton.topia.TopiaTestDAOHelper.TopiaTestEntityEnum; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.replication.model.ReplicationOperationPhase; +import org.nuiton.topia.replication.operation.*; +import org.nuiton.topia.test.entities.*; + +import java.io.File; +import java.io.IOException; +import java.util.Properties; + +/** + * TopiaReplicationServiceImplTest on model TopiaTest + * <p/> + * Created: 07 jun. 09 17:14:22 + * + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + */ +public class TopiaReplicationOperationTest extends AbstractTopiaReplicationServiceTest { + + /** Logger */ + private static final Log log = + LogFactory.getLog(TopiaReplicationOperationTest.class); + + protected static final TopiaEntityEnum[] contracts = { + TopiaTestEntityEnum.Person, + TopiaTestEntityEnum.Pet, + TopiaTestEntityEnum.Race + }; + + protected static final String entitiesList = + PersonImpl.class.getName() + "," + + PetImpl.class.getName() + "," + + RaceImpl.class.getName(); + + static protected Person person, person2; + + static protected Pet pet, pet2; + + static protected Race race, race2; + + protected static File tesDir; + + @BeforeClass + public static void beforeClass() throws IOException { + tesDir = TestHelper.getTestBasedir(TopiaReplicationOperationTest.class); + + } + + @AfterClass + public static void after() throws Exception { + AbstractTopiaReplicationServiceTest.after(); + } + + @Before + @Override + public void setUp() throws Exception { + + super.setUp(); + + person = update(person); + person2 = update(person2); + pet = update(pet); + pet2 = update(pet2); + race = update(race); + race2 = update(race2); + } + + @After + @Override + public void tearDown() throws Exception { + super.tearDown(); + if (dstCtxt != null && !dstCtxt.isClosed()) { + dstCtxt.closeContext(); + } + } + + @Test(expected = NullPointerException.class) + public void testGetOperation_nullOperationClass() throws Exception { + getModelBuilder().getOperationProvider().getOperation((Class<? extends TopiaReplicationOperation>) null); + } + + protected TopiaReplicationModelBuilder getModelBuilder() { + return service.getModelBuilder(); + } + + @Test + @Override + public void testGetOperation() throws Exception { + + getOperation(UnregistredOperation.class, false); + getOperation(UncreatableOperation.class, true); + getOperation(FakeOperation.class, true); + getOperation(Duplicate.class, true); +// getOperation(AttachAssociation.class, true); + getOperation(DettachAssociation.class, true); + } + + @Test(expected = NullPointerException.class) + public void testCreateOperation_nullModel() throws Exception { + getModelBuilder().createOperation(null, null, null, null); + } + + @Test(expected = NullPointerException.class) + public void testCreateOperation_nullType() throws Exception { + + model = getModelBuilder().createModel(context, contracts, true); + getModelBuilder().createOperation(model, null, null, null); + } + + @Test(expected = NullPointerException.class) + public void testCreateOperation_nullPhase() throws Exception { + + model = getModelBuilder().createModel(context, contracts, true); + getModelBuilder().createOperation(model, TopiaTestEntityEnum.Pet, null, null); + } + + @Test(expected = NullPointerException.class) + public void testCreateOperation_nullOperationClass() throws Exception { + + model = getModelBuilder().createModel(context, contracts, true); + getModelBuilder().createOperation(model, TopiaTestEntityEnum.Pet, ReplicationOperationPhase.before, null); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateOperation_noNode() throws Exception { + + model = getModelBuilder().createModel(context, contracts, true); + // le noeud Pet n'existe pas + getModelBuilder().addAfterOperation(model, TopiaTestEntityEnum.Pet, Duplicate.class); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateOperation_noOperation() throws Exception { + + model = getModelBuilder().createModel(context, contracts, true); + // le noeud Pet n'existe pas + getModelBuilder().addAfterOperation(model, TopiaTestEntityEnum.Pet, UnregistredOperation.class); + } + + @Test +// @Test(expected = UnsupportedOperationException.class) + public void testCreateSupportedBeforeOperation_Duplicate() throws Exception { + createSupportedBeforeOperation(TopiaTestEntityEnum.Person, person, Duplicate.class); + } + +// @Test(expected = UnsupportedOperationException.class) +// public void testCreateUnsupportedBeforeOperation_AttachAssociation() throws Exception { +// createUnsupportedBeforeOperation(TopiaTestEntityEnum.Person, person, AttachAssociation.class); +// } + + @Test +// @Test(expected = UnsupportedOperationException.class) + public void testCreateSupportedBeforeOperation_DetachAssociation() throws Exception { + createSupportedBeforeOperation(TopiaTestEntityEnum.Person, person, DettachAssociation.class); + } + + @Test +// @Test(expected = UnsupportedOperationException.class) + public void testCreateSupportedAfterOperation_Duplicate() throws Exception { + createSupportedAfterOperation(TopiaTestEntityEnum.Person, person, Duplicate.class); + } + +// @Test(expected = UnsupportedOperationException.class) +// public void testCreateSupportedAfterOperation_AttachAssociation() throws Exception { +// createUnsupportedAfterOperation(TopiaTestEntityEnum.Person, person, AttachAssociation.class); +// } + + @Test +// @Test(expected = UnsupportedOperationException.class) + public void testCreateSupportedAfterOperation_DetachAssociation() throws Exception { + createSupportedAfterOperation(TopiaTestEntityEnum.Person, person, DettachAssociation.class); + } + +// @Test(expected = UnsupportedOperationException.class) +// public void testCreateUnsupportedBeforeOperation_UncreatableOperation() throws Exception { +// createUnsupportedBeforeOperation(TopiaTestEntityEnum.Person, person, UncreatableOperation.class); +// } + +// @Test(expected = UnsupportedOperationException.class) +// public void testCreateUnsupportedAfterOperation_UncreatableOperation() throws Exception { +// createUnsupportedAfterOperation(TopiaTestEntityEnum.Person, person, UncreatableOperation.class); +// } + + @Test(expected = IllegalArgumentException.class) + public void testCreateOperation_wrongParameterNumber() throws Exception { + + model = getModelBuilder().createModel(context, contracts, true, pet.getTopiaId()); + getModelBuilder().addBeforeOperation(model, TopiaTestEntityEnum.Pet, FakeOperation.class); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateOperation_wrongParameterNumber2() throws Exception { + + model = getModelBuilder().createModel(context, contracts, true, pet.getTopiaId()); + getModelBuilder().addBeforeOperation(model, TopiaTestEntityEnum.Pet, FakeOperation.class, String.class, String.class); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateOperation_wrongParameterType() throws Exception { + + model = getModelBuilder().createModel(context, contracts, true, pet.getTopiaId()); + getModelBuilder().addBeforeOperation(model, TopiaTestEntityEnum.Pet, FakeOperation.class, Integer.class); + } + + @Test(expected = IllegalArgumentException.class) + public void testCreateOperation_nullParameter() throws Exception { + + model = getModelBuilder().createModel(context, contracts, true, pet.getTopiaId()); + getModelBuilder().addBeforeOperation(model, TopiaTestEntityEnum.Pet, FakeOperation.class, (Object) null); + } + + @Test + public void testCreateOperation() throws Exception { + + model = getModelBuilder().createModel(context, contracts, true, pet.getTopiaId()); + getModelBuilder().addBeforeOperation(model, TopiaTestEntityEnum.Pet, FakeOperation.class, "before"); + getModelBuilder().addAfterOperation(model, TopiaTestEntityEnum.Race, FakeOperation.class, "after"); + } + + @Test(expected = NullPointerException.class) + public void testDoReplicate_nullModel() throws Exception { + + service.doReplicate(null, null); + } + + @Test(expected = NullPointerException.class) + public void testDoReplicate_nullDstCtxt() throws Exception { + + model = getModelBuilder().createModel(context, contracts, true); + service.doReplicate(model, null); + } + + @Override + protected TopiaContext createDb(String name) throws Exception { + +// File localDB = new File(getTestDir(getClass()), "db_" + name); + + Properties config = getH2Properties(name); + + context = TopiaContextFactory.getContext(config); + + TopiaContextImplementor tx = + (TopiaContextImplementor) context.beginTransaction(); + + person = tx.getDAO(Person.class).create(Person.PROPERTY_NAME, "pudding master"); + race = tx.getDAO(Race.class).create(Race.PROPERTY_NAME, "race I"); + pet = tx.getDAO(Pet.class).create(Pet.PROPERTY_NAME, "pudding", Pet.PROPERTY_PERSON, person, Pet.PROPERTY_RACE, race); + + person2 = tx.getDAO(Person.class).create(Person.PROPERTY_NAME, "pudding II master"); + pet2 = tx.getDAO(Pet.class).create(Pet.PROPERTY_NAME, "pudding II"); + race2 = tx.getDAO(Race.class).create(Race.PROPERTY_NAME, "race II"); + + tx.commitTransaction(); + tx.closeContext(); + return context; + } + + @Override + protected TopiaEntityEnum[] getContracts() { + return contracts; + } + + @Override + protected Log getLog() { + return log; + } + + protected Properties getH2Properties(String dbName) throws IOException { + + Properties config = TestHelper.initTopiaContextConfiguration(tesDir, dbName); + +// config.setProperty("hibernate.show_sql", "false"); +// config.setProperty("hibernate.hbm2ddl.auto", "create"); + + config.setProperty("topia.persistence.classes", entitiesList); +// config.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); +// config.setProperty("hibernate.connection.driver_class", "org.h2.Driver"); +// config.setProperty("hibernate.connection.url", "jdbc:h2:file:" + f.getAbsolutePath() + ";create=true"); +// config.setProperty("hibernate.connection.username", "sa"); +// config.setProperty("hibernate.connection.password", ""); + + config.setProperty("topia.service.replication", TopiaReplicationServiceImpl.class.getName()); + + return config; + } + + @Override + protected TopiaContext createDb2(String name) throws Exception { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void testDetectTypes() throws Exception { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void testDetectAssociations() throws Exception { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void testDetectDirectDependencies() throws Exception { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void testDetectShell() throws Exception { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void testDetectDependencies() throws Exception { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void testDetectObjectsToDettach() throws Exception { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void testDetectOperations() throws Exception { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void testDoReplicate() throws Exception { + throw new UnsupportedOperationException("Not supported yet."); + } +} + + diff --git a/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationServiceImplAllTest.java b/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationServiceImplAllTest.java new file mode 100644 index 0000000..8d68747 --- /dev/null +++ b/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationServiceImplAllTest.java @@ -0,0 +1,306 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.nuiton.topia.TestHelper; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaContextFactory; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaTestDAOHelper.TopiaTestEntityEnum; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.test.entities.Person; +import org.nuiton.topia.test.entities.PersonImpl; +import org.nuiton.topia.test.entities.Pet; +import org.nuiton.topia.test.entities.PetImpl; +import org.nuiton.topia.test.entities.Race; +import org.nuiton.topia.test.entities.RaceImpl; + +import java.io.File; +import java.io.IOException; +import java.util.Properties; + +/** + * TopiaReplicationServiceImplTest on model TopiaTest + * <p/> + * Created: 07 jun. 09 17:14:22 + * + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + */ +public class TopiaReplicationServiceImplAllTest extends AbstractTopiaReplicationServiceTest { + + /** Logger */ + private static final Log log = LogFactory.getLog(TopiaReplicationServiceImplTest.class); + + protected static final TopiaEntityEnum[] contracts = { + TopiaTestEntityEnum.Person, + TopiaTestEntityEnum.Pet, + TopiaTestEntityEnum.Race + }; + + protected static final String entitiesList = + PersonImpl.class.getName() + "," + + PetImpl.class.getName() + "," + + RaceImpl.class.getName(); + + static protected Person person, person2; + + static protected Pet pet, pet2, pet3; + + static protected Race race, race2, race3; + + protected static File tesDir; + + @BeforeClass + public static void beforeClass() throws IOException { + tesDir = TestHelper.getTestBasedir(TopiaReplicationServiceImplAllTest.class); + } + + @AfterClass + public static void after() throws Exception { + AbstractTopiaReplicationServiceTest.after(); + } + + @Before + @Override + public void setUp() throws Exception { + + super.setUp(); + + person = update(person); + person2 = update(person2); + pet = update(pet); + pet2 = update(pet2); + race = update(race); + race2 = update(race2); + race3 = update(race3); + } + + @After + @Override + public void tearDown() throws Exception { + super.tearDown(); + if (dstCtxt != null && !dstCtxt.isClosed()) { + dstCtxt.closeContext(); + } + } + +// @Ignore + + @Test + @Override + public void testDetectTypes() throws Exception { + + detectTypes(race, Race.class); + detectTypes(pet, Pet.class, Person.class, Race.class); + detectTypes(person, Pet.class, Person.class, Race.class); + + detectTypes(pet2, Pet.class); + detectTypes(person2, Person.class); + detectTypes(race2, Race.class); + + detectTypes(race3, Race.class); + detectTypes(pet3, Pet.class, Race.class); + } + +// @Ignore + + @Test + @Override + public void testGetOperation() throws Exception { + } + +// @Ignore + + @Test + @Override + public void testDetectAssociations() throws Exception { + + detectAssociations(person, TopiaTestEntityEnum.Person, Person.PROPERTY_PET); + detectAssociations(race); + detectAssociations(pet); + + detectAssociations(person2); + detectAssociations(race2); + detectAssociations(pet2); + + } + +// @Ignore + + @Test + @Override + public void testDetectDirectDependencies() throws Exception { + + detectDirectDependencies(person); + detectDirectDependencies(race); + detectDirectDependencies(pet, TopiaTestEntityEnum.Pet, Pet.PROPERTY_PERSON, TopiaTestEntityEnum.Pet, Pet.PROPERTY_RACE); + + detectDirectDependencies(person2); + detectDirectDependencies(race2); + detectDirectDependencies(pet2); + } + +// @Ignore + + @Test + @Override + public void testDetectShell() throws Exception { + + detectShell(person, TopiaTestEntityEnum.Pet, TopiaTestEntityEnum.Race); + detectShell(race); + detectShell(pet, TopiaTestEntityEnum.Person, TopiaTestEntityEnum.Race); + detectShell(person2, TopiaTestEntityEnum.Pet, TopiaTestEntityEnum.Race); + detectShell(race2); + detectShell(pet2, TopiaTestEntityEnum.Person, TopiaTestEntityEnum.Race); + } + +// @Ignore + + @Test + @Override + public void testDetectDependencies() throws Exception { + + detectDependencies(null, + new TopiaTestEntityEnum[]{TopiaTestEntityEnum.Race}, new TopiaTestEntityEnum[]{TopiaTestEntityEnum.Person}, new TopiaTestEntityEnum[]{TopiaTestEntityEnum.Pet}); + } + +// @Ignore + + @Test + @Override + public void testDetectObjectsToDettach() throws Exception { + + detectObjectsToDettach(null, TopiaTestEntityEnum.Person, new String[]{Person.PROPERTY_PET}); + } + +// @Ignore + + @Test + @Override + public void testDetectOperations() throws Exception { + + detectOperations(null); + } + +// @Ignore + + @Test + @Override + public void testDoReplicate() throws Exception { + + doReplicateAll(); + + } + + @Override + protected TopiaContext createDb(String name) throws Exception { + +// File localDB = new File(getTestDir(getClass()), "db_" + name); + + Properties config = getH2Properties(name); + + context = TopiaContextFactory.getContext(config); + + TopiaContextImplementor tx = (TopiaContextImplementor) context.beginTransaction(); + + person = tx.getDAO(Person.class).create(Person.PROPERTY_NAME, "pudding master"); + race = tx.getDAO(Race.class).create(Race.PROPERTY_NAME, "race I"); + pet = tx.getDAO(Pet.class).create(Pet.PROPERTY_NAME, "pudding", Pet.PROPERTY_PERSON, person, Pet.PROPERTY_RACE, race); + + person2 = tx.getDAO(Person.class).create(Person.PROPERTY_NAME, "pudding II master"); + pet2 = tx.getDAO(Pet.class).create(Pet.PROPERTY_NAME, "pudding II"); + race2 = tx.getDAO(Race.class).create(Race.PROPERTY_NAME, "race II"); + + race3 = tx.getDAO(Race.class).create(Race.PROPERTY_NAME, "race III"); + pet3 = tx.getDAO(Pet.class).create(Pet.PROPERTY_NAME, "pudding III", Pet.PROPERTY_RACE, race3); + + tx.commitTransaction(); + tx.closeContext(); + return context; + } + + @Override + protected TopiaContext createDb2(String name) throws Exception { + +// File localDB = new File(getTestDir(getClass()), "db_" + name); + + Properties config = getH2Properties(name); + + return TopiaContextFactory.getContext(config); + } + + @Override + protected TopiaEntityEnum[] getContracts() { + return contracts; + } + + @Override + protected Log getLog() { + return log; + } + + protected Properties getH2Properties(String dbName) throws IOException { + + Properties config = TestHelper.initTopiaContextConfiguration(tesDir, dbName); + +// config.setProperty("hibernate.show_sql", "false"); +// config.setProperty("hibernate.hbm2ddl.auto", "create"); + + config.setProperty("topia.persistence.classes", entitiesList); +// config.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); +// config.setProperty("hibernate.connection.driver_class", "org.h2.Driver"); +// config.setProperty("hibernate.connection.url", "jdbc:h2:file:" + f.getAbsolutePath() + ";create=true"); +// config.setProperty("hibernate.connection.username", "sa"); +// config.setProperty("hibernate.connection.password", ""); + + config.setProperty(TopiaReplicationServiceImpl.TOPIA_SERVICE_NAME, TopiaReplicationServiceImpl.class.getName()); + + return config; + } + + + @Override + protected void createModel(TopiaEntity entity) throws TopiaException { + model = getModelBuilder().createModelForAll(getContracts()); + } + + @Override + protected void prepareModel(String... ids) throws TopiaException { + model = service.prepareForAll(getContracts()); + } + +} + + diff --git a/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationServiceImplTest.java b/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationServiceImplTest.java new file mode 100644 index 0000000..608e6c4 --- /dev/null +++ b/topia-service-replication/src/test/java/org/nuiton/topia/replication/TopiaReplicationServiceImplTest.java @@ -0,0 +1,458 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.nuiton.topia.TestHelper; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaContextFactory; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaTestDAOHelper.TopiaTestEntityEnum; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaEntityEnum; +import org.nuiton.topia.persistence.util.TopiaEntityIdsMap; +import org.nuiton.topia.replication.model.ReplicationModel; +import org.nuiton.topia.replication.operation.AttachAssociation; +import org.nuiton.topia.replication.operation.DettachAssociation; +import org.nuiton.topia.replication.operation.Duplicate; +import org.nuiton.topia.replication.operation.FakeOperation; +import org.nuiton.topia.replication.operation.UncreatableOperation; +import org.nuiton.topia.replication.operation.UnregistredOperation; +import org.nuiton.topia.test.entities.Person; +import org.nuiton.topia.test.entities.PersonImpl; +import org.nuiton.topia.test.entities.Pet; +import org.nuiton.topia.test.entities.PetImpl; +import org.nuiton.topia.test.entities.Race; +import org.nuiton.topia.test.entities.RaceImpl; + +import java.io.File; +import java.io.IOException; +import java.util.Properties; + +/** + * TopiaReplicationServiceImplTest on model TopiaTest + * <p/> + * Created: 07 jun. 09 17:14:22 + * + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + */ +public class TopiaReplicationServiceImplTest extends AbstractTopiaReplicationServiceTest { + + /** Logger */ + private static final Log log = + LogFactory.getLog(TopiaReplicationServiceImplTest.class); + + protected static final TopiaEntityEnum[] contracts = {TopiaTestEntityEnum.Person, TopiaTestEntityEnum.Pet, TopiaTestEntityEnum.Race}; + + protected static final String entitiesList = PersonImpl.class.getName() + "," + PetImpl.class.getName() + "," + RaceImpl.class.getName(); + + static protected Person person, person2; + + static protected Pet pet, pet2, pet3; + + static protected Race race, race2, race3; + + protected static File tesDir; + + @BeforeClass + public static void beforeClass() throws IOException { + tesDir = TestHelper.getTestBasedir( + TopiaReplicationServiceImplTest.class); + } + + @AfterClass + public static void after() throws Exception { + AbstractTopiaReplicationServiceTest.after(); + } + + @Before + @Override + public void setUp() throws Exception { + + super.setUp(); + + person = update(person); + person2 = update(person2); + pet = update(pet); + pet2 = update(pet2); + race = update(race); + race2 = update(race2); + race3 = update(race3); + } + + @After + @Override + public void tearDown() throws Exception { + super.tearDown(); + if (dstCtxt != null && !dstCtxt.isClosed()) { + dstCtxt.closeContext(); + } + } + + @Test + @Override + public void testDetectTypes() throws Exception { + + detectTypes(race, Race.class); + detectTypes(pet, Pet.class, Person.class, Race.class); + detectTypes(person, Pet.class, Person.class, Race.class); + + detectTypes(pet2, Pet.class); + detectTypes(person2, Person.class); + detectTypes(race2, Race.class); + + detectTypes(race3, Race.class); + detectTypes(pet3, Pet.class, Race.class); + } + + @Test + @Override + public void testGetOperation() throws Exception { + + getOperation(UnregistredOperation.class, false); + getOperation(UncreatableOperation.class, true); + getOperation(FakeOperation.class, true); + getOperation(Duplicate.class, true); + getOperation(AttachAssociation.class, true); + getOperation(DettachAssociation.class, true); + } + + @Test + @Override + public void testDetectAssociations() throws Exception { + + detectAssociations(person, TopiaTestEntityEnum.Person, Person.PROPERTY_PET); + detectAssociations(race); + detectAssociations(pet); + + detectAssociations(person2); + detectAssociations(race2); + detectAssociations(pet2); + } + + @Test + @Override + public void testDetectDirectDependencies() throws Exception { + + detectDirectDependencies(person); + detectDirectDependencies(race); + detectDirectDependencies(pet, TopiaTestEntityEnum.Pet, Pet.PROPERTY_PERSON, TopiaTestEntityEnum.Pet, Pet.PROPERTY_RACE); + + detectDirectDependencies(person2); + detectDirectDependencies(race2); + detectDirectDependencies(pet2); + } + + @Test + @Override + public void testDetectShell() throws Exception { + + detectShell(person, TopiaTestEntityEnum.Pet, TopiaTestEntityEnum.Race); + detectShell(race); + detectShell(pet, TopiaTestEntityEnum.Person, TopiaTestEntityEnum.Race); + detectShell(person2); + detectShell(race2); + detectShell(pet2); + } + + @Test + @Override + public void testDetectDependencies() throws Exception { + + detectDependencies(person, new TopiaTestEntityEnum[]{TopiaTestEntityEnum.Race}, new TopiaTestEntityEnum[]{TopiaTestEntityEnum.Person}, new TopiaTestEntityEnum[]{TopiaTestEntityEnum.Pet}); + detectDependencies(race, new TopiaTestEntityEnum[]{TopiaTestEntityEnum.Race}); + detectDependencies(pet, new TopiaTestEntityEnum[]{TopiaTestEntityEnum.Race}, new TopiaTestEntityEnum[]{TopiaTestEntityEnum.Person}, new TopiaTestEntityEnum[]{TopiaTestEntityEnum.Pet}); + + detectDependencies(person2, new TopiaTestEntityEnum[]{TopiaTestEntityEnum.Person}); + detectDependencies(race2, new TopiaTestEntityEnum[]{TopiaTestEntityEnum.Race}); + detectDependencies(pet2, new TopiaTestEntityEnum[]{TopiaTestEntityEnum.Pet}); + } + + @Test + @Override + public void testDetectObjectsToDettach() throws Exception { + + detectObjectsToDettach(person, TopiaTestEntityEnum.Person, new String[]{Person.PROPERTY_PET}); + detectObjectsToDettach(race); + detectObjectsToDettach(pet, TopiaTestEntityEnum.Person, new String[]{Person.PROPERTY_PET}); + + detectObjectsToDettach(person2); + detectObjectsToDettach(race2); + detectObjectsToDettach(pet2); + + detectObjectsToDettach(race3); + detectObjectsToDettach(pet3); + } + + @Test + @Override + public void testDetectOperations() throws Exception { + + //TODO Make some real test on detected operations... + + detectOperations(person); + detectOperations(pet); + detectOperations(race); + + detectOperations(person2); + detectOperations(pet2); + detectOperations(race2); + + detectOperations(race3); + detectOperations(pet3); + } + + @Test + @Override + public void testDoReplicate() throws Exception { + + doReplicate(TopiaTestEntityEnum.Person, person); + doReplicate(TopiaTestEntityEnum.Person, person2); + doReplicate(TopiaTestEntityEnum.Person, person, person2); + + doReplicate(TopiaTestEntityEnum.Pet, pet); + doReplicate(TopiaTestEntityEnum.Pet, pet2); + doReplicate(TopiaTestEntityEnum.Pet, pet, pet2, pet3); + doReplicate(TopiaTestEntityEnum.Pet, person2, pet3); + + doReplicate(TopiaTestEntityEnum.Race, race); + doReplicate(TopiaTestEntityEnum.Race, race2); + doReplicate(TopiaTestEntityEnum.Race, race, race2); + + } + + /** + * Cette methode montre pourquoi la simple replication ne peut pas + * fonctionne :) + * <p/> + * Le replicateur ne deplique pas dans le bon ordre et on a donc des + * violations de clef etrangeres... + * + * @throws Exception pour toute erreur + */ + @Test(expected = TopiaException.class) + public void testSimpleReplicateFailed() throws Exception { + + TopiaContext dstRootCtxt = createDb2("testSimpleReplicateFailed"); + + //model = service.prepare(contracts, pet.getTopiaId()); + + TopiaContext srcCtxt = ctxt.beginTransaction(); + dstCtxt = (TopiaContextImplementor) dstRootCtxt.beginTransaction(); + + try { + + srcCtxt.replicateEntity(dstCtxt, pet); + + dstCtxt.commitTransaction(); + + } finally { + srcCtxt.rollbackTransaction(); + srcCtxt.closeContext(); + dstCtxt.closeContext(); + } + } + + /** + * Cette methode montre comment manuellement on peut effectuer la + * replication (en dettachant les dependances qui forment des cycles) + * <p/> + * La methode utilisee ici peut ne pas fonctionner : si une clef metier est + * posee sur une dependance alors cela ne fonctionne pas. + * + * @throws Exception pour toute erreur + */ + @Test + public void testSimpleReplicateNotSure() throws Exception { + + TopiaContext dstRootCtxt = createDb2("testSimpleReplicateNotSure"); + + //model = service.prepare(contracts, pet.getTopiaId()); + + TopiaContext srcCtxt = ctxt; + dstCtxt = (TopiaContextImplementor) dstRootCtxt.beginTransaction(); + + try { + + + srcCtxt.replicateEntity(dstCtxt, race); + + // on dettache l'entite qui pose probleme + + pet.setPerson(null); + srcCtxt.replicateEntity(dstCtxt, pet); + srcCtxt.rollbackTransaction(); + + srcCtxt.replicateEntity(dstCtxt, person); + + dstCtxt.commitTransaction(); + ((Pet) dstCtxt.findByTopiaId(pet.getTopiaId())).setPerson((Person) dstCtxt.findByTopiaId(person.getTopiaId())); + dstCtxt.commitTransaction(); + + srcCtxt.rollbackTransaction(); + person = update(person); + + assertEntityEquals(person, dstCtxt.findByTopiaId(person.getTopiaId()), null); + } finally { + srcCtxt.rollbackTransaction(); + //srcCtxt.closeContext(); + dstCtxt.closeContext(); + } + } + + /** + * Cette methode montre comment manuellement on peut effectuer la + * replication (en dettachant les associations qui forment des cycles) + * <p/> + * La methode utilisee ici fonctionne mieux que la precedante : il parrait + * dificille de pose une une clef metier sur une association :). + * <p/> + * On remarque que l'on dettache l'assocation qui forme un cycle et que l'on + * est pas obligee de la reattachee car elle est bi-directionnelle. + * <p/> + * On doit optimiser l'algorithme dans la methode {@link + * ReplicationModel#adjustOperations(TopiaEntityIdsMap)}. + * + * @throws Exception pour toute erreur + */ + @Test + public void testSimpleReplicateSure() throws Exception { + + TopiaContext dstRootCtxt = createDb2("testSimpleReplicateSure"); + + //model = service.prepare(contracts, pet.getTopiaId()); + + TopiaContext srcCtxt = ctxt; + dstCtxt = (TopiaContextImplementor) dstRootCtxt.beginTransaction(); + + try { + + srcCtxt.replicateEntity(dstCtxt, race); + // on dettache l'association qui pose probleme + person.setPet(null); + srcCtxt.replicateEntity(dstCtxt, person); + + srcCtxt.replicateEntity(dstCtxt, pet); + srcCtxt.rollbackTransaction(); + dstCtxt.commitTransaction(); + + //((Person) dstCtxt.findByTopiaId(person.getTopiaId())).addPet(((Pet) dstCtxt.findByTopiaId(pet.getTopiaId()))); + //dstCtxt.commitTransaction(); + + srcCtxt.rollbackTransaction(); + + srcCtxt.closeContext(); + dstCtxt.closeContext(); + + ctxt = context.beginTransaction(); + dstCtxt = (TopiaContextImplementor) dstRootCtxt.beginTransaction(); + + person = update(person); + + assertEntityEquals(person, dstCtxt.findByTopiaId(person.getTopiaId()), null); + + } finally { + dstCtxt.closeContext(); + } + } + + @Override + protected TopiaContext createDb(String name) throws Exception { + +// File localDB = new File(getTestDir(getClass()), "db_" + name); + + Properties config = getH2Properties(name); + + context = TopiaContextFactory.getContext(config); + + TopiaContextImplementor tx = (TopiaContextImplementor) context.beginTransaction(); + + person = tx.getDAO(Person.class).create(Person.PROPERTY_NAME, "pudding master"); + race = tx.getDAO(Race.class).create(Race.PROPERTY_NAME, "race I"); + pet = tx.getDAO(Pet.class).create(Pet.PROPERTY_NAME, "pudding", Pet.PROPERTY_PERSON, person, Pet.PROPERTY_RACE, race); + + person2 = tx.getDAO(Person.class).create(Person.PROPERTY_NAME, "pudding II master"); + pet2 = tx.getDAO(Pet.class).create(Pet.PROPERTY_NAME, "pudding II"); + race2 = tx.getDAO(Race.class).create(Race.PROPERTY_NAME, "race II"); + + race3 = tx.getDAO(Race.class).create(Race.PROPERTY_NAME, "race III"); + pet3 = tx.getDAO(Pet.class).create(Pet.PROPERTY_NAME, "pudding III", Pet.PROPERTY_RACE, race3); + + tx.commitTransaction(); + tx.closeContext(); + return context; + } + + @Override + protected TopiaContext createDb2(String name) throws Exception { + +// File localDB = new File(getTestDir(getClass()), "db_" + name); +// +// log.info("db dir :\n" + localDB.getAbsolutePath()); + + Properties config = getH2Properties(name); + + return TopiaContextFactory.getContext(config); + } + + @Override + protected TopiaEntityEnum[] getContracts() { + return contracts; + } + + @Override + protected Log getLog() { + return log; + } + + protected Properties getH2Properties(String dbName) throws IOException { + + + Properties config = TestHelper.initTopiaContextConfiguration(tesDir, dbName); + +// config.setProperty("hibernate.show_sql", "false"); +// config.setProperty("hibernate.hbm2ddl.auto", "create"); + + config.setProperty("topia.persistence.classes", entitiesList); +// config.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); +// config.setProperty("hibernate.connection.driver_class", "org.h2.Driver"); +// config.setProperty("hibernate.connection.url", "jdbc:h2:file:" + f.getAbsolutePath() + "/db;create=true"); +// config.setProperty("hibernate.connection.username", "sa"); +// config.setProperty("hibernate.connection.password", ""); + + config.setProperty(TopiaReplicationServiceImpl.TOPIA_SERVICE_NAME, TopiaReplicationServiceImpl.class.getName()); + + return config; + } +} + + diff --git a/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/FakeOperation.java b/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/FakeOperation.java new file mode 100644 index 0000000..3c2498e --- /dev/null +++ b/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/FakeOperation.java @@ -0,0 +1,76 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication.operation; + +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.util.TopiaEntityHelper; +import org.nuiton.topia.replication.TopiaReplicationContext; +import org.nuiton.topia.replication.TopiaReplicationOperation; +import org.nuiton.topia.replication.model.ReplicationModel; +import org.nuiton.topia.replication.model.ReplicationNode; +import org.nuiton.topia.replication.model.ReplicationOperationDef; +import org.nuiton.topia.replication.model.ReplicationOperationPhase; + +import java.util.List; + +/** + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + */ +public class FakeOperation implements TopiaReplicationOperation { + + public static final Class<?>[] PARAMETERS_CLASSES = + new Class<?>[]{String.class}; + + @Override + public void register(ReplicationModel model, + ReplicationNode ownerNode, + ReplicationOperationPhase phase, + Object... parameters) { + + TopiaEntityHelper.checkParameters(PARAMETERS_CLASSES, parameters); + ReplicationOperationDef op = new ReplicationOperationDef( + phase, + getClass(), + ownerNode, + parameters + ); + ownerNode.addOperation(op); + } + + @Override + public void run(TopiaReplicationContext replicationContext, + ReplicationOperationDef operationDef, + TopiaContextImplementor srcCtxt, + TopiaContextImplementor dstCtxt, + List<? extends TopiaEntity> nodeEntities + ) + throws TopiaException { + + } + +} diff --git a/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/UncreatableOperation.java b/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/UncreatableOperation.java new file mode 100644 index 0000000..0d9101e --- /dev/null +++ b/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/UncreatableOperation.java @@ -0,0 +1,67 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication.operation; + +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.replication.TopiaReplicationContext; +import org.nuiton.topia.replication.TopiaReplicationOperation; +import org.nuiton.topia.replication.model.ReplicationModel; +import org.nuiton.topia.replication.model.ReplicationNode; +import org.nuiton.topia.replication.model.ReplicationOperationDef; +import org.nuiton.topia.replication.model.ReplicationOperationPhase; + +import java.util.List; + +import static org.nuiton.i18n.I18n.t; + +/** + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + */ +public class UncreatableOperation implements TopiaReplicationOperation { + + @Override + public void register(ReplicationModel model, + ReplicationNode ownerNode, + ReplicationOperationPhase phase, + Object... parameters) { + + throw new UnsupportedOperationException( + t("topia.replication.error.operation.uncreatable", getClass())); + } + + @Override + public void run(TopiaReplicationContext replicationContext, + ReplicationOperationDef operationDef, + TopiaContextImplementor srcCtxt, + TopiaContextImplementor dstCtxt, + List<? extends TopiaEntity> nodeEntities + ) + throws TopiaException { + } + +} diff --git a/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/UnregistredOperation.java b/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/UnregistredOperation.java new file mode 100644 index 0000000..4a3c225 --- /dev/null +++ b/topia-service-replication/src/test/java/org/nuiton/topia/replication/operation/UnregistredOperation.java @@ -0,0 +1,64 @@ +/* + * #%L + * ToPIA :: Service Replication + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.replication.operation; + +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.replication.TopiaReplicationContext; +import org.nuiton.topia.replication.TopiaReplicationOperation; +import org.nuiton.topia.replication.model.ReplicationModel; +import org.nuiton.topia.replication.model.ReplicationNode; +import org.nuiton.topia.replication.model.ReplicationOperationDef; +import org.nuiton.topia.replication.model.ReplicationOperationPhase; + +import java.util.List; + +/** + * @author tchemit <chemit@codelutin.com> + * @since 2.2.0 + */ +public class UnregistredOperation implements TopiaReplicationOperation { + + @Override + public void register(ReplicationModel model, + ReplicationNode ownerNode, + ReplicationOperationPhase phase, + Object... parameters) { + + } + + @Override + public void run( + TopiaReplicationContext replicationContext, + ReplicationOperationDef operationDef, + TopiaContextImplementor srcCtxt, + TopiaContextImplementor dstCtxt, + List<? extends TopiaEntity> nodeEntities + ) + throws TopiaException { + } + +} diff --git a/topia-service-replication/src/test/resources/META-INF/services/org.nuiton.topia.replication.TopiaReplicationOperation b/topia-service-replication/src/test/resources/META-INF/services/org.nuiton.topia.replication.TopiaReplicationOperation new file mode 100644 index 0000000..c156a73 --- /dev/null +++ b/topia-service-replication/src/test/resources/META-INF/services/org.nuiton.topia.replication.TopiaReplicationOperation @@ -0,0 +1,6 @@ +# +# la liste des operations disponibles pour le moteur de replication +# voir la classe org.nuiton.topia.replication.TopiaReplicationServiceImpl +# +org.nuiton.topia.replication.operation.FakeOperation +org.nuiton.topia.replication.operation.UncreatableOperation \ No newline at end of file diff --git a/topia-service-replication/src/test/resources/log4j.properties b/topia-service-replication/src/test/resources/log4j.properties new file mode 100644 index 0000000..db1b488 --- /dev/null +++ b/topia-service-replication/src/test/resources/log4j.properties @@ -0,0 +1,35 @@ +### +# #%L +# ToPIA :: Service Replication +# $Id$ +# $HeadURL$ +# %% +# Copyright (C) 2004 - 2014 CodeLutin +# %% +# 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 General Lesser Public License for more details. +# +# You should have received a copy of the GNU General Lesser Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/lgpl-3.0.html>. +# #L% +### +#\u00A0This log is used to display trace in generation + +# Global logging configuration +log4j.rootLogger=WARN, stdout +# Console output... +log4j.appender.stdout=org.apache.log4j.ConsoleAppender +log4j.appender.stdout.layout=org.apache.log4j.PatternLayout +log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) %M - %m%n +# package level +log4j.logger.org.nuiton.topia=INFO +#log4j.logger.org.nuiton.topia.persistence.util=DEBUG +#log4j.logger.org.nuiton.topia.replication=DEBUG diff --git a/topia-service-security/LICENSE.txt b/topia-service-security/LICENSE.txt new file mode 100644 index 0000000..cca7fc2 --- /dev/null +++ b/topia-service-security/LICENSE.txt @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/topia-service-security/README.txt b/topia-service-security/README.txt new file mode 100644 index 0000000..d2e50d3 --- /dev/null +++ b/topia-service-security/README.txt @@ -0,0 +1,2 @@ +To deploy new version of pom: mvn deploy +To install localy: mvn install diff --git a/topia-service-security/changelog.txt b/topia-service-security/changelog.txt new file mode 100644 index 0000000..9760a37 --- /dev/null +++ b/topia-service-security/changelog.txt @@ -0,0 +1,48 @@ +1.1.0 + * migrate to nuiton + +1.0.3 chemit 20090511 + * bump versions (lutinproject, lutinutil, topia, maven-license-switcher-plugin) + * use doxia-modules-jrst instead of maven-jrst-plugin + * improve download section on site + +1.0.2 chemit 20090220 +* 20090220 [chemit] - use lutinproject 3.4 + +1.0.1 chemit 20081215 +* 20081215 [chemit] - new release for isis-fish :) follow release of topia + +1.0 chemit 20081210 + * 20081205 [chemit] - improve poms, use lutinproject 3.2, migrate tests to JUnit4 + +0.9.1 + * [chatellier] Correction d'un bug de recherche de mapping sous windows (\) + +0.9 + * <chemit> use lutinproject 3.0 + clean pom + use topia 2.0.27 + * <poussin> modif dans service de migration pour permettre a l'app de + faire la migration via le callback + +0.8 + + * 20071120 [chatellier] update topia-service site + * 20071115 [chatellier] apply hibernate code style to open/close session and + transaction + * 20071115 [chatellier] refractoring separate specifique topia service + and real migration code + * 20071115 [chatellier] add schema existance detection support + and dont migrate if there is no table + * 20071111 [chatellier] add support for callback handler + * 20071111 [chatellier] use cascade to calculate dependencies order + * 20071109 [chatellier] change schema creation process to + correct duplicate foreign key creation + * 20071109 [chatellier] add inherit support to calculate dependancies + +0.6 ??? ??? + + * 20070426 [chatellier] use lutinutil.Resources.getUrls() to get mappings + * 20070426 [chatellier] change finder migration to use Class.forName() + * 20070420 [chatellier] remove the 'V' letter in old mapping directories name + * 20070420 [chatellier] add support to look for hibernate mapping in a tree + structure of directories + * 20070418 [chatellier] add migration service \ No newline at end of file diff --git a/topia-service-security/pom.xml b/topia-service-security/pom.xml new file mode 100644 index 0000000..22cd255 --- /dev/null +++ b/topia-service-security/pom.xml @@ -0,0 +1,165 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2010 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.nuiton</groupId> + <artifactId>topia</artifactId> + <version>2.9.3-SNAPSHOT</version> + </parent> + + <groupId>org.nuiton.topia</groupId> + <artifactId>topia-service-security</artifactId> + + <name>ToPIA :: Service Security</name> + <description>Hibernate based security service</description> + + <properties> + + <!-- TopiaSecurityutil uses some sun.xxx classes --> + <signatureArtifactId>java16-sun</signatureArtifactId> + <signatureVersion>1.10</signatureVersion> + + <!-- Post Release configuration --> + <skipPostRelease>false</skipPostRelease> + </properties> + + <dependencies> + + <!-- Sibling dependencies --> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>topia-persistence</artifactId> + <version>${project.version}</version> + </dependency> + + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>topia-persistence</artifactId> + <version>${project.version}</version> + <classifier>tests</classifier> + </dependency> + + <dependency> + <groupId>org.nuiton</groupId> + <artifactId>nuiton-utils</artifactId> + </dependency> + + <dependency> + <groupId>org.nuiton.i18n</groupId> + <artifactId>nuiton-i18n</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + </dependency> + + <dependency> + <groupId>commons-collections</groupId> + <artifactId>commons-collections</artifactId> + </dependency> + + <dependency> + <groupId>commons-logging</groupId> + <artifactId>commons-logging</artifactId> + </dependency> + + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + </dependency> + + <dependency> + <groupId>org.hibernate</groupId> + <artifactId>hibernate-core</artifactId> + </dependency> + + <!-- Depencies for test--> + <dependency> + <groupId>com.h2database</groupId> + <artifactId>h2</artifactId> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-log4j12</artifactId> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + </dependency> + + </dependencies> + + <build> + <resources> + + <resource> + <directory>${maven.gen.dir}/java</directory> + <includes> + <include>**/*.hbm.xml</include> + </includes> + </resource> + + </resources> + <plugins> + + <plugin> + <groupId>org.nuiton.eugene</groupId> + <artifactId>eugene-maven-plugin</artifactId> + <executions> + <execution> + <id>generate-sources</id> + <phase>generate-sources</phase> + <configuration> + <templates> + org.nuiton.topia.generator.TopiaMetaTransformer + </templates> + <defaultPackage>org.nuiton.topia</defaultPackage> + <fullPackagePath>org.nuiton.topia</fullPackagePath> + <overwrite>true</overwrite> + </configuration> + <goals> + <goal>generate</goal> + </goals> + </execution> + </executions> + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>topia-persistence</artifactId> + <version>${project.version}</version> + <scope>compile</scope> + </dependency> + </dependencies> + </plugin> + + </plugins> + </build> + +</project> diff --git a/topia-service-security/src/license/THIRD-PARTY.properties b/topia-service-security/src/license/THIRD-PARTY.properties new file mode 100644 index 0000000..c5c4bbe --- /dev/null +++ b/topia-service-security/src/license/THIRD-PARTY.properties @@ -0,0 +1,49 @@ +### +# #%L +# ToPIA :: Service Security +# $Id$ +# $HeadURL$ +# %% +# Copyright (C) 2004 - 2014 CodeLutin +# %% +# 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 General Lesser Public License for more details. +# +# You should have received a copy of the GNU General Lesser Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/lgpl-3.0.html>. +# #L% +### +# Generated by org.codehaus.mojo.license.AddThirdPartyMojo +#------------------------------------------------------------------------------- +# Already used licenses in project : +# - Apache License 2.0 +# - BSD License +# - COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 +# - Common Public License Version 1.0 +# - GNU Lesser General Public License, version 2.1 +# - GNU Library or Lesser General Public License +# - Indiana University Extreme! Lab Software License, vesion 1.1.1 +# - Lesser General Public License (LGPL) v 3.0 +# - Lesser General Public License (LPGL) +# - Lesser General Public License (LPGL) v 2.1 +# - MIT License +# - MPL 1.1 +# - New BSD License +# - The Apache Software License, Version 2.0 +# - The H2 License, Version 1.0 +# - license.txt +#------------------------------------------------------------------------------- +# Please fill the missing licenses for dependencies : +# +# +#Fri Mar 15 12:42:13 CET 2013 +commons-primitives--commons-primitives--1.0=The Apache Software License, Version 2.0 +dom4j--dom4j--1.6.1=BSD License diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/TopiaSecurityService.java b/topia-service-security/src/main/java/org/nuiton/topia/security/TopiaSecurityService.java new file mode 100644 index 0000000..f50b1df --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/TopiaSecurityService.java @@ -0,0 +1,54 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.security; + +import org.nuiton.topia.framework.TopiaService; + +public interface TopiaSecurityService extends TopiaService { + + String SERVICE_NAME = "security"; + + /** + * Vérifie si l'utilisateur actuellement loggué a le droit d'accéder à + * l'entité passée en paramètre pour les actions spécifiées. + * @param entityClass l'entité pour laquelle on vérifie les droits + * @param actions les actions [load, read, write, admin] + * @throws SecurityException if any security issues + */ + void checkPermission(Class<?> entityClass, int actions) + throws SecurityException; + + /** + * Vérifie si l'utilisateur actuellement loggué a le droit d'accéder à + * l'entité passée en paramètre pour les actions spécifiées. + * @param expression le topiaId de l'entité pour laquelle on vérifie les droits + * ou tout autre expression + * @param actions les actions [load, read, write, admin] + * @throws SecurityException if any security issues + */ + void checkPermission(String expression, int actions) + throws SecurityException; + +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/TopiaSecurityServiceImpl.java b/topia-service-security/src/main/java/org/nuiton/topia/security/TopiaSecurityServiceImpl.java new file mode 100644 index 0000000..077e401 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/TopiaSecurityServiceImpl.java @@ -0,0 +1,504 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.security; + +import org.apache.commons.collections.map.ReferenceMap; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaNotFoundException; +import org.nuiton.topia.TopiaSecurityDAOHelper; +import org.nuiton.topia.event.TopiaTransactionEvent; +import org.nuiton.topia.event.TopiaTransactionVetoable; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.persistence.TopiaId; +import org.nuiton.topia.security.entities.authorization.TopiaAssociationAuthorizationDAO; +import org.nuiton.topia.security.entities.authorization.TopiaAuthorization; +import org.nuiton.topia.security.entities.authorization.TopiaAuthorizationDAO; +import org.nuiton.topia.security.entities.authorization.TopiaEntityAuthorization; +import org.nuiton.topia.security.entities.authorization.TopiaEntityAuthorizationDAO; +import org.nuiton.topia.security.entities.authorization.TopiaEntityAuthorizationImpl; +import org.nuiton.topia.security.entities.authorization.TopiaExpressionLink; +import org.nuiton.topia.security.entities.authorization.TopiaExpressionLinkDAO; +import org.nuiton.topia.security.entities.user.TopiaGroupDAO; +import org.nuiton.topia.security.entities.user.TopiaUser; +import org.nuiton.topia.security.entities.user.TopiaUserDAO; +import org.nuiton.topia.security.jaas.TopiaConfiguration; +import org.nuiton.topia.security.jaas.TopiaPermission; +import org.nuiton.topia.security.jaas.TopiaPolicy; +import org.nuiton.topia.security.listener.EntityVetoable; +import org.nuiton.topia.security.listener.NoSecurityLoad; +import org.nuiton.topia.security.listener.PropertyReadListener; +import org.nuiton.topia.security.listener.PropertyVetoable; +import org.nuiton.topia.security.listener.PropertyWriteListener; +import org.nuiton.topia.security.util.TopiaSecurityCaching; +import org.nuiton.topia.security.util.TopiaSecurityUtil; + +import javax.security.auth.Subject; +import javax.security.auth.login.Configuration; +import java.security.AccessController; +import java.security.Permission; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.nuiton.topia.security.util.TopiaSecurityUtil.CREATE; +import static org.nuiton.topia.security.util.TopiaSecurityUtil.DELETE; +import static org.nuiton.topia.security.util.TopiaSecurityUtil.TOPIA_SECURITY_PERSISTENCE_CLASSES; +import static org.nuiton.topia.security.util.TopiaSecurityUtil.UPDATE; + +/** + * Implantation du manager pour la securite. C'est le point d'acces a l'ensemble + * des fonctionnalites de la securite. + * + * @author ruchaud + */ +public class TopiaSecurityServiceImpl implements TopiaSecurityService, + TopiaTransactionVetoable { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(TopiaSecurityServiceImpl.class); + + /* Context ToPIA */ + + private TopiaContext rootContext; + + private TopiaContext securityContext; + + /* Listeners */ + + private EntityVetoable entityVetoable = new EntityVetoable(this); + + private PropertyReadListener read = new PropertyReadListener(this); + + private PropertyWriteListener write = new PropertyWriteListener(this); + + private PropertyVetoable propertyVetoable = new PropertyVetoable(read, write); + + /* Policy */ + + private TopiaPolicy policy = new TopiaPolicy(this); + + /* Cache */ + + transient private TopiaSecurityCaching entitiesLoadingCache = new TopiaSecurityCaching(2); + + transient private Map<String, Permission> authorizationsCache = + Collections.synchronizedMap(new ReferenceMap(ReferenceMap.SOFT, ReferenceMap.SOFT)); + + transient private Map<String, Set<Permission>> permissionsCache = + Collections.synchronizedMap(new ReferenceMap(ReferenceMap.SOFT, ReferenceMap.SOFT)); + + /** + * Constructeur. Initialise la sécurité à partir du contexte passer en + * paramètre + */ + public TopiaSecurityServiceImpl() { + } + + /* (non-Javadoc) + * @see org.nuiton.topia.framework.TopiaService#getServiceName() + */ + + @Override + public String getServiceName() { + return SERVICE_NAME; + } + + /* + * (non-Javadoc) + * @see org.nuiton.topia.security.TopiaSecurityManager#getPersistenceClasses() + */ + + @Override + public Class<?>[] getPersistenceClasses() { + return TOPIA_SECURITY_PERSISTENCE_CLASSES; + } + + /* + * (non-Javadoc) + * @see org.nuiton.topia.security.TopiaSecurityManager#init() + */ + + @Override + public boolean preInit(TopiaContextImplementor context) { + return true; + } + + /* + * (non-Javadoc) + * @see org.nuiton.topia.security.TopiaSecurityManager#init() + */ + + @Override + public boolean postInit(TopiaContextImplementor context) { + rootContext = context; + securityContext = null; + + rootContext.addTopiaEntityVetoable(entityVetoable); + rootContext.addTopiaEntityListener(propertyVetoable); + rootContext.addTopiaTransactionVetoable(this); + + policy.installPolicy(); + Configuration.setConfiguration(new TopiaConfiguration("topia", this)); + + return true; + } + + /** + * Permet de propager la sécurité sur l'ensemble des contextes + * + * @param event + */ + @Override + public void beginTransaction(TopiaTransactionEvent event) { + TopiaContext context = event.getSource(); + context.addTopiaEntityVetoable(entityVetoable); + context.addTopiaEntityListener(propertyVetoable); + context.addTopiaTransactionVetoable(this); + } + + /* + * (non-Javadoc) + * @see org.nuiton.topia.security.TopiaSecurityManager#getSecurityContext() + */ + + public TopiaContext getSecurityContext() throws TopiaException { + if (securityContext == null) { + securityContext = rootContext.beginTransaction(); + } + return securityContext; + } + + /** + * Permet de récupérer le DAO dans le contexte de sécurité. + * + * @return DAO du TopiaUser + */ + public TopiaUserDAO getTopiaUserDAO() { + try { + return TopiaSecurityDAOHelper.getTopiaUserDAO(getSecurityContext()); + } catch (TopiaException te) { + log.error("Recuperation du TopiaUserDAO impossible", te); + } + return null; + } + + /** + * Permet de récupérer le DAO dans le contexte de sécurité. + * + * @return DAO du TopiaGroup + */ + public TopiaGroupDAO getTopiaGroupDAO() { + try { + return TopiaSecurityDAOHelper.getTopiaGroupDAO(getSecurityContext()); + } catch (TopiaException te) { + log.error("Recuperation du TopiaGroupDAO impossible", te); + } + return null; + } + + /** + * Permet de récupérer le DAO dans le contexte de sécurité. + * + * @return DAO du TopiaAuthorization + */ + public TopiaAuthorizationDAO getTopiaAuthorizationDAO() { + try { + return TopiaSecurityDAOHelper.getTopiaAuthorizationDAO(getSecurityContext()); + } catch (TopiaException te) { + log.error("Recuperation du TopiaAuthorizationDAO impossible", te); + } + return null; + } + + /** + * Permet de récupérer le DAO dans le contexte de sécurité. + * + * @return DAO du TopiaEntityAuthorization + */ + public TopiaEntityAuthorizationDAO getTopiaEntityAuthorizationDAO() { + try { + return TopiaSecurityDAOHelper.getTopiaEntityAuthorizationDAO(getSecurityContext()); + } catch (TopiaException te) { + log.error("Recuperation du TopiaEntityAuthorizationDAO impossible", te); + } + return null; + } + + /** + * Permet de récupérer le DAO dans le contexte de sécurité. + * + * @return DAO du TopiaExpressionLinkDAO + */ + public TopiaExpressionLinkDAO getTopiaIdLinkDAO() { + try { + return TopiaSecurityDAOHelper.getTopiaExpressionLinkDAO(getSecurityContext()); + } catch (TopiaException te) { + log.error("Recuperation du TopiaLinkAuthorizationDAO impossible", te); + } + return null; + } + + /** + * Permet de récupérer le DAO dans le contexte de sécurité. + * + * @return DAO du TopiaAssociationAuthorization + */ + public TopiaAssociationAuthorizationDAO getTopiaAssociationAuthorizationDAO() { + try { + return TopiaSecurityDAOHelper.getTopiaAssociationAuthorizationDAO(getSecurityContext()); + } catch (TopiaException te) { + log.error("Recuperation du TopiaAssociationAuthorizationDAO impossible", te); + } + return null; + } + + /** + * Permet d'ajouter dans le cache les permissions pour un principal donné. + * + * @param principalName nom du principal pour lequel on doit charger les + * permissions + * @return la liste des permissions + * @throws TopiaException + */ + public Set<Permission> putPermissionsCache(String principalName) throws TopiaException { + TopiaAuthorizationDAO authorizationDAO = getTopiaAuthorizationDAO(); + Collection<TopiaAuthorization> authorizations = authorizationDAO.findAll(); + + Set<Permission> permissions = new HashSet<Permission>(); + permissionsCache.put(principalName, permissions); + + String className = TopiaId.getClassNameAsString(principalName); + // AddEmptyPrincipals pr�cise si il faut les autorisations dont les principals sont vide + boolean addEmptyPrincipals = className.equals(TopiaUser.class.getName()); + + for (TopiaAuthorization authorization : authorizations) { + Set<?> principals = authorization.getPrincipals(); + if (principals.contains(principalName) + || addEmptyPrincipals && principals.isEmpty()) { + Permission permission = getAuthorizationCache(authorization); + permissions.add(permission); + } + } + getSecurityContext().commitTransaction(); + return permissions; + } + + /** + * Permet de récupérer dans le cache l'encapsulation de l'autorisation en + * permission et créer l'encapsulation si elle n'existe pas dans le cache + * + * @param authorization autorisation recherché + * @return permission encapsulation de l'autorisation en permission + */ + private Permission getAuthorizationCache(TopiaAuthorization authorization) { + String topiaIdAuthorization = authorization.getTopiaId(); + Permission permission = authorizationsCache.get(topiaIdAuthorization); + if (permission == null) { + permission = new TopiaPermission(authorization); + authorizationsCache.put(topiaIdAuthorization, permission); + } + return permission; + } + + /** + * Permet de récupérer depuis le cache les permissions pour un principal + * donné. + * + * @param principalName nom du principal + * @return permmissions d'un principal + */ + public Set<Permission> getPermissionsCache(String principalName) { + return permissionsCache.get(principalName); + } + + /** + * Permet de mettre dans le cache pour l'utilisateur en cours si il a droit + * l'autorisation ou non de charger une entité. + * + * @param topiaId identification de l'entité + * @param authorized autorisation sur l'entité, true pour autorisé et false + * pour non autorisé + */ + public void putEntitiesLoadingCache(String topiaId, boolean authorized) { + String userPrincipal = TopiaSecurityUtil.getUserPrincipal(); + if (userPrincipal != null) { + entitiesLoadingCache.put(authorized, userPrincipal, topiaId); + } + } + + /** + * Permet de récupérer dans le cache pour l'utilisateur en cours si il a + * droit l'autorisation ou non de charger une entité. + * + * @param topiaId identification de l'entité + * @return autorisation sur l'entité, true pour autorisé et false pour non + * autorisé + */ + public Boolean getEntitiesLoadingCache(String topiaId) { + String userPrincipal = TopiaSecurityUtil.getUserPrincipal(); + if (userPrincipal != null) { + return (Boolean) entitiesLoadingCache.get(userPrincipal, topiaId); + } + return null; + } + + /** + * Permet de supprimer un entrée dans le cache pour un utilisateur + * + * @param userPrincipal principal de l'utilisateur + */ + public void removeEntitiesLoadingCache(String userPrincipal) { + entitiesLoadingCache.clear(userPrincipal); + } + + /** + * Permet de tester le cache + * + * @param topiaId identification de l'entité + * @return vrai si il trouve sinon faux + */ + public boolean containEntitiesLoadingCache(String topiaId) { + Boolean authorized = getEntitiesLoadingCache(topiaId); + return authorized != null; + } + + /** + * Renvoi les identifiants qui remplacent l'identifiant en cours d'aprés la + * table de correspondance TopiaExpressionLink. + * + * @param topiaId identifiant à remplacer + * @return retourne l'identifiant remplacé + */ + //FIXME : Voir si on peut mettre en relation un objet vers plusieurs objets + protected List<String> getRealExpressions(String topiaId) { + try { + List<String> allBy = getSecurityContext().findAll("select distinct link.by from " + + TopiaExpressionLink.class.getName() + " link where link.replace=:replace", + "replace", topiaId); + getSecurityContext().commitTransaction(); + + if (allBy == null) { + allBy = new ArrayList<String>(); + } + + allBy.add(topiaId); + return allBy; + } catch (TopiaException te) { + throw new SecurityException("Replace expression for link failed", te); + } + } + + /** + * Permet de determiner seulement les actions pour lesquelles on doit + * vérifier les actions. + * + * @param topiaId identifiant de l'entité ToPIA + * @param actions actions à vérifier + * @return actions réellement à vérifer + */ + protected int getRealActions(String topiaId, int actions) { + try { + Class<?> klass = TopiaId.getClassName(topiaId); + + //if(TopiaSecurityUtil.isImplement(klass, NoSecurityLoad.class)) { + if (NoSecurityLoad.class.isAssignableFrom(klass)) { + //LOAD + actions &= UPDATE + DELETE + CREATE; + } + + /* TODO: + if(TopiaSecurityUtil.isImplement(klass, NoSecurityUpdate.class)) { + // UPDATE + actions &= LOAD + DELETE + CREATE; + } + ... + */ + } catch (TopiaNotFoundException te) { + if (log.isDebugEnabled()) { + log.debug("Real actions failed", te); + } + } + return actions; + } + /* (non-Javadoc) + * @see org.nuiton.topia.security.TopiaSecurityService#checkPermission(java.lang.Class, int) + */ + + @Override + public void checkPermission(Class<?> entityClass, int actions) throws SecurityException { + if (log.isTraceEnabled()) { + log.trace("Checking permissions to entity class : " + entityClass); + } + if (entityClass == null) { + throw new SecurityException("Class cannot be null"); + } + String topiaId = entityClass.getName() + "#*"; + checkPermission(topiaId, actions); + } + + /* (non-Javadoc) + * @see org.nuiton.topia.security.TopiaSecurityService#checkPermission(java.lang.String, int) + */ + + @Override + public void checkPermission(String topiaId, int actions) throws SecurityException { + int realActions = getRealActions(topiaId, actions); + /* Il reste des actions à vérifier */ + if (realActions != 0) { + Subject subject = Subject.getSubject(AccessController.getContext()); + if (subject != null) { + List<String> expressions = getRealExpressions(topiaId); + + boolean authorized = false; + for (String expression : expressions) { + TopiaEntityAuthorization authorization = new TopiaEntityAuthorizationImpl( + expression, realActions, subject.getPrincipals()); + try { + AccessController.checkPermission(new TopiaPermission(authorization)); + authorized = true; + break; + } catch (SecurityException se) { + authorized = false; + } + } + + if (!authorized) { + throw new SecurityException("Access denied to object \"" + topiaId + "\" for \"" + subject + "\""); + } + } else { + if (log.isWarnEnabled()) { + log.warn("Use doAs() and login first"); + } + } + } + } +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/entities/authorization/TopiaAssociationAuthorizationImpl.java b/topia-service-security/src/main/java/org/nuiton/topia/security/entities/authorization/TopiaAssociationAuthorizationImpl.java new file mode 100644 index 0000000..c44c4a7 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/entities/authorization/TopiaAssociationAuthorizationImpl.java @@ -0,0 +1,88 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.security.entities.authorization; + + +import static org.nuiton.topia.security.util.TopiaSecurityUtil.actionsString2Int; +import java.util.HashSet; +import java.util.List; + +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.persistence.TopiaId; + +/** + * Classe permettant les autorisations de type association. C'est à dire l'autorisation + * permet de donné une autorisation par rapport à une association dans le modèle. + * Attention il faut avoir une autorisation de chargement sur la l'entité où + * commence l'association + * @author ruchaud + */ +//TODO: Gestion d'un identifiant de début et de fin d'association typé +public class TopiaAssociationAuthorizationImpl extends + TopiaAssociationAuthorizationAbstract { + + private static final long serialVersionUID = 1L; + + /** + * Constructeur permettant initialisation des principals. + */ + public TopiaAssociationAuthorizationImpl() { + principals = new HashSet<String>(); + } + + @Override + public String getExpression() { + return idBeginAssociation; + } + + @Override + public boolean impliesExpression(String thisExpression, String thatExpression) { + String select = "count(test)"; + String from = TopiaId.getClassNameAsString(thisExpression) + " test join test." + nameAssociation + " as association"; + String where = "test.topiaId=:test and association.topiaId=:association"; + TopiaContext context = getTopiaContext(); + try { + List<?> find = context.findAll("select " + select + " from " + from + " where " + where, + "test", thisExpression, + "association", thatExpression); + return (Long)find.get(0) >= 1; + } catch (TopiaException e) { + e.printStackTrace(); + return false; + } + } + + @Override + public void setActions(String actions) { + this.actions = actionsString2Int(actions); + } + + @Override + public void setPrincipals(String principals) { + this.principals.add(principals); + } + +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/entities/authorization/TopiaAuthorizationImpl.java b/topia-service-security/src/main/java/org/nuiton/topia/security/entities/authorization/TopiaAuthorizationImpl.java new file mode 100644 index 0000000..45bd310 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/entities/authorization/TopiaAuthorizationImpl.java @@ -0,0 +1,116 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.security.entities.authorization; + +import static org.nuiton.topia.security.util.TopiaSecurityUtil.CREATE; +import static org.nuiton.topia.security.util.TopiaSecurityUtil.DELETE; +import static org.nuiton.topia.security.util.TopiaSecurityUtil.LOAD; +import static org.nuiton.topia.security.util.TopiaSecurityUtil.UPDATE; +import java.util.Set; + +/** + * Classe permettant la comparaison des autorisations. + * @author ruchaud + */ +//TODO: Inversion des autorisations +public abstract class TopiaAuthorizationImpl extends TopiaAuthorizationAbstract { + + /** + * Compare deux autorisations entres elles. + * @param other une autre autorisation + * @return vrai si l'autorisation implique l'autre + */ + public boolean implies(TopiaAuthorization other) { + return impliesExpression(getExpression(), other.getExpression()) && + impliesActions(getActions(), other.getActions()) && + impliesPrincipals(getPrincipals(), other.getPrincipals()); + } + + /** + * Comparare deux identifiants entres eux. + * thisId => thatId = ? + * @param thisExpression un identifiant + * @param thatExpression un autre identifiant + * @return vrai si thisId implique thatId + */ + public boolean impliesExpression(String thisExpression, String thatExpression) { + return thisExpression.equals(thatExpression) || + "*".equals(thisExpression) || + thatExpression.startsWith(thisExpression.substring(0, thisExpression.length()-1)) + && thisExpression.endsWith("*"); + } + + /** + * Compare deux actions entre elles. + * thisActions => thatActions = ? + * @param thisActions une action + * @param thatActions une autre action + * @return vrai si thisActions implique thatActions + */ + public boolean impliesActions(int thisActions, int thatActions) { + boolean result = true; + if ((thatActions & LOAD) == LOAD) { + result &= (thisActions & LOAD) == LOAD; + } + if ((thatActions & CREATE) == CREATE) { + result &= (thisActions & CREATE) == CREATE; + } + if ((thatActions & UPDATE) == UPDATE) { + result &= (thisActions & UPDATE) == UPDATE; + } + if ((thatActions & DELETE) == DELETE) { + result &= (thisActions & DELETE) == DELETE; + } + return result; + } + + /** + * Compare deux principals entre eux. + * thisPrincipals => thatPrincipals = ? + * @param thisPrincipals un principal + * @param thatPrincipals un autre principal + * @return vrai si thisPrincipals implique thatPrincipals + */ + public boolean impliesPrincipals(Set thisPrincipals, + Set thatPrincipals) { + + // this should never happen + if (thisPrincipals == null || thatPrincipals == null) + return false; + + // Permet de définir une autorisation sur l'ensemble des utilisateurs + if (thisPrincipals.size() == 0 || thatPrincipals.size() == 0) { + return true; + } + + return thatPrincipals != null // that contient bien le principal + // nécessaire ! + && (thisPrincipals.contains("*") || thatPrincipals + .containsAll(thisPrincipals)); + // (this contient une étoile (accepte tous)) ou (that contient + // tout ce que contient this) + + } +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/entities/authorization/TopiaEntityAuthorizationImpl.java b/topia-service-security/src/main/java/org/nuiton/topia/security/entities/authorization/TopiaEntityAuthorizationImpl.java new file mode 100644 index 0000000..8794f5b --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/entities/authorization/TopiaEntityAuthorizationImpl.java @@ -0,0 +1,62 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.security.entities.authorization; + +import static org.nuiton.topia.security.util.TopiaSecurityUtil.actionsString2Int; +import java.security.Principal; +import java.util.HashSet; +import java.util.Set; + +/** + * Classe permettant de définir des autorisations sur les entités. + * @author ruchaud + */ +//TODO: Rajouter la possibilité de faire des autorisations sur les champs d'une entité +public class TopiaEntityAuthorizationImpl extends TopiaEntityAuthorizationAbstract { + + private static final long serialVersionUID = 1L; + + public TopiaEntityAuthorizationImpl() { + principals = new HashSet(); + } + + public TopiaEntityAuthorizationImpl(String expression, int actions, Set<Principal> principals) { + this.expression = expression; + this.actions = actions; + this.principals = new HashSet(); + for (Principal principal : principals) { + this.principals.add(principal.getName()); + } + } + + public void setActions(String actions) { + this.actions = actionsString2Int(actions); + } + + public void setPrincipals(String principals) { + this.principals.add(principals); + } + +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/entities/authorization/TopiaExpressionLinkImpl.java b/topia-service-security/src/main/java/org/nuiton/topia/security/entities/authorization/TopiaExpressionLinkImpl.java new file mode 100644 index 0000000..626325b --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/entities/authorization/TopiaExpressionLinkImpl.java @@ -0,0 +1,52 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.security.entities.authorization; + +/** + * Permet de spécifier entre deux expressions qu'ils ont le même comportement au niveau + * de leurs permissions. + * <p> + * Exemple : + * <p> + * Objet 1 : Permission 1 + * <p> + * Objet 2 : Permission 2 + * <p> + * Link : Objet 1 Objet 2 + * <p> + * Conclusion : Objet 1 a les permissions en plus de l'objet 2 + * + * @author ruchaud + */ +public class TopiaExpressionLinkImpl extends TopiaExpressionLinkAbstract { + + private static final long serialVersionUID = 1L; + + public void set(String replace, String by) { + setReplace(replace); + setBy(by); + } + +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/entities/user/TopiaGroupImpl.java b/topia-service-security/src/main/java/org/nuiton/topia/security/entities/user/TopiaGroupImpl.java new file mode 100644 index 0000000..4a14203 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/entities/user/TopiaGroupImpl.java @@ -0,0 +1,74 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.security.entities.user; + +import java.util.ArrayList; +import java.util.List; + +/** + * Classe permettant la gestion de groupes d'utilisateurs. + * @author ruchaud + * + */ +public class TopiaGroupImpl extends TopiaGroupAbstract { + + private static final long serialVersionUID = 1L; + + /** + * Contructeur permettant l'initialisation des sous-groupes et des utilisateurs + * à vide. + */ + public TopiaGroupImpl() { + topiaUser = new ArrayList<TopiaUser>(); + subGroup = new ArrayList<TopiaGroup>(); + } + + /** + * Permet de récupérer les utilisateurs du groupe et de ses sous-groupes. + * @return liste des utilisateurs du groupe + */ + public List getAllUser() { + List<TopiaUser> result = new ArrayList<TopiaUser>(); + result.addAll(getTopiaUser()); + for (TopiaGroup group : getSubGroup()) { + result.addAll(group.getAllUser()); + } + return result; + } + + /** + * Permet de récupérer l'ensemble des groupes parents + * @return groupes parents + */ + public List getAllSuperGroup() { + List<TopiaGroup> result = new ArrayList<TopiaGroup>(); + if(superGroup != null) { + result.add(superGroup); + result.addAll(superGroup.getAllSuperGroup()); + } + return result; + } + +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/entities/user/TopiaUserImpl.java b/topia-service-security/src/main/java/org/nuiton/topia/security/entities/user/TopiaUserImpl.java new file mode 100644 index 0000000..047332c --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/entities/user/TopiaUserImpl.java @@ -0,0 +1,86 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.security.entities.user; + +import static org.nuiton.topia.security.util.TopiaSecurityUtil.hash; +import java.util.ArrayList; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +/** + * Classe permettant la gestion des utilisateurs. + * @author ruchaud + */ +public class TopiaUserImpl extends TopiaUserAbstract { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(TopiaUserImpl.class); + + private static final long serialVersionUID = 1L; + + /** + * Contructeur permettant d'initialiser la liste des groupes à vide. + */ + public TopiaUserImpl() { + topiaGroup = new ArrayList<TopiaGroup>(); + } + + /** + * Permet de vérifier la validité d'un mot de passe. + * @param password mot de passe à vérifier + */ + public boolean checkPassword(String password) { + if (this.password == null) { + return password == null; + } else{ + String hashed = hash(password); + log.debug("password is: " + this.password + " and hashed is " + hashed); + return this.password.equals(hashed); + } + } + + /** + * Permet de remplacer l'ancien mot de passe. + * @param newPassword nouveau mot de passe + */ + public void setPassword(String newPassword) { + password = hash(newPassword); + log.debug("setPassword to: " +password); + } + + /** + * Permet de remplacer l'ancien mot de passe si l'ancien mot de passe est + * correct. + * @param newPassword ancien mot de passe + * @param oldPassword nouveau mot de passe + */ + public void setPassword(String oldPassword, String newPassword) { + if (checkPassword(oldPassword)) { + setPassword(newPassword); + } + } + +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/jaas/TopiaCallbackHandler.java b/topia-service-security/src/main/java/org/nuiton/topia/security/jaas/TopiaCallbackHandler.java new file mode 100644 index 0000000..5436c17 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/jaas/TopiaCallbackHandler.java @@ -0,0 +1,85 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * + * TopiaCallbackHandler.java + * + * Created: 20 févr. 2006 + * + * @author Arnaud Thimel <thimel@codelutin.com> + * @version $Revision$ + * + * Mise a jour: $Date$ + * par : */ + +package org.nuiton.topia.security.jaas; + +import java.io.IOException; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * Classe permettant l'interfaçage entre l'application et la sécurité. + * @author ruchaud + */ +public class TopiaCallbackHandler implements CallbackHandler { + + private String username; + + private String password; + + /** + * Contructeur + * @param username login de l'utilisateur + * @param password mot de passe de l'utilisateur + */ + public TopiaCallbackHandler(String username, String password) { + this.username = username; + this.password = password; + } + + /* + * (non-Javadoc) + * + * @see javax.security.auth.callback.CallbackHandler#handle(javax.security.auth.callback.Callback[]) + */ + public void handle(Callback[] callbacks) throws IOException, + UnsupportedCallbackException { + for (Callback callback : callbacks) { + if (callback instanceof NameCallback) { + NameCallback nc = (NameCallback) callback; + nc.setName(username); + } else if (callback instanceof PasswordCallback) { + PasswordCallback pc = (PasswordCallback) callback; + pc.setPassword(password.toCharArray()); + } else + throw new UnsupportedCallbackException(callback); + } + } + +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/jaas/TopiaConfiguration.java b/topia-service-security/src/main/java/org/nuiton/topia/security/jaas/TopiaConfiguration.java new file mode 100644 index 0000000..5df42d4 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/jaas/TopiaConfiguration.java @@ -0,0 +1,159 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * + * TopiaConfiguration.java + * + * Created: 20 févr. 2006 + * + * @author Arnaud Thimel <thimel@codelutin.com> + * @version $Revision$ + * + * Mise a jour: $Date$ + * par : */ + +package org.nuiton.topia.security.jaas; + +import static org.nuiton.topia.security.util.TopiaSecurityUtil.SECURITY_MANAGER_KEY; +import static org.nuiton.topia.security.util.TopiaSecurityUtil.TOPIA_LOGIN_MODULE; + +import java.util.HashMap; +import java.util.Map; + +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; + +import org.nuiton.topia.security.TopiaSecurityService; + +/** + * Classe permettant de passer des paramètres entre le LoginModule et le + * CallbackHandler. Ici on passe le SecurityManager. + * @author ruchaud + */ +public class TopiaConfiguration extends Configuration { + + private Map<String, AppConfigurationEntry[]> appConfEntries; + + /** + * Créé une instance de TopiConfiguration avec un identifiant de + * configurationEntry et le nom du fichier de propriétés associé. + * + * @param name + * le nom de la configurationEntry + * @param securityManager le service + */ + public TopiaConfiguration(String name, TopiaSecurityService securityManager) { + appConfEntries = new HashMap<String, AppConfigurationEntry[]>(); + addEntry(name, securityManager); + } + + /** + * Ajoute une ConfigurationEntry avec le nom de fichier de propriétés + * associé + * + * @param name + * le nom de la configurationEntry + * @param securityManager + * le nom du fichier de propriétés + */ + private void addEntry(String name, TopiaSecurityService securityManager) { + AppConfigurationEntry[] confEntries = getAppConfigurationEntry(name); + if (confEntries != null) { + int i = 0; + for (; i < confEntries.length; i++) + if (TOPIA_LOGIN_MODULE.equals(confEntries[i] + .getLoginModuleName())) + break; + if (i == confEntries.length) { + AppConfigurationEntry[] tmpConfEntries = confEntries; + confEntries = new AppConfigurationEntry[confEntries.length + 1]; + for (int j = 0; j < confEntries.length; j++) + confEntries[j] = tmpConfEntries[j]; + confEntries[confEntries.length - 1] = createEntry(securityManager); + } else { + if ( /* Mauvais FLAG */ + !AppConfigurationEntry.LoginModuleControlFlag.REQUIRED + .equals(confEntries[i].getControlFlag()) + /* Ne contient pas la propriété */ + || !confEntries[i].getOptions().containsKey( + SECURITY_MANAGER_KEY) + /* Propriété mal initialisée */ + || !confEntries[i].getOptions() + .get(SECURITY_MANAGER_KEY).equals(securityManager)) + confEntries[i] = createEntry(securityManager, confEntries[i] + .getOptions()); + } + } else { + confEntries = new AppConfigurationEntry[1]; + confEntries[0] = createEntry(securityManager); + } + appConfEntries.put(name, confEntries); + } + + /** + * Créé une entry avec des options vides + * + * @param securityManager + * le SecurityHelper + * @return l'entry créée + */ + private AppConfigurationEntry createEntry(TopiaSecurityService securityManager) { + return createEntry(securityManager, null); + } + + /** + * Créé une entry en rajoutant les options nécessaires à l'attribut options + * + * @param securityManager + * le nom du fichier de propriétés + * @param options + * l'objet contenant les options précédentes + * @return l'entry créée + */ + private AppConfigurationEntry createEntry(TopiaSecurityService securityManager, Map options) { + if (options == null) + options = new HashMap<String, Object>(); + options.put(SECURITY_MANAGER_KEY, securityManager); + return new AppConfigurationEntry(TOPIA_LOGIN_MODULE, + AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options); + } + + /** + * Renvoie les entries associéess à l'attribut name + * + * @param name + * l'identifiant des entries demandées + * @return un tableau cotenant les entries demandées + */ + public AppConfigurationEntry[] getAppConfigurationEntry(String name) { + return appConfEntries.get(name); + } + + /* (non-Javadoc) + * @see javax.security.auth.login.Configuration#refresh() + */ + public void refresh() { + } + +} // TopiaConfiguration diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/jaas/TopiaLoginModule.java b/topia-service-security/src/main/java/org/nuiton/topia/security/jaas/TopiaLoginModule.java new file mode 100644 index 0000000..a42bf4b --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/jaas/TopiaLoginModule.java @@ -0,0 +1,218 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * +* TopiaLoginModule.java +* +* Created: 15 févr. 2006 +* +* @author Arnaud Thimel <thimel@codelutin.com> +* @version $Revision$ +* +* Mise a jour: $Date$ +* par : $Author$ +*/ + +package org.nuiton.topia.security.jaas; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.security.TopiaSecurityServiceImpl; +import org.nuiton.topia.security.entities.user.TopiaGroup; +import org.nuiton.topia.security.entities.user.TopiaUser; +import org.nuiton.topia.security.entities.user.TopiaUserDAO; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.login.LoginException; +import javax.security.auth.spi.LoginModule; +import java.security.Principal; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.nuiton.topia.security.util.TopiaSecurityUtil.SECURITY_MANAGER_KEY; + +/** + * LoginModule permettant l'authentification d'un utilisateur au près du + * système. + * + * @author ruchaud + */ +public class TopiaLoginModule implements LoginModule { + + private Log log = LogFactory.getLog(TopiaLoginModule.class); + + private Subject subject; + + private CallbackHandler callbackHandler; + + private Set<Principal> principals; + + private TopiaSecurityServiceImpl securityManager; + + /* (non-Javadoc) + * @see javax.security.auth.spi.LoginModule#initialize( + * javax.security.auth.Subject, + * javax.security.auth.callback.CallbackHandler, + * java.util.Map, + * java.util.Map) + */ + + public void initialize(Subject subject, CallbackHandler callbackHandler, + Map<String, ?> sharedState, Map<String, ?> options) { + this.subject = subject; + this.callbackHandler = callbackHandler; + principals = null; + securityManager = (TopiaSecurityServiceImpl) options.get(SECURITY_MANAGER_KEY); + } + + /* (non-Javadoc) + * @see javax.security.auth.spi.LoginModule#login() + */ + + public boolean login() throws LoginException { + if (callbackHandler == null) { + throw new LoginException("CallbackHandler cannot be null"); + } + if (securityManager == null) { + throw new LoginException("\"" + SECURITY_MANAGER_KEY + "\" property must be set"); + } + + String login, password = null; + + NameCallback nc = new NameCallback("login"); + PasswordCallback pc = new PasswordCallback("password", false); + + Callback[] callbacks = new Callback[2]; + + callbacks[0] = nc; + callbacks[1] = pc; + + try { + //Récupération du login et mot de passe + callbackHandler.handle(callbacks); + } catch (Exception eee) { + if (log.isWarnEnabled()) { + log.warn("Utilisation du CallbackHandler impossible", eee); + } + LoginException le = new LoginException( + "Utilisation du CallbackHandler impossible"); + le.initCause(eee); + throw le; + } + login = nc.getName(); + password = new String(pc.getPassword()); + pc.clearPassword(); + + //Vérification du login/pass et récupération des Principals + try { + TopiaUserDAO topiaUserDAO = securityManager.getTopiaUserDAO(); + TopiaUser user = topiaUserDAO.findByLogin(login); + + if (user != null && user.checkPassword(password)) { + // Récupération des principals + principals = new HashSet<Principal>(); + + String topiaIdUser = user.getTopiaId(); + principals.add(new TopiaPrincipal(topiaIdUser)); + securityManager.putPermissionsCache(topiaIdUser); + securityManager.removeEntitiesLoadingCache(topiaIdUser); + + // Force le rechargement des groupes d'où la non utilisation de + // la méthode : user.getTopiaGroup() + Set<TopiaGroup> groups = new HashSet<TopiaGroup>(securityManager.getSecurityContext().findAll( + "select topiaGroup from " + TopiaGroup.class.getName() + + " topiaGroup join topiaGroup.topiaUser as topiaUser where topiaUser = :user", "user", user)); + + if (groups != null) { + for (TopiaGroup group : groups) { + for (TopiaGroup superGroup : (List<TopiaGroup>) group.getAllSuperGroup()) { + String topiaIdGroup = superGroup.getTopiaId(); + principals.add(new TopiaPrincipal(topiaIdGroup)); + securityManager.putPermissionsCache(topiaIdGroup); + } + String topiaIdGroup = group.getTopiaId(); + principals.add(new TopiaPrincipal(topiaIdGroup)); + securityManager.putPermissionsCache(topiaIdGroup); + } + } + + securityManager.getSecurityContext().commitTransaction(); + } else { + // Echec d'authentification + principals = null; + throw new LoginException("Erreur lors de l'authentification " + login); + } + } catch (TopiaException te) { + // Echec de récupération de l'utilisateur + if (log.isWarnEnabled()) { + log.warn("Erreur lors de l'authentification", te); + } + LoginException le = new LoginException("Erreur lors de l'authentification"); + le.initCause(te); + throw le; + } + + return true; + } + + /* (non-Javadoc) + * @see javax.security.auth.spi.LoginModule#commit() + */ + + public boolean commit() throws LoginException { + subject.getPrincipals().addAll(principals); + return true; + } + + /* (non-Javadoc) + * @see javax.security.auth.spi.LoginModule#abort() + */ + + public boolean abort() throws LoginException { + //On effectue les mêmes actions que logout + return logout(); + } + + /* (non-Javadoc) + * @see javax.security.auth.spi.LoginModule#logout() + */ + + public boolean logout() throws LoginException { + //On libère les ressources + subject.getPrincipals().removeAll(principals); + subject = null; + principals.clear(); + principals = null; + callbackHandler = null; + return true; + } + +} //TopiaLoginModule diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/jaas/TopiaPermission.java b/topia-service-security/src/main/java/org/nuiton/topia/security/jaas/TopiaPermission.java new file mode 100644 index 0000000..d19177d --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/jaas/TopiaPermission.java @@ -0,0 +1,112 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * +* TopiaPermission.java +* +* Created: 16 févr. 2006 +* +* @author Arnaud Thimel <thimel@codelutin.com> +* @version $Revision$ +* +* Mise a jour: $Date$ +* par : $Author$ +*/ + +package org.nuiton.topia.security.jaas; + +import static org.nuiton.topia.security.util.TopiaSecurityUtil.actionsInt2String; + +import java.security.Permission; + +import org.nuiton.topia.security.entities.authorization.TopiaAuthorization; + +/** + * Classe permettant d'encapsuler les autorisations et de déléguer le travail aux + * autorisations. + * @author ruchaud + */ +public class TopiaPermission extends Permission { + + private static final long serialVersionUID = 1L; + + private TopiaAuthorization authorization; + + public TopiaPermission(TopiaAuthorization authorization) { + super(authorization.getExpression()); + this.authorization = authorization; + } + + /* + * (non-Javadoc) + * @see java.security.Permission#implies(java.security.Permission) + */ + @Override + public boolean implies(Permission permission) { + if (permission == null) + return false; + if (!(permission instanceof TopiaPermission)) + return false; + TopiaPermission other = (TopiaPermission)permission; + return authorization.implies(other.getAuthorization()); + } + + /* + * (non-Javadoc) + * @see java.security.Permission#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj == null) + return false; + if (obj == this) + return true; + if (!(obj instanceof TopiaPermission)) + return false; + TopiaPermission that = (TopiaPermission)obj; + return implies(that) && that.implies(this); + } + + /* + * (non-Javadoc) + * @see java.security.Permission#hashCode() + */ + @Override + public int hashCode() { + return authorization.hashCode(); + } + + /* + * (non-Javadoc) + * @see java.security.Permission#getActions() + */ + @Override + public String getActions() { + return actionsInt2String(authorization.getActions()); + } + + public TopiaAuthorization getAuthorization() { + return authorization; + } +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/jaas/TopiaPolicy.java b/topia-service-security/src/main/java/org/nuiton/topia/security/jaas/TopiaPolicy.java new file mode 100644 index 0000000..c11d571 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/jaas/TopiaPolicy.java @@ -0,0 +1,172 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * + * TopiaPolicy.java + * + * Created: 17 févr. 2006 + * + * @author Arnaud Thimel <thimel@codelutin.com> + * @version $Revision$ + * + * Mise a jour: $Date$ + * par : $Author$ + */ + +package org.nuiton.topia.security.jaas; + +import java.security.AccessController; +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Policy; +import java.security.Principal; +import java.security.ProtectionDomain; +import java.util.Set; + +import javax.security.auth.Subject; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.security.TopiaSecurityServiceImpl; + +/** + * Implantation d'un policy avec une prise en compte des permissions à la volée. + * @author ruchaud + */ +public class TopiaPolicy extends Policy { + + private Log log = LogFactory.getLog(TopiaPolicy.class); + + private TopiaSecurityServiceImpl securityManager; + + protected Policy parentPolicy; + + public TopiaPolicy(TopiaSecurityServiceImpl securityManager) { + this.securityManager = securityManager; + } + + /** + * Renvoie la Policy parente + * @see #installPolicy() + * @return l'attribut parentPolicy + */ + public Policy getParentPolicy() { + return parentPolicy; + } + + /** + * Remplace la Policy parente + * @param parentPolicy la nouvelle Policy parente + */ + public void setParentPolicy(Policy parentPolicy) { + this.parentPolicy = parentPolicy; + } + + /* (non-Javadoc) + * @see java.security.Policy#getPermissions(java.security.CodeSource) + */ + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + PermissionCollection pc = parentPolicy.getPermissions(codesource); + return pc; + } + + /* (non-Javadoc) + * @see java.security.Policy#getPermissions(java.security.ProtectionDomain) + */ + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + PermissionCollection pc = parentPolicy.getPermissions(domain); + + /* Vérification dans le cache */ + Subject subject = Subject.getSubject(AccessController.getContext()); + if (subject != null) { + for (Principal principal : subject.getPrincipals()) { + String principalName = principal.getName(); + Set<Permission> permissions = securityManager.getPermissionsCache(principalName); + if(permissions == null) { + try { + permissions = securityManager.putPermissionsCache(principalName); + } catch (TopiaException e) { + log.error("Récupération des TopiaPermission impossible", e); + } + } + for (Permission permission : permissions) { + pc.add(permission); + } + } + } else { + log.error("Récupération des TopiaPermission impossible"); + } + + return pc; + } + + /* (non-Javadoc) + * @see java.security.Policy#refresh() + */ + @Override + public void refresh() { + parentPolicy.refresh(); + } + + /* (non-Javadoc) + * @see java.security.Policy#implies(java.security.ProtectionDomain, + * java.security.Permission) + */ + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + PermissionCollection pc = getPermissions(domain); + if (pc == null) { + return false; + } + return pc.implies(permission); + } + + /** + * Installe cette TopiaPolicy. Si la Policy existante est déja cette + * TopiaPolicy alors la méthode n'a pas d'effet. Si une autre Policy existe + * deja alors cette TopiaPolicy, elle conserve l'ancienne Policy dans + * parentPolicy et la remplace alors. + */ + public void installPolicy() { + Policy policy = Policy.getPolicy(); + if (equals(policy)) + return; + if (policy instanceof TopiaPolicy) { + if (log.isDebugEnabled()) { + log.debug("Policy deja modifie en: " + policy); + } + } else { + if (log.isDebugEnabled()) { + log.debug("l'ancienne Policy etait: " + policy); + } + setParentPolicy(policy); + Policy.setPolicy(this); + } + } + +} // TopiaPolicy diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/jaas/TopiaPrincipal.java b/topia-service-security/src/main/java/org/nuiton/topia/security/jaas/TopiaPrincipal.java new file mode 100644 index 0000000..06550f4 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/jaas/TopiaPrincipal.java @@ -0,0 +1,83 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * +* TopiaPrincipal.java +* +* Created: 15 févr. 2006 +* +* @author Arnaud Thimel <thimel@codelutin.com> +* @version $Revision$ +* +* Mise a jour: $Date$ +* par : $Author$ +*/ + +package org.nuiton.topia.security.jaas; + +import java.security.Principal; + +/** + * Implantation des principals de JAAS. + * @author ruchaud + */ +public class TopiaPrincipal implements Principal { + + protected String name; + + /** + * Contructeur avec comme paramètre le nom du principal. + * @param name topiaId d'un group ou d'utilisateur + */ + public TopiaPrincipal(String name) { + this.name = name; + } + + /* (non-Javadoc) + * @see java.security.Principal#getName() + */ + public String getName() { + return name; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + return getClass().getName() + " : " + name; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object o) { + if (!(o instanceof TopiaPrincipal)) + return false; + if (name == null) { + return ((Principal)o).getName() == null; + } + return name.equals(((TopiaPrincipal)o).getName()); + } + +} //TopiaPrincipal diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/listener/EntityVetoable.java b/topia-service-security/src/main/java/org/nuiton/topia/security/listener/EntityVetoable.java new file mode 100644 index 0000000..8f6d0fc --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/listener/EntityVetoable.java @@ -0,0 +1,125 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * +* TopiaSecurityVetoableListener.java +* +* Created: 10 févr. 2006 +* +* @author Arnaud Thimel <thimel@codelutin.com> +* @version $Revision$ +* +* Mise a jour: $Date$ +* par : $Author$ +*/ + +package org.nuiton.topia.security.listener; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaNotFoundException; +import org.nuiton.topia.event.TopiaEntityEvent; +import org.nuiton.topia.event.TopiaEntityVetoable; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaId; +import org.nuiton.topia.security.TopiaSecurityServiceImpl; + +import static org.nuiton.topia.security.util.TopiaSecurityUtil.CREATE; +import static org.nuiton.topia.security.util.TopiaSecurityUtil.DELETE; +import static org.nuiton.topia.security.util.TopiaSecurityUtil.LOAD; + +/** + * Listenner permettant de vérifier les autorisations pour la création ou la + * suppression d'une entité. + * + * @author ruchaud + */ +public class EntityVetoable implements TopiaEntityVetoable { + + private static Log log = LogFactory.getLog(EntityVetoable.class); + + private TopiaSecurityServiceImpl securityManager; + + public EntityVetoable(TopiaSecurityServiceImpl securityManager) { + this.securityManager = securityManager; + } + + @Override + public void create(TopiaEntityEvent event) { + TopiaEntity entity = event.getEntity(); + String topiaId = entity.getTopiaId(); + Class<? extends TopiaEntity> clazz; + try { + clazz = TopiaId.getClassName(topiaId); + } catch (TopiaNotFoundException e) { + // Ne devrait jamais ce produire + throw new SecurityException("Access denied to entity creation", e); + } + + if (log.isDebugEnabled()) { + log.debug("[Security] create entity : " + clazz.getName()); + } + securityManager.checkPermission(clazz, CREATE); + } + + @Override + public void delete(TopiaEntityEvent event) { + String topiaId = event.getEntity().getTopiaId(); + if (log.isDebugEnabled()) { + log.debug("[Security] delete entity : " + topiaId); + } + securityManager.checkPermission(topiaId, DELETE); + } + + @Override + public void load(TopiaEntityEvent event) { + boolean authorized = true; + TopiaEntity entity = event.getEntity(); + String topiaId = entity.getTopiaId(); + + if (log.isDebugEnabled()) { + log.debug("[Security] load entity : " + topiaId); + } + + /* Vérification dans le cache */ + boolean contain = securityManager.containEntitiesLoadingCache(topiaId); + + if (!contain) { + try { + securityManager.checkPermission(topiaId, LOAD); + + } catch (SecurityException te) { + authorized = false; + } + + /* Mise en cache */ + securityManager.putEntitiesLoadingCache(topiaId, authorized); + } + } + + @Override + public void update(TopiaEntityEvent event) { + } + +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/listener/NoSecurityLoad.java b/topia-service-security/src/main/java/org/nuiton/topia/security/listener/NoSecurityLoad.java new file mode 100644 index 0000000..9a3a893 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/listener/NoSecurityLoad.java @@ -0,0 +1,33 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.security.listener; + +/** + * Interface permettant à préciser que l'entité n'est pas soumis aux autorisations + * de chargement sur les entités. + * @author ruchaud + */ +public interface NoSecurityLoad { +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/listener/PropertyReadListener.java b/topia-service-security/src/main/java/org/nuiton/topia/security/listener/PropertyReadListener.java new file mode 100644 index 0000000..86a7ca1 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/listener/PropertyReadListener.java @@ -0,0 +1,79 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.security.listener; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.persistence.TopiaEntityAbstract; +import org.nuiton.topia.security.TopiaSecurityServiceImpl; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; + +import static org.nuiton.topia.security.util.TopiaSecurityUtil.LOAD; + +/** + * Listenner permettant de vérifier les autorisations pour la chargement d'une + * propriété sur une entités. + * + * @author ruchaud + */ +//TODO: Gestion d'une sécurité partiel ou total c'est à dire retour d'une valeur par défaut +public class PropertyReadListener implements VetoableChangeListener { + + private static Log log = LogFactory.getLog(PropertyReadListener.class); + + private TopiaSecurityServiceImpl securityManager; + + public PropertyReadListener(TopiaSecurityServiceImpl securityManager) { + this.securityManager = securityManager; + } + + @Override + public void vetoableChange(PropertyChangeEvent event) throws PropertyVetoException { + Object source = event.getSource(); + TopiaEntityAbstract entity = (TopiaEntityAbstract) source; + + /* Vérification dans le cache */ + Boolean authorized = securityManager.getEntitiesLoadingCache(entity.getTopiaId()); + if (authorized != null) { + if (!authorized) { + throw new SecurityException("Access denied to Read entity " + entity.getTopiaId() + " on " + event.getPropertyName()); + } + } else { // Sinon + try { + securityManager.checkPermission(entity.getTopiaId(), LOAD); + } catch (SecurityException te) { + if (log.isWarnEnabled()) { + log.warn("[Security] Read denied to : " + entity.getTopiaId(), te); + } + throw new SecurityException("Access denied to Read entity " + entity.getTopiaId() + " on " + event.getPropertyName(), te); + } + } + + } + +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/listener/PropertyVetoable.java b/topia-service-security/src/main/java/org/nuiton/topia/security/listener/PropertyVetoable.java new file mode 100644 index 0000000..533c6c0 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/listener/PropertyVetoable.java @@ -0,0 +1,105 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * +* TopiaSecurityVetoableListener.java +* +* Created: 10 févr. 2006 +* +* @author Arnaud Thimel <thimel@codelutin.com> +* @version $Revision$ +* +* Mise a jour: $Date$ +* par : $Author$ +*/ + +package org.nuiton.topia.security.listener; + +import org.nuiton.topia.event.TopiaEntityEvent; +import org.nuiton.topia.event.TopiaEntityListener; +import org.nuiton.topia.persistence.TopiaEntity; + +/** + * Ajout en cas de chargement ou de creation d'entités des listeners pour la + * sécurité sur leurs champs. + * @author ruchaud + */ +public class PropertyVetoable implements TopiaEntityListener { + + protected PropertyReadListener read; + protected PropertyWriteListener write; + + /** + * Contructeur avec comme paramètre les listeners à attacher au chargement ou + * à la création. + * @param read Listener en lecture d'un champ + * @param write Listener en écriture d'un champ + */ + public PropertyVetoable(PropertyReadListener read, PropertyWriteListener write) { + this.read = read; + this.write = write; + } + + private void putVetoables(TopiaEntityEvent event) { + TopiaEntity entity = event.getEntity(); + if (!(entity instanceof NoSecurityLoad)) { + entity.addVetoableListener(read); + } + entity.addVetoableChangeListener(write); + } + + /* + * (non-Javadoc) + * @see org.nuiton.topia.event.TopiaEntityLoadListener#entityLoaded(org.nuiton.topia.event.TopiaEntityLoadEvent) + */ + @Override + public void load(TopiaEntityEvent event) { + putVetoables(event); + } + + /* + * (non-Javadoc) + * @see org.nuiton.topia.event.TopiaEntityListener#entityCreated(org.nuiton.topia.event.TopiaEntityEvent) + */ + @Override + public void create(TopiaEntityEvent event) { + } + + /* + * (non-Javadoc) + * @see org.nuiton.topia.event.TopiaEntityListener#entityDeleted(org.nuiton.topia.event.TopiaEntityEvent) + */ + @Override + public void delete(TopiaEntityEvent event) { + } + + /* + * (non-Javadoc) + * @see org.nuiton.topia.event.TopiaEntityListener#entityUpdated(org.nuiton.topia.event.TopiaEntityEvent) + */ + @Override + public void update(TopiaEntityEvent event) { + } + +} //TopiaSecurityVetoableListener diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/listener/PropertyWriteListener.java b/topia-service-security/src/main/java/org/nuiton/topia/security/listener/PropertyWriteListener.java new file mode 100644 index 0000000..0891fad --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/listener/PropertyWriteListener.java @@ -0,0 +1,68 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.security.listener; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.persistence.TopiaEntityAbstract; +import org.nuiton.topia.security.TopiaSecurityServiceImpl; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyVetoException; +import java.beans.VetoableChangeListener; + +import static org.nuiton.topia.security.util.TopiaSecurityUtil.UPDATE; + +/** + * Listenner permettant de vérifier les autorisations pour le modification d'une + * propriété sur une entités. + * + * @author ruchaud + */ +public class PropertyWriteListener implements VetoableChangeListener { + + private static Log log = LogFactory.getLog(PropertyWriteListener.class); + + private TopiaSecurityServiceImpl securityManager; + + public PropertyWriteListener(TopiaSecurityServiceImpl securityManager) { + this.securityManager = securityManager; + } + + @Override + public void vetoableChange(PropertyChangeEvent event) throws PropertyVetoException { + Object source = event.getSource(); + TopiaEntityAbstract entity = (TopiaEntityAbstract) source; + try { + securityManager.checkPermission(entity.getTopiaId(), UPDATE); + } catch (SecurityException te) { + if (log.isWarnEnabled()) { + log.warn("[Security] Write denied to : " + entity.getTopiaId(), te); + } + throw new SecurityException("Access denied to Write entity " + entity.getTopiaId() + " on " + event.getPropertyName(), te); + } + } + +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/util/TopiaSecurityCaching.java b/topia-service-security/src/main/java/org/nuiton/topia/security/util/TopiaSecurityCaching.java new file mode 100644 index 0000000..caf9e58 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/util/TopiaSecurityCaching.java @@ -0,0 +1,132 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.security.util; + +import static org.apache.commons.collections.map.AbstractReferenceMap.SOFT; + +import java.util.Collections; +import java.util.Map; + +import org.apache.commons.collections.map.ReferenceMap; + +/** + * La classe permet d'avoir un ensemble de clés pour identifier de manière unique une valeur. + * @author ruchaud + */ +public class TopiaSecurityCaching { + + /** + * Le niveau correspond au nombre d'éléments de la clé + */ + protected int level; + + /** + * Map pour le stockage du première niveau + */ + protected Map map; + + /** + * Contruit un TopiaSecurityCaching en précisant le nombre d'élément de la clé + * @param level nombre d'élément de la clé, doit être suppérieur à 0 + */ + public TopiaSecurityCaching(int level) { + this.level = level; + map = Collections.synchronizedMap(new ReferenceMap(SOFT, SOFT)); + } + + /** + * Contruit un TopiaSecurityCaching avec un nombre d'élément de la clé par + * défaut à 1 + */ + public TopiaSecurityCaching() { + this(1); + } + + /** + * Permet de stocker la valeur pour une clé donnée. + * @param value valeur à stocker + * @param keys clé de la valeur + */ + public void put(Object value, Object ... keys) { + if(keys.length != level) { + throw new ArrayIndexOutOfBoundsException(); + } + + Map current = map; + for (int i = 0; i < keys.length -1 ; i++) { + Object key = keys[i]; + Map next = (Map) current.get(key); + if (next == null) { + next = Collections.synchronizedMap(new ReferenceMap(SOFT, SOFT)); + current.put(key, next); + } + current = next; + } + current.put(keys[keys.length - 1], value); + } + + /** + * Permet de supprimer une valeur pour une clé donnée. + * @param keys clé de la valeur + */ + public void clear(Object ... keys) { + Map current = map; + for (int i = 0; i < keys.length -1 ; i++) { + Object key = keys[i]; + Map next = (Map) current.get(key); + if (next == null) { + next = Collections.synchronizedMap(new ReferenceMap(SOFT, SOFT)); + current.put(key, next); + } + current = next; + } + current.clear(); + } + + /** + * Permet de récupérer une valeur pour une clé donnée. + * @param keys clé de la valeur + * @return valeur stocké pour la clé donnée retourne null si la clé n'est pas + * valide + */ + public Object get(Object ... keys) { + if(keys.length != level) { + throw new ArrayIndexOutOfBoundsException(); + } + + Map current = map; + for (int i = 0; i < keys.length -1 ; i++) { + Object key = keys[i]; + Map next = (Map) current.get(key); + if (next == null) { + return null; + } else { + current = next; + } + } + + return current.get(keys[keys.length - 1]); + } +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/util/TopiaSecurityFactoryFilter.java b/topia-service-security/src/main/java/org/nuiton/topia/security/util/TopiaSecurityFactoryFilter.java new file mode 100644 index 0000000..b8517f9 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/util/TopiaSecurityFactoryFilter.java @@ -0,0 +1,114 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.security.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.security.TopiaSecurityService; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +/** + * La classe permet de filtrer n'importe quel objet (Collection, List, + * TopiaEntity, ...) par rapport à une permission. + * + * @author ruchaud + */ +public class TopiaSecurityFactoryFilter { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(TopiaSecurityUtil.class); + + private static final long serialVersionUID = 1L; + + private TopiaSecurityService securityManager; + + public TopiaSecurityFactoryFilter(TopiaSecurityService securityManager) { + this.securityManager = securityManager; + } + + public void filter(Collection<TopiaEntity> entities, int actions, String... fields) { + //TODO: Gestion des autorisations sur les champs (cf TopiaEntityAuthorization) + throw new UnsupportedOperationException(); + } + + /** + * Filtre une entité + * + * @param entity entité à filtrer + * @param actions la filtre + * @return null si non autorisé sinon l'entité + */ + public TopiaEntity filter(TopiaEntity entity, int actions) { + try { + securityManager.checkPermission(entity.getTopiaId(), actions); + } catch (SecurityException e) { + if (log.isDebugEnabled()) { + log.debug("Return Null because : " + e); + } + return null; + } + return entity; + } + + /** + * Filtre une collection + * + * @param entities la collection à filtrer + * @param actions le filtre + * @return la collection filtrée + */ + public <E extends TopiaEntity> Collection<E> filter(Collection<E> entities, int actions) { + Collection<E> result = new ArrayList<E>(entities); + for (Iterator<?> iterator = result.iterator(); iterator.hasNext();) { + TopiaEntity entity = (TopiaEntity) iterator.next(); + try { + securityManager.checkPermission(entity.getTopiaId(), actions); + } catch (SecurityException e) { + iterator.remove(); + if (log.isDebugEnabled()) { + log.debug("Removed because : " + e); + } + } + } + return result; + } + + /** + * Filtre sur une liste + * + * @param entities la liste à filtrer + * @param actions le filtre + * @return la liste filtrée + */ + public <E extends TopiaEntity> List<E> filter(List<E> entities, int actions) { + return (List<E>) filter((Collection<E>) entities, actions); + } + +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/security/util/TopiaSecurityUtil.java b/topia-service-security/src/main/java/org/nuiton/topia/security/util/TopiaSecurityUtil.java new file mode 100644 index 0000000..e945bc0 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/security/util/TopiaSecurityUtil.java @@ -0,0 +1,258 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * +* TopiaSecurityUtil.java +* +* Created: 15 févr. 2006 +* +* @author Arnaud Thimel <thimel@codelutin.com> +* @version $Revision$ +* +* Mise a jour: $Date$ +* par : $Author$ +*/ + +package org.nuiton.topia.security.util; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaContextFactory; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.framework.TopiaContextImpl; +import org.nuiton.topia.persistence.TopiaId; +import org.nuiton.topia.security.entities.authorization.TopiaAssociationAuthorizationImpl; +import org.nuiton.topia.security.entities.authorization.TopiaAuthorizationImpl; +import org.nuiton.topia.security.entities.authorization.TopiaEntityAuthorizationImpl; +import org.nuiton.topia.security.entities.authorization.TopiaExpressionLinkImpl; +import org.nuiton.topia.security.entities.user.TopiaGroupImpl; +import org.nuiton.topia.security.entities.user.TopiaUser; +import org.nuiton.topia.security.entities.user.TopiaUserImpl; +import org.nuiton.topia.security.jaas.TopiaLoginModule; +import sun.misc.BASE64Encoder; + +import javax.security.auth.Subject; +import java.security.AccessController; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.util.Properties; +import java.util.StringTokenizer; + +/** + * Classe utilitaire + * + * @author ruchaud + * + */ +public class TopiaSecurityUtil { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(TopiaSecurityUtil.class); + + public static final int LOAD = 0x1; + public static final String LOAD_TEXT = "LOAD"; + public static final int CREATE = 0x2; + public static final String CREATE_TEXT = "CREATE"; + public static final int UPDATE = 0x4; + public static final String UPDATE_TEXT = "UPDATE"; + public static final int DELETE = 0x8; + public static final String DELETE_TEXT = "DELETE"; + + public static final String SECURITY_MANAGER_KEY = "topia.app.security.manager"; + + public static final String TOPIA_LOGIN_MODULE = TopiaLoginModule.class.getName(); + + public static final Class<?>[] TOPIA_SECURITY_PERSISTENCE_CLASSES = new Class [] { + TopiaUserImpl.class, + TopiaEntityAuthorizationImpl.class, + TopiaGroupImpl.class, + TopiaExpressionLinkImpl.class, + TopiaAssociationAuthorizationImpl.class, + TopiaAuthorizationImpl.class, + }; + + /** + * Applique un algorithme de hashage sur la chaine de caratère passée en + * paramètre + * @param msg la chaine de caractère sur laquelle on veut opérer le hashage + * @return La chaine de caractère une fois l'algorithme appliqué + */ + public static String hash(String msg) { + return digestSHAHex(msg); + } + + /** + * Applique un algorithme de hashage sur la chaine de caratère passée en + * paramètre + * @param msg la chaine de caratère sur laquelle on veut opérer le hashage + * @return La chaine de caractère une fois l'algorithme appliqué + */ + public static String digestSHABase64(String msg) { + if (msg == null) { + return null; + } + try { + MessageDigest digest = MessageDigest.getInstance("SHA"); + byte[] bytes = msg.getBytes(); + bytes = digest.digest(bytes); + BASE64Encoder encoder = new sun.misc.BASE64Encoder(); + String msgHashed = encoder.encode(bytes); + return msgHashed; + } catch (NoSuchAlgorithmException nsee) { + return msg; + } + } + + /** + * Fait le checksum SHA de la chaine de caractere le resultat est retourne + * sous forme de chaine Hexadecimal. + * @param ch ? + * @return ? + */ + static public String digestSHAHex(String ch){ + if(ch == null){ + return null; + } + try{ + MessageDigest md = MessageDigest.getInstance("SHA"); + md.update(ch.getBytes()); + byte[] digest = md.digest(); + + StringBuffer result = new StringBuffer(); + for (int i=0; i < digest.length; i++) { + String hex = Integer.toHexString(0xFF & digest[i]); + if (hex.length() == 1) { + result.append("0").append(hex); + } else { + result.append(hex); + } + } + + return result.toString(); + }catch(NoSuchAlgorithmException eee){ + log.warn("Impossible de trouve l'algo SHA", eee); + return ch; + } + } + + /** + * Transforme actions en un entier. + * @param actions - + * combinaison de mots cles "load" "update" "create" et "delete" + * separes par des virgules. Ex : "load,update" + * @return 0 si aucune permission. Une combinaison des permissions + */ + public static int actionsString2Int(String actions) { + int result = 0x0; + StringTokenizer tokens = new StringTokenizer(actions, ","); + while (tokens.hasMoreTokens()) { + String action = tokens.nextToken().trim(); + if (LOAD_TEXT.equalsIgnoreCase(action)) { + result |= LOAD; + } else if (CREATE_TEXT.equalsIgnoreCase(action)) { + result |= CREATE; + } else if (UPDATE_TEXT.equalsIgnoreCase(action)) { + result |= UPDATE; + } else if (DELETE_TEXT.equalsIgnoreCase(action)) { + result |= DELETE; + } else { + throw new IllegalArgumentException("action not supported: " + + action); + } + } + return result; + } + + /** + * Transforme actions en une chaine de caractères + * @param actions les actions sous forme d'un entier + * @return La chaine des actions passé en paramètre + */ + public static String actionsInt2String(int actions) { + StringBuffer result = new StringBuffer(); + if ((actions & LOAD) == LOAD) { + result.append(LOAD_TEXT); + result.append(","); + } + if ((actions & CREATE) == CREATE) { + result.append(CREATE_TEXT); + result.append(","); + } + if ((actions & UPDATE) == UPDATE) { + result.append(UPDATE_TEXT); + result.append(","); + } + if ((actions & DELETE) == DELETE) { + result.append(DELETE_TEXT); + result.append(","); + } + + if (result.length() > 0) { + return result.substring(0, result.length() - 1); + } else { + return ""; + } + } + + /** + * Permet de récupérer parmis la liste des principals, le principal de type + * TopiaUser + * @return topiaId du principal de l'utilisateur + */ + public static String getUserPrincipal() { + Subject subject = Subject.getSubject(AccessController.getContext()); + if (subject != null) { + for (Principal principal : subject.getPrincipals()) { + String className = TopiaId.getClassNameAsString(principal.getName()); + if(className.equals(TopiaUser.class.getName())) { + return principal.getName(); + } + } + } + return null; + } + + /** + * Création d'un context sans sécurité + * @param transaction TopiaContext avec lequel le nouveau contexte est crée + * @return retourne un contexte sans sécurité + * @throws TopiaException if any topia pb + */ + public static TopiaContext beginTransactionWithoutSecurity(TopiaContext transaction) throws TopiaException { + TopiaContextImpl context = (TopiaContextImpl) transaction; + Properties config = context.getConfig(); + + config.setProperty("topia.service.security", ""); + String persistences = config.getProperty("topia.persistence.classes"); + for (Class<?> klass : TOPIA_SECURITY_PERSISTENCE_CLASSES) { + persistences += "," + klass.getName(); + } + config.setProperty("topia.persistence.classes", persistences); + + TopiaContext topiaContext = TopiaContextFactory.getContext(config); + return topiaContext.beginTransaction(); + } +} //TopiaSecurityUtil diff --git a/topia-service-security/src/main/java/org/nuiton/topia/taas/TaasService.java b/topia-service-security/src/main/java/org/nuiton/topia/taas/TaasService.java new file mode 100644 index 0000000..492d7e8 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/taas/TaasService.java @@ -0,0 +1,468 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/** + * TopiaSecurityVetoableListener.java + * + * Created: 10 févr. 2006 + * + * @author Arnaud Thimel <thimel@codelutin.com> + * @version $Revision$ + * + * Mise a jour: $Date$ + * par : sletellier + */ + +package org.nuiton.topia.taas; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaContextFactory; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaNotFoundException; +import org.nuiton.topia.event.TopiaTransactionEvent; +import org.nuiton.topia.event.TopiaTransactionVetoable; +import org.nuiton.topia.framework.TopiaContextImplementor; +import org.nuiton.topia.framework.TopiaService; +import org.nuiton.topia.persistence.TopiaDAO; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.persistence.TopiaEntityAbstract; +import org.nuiton.topia.persistence.TopiaId; +import org.nuiton.topia.taas.entities.TaasAuthorizationImpl; +import org.nuiton.topia.taas.entities.TaasPrincipalImpl; +import org.nuiton.topia.taas.entities.TaasUserImpl; +import org.nuiton.topia.taas.event.TaasAccessEntity; +import org.nuiton.topia.taas.event.TaasEntityVetoable; +import org.nuiton.topia.taas.jaas.TaasConfiguration; +import org.nuiton.topia.taas.jaas.TaasLoginModule; +import org.nuiton.topia.taas.jaas.TaasPermission; +import org.nuiton.topia.taas.jaas.TaasPolicy; +import org.nuiton.topia.taas.jaas.TaasPrincipalWrapper; +import org.nuiton.topia.taas.jaas.TaasSubjectFinder; +import org.nuiton.topia.taas.jaas.TaasSubjectFinderImpl; + +import javax.security.auth.Subject; +import javax.security.auth.login.Configuration; +import java.lang.reflect.Constructor; +import java.security.AccessController; +import java.security.Permission; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; +import java.util.Set; + +import static org.nuiton.topia.taas.TaasUtil.getPrincipalNames; + +/** + * Service pour la sécurité + * <p/> + * Pour utiliser le service taas, il suffit de rajouter les lignes suivantes + * dans le TopiaContext.properties :<p> topia.service.taas=org.nuiton.topia.taas.TaasService + * topia.service.taas.event=org.nuiton.topia.taas.event.TaasEntityVetoable + * topia.service.taas.subject=org.nuiton.topia.taas.jaas.TaasSubjectImpl + * + * @author julien + */ +public class TaasService implements TopiaService, TopiaTransactionVetoable { + + static private Log log = LogFactory.getLog(TaasService.class); + + public static final String SERVICE_NAME = "taas"; + + public static final String SERVICE_LOGIN_MODULE = + TaasLoginModule.class.getName(); + + public static final String SERVICE_EVENT = + "topia.service.taas.event"; + + public static final String SERVICE_SUBJECT = + "topia.service.taas.subject"; + public static final String TOPIA_SERVICE_TAAS = "topia.service.taas"; + + private TaasPolicy policy = new TaasPolicy(this); + + private TopiaContextImplementor rootContext; + + private TopiaContext rootContextNoSecure; + + private TaasAccessEntity accessEntity; + + private TaasSubjectFinder subjectFinder; + + /** Contructeur par defaut */ + public TaasService() { + } + + @Override + public Class<?>[] getPersistenceClasses() { + return getTaasPersistenceClasses(); + } + + public static Class<?>[] getTaasPersistenceClasses() { + return new Class<?>[]{ + TaasUserImpl.class, + TaasPrincipalImpl.class, + TaasAuthorizationImpl.class, + }; + } + + @Override + public String getServiceName() { + return SERVICE_NAME; + } + + @Override + public boolean preInit(TopiaContextImplementor context) { + + rootContext = context; + try { + Properties config = + (Properties)rootContext.getConfig().clone(); + + // Recuperation du parametre pour l'evenemnt + String eventString = config.getProperty(SERVICE_EVENT); + if (eventString != null && !"".equals(eventString)) { + Class<?> eventClass = Class.forName(eventString); + Constructor<?> eventConstructor = eventClass.getConstructor(TaasService.class); + accessEntity = (TaasAccessEntity) eventConstructor.newInstance(this); + } else { + accessEntity = new TaasEntityVetoable(this); + } + + // Recuperation du parametre pour le subject + String subjectString = config.getProperty(SERVICE_SUBJECT); + if (subjectString != null && !"".equals(subjectString)) { + Class<?> subjectClass = Class.forName(subjectString); + subjectFinder = (TaasSubjectFinder) subjectClass.newInstance(); + } else { + subjectFinder = new TaasSubjectFinderImpl(); + } + +// // Recupere un context root sans securite +// Properties config = rootContext.getConfig(); + + // Hack to not reload this service + config.remove(TOPIA_SERVICE_TAAS); + + String persistences = config.getProperty("topia.persistence.classes"); + for (Class<?> klass : getPersistenceClasses()) { + persistences += "," + klass.getName(); + } + config.setProperty("topia.persistence.classes", persistences); + + rootContextNoSecure = TopiaContextFactory.getContext(config); + + } catch (Exception e) { + throw new SecurityException("Init security error", e); + } + + // Do it after accessEntity instantiation + initSecurity(rootContext); + + return true; + } + + @Override + public void beginTransaction(TopiaTransactionEvent event) { + TopiaContext context = event.getSource(); + if (subjectFinder != null) { + initSecurity(context); + } + } + + /** + * Initialisation des vetoables + * + * @param context context topia en cours + */ + private void initSecurity(TopiaContext context) { + List<Class<?>> entitiesClasses = rootContext.getPersistenceClasses(); + for (Class<?> clazz : entitiesClasses) { + context.addTopiaEntityVetoable((Class) clazz, accessEntity); + } + + Class<?>[] noLoadClasses = getPersistenceClasses(); + for (Class<?> clazz : noLoadClasses) { + context.addTopiaEntityVetoable((Class) clazz, accessEntity); + } + + context.addTopiaEntitiesVetoable(accessEntity); + context.addTopiaTransactionVetoable(this); + } + + @Override + public boolean postInit(TopiaContextImplementor context) { + policy.installPolicy(); + + // Si pas de configuration autre que celle par defaut + if (Configuration.getConfiguration() == null) { + Configuration.setConfiguration( + new TaasConfiguration(SERVICE_NAME, this)); + } + return true; + } + + /** + * Permet d'obtenir le context root + * + * @return context root + */ + public TopiaContextImplementor getRootContext() { + return rootContext; + } + + /** + * Permet de recuperer un context root sans securite + * + * @return context root non securise + * @throws TopiaException + */ + public TopiaContext getRootContextNoSecure() + throws TopiaException { + return rootContextNoSecure; + } + + /** + * Permet de récupérer le subject en cours + * @return subject + */ + public Subject findSubject() { + Subject subject = subjectFinder.findSubject(); + + if (log.isDebugEnabled()) { + log.debug("findSubject : " + + subjectFinder + " value " + + subject); + } + + return subject; + } + + /** + * Permet de verifier les authorizations sur une collection et de supprimer + * les donnees non autorisées + * + * @param entities collection d'entites + * @param actions actions + * @throws SecurityException en cas d'erreur de sécurité + */ + public void check(Collection<? extends TopiaEntity> entities, int actions) + throws SecurityException { + Subject subj = findSubject(); + if (subj != null) { + for (Iterator<? extends TopiaEntity> iterator = + entities.iterator(); iterator.hasNext();) { + TopiaEntity entity = iterator.next(); + try { + TaasPermission myp = new TaasPermission(entity.getTopiaId(), actions); + checkPermission(subj, myp); + } catch (SecurityException se) { + if (log.isDebugEnabled()) { + log.debug(getPrincipalNames(subj) + + " does not have permissions to load: " + entity, se); + } + iterator.remove(); + } + } + } else { + throw new SecurityException("Use doAs() and login first"); + } + } + + /** + * Permet de vérifier les authorizations + * + * @param entity entité + * @param actions actions + * @throws SecurityException en cas d'erreur de sécurité + */ + public void check(TopiaEntity entity, int actions) + throws SecurityException { + check(entity.getTopiaId(), actions); + } + + /** + * Permet de vérifier les authorizations + * + * @param topiaId id de l'entite + * @param actions actions + * @throws SecurityException en cas d'erreur de sécurité + */ + public void check(String topiaId, int actions) throws SecurityException { + Subject subj = findSubject(); + if (subj != null) { + try { + TaasPermission myp = new TaasPermission(topiaId, actions); + checkPermission(subj, myp); + } catch (SecurityException se) { + throw new SecurityException("Access denied to object \"" + + topiaId + "\" for \"" + getPrincipalNames(subj) + "\"", se); + } + } else { + throw new SecurityException("Use doAs() and login first"); + } + } + + /** + * Hack pour faire fonctionner la security. Normalement cette methode devrait etre seulement + * <pre> + * AccessController.checkPermission(myp); + * </pre> + * + * Mais comme ca ne fonctionne pas et pas vraiment de raison. Que le code au final a seulement besoin + * de checker les TaasPermissions des principales du subject. Cette methode est plus simple et plus rapide + * que le mode normal. + * + * @param subj + * @param myp + */ + protected void checkPermission(Subject subj, Permission myp) { + + // Code that note use realy jaas + Set<TaasPrincipalWrapper> ps = subj.getPrincipals(TaasPrincipalWrapper.class); + for (TaasPrincipalWrapper p : ps) { + if (log.isDebugEnabled()) { + log.debug("Check permissions for principal wrapper : " + p); + } + if (p.getPermissions().implies(myp)) { + return; + } + } + throw new SecurityException("Access denied to object " + myp); + + // Old code that use realy jaas + //AccessController.checkPermission(myp); + } + + /** + * Permet de vérifier les authorizations + * + * @param entity entité + * @param actions actions + * @throws SecurityException en cas d'erreur de sécurité + */ + public void checkRequestPermission(TopiaEntity entity, int actions) + throws SecurityException { + Subject subj = findSubject(); + if (subj != null) { + + List<Permission> permissions = getRequestPermission(entity, actions); + + if (permissions == null) { + try { + TaasPermission myp = new TaasPermission(entity.getTopiaId(), actions); + checkPermission(subj, myp); + } catch (SecurityException se) { + throw new SecurityException("Access denied to object \"" + + entity.getTopiaId() + "\" for \"" + + getPrincipalNames(subj) + "\"", se); + } + } else { + for (Permission permission : permissions) { + try { + checkPermission(subj, permission); + break; + } catch (SecurityException se) { + throw new SecurityException("Access denied to object \"" + + entity.getTopiaId() + "\" for \"" + + getPrincipalNames(subj) + "\"", se); + } + } + } + } else { + throw new SecurityException("Use doAs() and login first"); + } + } + + /** + * Permet de vérifier les authorizations sur une collection et de supprimer + * les données non autorisées + * + * @param entities collection d'entités + * @param actions actions + * @throws SecurityException en cas d'erreur de sécurité + */ + public void checkRequestPermission(Collection<? extends TopiaEntity> entities, + int actions) throws SecurityException { + Subject subj = findSubject(); + if (subj != null) { + + for (Iterator<? extends TopiaEntity> iterator = entities.iterator(); iterator.hasNext();) { + TopiaEntity entity = iterator.next(); + List<Permission> permissions = getRequestPermission(entity, actions); + + if (permissions == null) { + try { + AccessController.checkPermission(new TaasPermission(entity.getTopiaId(), actions)); + } catch (SecurityException se) { + iterator.remove(); + } + } else { + for (Permission permission : permissions) { + try { + AccessController.checkPermission(permission); + break; + } catch (SecurityException se) { + iterator.remove(); + } + } + } + } + } else { + throw new SecurityException("Use doAs() and login first"); + } + } + + /** + * Récupération des requests permissions dans les DAOs + * + * @param entity entité + * @param actions actions + * @return permissions à vérifier + */ + public List<Permission> getRequestPermission(TopiaEntity entity, int actions) { + String topiaId = entity.getTopiaId(); + Class<? extends TopiaEntity> klass; + + try { + klass = TopiaId.getClassName(topiaId); + } catch (TopiaNotFoundException e) { + throw new SecurityException("Invalid topiaId", e); + } + + List<Permission> permissions; + try { + TopiaContextImplementor transaction = (TopiaContextImplementor) + ((TopiaEntityAbstract)entity).getTopiaContext(); + TopiaDAO<?> dao = transaction.getDAO(klass); + permissions = dao.getRequestPermission(topiaId, actions); + } catch (TopiaException e) { + throw new SecurityException("Error in getRequestPermission for " + klass.getName(), e); + } + return permissions; + } + +} //TaasService diff --git a/topia-service-security/src/main/java/org/nuiton/topia/taas/TaasUtil.java b/topia-service-security/src/main/java/org/nuiton/topia/taas/TaasUtil.java new file mode 100644 index 0000000..48a9942 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/taas/TaasUtil.java @@ -0,0 +1,241 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * +* TopiaSecurityUtil.java +* +* Created: 15 févr. 2006 +* +* @author Arnaud Thimel <thimel@codelutin.com> +* @version $Revision$ +* +* Mise a jour: $Date$ +* par : $Author$ +*/ + +package org.nuiton.topia.taas; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.util.HashSet; +import java.util.Set; +import java.util.StringTokenizer; + +import javax.security.auth.Subject; + +import org.apache.commons.lang3.ArrayUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + + +/** + * Classe utilitaire + * + * @author ruchaud + * + */ +public class TaasUtil { + + /** to use log facility, just put in your code: log.info(\"...\"); */ + static private Log log = LogFactory.getLog(TaasUtil.class); + + public static final int LOAD = 0x1; + public static final String LOAD_TEXT = "LOAD"; + public static final int CREATE = 0x2; + public static final String CREATE_TEXT = "CREATE"; + public static final int UPDATE = 0x4; + public static final String UPDATE_TEXT = "UPDATE"; + public static final int DELETE = 0x8; + public static final String DELETE_TEXT = "DELETE"; + + /** + * Applique un algorithme de hashage sur la chaine de caratere passee en + * parametre + * @param msg la chaine de caratere sur laquelle on veut operer le hashage + * @return La chaine de caractere une fois l'algorithme applique + */ + public static String hash(String msg) { + return digestSHAHex(msg); + } + + /** + * Applique un algorithme de hashage sur la chaine de caratère passée en + * paramètre + * @param msg la chaine de caratère sur laquelle on veut opérer le hashage + * @return La chaine de caractère une fois l'algorithme appliqué + */ + public static String digestSHABase64(String msg) { + if (msg == null) { + return null; + } + try { + MessageDigest digest = MessageDigest.getInstance("SHA"); + byte[] bytes = msg.getBytes(); + bytes = digest.digest(bytes); + sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder(); + String msgHashed = encoder.encode(bytes); + return msgHashed; + } catch (NoSuchAlgorithmException nsee) { + return msg; + } + } + + /** + * Fait le checksum SHA de la chaine de caractere le resultat est retourne + * sous forme de chaine Hexadecimal. + * @param ch la chaine a traiter + * @return le checksum SHA en mode hexadecimal + */ + static public String digestSHAHex(String ch){ + if(ch == null){ + return null; + } + try{ + MessageDigest md = MessageDigest.getInstance("SHA"); + md.update(ch.getBytes()); + byte[] digest = md.digest(); + + StringBuffer result = new StringBuffer(); + for (int i=0; i < digest.length; i++) { + String hex = Integer.toHexString(0xFF & digest[i]); + if (hex.length() == 1) { + result.append("0" + hex); + } else { + result.append(hex); + } + } + + return result.toString(); + }catch(NoSuchAlgorithmException eee){ + log.warn("Impossible de trouve l'algo SHA", eee); + return ch; + } + } + + /** + * Transforme actions en un entier. + * @param actions - + * combinaison de mots cles "load" "update" "create" et "delete" + * separes par des virgules. Ex : "load,update" + * @return 0 si aucune permission. Une combinaison des permissions + */ + public static int actionsString2Int(String actions) { + int result = 0x0; + StringTokenizer tokens = new StringTokenizer(actions, ","); + while (tokens.hasMoreTokens()) { + String action = tokens.nextToken().trim(); + if (LOAD_TEXT.equalsIgnoreCase(action)) { + result |= LOAD; + } else if (CREATE_TEXT.equalsIgnoreCase(action)) { + result |= CREATE; + } else if (UPDATE_TEXT.equalsIgnoreCase(action)) { + result |= UPDATE; + } else if (DELETE_TEXT.equalsIgnoreCase(action)) { + result |= DELETE; + } else { + throw new IllegalArgumentException("action not supported: " + + action); + } + } + return result; + } + + /** + * Transforme actions en une chaîne de caractères + * @param actions les actions sous forme d'un entier + * @return La chaine des actions passé en paramètre + */ + public static String actionsInt2String(int actions) { + StringBuffer result = new StringBuffer(); + if ((actions & LOAD) == LOAD) { + result.append(LOAD_TEXT); + result.append(","); + } + if ((actions & CREATE) == CREATE) { + result.append(CREATE_TEXT); + result.append(","); + } + if ((actions & UPDATE) == UPDATE) { + result.append(UPDATE_TEXT); + result.append(","); + } + if ((actions & DELETE) == DELETE) { + result.append(DELETE_TEXT); + result.append(","); + } + + if (result.length() > 0) { + return result.substring(0, result.length() - 1); + } else { + return ""; + } + } + + /** + * Détermine si la classe implémente une interface + * <p> + * interface A <---- class B <---- class C + * <p> + * interface D <---- class E + * <p> + * isImplement(C, A) = true + * <p> + * isImplement(E, A) = false + * + * @param klass la classe + * @param iface l'interface + * @return vrai si la classe implémente l'interface sinon faux + */ + public static boolean isImplement(Class<?> klass, Class<?> iface) { + boolean result = false; + + Class<?>[] interfaces = klass.getInterfaces(); + result |= ArrayUtils.contains(interfaces, iface); + + Class<?> superclass = klass.getSuperclass(); + if(!result && superclass != null) { + result |= isImplement(superclass, iface); + } + + return result; + } + + /** + * Renvoie dans un Set les attribut 'name' des principals du Subject passe + * en param + * @param subj + * @return les attribut 'name' des principals du Subject passe + */ + public static Set<String> getPrincipalNames(Subject subj) { + Set<String> result = new HashSet<String>(); + if (subj != null && subj.getPrincipals() != null) { + for (Principal p : subj.getPrincipals()) { + result.add(p.getName()); + } + } + return result; + } + +} //TopiaSecurityUtil diff --git a/topia-service-security/src/main/java/org/nuiton/topia/taas/event/TaasAccessEntity.java b/topia-service-security/src/main/java/org/nuiton/topia/taas/event/TaasAccessEntity.java new file mode 100644 index 0000000..31326e8 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/taas/event/TaasAccessEntity.java @@ -0,0 +1,32 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.taas.event; + +import org.nuiton.topia.event.TopiaEntitiesVetoable; +import org.nuiton.topia.event.TopiaEntityVetoable; + +public interface TaasAccessEntity extends TopiaEntitiesVetoable, TopiaEntityVetoable { + +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/taas/event/TaasEntityVetoable.java b/topia-service-security/src/main/java/org/nuiton/topia/taas/event/TaasEntityVetoable.java new file mode 100644 index 0000000..0e2f06b --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/taas/event/TaasEntityVetoable.java @@ -0,0 +1,136 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.taas.event; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.event.TopiaEntitiesEvent; +import org.nuiton.topia.event.TopiaEntityEvent; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.taas.TaasService; +import org.nuiton.topia.taas.entities.TaasAuthorization; +import org.nuiton.topia.taas.entities.TaasPrincipal; +import org.nuiton.topia.taas.entities.TaasUser; + +import java.util.List; + +import static org.nuiton.topia.security.util.TopiaSecurityUtil.UPDATE; +import static org.nuiton.topia.taas.TaasUtil.CREATE; +import static org.nuiton.topia.taas.TaasUtil.DELETE; +import static org.nuiton.topia.taas.TaasUtil.LOAD; + +public class TaasEntityVetoable implements TaasAccessEntity { + + private static Log log = LogFactory.getLog(TaasEntityVetoable.class); + + protected TaasService taasService; + + /** Contructeur par défaut */ + public TaasEntityVetoable(TaasService taasService) { + this.taasService = taasService; + } + + /* (non-Javadoc) + * @see org.nuiton.topia.event.TopiaVetoableEntityListener#createEntity(org.nuiton.topia.event.TopiaVetoableEntityEvent) + */ + + public void create(TopiaEntityEvent event) { + TopiaEntity entity = event.getEntity(); + String topiaId = entity.getTopiaId(); + Class<? extends TopiaEntity> clazz; + + if (log.isDebugEnabled()) { + log.debug("[Security] create entity : " + topiaId); + } + taasService.check(entity, CREATE); + } + + /* (non-Javadoc) + * @see org.nuiton.topia.event.TopiaVetoableEntityListener#deleteEntity(org.nuiton.topia.event.TopiaVetoableEntityEvent) + */ + + public void delete(TopiaEntityEvent event) { + TopiaEntity entity = event.getEntity(); + String topiaId = entity.getTopiaId(); + + if (log.isDebugEnabled()) { + log.debug("[Security] delete entity : " + topiaId); + } + taasService.check(entity, DELETE); + } + + /* + * (non-Javadoc) + * @see org.nuiton.topia.event.TopiaVetoableEntityLoadListener#loadEntity(org.nuiton.topia.event.TopiaVetoableEntityLoadEvent) + */ + + public void load(TopiaEntityEvent event) { +// TopiaEntity entity = event.getEntity(); +// String topiaId = entity.getTopiaId(); +// +// if (log.isDebugEnabled()) { +// log.debug("[Security] load entity : " + topiaId); +// } +// taasService.check(entity, LOAD); + } + + /* (non-Javadoc) + * @see org.nuiton.topia.event.TopiaVetoableEntityListener#updateEntity(org.nuiton.topia.event.TopiaVetoableEntityEvent) + */ + + public void update(TopiaEntityEvent event) { + TopiaEntity entity = event.getEntity(); + String topiaId = entity.getTopiaId(); + + if (log.isDebugEnabled()) { + log.debug("[Security] update entity : " + topiaId); + } + taasService.check(entity, UPDATE); + } + + /* + * (non-Javadoc) + * @see org.nuiton.topia.event.TopiaEntitiesVetoable#load(org.nuiton.topia.event.TopiaEntitiesEvent) + */ + + public <E extends TopiaEntity> List<E> load(TopiaEntitiesEvent<E> event) { + if (log.isDebugEnabled()) { + log.debug("[Security] load entities"); + } + + List<E> entities = event.getEntities(); + + if (!entities.isEmpty()) { + E entity = entities.get(0); + if (!(entity instanceof TaasUser || + entity instanceof TaasPrincipal || + entity instanceof TaasAuthorization)) { + taasService.check(entities, LOAD); + } + } + return entities; + } + +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/taas/event/TaasEntityVetoableRequestPermission.java b/topia-service-security/src/main/java/org/nuiton/topia/taas/event/TaasEntityVetoableRequestPermission.java new file mode 100644 index 0000000..99a104c --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/taas/event/TaasEntityVetoableRequestPermission.java @@ -0,0 +1,123 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.taas.event; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.event.TopiaEntitiesEvent; +import org.nuiton.topia.event.TopiaEntityEvent; +import org.nuiton.topia.persistence.TopiaEntity; +import org.nuiton.topia.taas.TaasService; + +import java.util.List; + +import static org.nuiton.topia.security.util.TopiaSecurityUtil.UPDATE; +import static org.nuiton.topia.taas.TaasUtil.CREATE; +import static org.nuiton.topia.taas.TaasUtil.DELETE; +import static org.nuiton.topia.taas.TaasUtil.LOAD; + +public class TaasEntityVetoableRequestPermission implements TaasAccessEntity { + + private static Log log = LogFactory.getLog(TaasEntityVetoableRequestPermission.class); + + protected TaasService taasService; + + /** Contructeur par défaut */ + public TaasEntityVetoableRequestPermission(TaasService taasService) { + this.taasService = taasService; + } + + /* (non-Javadoc) + * @see org.nuiton.topia.event.TopiaVetoableEntityListener#createEntity(org.nuiton.topia.event.TopiaVetoableEntityEvent) + */ + + public void create(TopiaEntityEvent event) { + TopiaEntity entity = event.getEntity(); + String topiaId = entity.getTopiaId(); + Class<? extends TopiaEntity> clazz; + + if (log.isDebugEnabled()) { + log.debug("[Security] create entity : " + topiaId); + } + taasService.checkRequestPermission(entity, CREATE); + } + + /* (non-Javadoc) + * @see org.nuiton.topia.event.TopiaVetoableEntityListener#deleteEntity(org.nuiton.topia.event.TopiaVetoableEntityEvent) + */ + + public void delete(TopiaEntityEvent event) { + TopiaEntity entity = event.getEntity(); + String topiaId = entity.getTopiaId(); + + if (log.isDebugEnabled()) { + log.debug("[Security] delete entity : " + topiaId); + } + taasService.checkRequestPermission(entity, DELETE); + } + + /* + * (non-Javadoc) + * @see org.nuiton.topia.event.TopiaVetoableEntityLoadListener#loadEntity(org.nuiton.topia.event.TopiaVetoableEntityLoadEvent) + */ + + public void load(TopiaEntityEvent event) { +// TopiaEntity entity = event.getEntity(); +// String topiaId = entity.getTopiaId(); +// +// if (log.isDebugEnabled()) { +// log.debug("[Security] load entity : " + topiaId); +// } +// taasService.checkRequestPermission(entity, LOAD); + } + + /* (non-Javadoc) + * @see org.nuiton.topia.event.TopiaVetoableEntityListener#updateEntity(org.nuiton.topia.event.TopiaVetoableEntityEvent) + */ + + public void update(TopiaEntityEvent event) { + TopiaEntity entity = event.getEntity(); + String topiaId = entity.getTopiaId(); + + if (log.isDebugEnabled()) { + log.debug("[Security] update entity : " + topiaId); + } + taasService.checkRequestPermission(entity, UPDATE); + } + + /* + * (non-Javadoc) + * @see org.nuiton.topia.event.TopiaEntitiesVetoable#load(org.nuiton.topia.event.TopiaEntitiesEvent) + */ + + public <E extends TopiaEntity> List<E> load(TopiaEntitiesEvent<E> event) { + if (log.isDebugEnabled()) { + log.debug("[Security] load entities"); + } + List<E> entities = event.getEntities(); + taasService.checkRequestPermission(entities, LOAD); + return entities; + } +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasCallbackHandler.java b/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasCallbackHandler.java new file mode 100644 index 0000000..e1e4828 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasCallbackHandler.java @@ -0,0 +1,85 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * + * TopiaCallbackHandler.java + * + * Created: 20 févr. 2006 + * + * @author Arnaud Thimel <thimel@codelutin.com> + * @version $Revision$ + * + * Mise a jour: $Date$ + * par : */ + +package org.nuiton.topia.taas.jaas; + +import java.io.IOException; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * Classe permettant l'interfaçage entre l'application et la sécurité. + * @author ruchaud + */ +public class TaasCallbackHandler implements CallbackHandler { + + private String username; + + private String password; + + /** + * Contructeur + * @param username login de l'utilisateur + * @param password mot de passe de l'utilisateur + */ + public TaasCallbackHandler(String username, String password) { + this.username = username; + this.password = password; + } + + /* + * (non-Javadoc) + * + * @see javax.security.auth.callback.CallbackHandler#handle(javax.security.auth.callback.Callback[]) + */ + public void handle(Callback[] callbacks) throws IOException, + UnsupportedCallbackException { + for (Callback callback : callbacks) { + if (callback instanceof NameCallback) { + NameCallback nc = (NameCallback) callback; + nc.setName(username); + } else if (callback instanceof PasswordCallback) { + PasswordCallback pc = (PasswordCallback) callback; + pc.setPassword(password.toCharArray()); + } else + throw new UnsupportedCallbackException(callback); + } + } + +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasConfiguration.java b/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasConfiguration.java new file mode 100644 index 0000000..3dc643a --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasConfiguration.java @@ -0,0 +1,161 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * + * TopiaConfiguration.java + * + * Created: 20 févr. 2006 + * + * @author Arnaud Thimel <thimel@codelutin.com> + * @version $Revision$ + * + * Mise a jour: $Date$ + * par : */ + +package org.nuiton.topia.taas.jaas; + +import java.util.HashMap; +import java.util.Map; + +import javax.security.auth.login.AppConfigurationEntry; +import javax.security.auth.login.Configuration; + +import org.nuiton.topia.taas.TaasService; + +/** + * Classe permettant de passer des paramètres entre le LoginModule et le + * CallbackHandler. Ici on passe le SecurityManager. + * @author ruchaud + */ +public class TaasConfiguration extends Configuration { + + private Map<String, AppConfigurationEntry[]> appConfEntries; + + /** + * Créé une instance de TopiConfiguration avec un identifiant de + * configurationEntry et le nom du fichier de propriétés associé. + * + * @param name + * le nom de la configurationEntry + * @param taasService le service + */ + public TaasConfiguration(String name, TaasService taasService) { + appConfEntries = new HashMap<String, AppConfigurationEntry[]>(); + addEntry(name, taasService); + } + + /** + * Ajoute une ConfigurationEntry avec le nom de fichier de propriétés + * associé + * + * @param name + * le nom de la configurationEntry + * @param taasService + * le nom du fichier de propriétés + */ + private void addEntry(String name, TaasService taasService) { + AppConfigurationEntry[] confEntries = getAppConfigurationEntry(name); + if (confEntries != null) { + int i = 0; + for (; i < confEntries.length; i++) { + if (TaasService.SERVICE_LOGIN_MODULE.equals(confEntries[i].getLoginModuleName())) { + break; + } + } + if (i == confEntries.length) { + AppConfigurationEntry[] tmpConfEntries = confEntries; + confEntries = new AppConfigurationEntry[confEntries.length + 1]; + for (int j = 0; j < confEntries.length; j++) { + confEntries[j] = tmpConfEntries[j]; + } + confEntries[confEntries.length - 1] = createEntry(taasService); + } else { + if ( /* Mauvais FLAG */ + !AppConfigurationEntry.LoginModuleControlFlag.REQUIRED + .equals(confEntries[i].getControlFlag()) + /* Ne contient pas la propriété */ + || !confEntries[i].getOptions().containsKey( + TaasService.SERVICE_NAME) + /* Propriété mal initialisée */ + || !confEntries[i].getOptions() + .get(TaasService.SERVICE_NAME).equals(taasService)) { + confEntries[i] = createEntry(taasService, confEntries[i].getOptions()); + } + } + } else { + confEntries = new AppConfigurationEntry[1]; + confEntries[0] = createEntry(taasService); + } + appConfEntries.put(name, confEntries); + } + + /** + * Créé une entry avec des options vides + * + * @param taasService + * le SecurityHelper + * @return l'entry créée + */ + private AppConfigurationEntry createEntry(TaasService taasService) { + return createEntry(taasService, null); + } + + /** + * Créé une entry en rajoutant les options nécessaires à l'attribut options + * + * @param taasService + * le nom du fichier de propriétés + * @param options + * l'objet contenant les options précédentes + * @return l'entry créée + */ + private AppConfigurationEntry createEntry(TaasService taasService, Map options) { + if (options == null) { + options = new HashMap<String, Object>(); + } + options.put(TaasService.SERVICE_NAME, taasService); + return new AppConfigurationEntry(TaasService.SERVICE_LOGIN_MODULE, + AppConfigurationEntry.LoginModuleControlFlag.REQUIRED, options); + } + + /** + * Renvoie les entries associéess à l'attribut name + * + * @param name + * l'identifiant des entries demandées + * @return un tableau cotenant les entries demandées + */ + @Override + public AppConfigurationEntry[] getAppConfigurationEntry(String name) { + return appConfEntries.get(name); + } + + /* (non-Javadoc) + * @see javax.security.auth.login.Configuration#refresh() + */ + @Override + public void refresh() { + } + +} // TopiaConfiguration diff --git a/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasLoginModule.java b/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasLoginModule.java new file mode 100644 index 0000000..3bdc695 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasLoginModule.java @@ -0,0 +1,244 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * +* TopiaLoginModule.java +* +* Created: 15 févr. 2006 +* +* @author Arnaud Thimel <thimel@codelutin.com> +* @version $Revision$ +* +* Mise a jour: $Date$ +* par : $Author$ +*/ + +package org.nuiton.topia.taas.jaas; + +import java.util.Collection; +import java.util.Date; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.login.LoginException; +import javax.security.auth.spi.LoginModule; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaSecurityDAOHelper; +import org.nuiton.topia.taas.TaasService; +import org.nuiton.topia.taas.TaasUtil; +import org.nuiton.topia.taas.entities.TaasPrincipal; +import org.nuiton.topia.taas.entities.TaasUser; +import org.nuiton.topia.taas.entities.TaasUserDAO; + +/** + * LoginModule permettant l'authentification d'un utilisateur au près du système. + * @author ruchaud + */ +public class TaasLoginModule implements LoginModule { + + private Log log = LogFactory.getLog(TaasLoginModule.class); + + protected Subject subject; + protected CallbackHandler callbackHandler; + protected Set<TaasPrincipalWrapper> principals; + protected TaasUser privateCredential; + protected TaasService taasService; + + /* (non-Javadoc) + * @see javax.security.auth.spi.LoginModule#initialize( + * javax.security.auth.Subject, + * javax.security.auth.callback.CallbackHandler, + * java.util.Map, + * java.util.Map) + */ + @Override + public void initialize(Subject subject, CallbackHandler callbackHandler, + Map<String,?> sharedState, Map<String,?> options) { + + this.subject = subject; + this.callbackHandler = callbackHandler; + principals = null; + privateCredential = null; + taasService = (TaasService)options.get(TaasService.SERVICE_NAME); + } + + /* (non-Javadoc) + * @see javax.security.auth.spi.LoginModule#login() + */ + @Override + public boolean login() throws LoginException { + try { + if (callbackHandler == null) { + throw new LoginException("CallbackHandler cannot be null"); + } + if (taasService == null) { + throw new LoginException("TaasService property must be set"); + } + + String login, password; + + NameCallback nc = new NameCallback("login"); + PasswordCallback pc = new PasswordCallback("password", false); + + Callback[] callbacks = new Callback[2]; + + callbacks[0] = nc; + callbacks[1] = pc; + + try { + //Récupération du login et mot de passe + callbackHandler.handle(callbacks); + } catch (Exception eee) { + if (log.isWarnEnabled()) { + log.warn("Utilisation du CallbackHandler impossible", eee); + } + LoginException le = new LoginException( + "Utilisation du CallbackHandler impossible"); + le.initCause(eee); + throw le; + } + login = nc.getName(); + password = new String(pc.getPassword()); + pc.clearPassword(); + + String hashed = TaasUtil.hash(password); + + //Vérification du login/pass et récupération des Principals + TopiaContext transaction = null; + try { + TopiaContext rootContext = taasService.getRootContextNoSecure(); + transaction = rootContext.beginTransaction(); + + TaasUserDAO userDAO = TopiaSecurityDAOHelper.getTaasUserDAO(transaction); + TaasUser user = userDAO.findByLogin(login); + + if(user != null && user.getPassword().equals(hashed) && user.isEnabled()) { + privateCredential = user; + + // Update connection information + user.setLastConnectionDate(new Date()); + int numberOfConnection = user.getNumberOfConnection(); + user.setNumberOfConnection(numberOfConnection + 1); + userDAO.update(user); + + // Récupération des principals + principals = new HashSet<TaasPrincipalWrapper>(); + + Collection<TaasPrincipal> taasPrincipals = user.getPrincipals(); + for (TaasPrincipal taasPrincipal : taasPrincipals) { + principals.add(new TaasPrincipalWrapper(taasPrincipal)); + } + } else { + // Echec d'authentification + principals = null; + privateCredential = null; + throw new LoginException("Erreur lors de l'authentification " + login); + } + } catch (Exception e) { + LoginException le = new LoginException("Erreur lors de l'authentification" + login); + le.initCause(e); + log.error("Erreur lors de l'authentification", le); + throw le; + } finally { + if (transaction != null) { + try { + transaction.commitTransaction(); + transaction.closeContext(); + } catch (TopiaException e) { + LoginException le = new LoginException("Erreur lors de l'authentification" + login); + le.initCause(e); + log.error("Erreur lors de l'authentification", le); + throw le; + } + } + } + } catch(LoginException eee) { + log.error("LoginException : ", eee); + } catch(Throwable eee){ + log.error("Exception : ", eee); + throw new LoginException(eee.getMessage()); + } + return true; + } + + /* (non-Javadoc) + * @see javax.security.auth.spi.LoginModule#commit() + */ + @Override + public boolean commit() throws LoginException { + try { + subject.getPrincipals().addAll(principals); + if (log.isDebugEnabled()) { + for (TaasPrincipalWrapper principal : principals) { + log.debug("Permissions for principal " + + principal.getName() + " : " + + principal.getPermissions()); + } + } + subject.getPrivateCredentials().add(privateCredential); + if (log.isDebugEnabled()) { + log.debug("Private credential size : " + + subject.getPrivateCredentials().size() + + " for subject : " + subject); + } + } catch (Exception eee) { + log.error("Cant commit : ", eee); + throw new LoginException(eee.getMessage()); + } + return true; + } + + /* (non-Javadoc) + * @see javax.security.auth.spi.LoginModule#abort() + */ + @Override + public boolean abort() throws LoginException { + //On effectue les mêmes actions que logout + return logout(); + } + + /* (non-Javadoc) + * @see javax.security.auth.spi.LoginModule#logout() + */ + @Override + public boolean logout() throws LoginException { + //On libère les ressources + subject.getPrincipals().removeAll(principals); + subject = null; + principals.clear(); + principals = null; + callbackHandler = null; + return true; + } +} //TopiaLoginModule diff --git a/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasPermission.java b/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasPermission.java new file mode 100644 index 0000000..d369238 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasPermission.java @@ -0,0 +1,202 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * +* TopiaPermission.java +* +* Created: 16 févr. 2006 +* +* @author Arnaud Thimel <thimel@codelutin.com> +* @version $Revision$ +* +* Mise a jour: $Date$ +* par : $Author$ +*/ + +package org.nuiton.topia.taas.jaas; + +import static org.nuiton.topia.taas.TaasUtil.CREATE; +import static org.nuiton.topia.taas.TaasUtil.DELETE; +import static org.nuiton.topia.taas.TaasUtil.LOAD; +import static org.nuiton.topia.taas.TaasUtil.UPDATE; + +import java.security.Permission; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.taas.entities.TaasAuthorization; + +/** + * Classe permettant d'encapsuler les autorisations et de déléguer le travail aux + * autorisations. + * @author ruchaud + */ +public class TaasPermission extends Permission { + + private static final long serialVersionUID = 1L; + + private static Log log = LogFactory.getLog(TaasPermission.class); + + public String authorizationExpression; + + public int authorizationActions; + + /** + * Contructeur à partir des valeurs + * @param expression expression + * @param actions actions + */ + public TaasPermission(String expression, int actions) { + super(expression); + authorizationExpression = expression; + authorizationActions = actions; + } + + /** + * Constructeur à partir d'une autorisation + * @param authorization autorisation + */ + public TaasPermission(TaasAuthorization authorization) { + this(authorization.getExpression(), authorization.getActions()); + } + + /* + * (non-Javadoc) + * @see java.security.Permission#implies(java.security.Permission) + */ + @Override + public boolean implies(Permission permission) { + if (permission == null) { + return false; + } + if (!(permission instanceof TaasPermission)) { + return false; + } + TaasPermission other = (TaasPermission)permission; + boolean isImplies = impliesExpression(authorizationExpression, other.getAuthorizationExpression()) && + impliesActions(authorizationActions, other.getAuthorizationActions()); + + log.debug("Implies " + permission + " with other " + other + " implies : " + isImplies); + return isImplies; + } + + /* + * (non-Javadoc) + * @see java.security.Permission#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (obj == this) { + return true; + } + if (!(obj instanceof TaasPermission)) { + return false; + } + TaasPermission that = (TaasPermission)obj; + return implies(that) && that.implies(this); + } + + /* + * (non-Javadoc) + * @see java.security.Permission#hashCode() + */ + @Override + public int hashCode() { + return authorizationExpression.hashCode() * 100 + authorizationActions; + } + + /* + * (non-Javadoc) + * @see java.security.Permission#getActions() + */ + @Override + public String getActions() { + return String.valueOf(authorizationActions); + } + + /** + * Retourne les actions de l'authorization + * @return actions + */ + public int getAuthorizationActions() { + return authorizationActions; + } + + /** + * Retourne l'expression de l'authorization + * @return expression + */ + public String getAuthorizationExpression() { + return authorizationExpression; + } + + /** + * Comparare deux identifiants entres eux. + * thisId => thatId = ? + * @param thisExpression un identifiant + * @param thatExpression un autre identifiant + * @return vrai si thisId implique thatId + */ + public boolean impliesExpression(String thisExpression, String thatExpression) { + boolean result = thisExpression.equals(thatExpression) || + "*".equals(thisExpression) || + thatExpression.startsWith(thisExpression.substring(0, thisExpression.length() - 1)) + && thisExpression.endsWith("*"); + + if (log.isDebugEnabled()) { + log.debug("Implies expression : " + thisExpression + + " with " + thatExpression + + " return " + result); + } + + return result; + } + + /** + * Compare deux actions entre elles. + * thisActions => thatActions = ? + * @param thisActions une action + * @param thatActions une autre action + * @return vrai si thisActions implique thatActions + */ + public boolean impliesActions(int thisActions, int thatActions) { + boolean result = true; + if ((thatActions & LOAD) == LOAD) { + result &= (thisActions & LOAD) == LOAD; + } + if ((thatActions & CREATE) == CREATE) { + result &= (thisActions & CREATE) == CREATE; + } + if ((thatActions & UPDATE) == UPDATE) { + result &= (thisActions & UPDATE) == UPDATE; + } + if ((thatActions & DELETE) == DELETE) { + result &= (thisActions & DELETE) == DELETE; + } + return result; + } +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasPolicy.java b/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasPolicy.java new file mode 100644 index 0000000..ed6dc91 --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasPolicy.java @@ -0,0 +1,174 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * + * TopiaPolicy.java + * + * Created: 17 févr. 2006 + * + * @author Arnaud Thimel <thimel@codelutin.com> + * @version $Revision$ + * + * Mise a jour: $Date$ + * par : */ + +package org.nuiton.topia.taas.jaas; + +import java.security.CodeSource; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Policy; +import java.security.Principal; +import java.security.ProtectionDomain; +import java.util.Enumeration; + +import javax.security.auth.Subject; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.taas.TaasService; + +/** + * Implantation d'un policy avec une prise en compte des permissions à la volée. + * @author ruchaud + */ +public class TaasPolicy extends Policy { + + private Log log = LogFactory.getLog(TaasPolicy.class); + + protected Policy parentPolicy; + + protected TaasService taasService; + + public TaasPolicy(TaasService taasService) { + this.taasService = taasService; + } + + /** + * Renvoie la Policy parente + * @see #installPolicy() + * @return l'attribut parentPolicy + */ + public Policy getParentPolicy() { + return parentPolicy; + } + + /** + * Remplace la Policy parente + * @param parentPolicy la nouvelle Policy parente + */ + public void setParentPolicy(Policy parentPolicy) { + this.parentPolicy = parentPolicy; + } + + /* (non-Javadoc) + * @see java.security.Policy#getPermissions(java.security.CodeSource) + */ + @Override + public PermissionCollection getPermissions(CodeSource codesource) { + PermissionCollection pc = parentPolicy.getPermissions(codesource); + return pc; + } + + /* (non-Javadoc) + * @see java.security.Policy#getPermissions(java.security.ProtectionDomain) + */ + @Override + public PermissionCollection getPermissions(ProtectionDomain domain) { + if (log.isDebugEnabled()) { + log.debug("Get all permissions for domain : " + (domain==null?null:domain.getClass())); + } + try { + PermissionCollection pc = parentPolicy.getPermissions(domain); + + Subject subject = taasService.findSubject(); + if (subject != null) { + for (Principal principal : subject.getPrincipals()) { + if(principal instanceof TaasPrincipalWrapper) { + TaasPrincipalWrapper principalWrapper = (TaasPrincipalWrapper) principal; + PermissionCollection permissions = principalWrapper.getPermissions(); + + Enumeration<Permission> enumeration = permissions.elements(); + while(enumeration.hasMoreElements()){ + Permission permission = enumeration.nextElement(); + pc.add(permission); + } + } + } + } else { + log.error("Récupération des Permissions impossible"); + } + return pc; + } catch (Throwable eee) { + log.error("Cant get permissions : ", eee); + } + return null; + } + + /* (non-Javadoc) + * @see java.security.Policy#refresh() + */ + @Override + public void refresh() { + parentPolicy.refresh(); + } + + /* (non-Javadoc) + * @see java.security.Policy#implies(java.security.ProtectionDomain, + * java.security.Permission) + */ + @Override + public boolean implies(ProtectionDomain domain, Permission permission) { + PermissionCollection pc = getPermissions(domain); + if (pc == null) { + return false; + } + return pc.implies(permission); + } + + /** + * Installe cette TopiaPolicy. Si la Policy existante est déja cette + * TopiaPolicy alors la méthode n'a pas d'effet. Si une autre Policy existe + * deja alors cette TopiaPolicy, elle conserve l'ancienne Policy dans + * parentPolicy et la remplace alors. + */ + public void installPolicy() { + Policy policy = Policy.getPolicy(); + if (equals(policy)) { + return; + } + if (policy instanceof TaasPolicy) { + if (log.isDebugEnabled()) { + log.debug("Policy deja modifie en: " + policy); + } + } else { + if (log.isDebugEnabled()) { + log.debug("l'ancienne Policy etait: " + policy); + } + setParentPolicy(policy); + Policy.setPolicy(this); + } + } + +} // TopiaPolicy diff --git a/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasPrincipalWrapper.java b/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasPrincipalWrapper.java new file mode 100644 index 0000000..9400d4f --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasPrincipalWrapper.java @@ -0,0 +1,111 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * +* TopiaPrincipal.java +* +* Created: 15 févr. 2006 +* +* @author Arnaud Thimel <thimel@codelutin.com> +* @version $Revision$ +* +* Mise a jour: $Date$ +* par : $Author$ +*/ + +package org.nuiton.topia.taas.jaas; + +import java.security.PermissionCollection; +import java.security.Permissions; +import java.security.Principal; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.nuiton.topia.taas.entities.TaasAuthorization; +import org.nuiton.topia.taas.entities.TaasPrincipal; + +/** + * Implantation des principals de JAAS. + * @author ruchaud + */ +public class TaasPrincipalWrapper implements Principal { + + private Log log = LogFactory.getLog(TaasPrincipalWrapper.class); + + protected String name; + + protected PermissionCollection permissions; + + /** + * Contructeur avec comme paramètre le nom du principal. + * @param principal ? + */ + public TaasPrincipalWrapper(TaasPrincipal principal) { + name = principal.getName(); + permissions = new Permissions(); + for (TaasAuthorization authorization : principal.getAuthorizations()) { + TaasPermission permission = new TaasPermission(authorization); + permissions.add(permission); + } + } + + /* (non-Javadoc) + * @see java.security.Principal#getName() + */ + @Override + public String getName() { + return name; + } + + /** + * Récupération des permissions + * @return permissions + */ + public PermissionCollection getPermissions() { + return permissions; + } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return getClass().getName() + " : " + name; + } + + /* (non-Javadoc) + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + if (!(o instanceof TaasPrincipalWrapper)) { + return false; + } + if (name == null) { + return ((Principal)o).getName() == null; + } + return name.equals(((TaasPrincipalWrapper)o).getName()); + } + +} //TopiaPrincipal diff --git a/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasSubjectFinder.java b/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasSubjectFinder.java new file mode 100644 index 0000000..6a3e08d --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasSubjectFinder.java @@ -0,0 +1,51 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * + * TaasSubject.java + * + * Created: 10 févr. 2006 + * + * @author Arnaud Thimel <thimel@codelutin.com> + * @version $Revision$ + * + * Mise a jour: $Date$ + * par : */ + +package org.nuiton.topia.taas.jaas; + +import javax.security.auth.Subject; + +/** + * Interface permettant de récupérer le subject en fonction du context + * d'èxecution de taas (java, ejb, ...) + * + * @author julien + * + */ +public interface TaasSubjectFinder { + + Subject findSubject(); + +} diff --git a/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasSubjectFinderImpl.java b/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasSubjectFinderImpl.java new file mode 100644 index 0000000..881150a --- /dev/null +++ b/topia-service-security/src/main/java/org/nuiton/topia/taas/jaas/TaasSubjectFinderImpl.java @@ -0,0 +1,58 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +/* * + * TaasSubjectImpl.java + * + * Created: 10 févr. 2006 + * + * @author Arnaud Thimel <thimel@codelutin.com> + * @version $Revision$ + * + * Mise a jour: $Date$ + * par : */ + +package org.nuiton.topia.taas.jaas; + +import java.security.AccessControlContext; +import java.security.AccessController; + +import javax.security.auth.Subject; + +/** + * Implémentation d'un récupération du subject + * + * @author julien + * + */ +public class TaasSubjectFinderImpl implements TaasSubjectFinder { + + @Override + public Subject findSubject() { + AccessControlContext context = AccessController.getContext(); + Subject subject = Subject.getSubject(context); + return subject; + } + +} diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/security/entities/authorization/TopiaAssociationAuthorizationImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/security/entities/authorization/TopiaAssociationAuthorizationImpl.hbm.xml new file mode 100644 index 0000000..5140524 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/security/entities/authorization/TopiaAssociationAuthorizationImpl.hbm.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.codelutin.topia.security.entities.authorization"> + <union-subclass name="org.codelutin.topia.security.entities.authorization.TopiaAssociationAuthorizationImpl" extends="org.codelutin.topia.security.entities.authorization.TopiaAuthorizationImpl" table="topiaAssociationAuthorization" node="org.codelutin.topia.security.entities.authorization.TopiaAssociationAuthorizationImpl" abstract="false" proxy="org.codelutin.topia.security.entities.authorization.TopiaAssociationAuthorization" > + <!--key column="topiaId"/--> + <property name="idBeginAssociation" type="text" access="field" column="idBeginAssociation" node="idBeginAssociation"/> + <property name="nameAssociation" type="text" access="field" column="nameAssociation" node="nameAssociation"/> + <property name="actions" type="int" access="field" column="actions" node="actions"/> + <property name="principals" type="serializable" access="field" column="principals" node="principals"/> + </union-subclass> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/security/entities/authorization/TopiaAuthorizationImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/security/entities/authorization/TopiaAuthorizationImpl.hbm.xml new file mode 100644 index 0000000..32a1847 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/security/entities/authorization/TopiaAuthorizationImpl.hbm.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.codelutin.topia.security.entities.authorization"> + <class name="org.codelutin.topia.security.entities.authorization.TopiaAuthorizationImpl" table="topiaAuthorization" node="org.codelutin.topia.security.entities.authorization.TopiaAuthorizationImpl" abstract="true" proxy="org.codelutin.topia.security.entities.authorization.TopiaAuthorization" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="date" node="@topiaCreateDate"/> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/security/entities/authorization/TopiaEntityAuthorizationImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/security/entities/authorization/TopiaEntityAuthorizationImpl.hbm.xml new file mode 100644 index 0000000..c79cd89 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/security/entities/authorization/TopiaEntityAuthorizationImpl.hbm.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.codelutin.topia.security.entities.authorization"> + <union-subclass name="org.codelutin.topia.security.entities.authorization.TopiaEntityAuthorizationImpl" extends="org.codelutin.topia.security.entities.authorization.TopiaAuthorizationImpl" table="topiaEntityAuthorization" node="org.codelutin.topia.security.entities.authorization.TopiaEntityAuthorizationImpl" abstract="false" proxy="org.codelutin.topia.security.entities.authorization.TopiaEntityAuthorization" > + <!--key column="topiaId"/--> + <property name="expression" type="text" access="field" column="expression" node="expression"/> + <property name="actions" type="int" access="field" column="actions" node="actions"/> + <property name="principals" type="serializable" access="field" column="principals" node="principals"/> + </union-subclass> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/security/entities/authorization/TopiaExpressionLinkImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/security/entities/authorization/TopiaExpressionLinkImpl.hbm.xml new file mode 100644 index 0000000..335c555 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/security/entities/authorization/TopiaExpressionLinkImpl.hbm.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.codelutin.topia.security.entities.authorization"> + <class name="org.codelutin.topia.security.entities.authorization.TopiaExpressionLinkImpl" table="topiaExpressionLink" node="org.codelutin.topia.security.entities.authorization.TopiaExpressionLinkImpl" abstract="false" proxy="org.codelutin.topia.security.entities.authorization.TopiaExpressionLink" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="date" node="@topiaCreateDate"/> + <property name="replace" type="text" access="field" column="replace" node="replace"/> + <property name="by" type="text" access="field" column="by" node="by"/> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/security/entities/user/TopiaGroupImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/security/entities/user/TopiaGroupImpl.hbm.xml new file mode 100644 index 0000000..d378360 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/security/entities/user/TopiaGroupImpl.hbm.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.codelutin.topia.security.entities.user"> + <class name="org.codelutin.topia.security.entities.user.TopiaGroupImpl" table="topiaGroup" node="org.codelutin.topia.security.entities.user.TopiaGroupImpl" abstract="false" proxy="org.codelutin.topia.security.entities.user.TopiaGroup" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="date" node="@topiaCreateDate"/> + <property name="name" type="text" access="field" column="name" node="name"/> + <property name="description" type="text" access="field" column="description" node="description"/> + <bag name="topiaUser" table="topiagroup_topiauser" lazy="true" node="topiaUser" embed-xml="true"> + <key column="topiaGroup"/> + <many-to-many class="org.codelutin.topia.security.entities.user.TopiaUserImpl" column="topiaUser" node="topiaId"/> + </bag> + <many-to-one name="superGroup" class="org.codelutin.topia.security.entities.user.TopiaGroupImpl" column="superGroup" node="superGroup/@topiaId" embed-xml="false" /> + <bag name="subGroup" inverse="true" lazy="true" node="subGroup" embed-xml="false"> + <key column="superGroup"/> + <one-to-many class="org.codelutin.topia.security.entities.user.TopiaGroupImpl" node="topiaId" embed-xml="false"/> + </bag> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/security/entities/user/TopiaUserImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/security/entities/user/TopiaUserImpl.hbm.xml new file mode 100644 index 0000000..4bea86e --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/security/entities/user/TopiaUserImpl.hbm.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.codelutin.topia.security.entities.user"> + <class name="org.codelutin.topia.security.entities.user.TopiaUserImpl" table="topiaUser" node="org.codelutin.topia.security.entities.user.TopiaUserImpl" abstract="false" proxy="org.codelutin.topia.security.entities.user.TopiaUser" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="date" node="@topiaCreateDate"/> + <property name="login" type="text" access="field" column="login" node="login"/> + <property name="email" type="text" access="field" column="email" node="email"/> + <property name="password" type="text" access="field" column="password" node="password"/> + <property name="note" type="text" access="field" column="note" node="note"/> + <property name="linkApplication" type="text" access="field" column="linkApplication" node="linkApplication"/> + <bag name="topiaGroup" table="topiagroup_topiauser" inverse="true" lazy="true" node="topiaGroup" embed-xml="true"> + <key column="topiaUser"/> + <many-to-many class="org.codelutin.topia.security.entities.user.TopiaGroupImpl" column="topiaGroup" node="topiaId"/> + </bag> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/taas/entities/TaasAuthorizationImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/taas/entities/TaasAuthorizationImpl.hbm.xml new file mode 100644 index 0000000..bd71811 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/taas/entities/TaasAuthorizationImpl.hbm.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.codelutin.topia.taas.entities"> + <class name="org.codelutin.topia.taas.entities.TaasAuthorizationImpl" table="taasAuthorization" node="org.codelutin.topia.taas.entities.TaasAuthorizationImpl" abstract="false" proxy="org.codelutin.topia.taas.entities.TaasAuthorization" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="date" node="@topiaCreateDate"/> + <property name="expression" type="text" access="field" column="expression" node="expression"/> + <property name="actions" type="int" access="field" column="actions" node="actions"/> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/taas/entities/TaasPrincipalImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/taas/entities/TaasPrincipalImpl.hbm.xml new file mode 100644 index 0000000..80d1f45 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/taas/entities/TaasPrincipalImpl.hbm.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.codelutin.topia.taas.entities"> + <class name="org.codelutin.topia.taas.entities.TaasPrincipalImpl" table="taasPrincipal" node="org.codelutin.topia.taas.entities.TaasPrincipalImpl" abstract="false" proxy="org.codelutin.topia.taas.entities.TaasPrincipal" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="date" node="@topiaCreateDate"/> + <property name="name" type="text" access="field" column="name" node="name"/> + <bag name="authorizations" table="authorizations_principals" lazy="false" node="authorizations" embed-xml="true"> + <key column="principals"/> + <many-to-many class="org.codelutin.topia.taas.entities.TaasAuthorizationImpl" column="authorizations" node="topiaId"/> + </bag> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/taas/entities/TaasUserImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/taas/entities/TaasUserImpl.hbm.xml new file mode 100644 index 0000000..82e25ba --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/0/org/codelutin/topia/taas/entities/TaasUserImpl.hbm.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.codelutin.topia.taas.entities"> + <class name="org.codelutin.topia.taas.entities.TaasUserImpl" table="taasUser" node="org.codelutin.topia.taas.entities.TaasUserImpl" abstract="false" proxy="org.codelutin.topia.taas.entities.TaasUser" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="date" node="@topiaCreateDate"/> + <property name="login" type="text" access="field" column="login" node="login"/> + <property name="password" type="text" access="field" column="password" node="password"/> + <property name="email" type="text" access="field" column="email" node="email"/> + <property name="enabled" type="boolean" access="field" column="enabled" node="enabled"/> + <property name="lastConnectionDate" type="java.util.Date" access="field" column="lastConnectionDate" node="lastConnectionDate"/> + <property name="numberOfConnection" type="int" access="field" column="numberOfConnection" node="numberOfConnection"/> + <property name="link" type="text" access="field" column="link" node="link"/> + <bag name="principals" table="principals_users" lazy="true" node="principals" embed-xml="true"> + <key column="users"/> + <many-to-many class="org.codelutin.topia.taas.entities.TaasPrincipalImpl" column="principals" node="topiaId"/> + </bag> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/security/entities/authorization/TopiaAssociationAuthorizationImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/security/entities/authorization/TopiaAssociationAuthorizationImpl.hbm.xml new file mode 100644 index 0000000..83e5fad --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/security/entities/authorization/TopiaAssociationAuthorizationImpl.hbm.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.nuiton.topia.security.entities.authorization"> + <union-subclass name="org.nuiton.topia.security.entities.authorization.TopiaAssociationAuthorizationImpl" extends="org.nuiton.topia.security.entities.authorization.TopiaAuthorizationImpl" table="topiaAssociationAuthorization" node="org.nuiton.topia.security.entities.authorization.TopiaAssociationAuthorizationImpl" abstract="false" proxy="org.nuiton.topia.security.entities.authorization.TopiaAssociationAuthorization" > + <!--key column="topiaId"/--> + <property name="idBeginAssociation" type="text" access="field" column="idBeginAssociation" node="idBeginAssociation"/> + <property name="nameAssociation" type="text" access="field" column="nameAssociation" node="nameAssociation"/> + <property name="actions" type="int" access="field" column="actions" node="actions"/> + <property name="principals" type="serializable" access="field" column="principals" node="principals"/> + </union-subclass> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/security/entities/authorization/TopiaAuthorizationImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/security/entities/authorization/TopiaAuthorizationImpl.hbm.xml new file mode 100644 index 0000000..ab1af0b --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/security/entities/authorization/TopiaAuthorizationImpl.hbm.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.nuiton.topia.security.entities.authorization"> + <class name="org.nuiton.topia.security.entities.authorization.TopiaAuthorizationImpl" table="topiaAuthorization" node="org.nuiton.topia.security.entities.authorization.TopiaAuthorizationImpl" abstract="true" proxy="org.nuiton.topia.security.entities.authorization.TopiaAuthorization" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="timestamp" node="@topiaCreateDate"/> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/security/entities/authorization/TopiaEntityAuthorizationImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/security/entities/authorization/TopiaEntityAuthorizationImpl.hbm.xml new file mode 100644 index 0000000..53da5a3 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/security/entities/authorization/TopiaEntityAuthorizationImpl.hbm.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.nuiton.topia.security.entities.authorization"> + <union-subclass name="org.nuiton.topia.security.entities.authorization.TopiaEntityAuthorizationImpl" extends="org.nuiton.topia.security.entities.authorization.TopiaAuthorizationImpl" table="topiaEntityAuthorization" node="org.nuiton.topia.security.entities.authorization.TopiaEntityAuthorizationImpl" abstract="false" proxy="org.nuiton.topia.security.entities.authorization.TopiaEntityAuthorization" > + <!--key column="topiaId"/--> + <property name="expression" type="text" access="field" column="expression" node="expression"/> + <property name="actions" type="int" access="field" column="actions" node="actions"/> + <property name="principals" type="serializable" access="field" column="principals" node="principals"/> + </union-subclass> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/security/entities/authorization/TopiaExpressionLinkImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/security/entities/authorization/TopiaExpressionLinkImpl.hbm.xml new file mode 100644 index 0000000..9266577 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/security/entities/authorization/TopiaExpressionLinkImpl.hbm.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.nuiton.topia.security.entities.authorization"> + <class name="org.nuiton.topia.security.entities.authorization.TopiaExpressionLinkImpl" table="topiaExpressionLink" node="org.nuiton.topia.security.entities.authorization.TopiaExpressionLinkImpl" abstract="false" proxy="org.nuiton.topia.security.entities.authorization.TopiaExpressionLink" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="timestamp" node="@topiaCreateDate"/> + <property name="replace" type="text" access="field" column="replace" node="replace"/> + <property name="by" type="text" access="field" column="by" node="by"/> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/security/entities/user/TopiaGroupImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/security/entities/user/TopiaGroupImpl.hbm.xml new file mode 100644 index 0000000..18d3bb5 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/security/entities/user/TopiaGroupImpl.hbm.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.nuiton.topia.security.entities.user"> + <class name="org.nuiton.topia.security.entities.user.TopiaGroupImpl" table="topiaGroup" node="org.nuiton.topia.security.entities.user.TopiaGroupImpl" abstract="false" proxy="org.nuiton.topia.security.entities.user.TopiaGroup" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="timestamp" node="@topiaCreateDate"/> + <property name="name" type="text" access="field" column="name" node="name"/> + <property name="description" type="text" access="field" column="description" node="description"/> + <bag name="topiaUser" table="topiagroup_topiauser" lazy="true" node="topiaUser" embed-xml="true"> + <key column="topiaGroup"/> + <many-to-many class="org.nuiton.topia.security.entities.user.TopiaUserImpl" column="topiaUser" node="topiaId"/> + </bag> + <many-to-one name="superGroup" class="org.nuiton.topia.security.entities.user.TopiaGroupImpl" column="superGroup" node="superGroup/@topiaId" embed-xml="false"/> + <bag name="subGroup" inverse="true" lazy="true" node="subGroup" embed-xml="false"> + <key column="superGroup"/> + <one-to-many class="org.nuiton.topia.security.entities.user.TopiaGroupImpl" node="topiaId" embed-xml="false"/> + </bag> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/security/entities/user/TopiaUserImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/security/entities/user/TopiaUserImpl.hbm.xml new file mode 100644 index 0000000..6e13852 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/security/entities/user/TopiaUserImpl.hbm.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.nuiton.topia.security.entities.user"> + <class name="org.nuiton.topia.security.entities.user.TopiaUserImpl" table="topiaUser" node="org.nuiton.topia.security.entities.user.TopiaUserImpl" abstract="false" proxy="org.nuiton.topia.security.entities.user.TopiaUser" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="timestamp" node="@topiaCreateDate"/> + <property name="login" type="text" access="field" column="login" node="login"/> + <property name="email" type="text" access="field" column="email" node="email"/> + <property name="password" type="text" access="field" column="password" node="password"/> + <property name="note" type="text" access="field" column="note" node="note"/> + <property name="linkApplication" type="text" access="field" column="linkApplication" node="linkApplication"/> + <bag name="topiaGroup" table="topiagroup_topiauser" inverse="true" lazy="true" node="topiaGroup" embed-xml="true"> + <key column="topiaUser"/> + <many-to-many class="org.nuiton.topia.security.entities.user.TopiaGroupImpl" column="topiaGroup" node="topiaId"/> + </bag> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/taas/entities/TaasAuthorizationImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/taas/entities/TaasAuthorizationImpl.hbm.xml new file mode 100644 index 0000000..b8f3154 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/taas/entities/TaasAuthorizationImpl.hbm.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.nuiton.topia.taas.entities"> + <class name="org.nuiton.topia.taas.entities.TaasAuthorizationImpl" table="taasAuthorization" node="org.nuiton.topia.taas.entities.TaasAuthorizationImpl" abstract="false" proxy="org.nuiton.topia.taas.entities.TaasAuthorization" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="timestamp" node="@topiaCreateDate"/> + <property name="expression" type="text" access="field" column="expression" node="expression"/> + <property name="actions" type="int" access="field" column="actions" node="actions"/> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/taas/entities/TaasPrincipalImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/taas/entities/TaasPrincipalImpl.hbm.xml new file mode 100644 index 0000000..880bf84 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/taas/entities/TaasPrincipalImpl.hbm.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.nuiton.topia.taas.entities"> + <class name="org.nuiton.topia.taas.entities.TaasPrincipalImpl" table="taasPrincipal" node="org.nuiton.topia.taas.entities.TaasPrincipalImpl" abstract="false" proxy="org.nuiton.topia.taas.entities.TaasPrincipal" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="timestamp" node="@topiaCreateDate"/> + <property name="name" type="text" access="field" column="name" node="name"/> + <bag name="authorizations" table="authorizations_principals" lazy="false" node="authorizations" embed-xml="true"> + <key column="principals"/> + <many-to-many class="org.nuiton.topia.taas.entities.TaasAuthorizationImpl" column="authorizations" node="topiaId"/> + </bag> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/taas/entities/TaasUserImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/taas/entities/TaasUserImpl.hbm.xml new file mode 100644 index 0000000..5370cd7 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1.1/org/nuiton/topia/taas/entities/TaasUserImpl.hbm.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.nuiton.topia.taas.entities"> + <class name="org.nuiton.topia.taas.entities.TaasUserImpl" table="taasUser" node="org.nuiton.topia.taas.entities.TaasUserImpl" abstract="false" proxy="org.nuiton.topia.taas.entities.TaasUser" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="timestamp" node="@topiaCreateDate"/> + <property name="login" type="text" access="field" column="login" node="login"/> + <property name="password" type="text" access="field" column="password" node="password"/> + <property name="email" type="text" access="field" column="email" node="email"/> + <property name="enabled" type="boolean" access="field" column="enabled" node="enabled"/> + <property name="lastConnectionDate" type="java.util.Date" access="field" column="lastConnectionDate" node="lastConnectionDate"/> + <property name="numberOfConnection" type="int" access="field" column="numberOfConnection" node="numberOfConnection"/> + <property name="link" type="text" access="field" column="link" node="link"/> + <bag name="principals" table="principals_users" lazy="true" node="principals" embed-xml="true"> + <key column="users"/> + <many-to-many class="org.nuiton.topia.taas.entities.TaasPrincipalImpl" column="principals" node="topiaId"/> + </bag> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/security/entities/authorization/TopiaAssociationAuthorizationImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/security/entities/authorization/TopiaAssociationAuthorizationImpl.hbm.xml new file mode 100644 index 0000000..83e5fad --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/security/entities/authorization/TopiaAssociationAuthorizationImpl.hbm.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.nuiton.topia.security.entities.authorization"> + <union-subclass name="org.nuiton.topia.security.entities.authorization.TopiaAssociationAuthorizationImpl" extends="org.nuiton.topia.security.entities.authorization.TopiaAuthorizationImpl" table="topiaAssociationAuthorization" node="org.nuiton.topia.security.entities.authorization.TopiaAssociationAuthorizationImpl" abstract="false" proxy="org.nuiton.topia.security.entities.authorization.TopiaAssociationAuthorization" > + <!--key column="topiaId"/--> + <property name="idBeginAssociation" type="text" access="field" column="idBeginAssociation" node="idBeginAssociation"/> + <property name="nameAssociation" type="text" access="field" column="nameAssociation" node="nameAssociation"/> + <property name="actions" type="int" access="field" column="actions" node="actions"/> + <property name="principals" type="serializable" access="field" column="principals" node="principals"/> + </union-subclass> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/security/entities/authorization/TopiaAuthorizationImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/security/entities/authorization/TopiaAuthorizationImpl.hbm.xml new file mode 100644 index 0000000..3270d0c --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/security/entities/authorization/TopiaAuthorizationImpl.hbm.xml @@ -0,0 +1,33 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.nuiton.topia.security.entities.authorization"> + <class name="org.nuiton.topia.security.entities.authorization.TopiaAuthorizationImpl" table="topiaAuthorization" node="org.nuiton.topia.security.entities.authorization.TopiaAuthorizationImpl" abstract="true" proxy="org.nuiton.topia.security.entities.authorization.TopiaAuthorization" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="date" node="@topiaCreateDate"/> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/security/entities/authorization/TopiaEntityAuthorizationImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/security/entities/authorization/TopiaEntityAuthorizationImpl.hbm.xml new file mode 100644 index 0000000..53da5a3 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/security/entities/authorization/TopiaEntityAuthorizationImpl.hbm.xml @@ -0,0 +1,34 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.nuiton.topia.security.entities.authorization"> + <union-subclass name="org.nuiton.topia.security.entities.authorization.TopiaEntityAuthorizationImpl" extends="org.nuiton.topia.security.entities.authorization.TopiaAuthorizationImpl" table="topiaEntityAuthorization" node="org.nuiton.topia.security.entities.authorization.TopiaEntityAuthorizationImpl" abstract="false" proxy="org.nuiton.topia.security.entities.authorization.TopiaEntityAuthorization" > + <!--key column="topiaId"/--> + <property name="expression" type="text" access="field" column="expression" node="expression"/> + <property name="actions" type="int" access="field" column="actions" node="actions"/> + <property name="principals" type="serializable" access="field" column="principals" node="principals"/> + </union-subclass> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/security/entities/authorization/TopiaExpressionLinkImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/security/entities/authorization/TopiaExpressionLinkImpl.hbm.xml new file mode 100644 index 0000000..9d11427 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/security/entities/authorization/TopiaExpressionLinkImpl.hbm.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.nuiton.topia.security.entities.authorization"> + <class name="org.nuiton.topia.security.entities.authorization.TopiaExpressionLinkImpl" table="topiaExpressionLink" node="org.nuiton.topia.security.entities.authorization.TopiaExpressionLinkImpl" abstract="false" proxy="org.nuiton.topia.security.entities.authorization.TopiaExpressionLink" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="date" node="@topiaCreateDate"/> + <property name="replace" type="text" access="field" column="replace" node="replace"/> + <property name="by" type="text" access="field" column="by" node="by"/> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/security/entities/user/TopiaGroupImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/security/entities/user/TopiaGroupImpl.hbm.xml new file mode 100644 index 0000000..71d200d --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/security/entities/user/TopiaGroupImpl.hbm.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.nuiton.topia.security.entities.user"> + <class name="org.nuiton.topia.security.entities.user.TopiaGroupImpl" table="topiaGroup" node="org.nuiton.topia.security.entities.user.TopiaGroupImpl" abstract="false" proxy="org.nuiton.topia.security.entities.user.TopiaGroup" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="date" node="@topiaCreateDate"/> + <property name="name" type="text" access="field" column="name" node="name"/> + <property name="description" type="text" access="field" column="description" node="description"/> + <bag name="topiaUser" table="topiagroup_topiauser" lazy="true" node="topiaUser" embed-xml="true"> + <key column="topiaGroup"/> + <many-to-many class="org.nuiton.topia.security.entities.user.TopiaUserImpl" column="topiaUser" node="topiaId"/> + </bag> + <many-to-one name="superGroup" class="org.nuiton.topia.security.entities.user.TopiaGroupImpl" column="superGroup" node="superGroup/@topiaId" embed-xml="false"/> + <bag name="subGroup" inverse="true" lazy="true" node="subGroup" embed-xml="false"> + <key column="superGroup"/> + <one-to-many class="org.nuiton.topia.security.entities.user.TopiaGroupImpl" node="topiaId" embed-xml="false"/> + </bag> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/security/entities/user/TopiaUserImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/security/entities/user/TopiaUserImpl.hbm.xml new file mode 100644 index 0000000..c96ab48 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/security/entities/user/TopiaUserImpl.hbm.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.nuiton.topia.security.entities.user"> + <class name="org.nuiton.topia.security.entities.user.TopiaUserImpl" table="topiaUser" node="org.nuiton.topia.security.entities.user.TopiaUserImpl" abstract="false" proxy="org.nuiton.topia.security.entities.user.TopiaUser" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="date" node="@topiaCreateDate"/> + <property name="login" type="text" access="field" column="login" node="login"/> + <property name="email" type="text" access="field" column="email" node="email"/> + <property name="password" type="text" access="field" column="password" node="password"/> + <property name="note" type="text" access="field" column="note" node="note"/> + <property name="linkApplication" type="text" access="field" column="linkApplication" node="linkApplication"/> + <bag name="topiaGroup" table="topiagroup_topiauser" inverse="true" lazy="true" node="topiaGroup" embed-xml="true"> + <key column="topiaUser"/> + <many-to-many class="org.nuiton.topia.security.entities.user.TopiaGroupImpl" column="topiaGroup" node="topiaId"/> + </bag> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/taas/entities/TaasAuthorizationImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/taas/entities/TaasAuthorizationImpl.hbm.xml new file mode 100644 index 0000000..206c899 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/taas/entities/TaasAuthorizationImpl.hbm.xml @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.nuiton.topia.taas.entities"> + <class name="org.nuiton.topia.taas.entities.TaasAuthorizationImpl" table="taasAuthorization" node="org.nuiton.topia.taas.entities.TaasAuthorizationImpl" abstract="false" proxy="org.nuiton.topia.taas.entities.TaasAuthorization" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="date" node="@topiaCreateDate"/> + <property name="expression" type="text" access="field" column="expression" node="expression"/> + <property name="actions" type="int" access="field" column="actions" node="actions"/> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/taas/entities/TaasPrincipalImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/taas/entities/TaasPrincipalImpl.hbm.xml new file mode 100644 index 0000000..7b521c9 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/taas/entities/TaasPrincipalImpl.hbm.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.nuiton.topia.taas.entities"> + <class name="org.nuiton.topia.taas.entities.TaasPrincipalImpl" table="taasPrincipal" node="org.nuiton.topia.taas.entities.TaasPrincipalImpl" abstract="false" proxy="org.nuiton.topia.taas.entities.TaasPrincipal" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="date" node="@topiaCreateDate"/> + <property name="name" type="text" access="field" column="name" node="name"/> + <bag name="authorizations" table="authorizations_principals" lazy="false" node="authorizations" embed-xml="true"> + <key column="principals"/> + <many-to-many class="org.nuiton.topia.taas.entities.TaasAuthorizationImpl" column="authorizations" node="topiaId"/> + </bag> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/taas/entities/TaasUserImpl.hbm.xml b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/taas/entities/TaasUserImpl.hbm.xml new file mode 100644 index 0000000..a272c03 --- /dev/null +++ b/topia-service-security/src/main/resources/oldmappings/TopiaSecurity/1/org/nuiton/topia/taas/entities/TaasUserImpl.hbm.xml @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> +<hibernate-mapping default-access="field" auto-import="true" package="org.nuiton.topia.taas.entities"> + <class name="org.nuiton.topia.taas.entities.TaasUserImpl" table="taasUser" node="org.nuiton.topia.taas.entities.TaasUserImpl" abstract="false" proxy="org.nuiton.topia.taas.entities.TaasUser" > + <id name="topiaId" type="string" length="255" node="@topiaId"/> + <version name="topiaVersion" type="long" node="@topiaVersion"/> + <property name="topiaCreateDate" type="date" node="@topiaCreateDate"/> + <property name="login" type="text" access="field" column="login" node="login"/> + <property name="password" type="text" access="field" column="password" node="password"/> + <property name="email" type="text" access="field" column="email" node="email"/> + <property name="enabled" type="boolean" access="field" column="enabled" node="enabled"/> + <property name="lastConnectionDate" type="java.util.Date" access="field" column="lastConnectionDate" node="lastConnectionDate"/> + <property name="numberOfConnection" type="int" access="field" column="numberOfConnection" node="numberOfConnection"/> + <property name="link" type="text" access="field" column="link" node="link"/> + <bag name="principals" table="principals_users" lazy="true" node="principals" embed-xml="true"> + <key column="users"/> + <many-to-many class="org.nuiton.topia.taas.entities.TaasPrincipalImpl" column="principals" node="topiaId"/> + </bag> + </class> +</hibernate-mapping> diff --git a/topia-service-security/src/main/xmi/TopiaSecurity.properties b/topia-service-security/src/main/xmi/TopiaSecurity.properties new file mode 100644 index 0000000..a5e22d3 --- /dev/null +++ b/topia-service-security/src/main/xmi/TopiaSecurity.properties @@ -0,0 +1,28 @@ +### +# #%L +# ToPIA :: Service Security +# $Id$ +# $HeadURL$ +# %% +# Copyright (C) 2004 - 2014 CodeLutin +# %% +# 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 General Lesser Public License for more details. +# +# You should have received a copy of the GNU General Lesser Public +# License along with this program. If not, see +# <http://www.gnu.org/licenses/lgpl-3.0.html>. +# #L% +### +model.tagvalue.java.lang.String=text +model.tagvalue.java.util.Set=serializable + +org.nuiton.topia.taas.entities.TaasUser.attribute.principals.tagvalue.lazy=false +doNotGenerateBooleanGetMethods=true diff --git a/topia-service-security/src/main/xmi/TopiaSecurity.zargo b/topia-service-security/src/main/xmi/TopiaSecurity.zargo new file mode 100644 index 0000000..67e8cd1 Binary files /dev/null and b/topia-service-security/src/main/xmi/TopiaSecurity.zargo differ diff --git a/topia-service-security/src/site/rst/index.rst b/topia-service-security/src/site/rst/index.rst new file mode 100644 index 0000000..5f2c146 --- /dev/null +++ b/topia-service-security/src/site/rst/index.rst @@ -0,0 +1,30 @@ +.. - +.. * #%L +.. * ToPIA :: Service Security +.. * $Id$ +.. * $HeadURL$ +.. * %% +.. * Copyright (C) 2004 - 2014 CodeLutin +.. * %% +.. * 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 General Lesser Public License for more details. +.. * +.. * You should have received a copy of the GNU General Lesser Public +.. * License along with this program. If not, see +.. * <http://www.gnu.org/licenses/lgpl-3.0.html>. +.. * #L% +.. - + +====================== +ToPIA Sécurité Service +====================== + +Permet la gestion des authentifications et des autorisations. Il repose sur le +mécanisme de JAAS. diff --git a/topia-service-security/src/site/site.xml b/topia-service-security/src/site/site.xml new file mode 100644 index 0000000..49a01ef --- /dev/null +++ b/topia-service-security/src/site/site.xml @@ -0,0 +1,65 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + #%L + ToPIA :: Service Security + $Id$ + $HeadURL$ + %% + Copyright (C) 2004 - 2014 CodeLutin + %% + 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 General Lesser Public License for more details. + + You should have received a copy of the GNU General Lesser Public + License along with this program. If not, see + <http://www.gnu.org/licenses/lgpl-3.0.html>. + #L% + --> + +<project name="${project.name}"> + + <bannerLeft> + <name>${project.name}</name> + <href>index.html</href> + </bannerLeft> + + <body> + + <breadcrumbs> + <item name="${project.name}" href="index.html"/> + </breadcrumbs> + + <menu ref="parent"/> + + <menu name="Utilisateur" inherit="top"/> + + <menu name="Téléchargement"> + <item + href="${repository.home.url}/org/nuiton/topia/${project.artifactId}/${project.version}/${project.build.finalName}.jar" + name="Librairie (jar)"/> + <item + href="${repository.home.url}/org/nuiton/topia/${project.artifactId}/${project.version}/${project.build.finalName}-javadoc.jar" + name="Javadoc (jar)"/> + <item + href="${repository.home.url}/org/nuiton/topia/${project.artifactId}/${project.version}/${project.build.finalName}-sources.jar" + name="Sources (jar)"/> + </menu> + + <menu name="Developpeur"> + <item name="Historique" href="/History.html"/> + <item name="Sécurité" href="/Security.html"/> + <item name="Recherche" href="/Search.html"/> + <item name="Migration" href="/Migration.html"/> + </menu> + + <menu ref="reports"/> + + </body> +</project> diff --git a/topia-service-security/src/test/java/org/nuiton/topia/TestUtils.java b/topia-service-security/src/test/java/org/nuiton/topia/TestUtils.java new file mode 100644 index 0000000..22254f2 --- /dev/null +++ b/topia-service-security/src/test/java/org/nuiton/topia/TestUtils.java @@ -0,0 +1,92 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia; + +import org.apache.commons.io.FileUtils; +import org.nuiton.util.FileUtil; + +import java.io.File; +import java.io.IOException; + +/** + * Une classe pour avoir des choses utiles pour tous les tests. + * + * @author Chatellier Eric + * <p/> + * Last update : $Date$ + * @deprecated since 2.5, everything usefull already exists in the class {@link TestHelper} from {@code topia-persistence module}. + */ +@Deprecated +public abstract class TestUtils { + + protected static File basedir; + + protected static File targetdir; + + protected static File dirDatabase; + + public static File getBasedir() { + if (basedir == null) { + String base = System.getProperty("basedir"); + if (base == null || base.isEmpty()) { + base = new File("").getAbsolutePath(); + } + basedir = new File(base); + System.out.println("basedir for test " + basedir); + } + return basedir; + } + + public static File getTargetdir() { + if (targetdir == null) { + targetdir = new File(getBasedir(), "target"); + System.out.println("targetdir for test " + targetdir); + } + return targetdir; + } + + /** + * Create a temp dir and init isis with that temp dir as database. + * + * @throws Exception + */ + public static void init() throws Exception { + + File mavenTestDir = new File(getTargetdir() + File.separator + "surefire-workdir"); + dirDatabase = FileUtil.createTempDirectory("topia-test", "", mavenTestDir); + } + + public static File getDirDatabase() { + return dirDatabase; + } + + /** Delete created temp directory. */ + public static void clean() throws IOException { + if (dirDatabase != null) { + FileUtils.deleteDirectory(dirDatabase); + dirDatabase = null; + } + } +} diff --git a/topia-service-security/src/test/java/org/nuiton/topia/security/TopiaSecurityTest.java b/topia-service-security/src/test/java/org/nuiton/topia/security/TopiaSecurityTest.java new file mode 100644 index 0000000..6e8a1db --- /dev/null +++ b/topia-service-security/src/test/java/org/nuiton/topia/security/TopiaSecurityTest.java @@ -0,0 +1,456 @@ +/* + * #%L + * ToPIA :: Service Security + * $Id$ + * $HeadURL$ + * %% + * Copyright (C) 2004 - 2014 CodeLutin + * %% + * 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 General Lesser Public License for more details. + * + * You should have received a copy of the GNU General Lesser Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/lgpl-3.0.html>. + * #L% + */ + +package org.nuiton.topia.security; + +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.junit.After; +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TestName; +import org.nuiton.i18n.I18n; +import org.nuiton.topia.TestHelper; +import org.nuiton.topia.TopiaContext; +import org.nuiton.topia.TopiaContextFactory; +import org.nuiton.topia.TopiaException; +import org.nuiton.topia.TopiaSecurityDAOHelper; +import org.nuiton.topia.TopiaTestDAOHelper; +import org.nuiton.topia.security.entities.authorization.TopiaAssociationAuthorization; +import org.nuiton.topia.security.entities.authorization.TopiaAssociationAuthorizationDAO; +import org.nuiton.topia.security.entities.authorization.TopiaEntityAuthorization; +import org.nuiton.topia.security.entities.authorization.TopiaEntityAuthorizationDAO; +import org.nuiton.topia.security.entities.authorization.TopiaExpressionLink; +import org.nuiton.topia.security.entities.authorization.TopiaExpressionLinkDAO; +import org.nuiton.topia.security.entities.user.TopiaGroup; +import org.nuiton.topia.security.entities.user.TopiaGroupDAO; +import org.nuiton.topia.security.entities.user.TopiaUser; +import org.nuiton.topia.security.entities.user.TopiaUserDAO; +import org.nuiton.topia.security.jaas.TopiaCallbackHandler; +import org.nuiton.topia.security.util.TopiaSecurityFactoryFilter; +import org.nuiton.topia.test.entities.Person; +import org.nuiton.topia.test.entities.PersonDAO; +import org.nuiton.topia.test.entities.PersonImpl; +import org.nuiton.topia.test.entities.Pet; +import org.nuiton.topia.test.entities.PetDAO; +import org.nuiton.topia.test.entities.PetImpl; +import org.nuiton.topia.test.entities.RaceImpl; + +import javax.security.auth.Subject; +import javax.security.auth.login.LoginContext; +import java.io.File; +import java.security.PrivilegedExceptionAction; +import java.util.ArrayList; +import java.util.List; +import java.util.Locale; +import java.util.Properties; + +import static org.nuiton.topia.security.util.TopiaSecurityUtil.LOAD; +import static org.nuiton.topia.security.util.TopiaSecurityUtil.UPDATE; + +/** + * Tests unitaires. Ne pas oublier de lancer le script d'initialisation de la + * base données. + * + * @author ruchaud + */ +public class TopiaSecurityTest { + + private static final Log log = LogFactory.getLog(TopiaSecurityTest.class); + + protected String dbPath; + + protected TopiaContext context; + + protected TopiaSecurityService securityManager; + + protected TopiaSecurityFactoryFilter factoryFilter; + // FIXME comment il trouve les autres tout seul ? + // The grande question !!! + + + protected static File tesDir; + + protected static boolean init; + + protected static String entitiesList = + PersonImpl.class.getName() + "," + + PetImpl.class.getName() + "," + + RaceImpl.class.getName(); + + @BeforeClass + public static void init() throws Exception { + I18n.init(null, Locale.FRANCE); + tesDir = TestHelper.getTestBasedir(TopiaSecurityTest.class); + + } + + @AfterClass + public static void clean() { + // tchemit 2010-11-28 : no never delete data after a test... +// TestUtils.clean(); + } + + @Ignore + protected Properties getProperties() { + Properties config = new Properties(); +// Properties config = TestHelper.initTopiaContextConfiguration(tesDir,"topia-security"); + + if (log.isDebugEnabled()) { + config.setProperty("hibernate.show_sql", "true"); + } + + config.setProperty("topia.persistence.classes", entitiesList); + + config.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect"); + config.setProperty("hibernate.connection.username", "sa"); + config.setProperty("hibernate.connection.password", ""); + config.setProperty("hibernate.connection.driver_class", "org.h2.Driver"); + +// config.setProperty("hibernate.connection.url", "jdbc:h2:" + new File(tesDir, "topia-security")); + config.setProperty("hibernate.connection.url", "jdbc:h2:" + dbPath); + + // add this to use security service + config.setProperty("topia.service.security", "org.nuiton.topia.security.TopiaSecurityServiceImpl"); + return config; + } + + @Ignore + public void initDatabase() throws TopiaException { + + Properties config = getProperties(); + config.setProperty("hibernate.hbm2ddl.auto", "create"); + + /* Transaction */ + TopiaContext rootContext = TopiaContextFactory.getContext(config); + TopiaContext childContext = rootContext.beginTransaction(); + + /* DAOs */ + PersonDAO personDAO = TopiaTestDAOHelper.getPersonDAO(childContext); + PetDAO petDAO = TopiaTestDAOHelper.getPetDAO(childContext); + + TopiaUserDAO topiaUserDAO = TopiaSecurityDAOHelper.getTopiaUserDAO(childContext); + TopiaGroupDAO topiaGroupDAO = TopiaSecurityDAOHelper.getTopiaGroupDAO(childContext); + TopiaEntityAuthorizationDAO topiaEntityAuthorizationDAO = TopiaSecurityDAOHelper.getTopiaEntityAuthorizationDAO(childContext); + TopiaExpressionLinkDAO linkDAO = TopiaSecurityDAOHelper.getTopiaExpressionLinkDAO(childContext); + TopiaAssociationAuthorizationDAO topiaAssociationAuthorizationDAO = TopiaSecurityDAOHelper.getTopiaAssociationAuthorizationDAO(childContext); + + /* Création d'un admin */ + TopiaUser admin = topiaUserDAO.create(); + admin.setLogin("admin"); + admin.setPassword("azerty"); + topiaUserDAO.update(admin); + childContext.commitTransaction(); + + /* Création d'un utilisateur */ + TopiaUser thimel = topiaUserDAO.create(); + thimel.setLogin("thimel"); + thimel.setPassword("zou;bi@da"); + topiaUserDAO.update(thimel); + childContext.commitTransaction(); + + /* Création d'un groupe avec un utilisateur */ + TopiaUser ruchaud = topiaUserDAO.create(); + ruchaud.setLogin("ruchaud"); + ruchaud.setPassword("mdp"); + TopiaGroup groupRuchaud = topiaGroupDAO.create(); + groupRuchaud.setName("essai"); + + groupRuchaud.setTopiaUser(new ArrayList<TopiaUser>()); + ruchaud.addTopiaGroup(groupRuchaud); + + topiaGroupDAO.update(groupRuchaud); + topiaUserDAO.update(ruchaud); + childContext.commitTransaction(); + + /* Création des personnes */ + Person benjamin = personDAO.create(); + benjamin.setName("poussin"); + benjamin.setFirstname("benjamin"); + personDAO.update(benjamin); + childContext.commitTransaction(); + + Person jacques = personDAO.create(); + jacques.setName("poussin"); + jacques.setFirstname("jacques"); + personDAO.update(jacques); + childContext.commitTransaction(); + + Person mylene = personDAO.create(); + mylene.setName("poussin"); + mylene.setFirstname("mylene"); + personDAO.update(mylene); + childContext.commitTransaction(); + + /* Création des annimaux */ + Pet debux = petDAO.create(); + debux.setName("debux"); + debux.setType("chat"); + debux.setPerson(jacques); + petDAO.update(debux); + childContext.commitTransaction(); + + Pet pluto = petDAO.create(); + pluto.setName("pluto"); + pluto.setType("chien"); + pluto.setPerson(jacques); + petDAO.update(pluto); + childContext.commitTransaction(); + + Pet fliper = petDAO.create(); + fliper.setName("fliper"); + fliper.setType("dauphin"); + fliper.setPerson(mylene); + petDAO.update(fliper); + childContext.commitTransaction(); + + /* Création des autorisations Entity */ + TopiaEntityAuthorization authorizationForAdmin = topiaEntityAuthorizationDAO.create(); + authorizationForAdmin.setExpression("*"); + authorizationForAdmin.setActions(15); + authorizationForAdmin.setPrincipals(admin.getTopiaId()); + topiaEntityAuthorizationDAO.update(authorizationForAdmin); + childContext.commitTransaction(); + + TopiaEntityAuthorization authorizationForRuchaud = topiaEntityAuthorizationDAO.create(); + authorizationForRuchaud.setExpression(Person.class.getName() + "#*"); + authorizationForRuchaud.setActions(LOAD); + authorizationForRuchaud.setPrincipals(groupRuchaud.getTopiaId()); + topiaEntityAuthorizationDAO.update(authorizationForRuchaud); + childContext.commitTransaction(); + + TopiaEntityAuthorization authorizationForThimel = topiaEntityAuthorizationDAO.create(); + authorizationForThimel.setExpression(jacques.getTopiaId()); + authorizationForThimel.setActions(LOAD); + authorizationForThimel.setPrincipals(thimel.getTopiaId()); + topiaEntityAuthorizationDAO.update(authorizationForThimel); + childContext.commitTransaction(); + + /* Création d'une autorisation Link */ + TopiaExpressionLink link = linkDAO.create(); + link.setReplace(mylene.getTopiaId()); + link.setBy(jacques.getTopiaId()); + linkDAO.update(link); + childContext.commitTransaction(); + + /* Création d'une authorisation association */ + TopiaAssociationAuthorization associationAuthorization = topiaAssociationAuthorizationDAO.create(); + associationAuthorization.setIdBeginAssociation(jacques.getTopiaId()); + associationAuthorization.setNameAssociation("pet"); + associationAuthorization.setActions(LOAD); + associationAuthorization.setPrincipals(ruchaud.getTopiaId()); + topiaAssociationAuthorizationDAO.update(associationAuthorization); + childContext.commitTransaction(); + + associationAuthorization = topiaAssociationAuthorizationDAO.create(); + associationAuthorization.setIdBeginAssociation(mylene.getTopiaId()); + associationAuthorization.setNameAssociation("pet"); + associationAuthorization.setActions(UPDATE); + associationAuthorization.setPrincipals(ruchaud.getTopiaId()); + topiaAssociationAuthorizationDAO.update(associationAuthorization); + childContext.commitTransaction(); + + childContext.closeContext(); + } + + @Before + public void setUp() throws TopiaException { + dbPath = new File(tesDir, "topia-security").getAbsolutePath(); + if (!init) { + + initDatabase(); + init = true; + } + context = TopiaContextFactory.getContext(getProperties()); + securityManager = context.getService(TopiaSecurityService.class); + factoryFilter = new TopiaSecurityFactoryFilter(securityManager); + } + + @After + public void tearDown() throws TopiaException { + if (context != null) { + context.closeContext(); + } + } + @Test + public void testLoginThimel() throws Exception { + /* Authentification de l'utilisateur Thimel */ + LoginContext loginContext = new LoginContext("topia", new TopiaCallbackHandler( + "thimel", "zou;bi@da")); + loginContext.login(); + Subject subject = loginContext.getSubject(); + + /* Test */ + Assert.assertEquals(subject.getPrincipals().size(), 1); + + loginContext.logout(); + } + + @Test + public void testLoginRuchaud() throws Exception { + /* Authentification de l'utilisateur Ruchaud */ + LoginContext loginContext = new LoginContext("topia", new TopiaCallbackHandler( + "ruchaud", "mdp")); + loginContext.login(); + Subject subject = loginContext.getSubject(); + + /* Test */ + Assert.assertEquals(subject.getPrincipals().size(), 2); + + loginContext.logout(); + } + + @Test + public void testAuthorizationThimel() throws Exception { + /* Authentification de l'utilisateur Thimel */ + LoginContext loginContext = new LoginContext("topia", new TopiaCallbackHandler( + "thimel", "zou;bi@da")); + loginContext.login(); + Subject subject = loginContext.getSubject(); + + /* Tests */ + Subject.doAsPrivileged(subject, new PrivilegedExceptionAction<Object>() { + + @Override + public Object run() throws Exception { + TopiaContext childContext = context.beginTransaction(); + + /* Personnes */ + PersonDAO personDAO = TopiaTestDAOHelper.getPersonDAO(childContext); + List<Person> findAllPerson = personDAO.findAll(); + + List<Person> personsLOAD = factoryFilter.filter(findAllPerson, LOAD); + Assert.assertEquals(2, personsLOAD.size()); + + List<Person> personsUPDATE = factoryFilter.filter(findAllPerson, UPDATE); + Assert.assertEquals(0, personsUPDATE.size()); + + /* Annimaux */ + PetDAO petDAO = TopiaTestDAOHelper.getPetDAO(childContext); + List<Pet> findAllPet = petDAO.findAll(); + + List<Pet> petLOAD = factoryFilter.filter(findAllPet, LOAD); + Assert.assertEquals(0, petLOAD.size()); + + List<Pet> petUPDATE = factoryFilter.filter(findAllPet, UPDATE); + Assert.assertEquals(0, petUPDATE.size()); + return null; + } + }, null); + + loginContext.logout(); + } + + @Test + public void testAuthorizationRuchaud() throws Exception { + /* Authentification de l'utilisateur Admin */ + LoginContext loginContext = new LoginContext("topia", new TopiaCallbackHandler( + "ruchaud", "mdp")); + loginContext.login(); + Subject subject = loginContext.getSubject(); + + /* Tests */ + Subject.doAsPrivileged(subject, new PrivilegedExceptionAction<Object>() { + + @Override + public Object run() throws Exception { + TopiaContext childContext = context.beginTransaction(); + + /* Personnes */ + PersonDAO personDAO = TopiaTestDAOHelper.getPersonDAO(childContext); + List<Person> findAllPerson = personDAO.findAll(); + + List<Person> personsLOAD = factoryFilter.filter(findAllPerson, LOAD); + Assert.assertEquals(3, personsLOAD.size()); +// Assert.assertEquals(12, personsLOAD.size()); + + List<Person> personsUPDATE = factoryFilter.filter(findAllPerson, UPDATE); + Assert.assertEquals(0, personsUPDATE.size()); + + /* Annimaux */ + PetDAO petDAO = TopiaTestDAOHelper.getPetDAO(childContext); + List<Pet> findAllPet = petDAO.findAll(); + + List<Pet> petLOAD = factoryFilter.filter(findAllPet, LOAD); + Assert.assertEquals(2, petLOAD.size()); + + List<Pet> petUPDATE = factoryFilter.filter(findAllPet, UPDATE); + Assert.assertEquals(1, petUPDATE.size()); + return null; + } + }, null); + + loginContext.logout(); + } + + @Test + public void testAuthorizationAdmin() throws Exception { + /* Authentification de l'utilisateur Ruchaud */ + LoginContext loginContext = new LoginContext("topia", new TopiaCallbackHandler( + "admin", "azerty")); + loginContext.login(); + Subject subject = loginContext.getSubject(); + + /* Tests */ + Subject.doAsPrivileged(subject, new PrivilegedExceptionAction<Object>() { + + @Override + public Object run() throws Exception { + TopiaContext childContext = context.beginTransaction(); + + /* Personnes */ + PersonDAO personDAO = TopiaTestDAOHelper.getPersonDAO(childContext); + List<Person> findAllPerson = personDAO.findAll(); + + List<Person> personsLOAD = factoryFilter.filter(findAllPerson, LOAD); + Assert.assertEquals(3, personsLOAD.size()); +// Assert.assertEquals(15, personsLOAD.size()); + + List<Person> personsUPDATE = factoryFilter.filter(findAllPerson, UPDATE); + Assert.assertEquals(3, personsUPDATE.size()); +// Assert.assertEquals(15, personsUPDATE.size()); + + /* Annimaux */ + PetDAO petDAO = TopiaTestDAOHelper.getPetDAO(childContext); + List<Pet> findAllPet = petDAO.findAll(); + + List<Pet> petLOAD = factoryFilter.filter(findAllPet, LOAD); + Assert.assertEquals(3, petLOAD.size()); +// Assert.assertEquals(15, petLOAD.size()); + + List<Pet> petUPDATE = factoryFilter.filter(findAllPet, UPDATE); + Assert.assertEquals(3, petUPDATE.size()); +// Assert.assertEquals(15, petUPDATE.size()); + return null; + } + }, null); + + loginContext.logout(); + } +} -- To stop receiving notification emails like this one, please contact nuiton.org SCM administrator <admin+scm@nuiton.org>.
This is an automated email from the git hooks/post-receive script. New commit to branch develop-2.x in repository topia. See http://git.nuiton.org/topia.git commit b9faa888ccd8fdf0d1cf7db049a9cf2f6d22b737 Author: Maven Release <maven-release@codelutin.com> Date: Mon Nov 24 15:12:02 2014 +0000 [jgitflow-maven-plugin]updating poms for 2.9.4-SNAPSHOT development --- pom.xml | 2 +- topia-persistence/pom.xml | 2 +- topia-service-migration/pom.xml | 2 +- topia-service-replication/pom.xml | 2 +- topia-service-security/pom.xml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/pom.xml b/pom.xml index f29d100..a020de9 100644 --- a/pom.xml +++ b/pom.xml @@ -30,7 +30,7 @@ </parent> <artifactId>topia</artifactId> - <version>2.9.3-SNAPSHOT</version> + <version>2.9.4-SNAPSHOT</version> <packaging>pom</packaging> <name>ToPIA</name> diff --git a/topia-persistence/pom.xml b/topia-persistence/pom.xml index 88b19b2..c4b91a8 100644 --- a/topia-persistence/pom.xml +++ b/topia-persistence/pom.xml @@ -30,7 +30,7 @@ <parent> <groupId>org.nuiton</groupId> <artifactId>topia</artifactId> - <version>2.9.3-SNAPSHOT</version> + <version>2.9.4-SNAPSHOT</version> </parent> <groupId>org.nuiton.topia</groupId> diff --git a/topia-service-migration/pom.xml b/topia-service-migration/pom.xml index d0d1254..f77c2fe 100644 --- a/topia-service-migration/pom.xml +++ b/topia-service-migration/pom.xml @@ -30,7 +30,7 @@ <parent> <groupId>org.nuiton</groupId> <artifactId>topia</artifactId> - <version>2.9.3-SNAPSHOT</version> + <version>2.9.4-SNAPSHOT</version> </parent> <groupId>org.nuiton.topia</groupId> diff --git a/topia-service-replication/pom.xml b/topia-service-replication/pom.xml index 9c1f6ee..67a6475 100644 --- a/topia-service-replication/pom.xml +++ b/topia-service-replication/pom.xml @@ -30,7 +30,7 @@ <parent> <groupId>org.nuiton</groupId> <artifactId>topia</artifactId> - <version>2.9.3-SNAPSHOT</version> + <version>2.9.4-SNAPSHOT</version> </parent> <groupId>org.nuiton.topia</groupId> diff --git a/topia-service-security/pom.xml b/topia-service-security/pom.xml index 22cd255..8558783 100644 --- a/topia-service-security/pom.xml +++ b/topia-service-security/pom.xml @@ -30,7 +30,7 @@ <parent> <groupId>org.nuiton</groupId> <artifactId>topia</artifactId> - <version>2.9.3-SNAPSHOT</version> + <version>2.9.4-SNAPSHOT</version> </parent> <groupId>org.nuiton.topia</groupId> -- To stop receiving notification emails like this one, please contact nuiton.org SCM administrator <admin+scm@nuiton.org>.
participants (1)
-
nuiton.org scm