Compare commits

..

No commits in common. 'master' and 'kern_tomcat10_test_his' have entirely different histories.

  1. 495
      .classpath
  2. 16
      .externalToolBuilders/Ant Superx Version.launch
  3. 294
      .gitignore
  4. 28
      .project
  5. 356
      lizenz.txt
  6. 901
      src-patches/com/sun/jersey/spi/container/servlet/WebComponent.java
  7. 366
      src-patches/org/dbforms/util/MultipartRequest.java
  8. 699
      src-patches/org/springframework/security/web/util/OnCommittedResponseWrapper.java
  9. 10
      src/de/memtext/util/CryptUtils.java
  10. 94
      src/de/statspez/plausi/astat/csv/Studienfach_Land.csv
  11. 41
      src/de/superx/access/DbSqlRestrictionProvider.java
  12. 5
      src/de/superx/access/InlineSelectVisitor.java
  13. 13
      src/de/superx/bianalysis/ExcelSheetBuilder.java
  14. 5
      src/de/superx/bianalysis/ReportDefinition.java
  15. 47
      src/de/superx/bianalysis/ReportMetadata.java
  16. 54
      src/de/superx/bianalysis/bin/BiAnalysisCLI.java
  17. 3
      src/de/superx/bianalysis/jasper/JasperReportJson.java
  18. 5
      src/de/superx/bianalysis/metadata/MetadataImporter.java
  19. 10
      src/de/superx/bianalysis/metadata/models/json/MetaDimension.java
  20. 2
      src/de/superx/bianalysis/metadata/models/json/MetaDimensionAttribute.java
  21. 3
      src/de/superx/bianalysis/models/RightParam.java
  22. 4
      src/de/superx/bianalysis/repository/dto/AttributeDto.java
  23. 21
      src/de/superx/bianalysis/rest/BiAnalysisAdminApi.java
  24. 38
      src/de/superx/bianalysis/rest/BiAnalysisApi.java
  25. 19
      src/de/superx/bianalysis/service/BiAnalysisManager.java
  26. 160
      src/de/superx/bianalysis/service/DbMetaAdapter.java
  27. 11
      src/de/superx/bianalysis/service/IllegalMetaDataOperation.java
  28. 2
      src/de/superx/bianalysis/service/JasperReportExport.java
  29. 212
      src/de/superx/bianalysis/service/UserRestrictionService.java
  30. 21
      src/de/superx/bianalysis/sqlgeneration/SQLGenerator.java
  31. 6
      src/de/superx/bin/ComponentAdminCLI.java
  32. 4
      src/de/superx/bin/DataProfiler.java
  33. 4
      src/de/superx/bin/EtlFuzzer.java
  34. 12
      src/de/superx/bin/ExecuteMask.java
  35. 11
      src/de/superx/bin/FMParser.java
  36. 3
      src/de/superx/bin/fm/EtlStarter.java
  37. 4
      src/de/superx/bin/fm/SqlVarsDirective.java
  38. 14
      src/de/superx/common/FieldXmlEncoder.java
  39. 23
      src/de/superx/common/Maske.java
  40. 236
      src/de/superx/common/SxUser.java
  41. 4
      src/de/superx/db/SpringBeanConnectionProvider.java
  42. 21
      src/de/superx/db/export/ExportConfigReader.java
  43. 110
      src/de/superx/db/export/ExportConfigWriter.java
  44. 111
      src/de/superx/db/export/ExportTableConfig.java
  45. 8
      src/de/superx/db/export/ImportMode.java
  46. 11
      src/de/superx/dbt/DbtAction.java
  47. 8
      src/de/superx/dbt/DbtExecutionState.java
  48. 59
      src/de/superx/dbt/DbtMetrics.java
  49. 7
      src/de/superx/dbt/DbtPermissionException.java
  50. 443
      src/de/superx/dbt/DbtRun.java
  51. 24
      src/de/superx/dbt/DbtRunRequest.java
  52. 62
      src/de/superx/dbt/DbtRunResult.java
  53. 133
      src/de/superx/dbt/DbtUtils.java
  54. 460
      src/de/superx/dbt/DbtWrapper.java
  55. 13
      src/de/superx/etl/biad/DBSource.java
  56. 4
      src/de/superx/jdbc/entity/Cif.java
  57. 4
      src/de/superx/jdbc/entity/Cifx.java
  58. 43
      src/de/superx/jdbc/entity/CifxCustomize.java
  59. 9
      src/de/superx/jdbc/repository/CifxCustomizeRepository.java
  60. 10
      src/de/superx/job/ContainerNode.java
  61. 6
      src/de/superx/job/ExecuteSQLFluffAction.java
  62. 4
      src/de/superx/job/ExtractAction.java
  63. 4
      src/de/superx/rest/Cache.java
  64. 36
      src/de/superx/rest/EtlJobApi.java
  65. 2
      src/de/superx/rest/LogsProvider.java
  66. 2
      src/de/superx/rest/Report.java
  67. 8
      src/de/superx/rest/RestControllerBase.java
  68. 5
      src/de/superx/rest/model/Column.java
  69. 10
      src/de/superx/rest/model/ColumnType.java
  70. 1
      src/de/superx/rest/model/Result.java
  71. 9
      src/de/superx/rest/model/job/Connector.java
  72. 4
      src/de/superx/saiku/SuperxMondrianDataSourceResolver.java
  73. 7
      src/de/superx/servlet/AbstractSuperXServlet.java
  74. 446
      src/de/superx/servlet/DbFormsController.java
  75. 1652
      src/de/superx/servlet/SuperXManager.java
  76. 36
      src/de/superx/servlet/SuperXmlPwChanger.java
  77. 7
      src/de/superx/servlet/SxPool.java
  78. 53
      src/de/superx/servlet/UserInitializer.java
  79. 281
      src/de/superx/spring/AppConfig.java
  80. 130
      src/de/superx/spring/CustomDatabases.java
  81. 340
      src/de/superx/spring/DatabaseUtils.java
  82. 301
      src/de/superx/spring/HIS1Databases.java
  83. 28
      src/de/superx/spring/HisInOneConfiguration.java
  84. 13
      src/de/superx/spring/batch/job/BiJobLoggingListener.java
  85. 6
      src/de/superx/spring/batch/reader/JdbcExtractReader.java
  86. 86
      src/de/superx/spring/batch/tasklet/ExecuteDbtTasklet.java
  87. 134
      src/de/superx/spring/batch/tasklet/ExecuteSQLFluffTasklet.java
  88. 18
      src/de/superx/spring/batch/tasklet/ExecuteSqlTasklet.java
  89. 9
      src/de/superx/spring/batch/tasklet/PgCopyLoadTasklet.java
  90. 2
      src/de/superx/spring/cli/config/CLIConfig.java
  91. 257
      src/de/superx/spring/config/BatchConfig.java
  92. 4
      src/de/superx/spring/config/BiApplicationContextInit.java
  93. 48
      src/de/superx/spring/service/ApplicationContextHolder.java
  94. 54
      src/de/superx/spring/service/BatchJobDescriptionAdapter.java
  95. 108
      src/de/superx/spring/service/EntityJobDescriptionSource.java
  96. 48
      src/de/superx/spring/service/JobAdapter.java
  97. 67
      src/de/superx/spring/service/export/TableExportService.java
  98. 202
      src/de/superx/spring/service/export/TableImportService.java
  99. 4
      src/de/superx/stat/StatisticExport.java
  100. 375
      src/de/superx/util/DbFormsMultipartRequest.java
  101. Some files were not shown because too many files have changed in this diff Show More

495
.classpath

@ -1,93 +1,75 @@ @@ -1,93 +1,75 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="test-src"/>
<classpathentry kind="src" path="src-patches"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/xml-apis-1.4.01.jar"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
<attributes>
<attribute name="module" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/HikariCP-4.0.3.jar" sourcepath="superx/WEB-INF/lib/src/HikariCP-4.0.3-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/HikariCP-4.0.3.jar/../javadoc/HikariCP-4.0.3-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/JSAP-2.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/ST4-4.0.4.jar" sourcepath="superx/WEB-INF/lib/src/ST4-4.0.4-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/SparseBitSet-1.2.jar" sourcepath="superx/WEB-INF/lib/src/SparseBitSet-1.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/activation-1.1.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/angus-activation-2.0.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/angus-mail-2.0.5.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/ant-launcher-1.10.14.jar" sourcepath="superx/WEB-INF/lib/src/ant-launcher-1.10.14-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/ant-launcher-1.10.14.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/antlr-2.7.7.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/antlr-runtime-3.4.jar" sourcepath="superx/WEB-INF/lib/src/antlr-runtime-3.4-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/aopalliance-1.0.jar" sourcepath="superx/WEB-INF/lib/src/aopalliance-1.0-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/antlr-runtime-3.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/aopalliance-1.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/asm-3.3.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/asm-commons-3.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/asm-tree-3.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/aspectjrt-1.9.20.jar" sourcepath="superx/WEB-INF/lib/src/aspectjrt-1.9.20-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/aspectjweaver-1.9.20.jar" sourcepath="superx/WEB-INF/lib/src/aspectjweaver-1.9.20-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/aspectjrt-1.9.20.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/aspectjweaver-1.9.20.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/avalon-framework-4.2.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/batik-all-1.17.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/bliki-core-3.0.17.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/bson-4.2.0.jar" sourcepath="superx/WEB-INF/lib/src/bson-4.2.0-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/bson-4.2.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/cewolf-0.10.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/cglib-nodep-3.2.4.jar" sourcepath="superx/WEB-INF/lib/src/cglib-nodep-3.2.4-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/cglib-nodep-3.2.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-beanutils-1.9.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-cli-1.2.jar" sourcepath="superx/WEB-INF/lib/src/commons-cli-1.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-codec-1.15.jar" sourcepath="superx/WEB-INF/lib/src/commons-codec-1.15-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-collections4-4.1.jar" sourcepath="superx/WEB-INF/lib/src/commons-collections4-4.1-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-compress-1.27.0.jar" sourcepath="superx/WEB-INF/lib/src/commons-compress-1.27.0-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-csv-1.8.jar" sourcepath="superx/WEB-INF/lib/src/commons-csv-1.8-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-dbcp-1.4.jar" sourcepath="superx/WEB-INF/lib/src/commons-dbcp-1.4-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-digester-1.7.jar" sourcepath="superx/WEB-INF/lib/src/commons-digester-1.7-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-cli-1.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-codec-1.15.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-collections-3.2.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-collections4-4.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-compress-1.27.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-csv-1.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-dbcp-1.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-digester-1.7.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-el-1.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-exec-1.1.jar" sourcepath="superx/WEB-INF/lib/src/commons-exec-1.1-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-fileupload-migrated-1.6.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-httpclient-3.1.jar" sourcepath="superx/WEB-INF/lib/src/commons-httpclient-3.1-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-io-2.16.1.jar" sourcepath="superx/WEB-INF/lib/src/commons-io-2.16.1-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-exec-1.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-fileupload-1.5.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-httpclient-3.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-io-2.16.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-javaflow-20060411.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-lang-2.4.jar" sourcepath="superx/WEB-INF/lib/src/commons-lang-2.4-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-lang3-3.4.jar" sourcepath="superx/WEB-INF/lib/src/commons-lang3-3.4-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/commons-lang3-3.4.jar/../javadoc/commons-lang3-3.4-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-logging-1.1.1.jar" sourcepath="superx/WEB-INF/lib/src/commons-logging-1.1.1-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-math-1.2.jar" sourcepath="superx/WEB-INF/lib/src/commons-math-1.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-math3-3.6.1.jar" sourcepath="superx/WEB-INF/lib/src/commons-math3-3.6.1-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-pool-1.4.jar" sourcepath="superx/WEB-INF/lib/src/commons-pool-1.4-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-lang-2.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-lang3-3.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-logging-1.1.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-math-1.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-math3-3.6.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-pool-1.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-validator-1.0.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-vfs-1.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-vfs2-2.10.0.jar" sourcepath="superx/WEB-INF/lib/src/commons-vfs2-2.10.0-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/concurrent-1.3.4.jar" sourcepath="superx/WEB-INF/lib/src/concurrent-1.3.4-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/commons-vfs2-2.1-20150824.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/concurrent-1.3.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/connect.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/curvesapi-1.06.jar" sourcepath="superx/WEB-INF/lib/src/curvesapi-1.06-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/dbforms2.5.jar" sourcepath="superx/WEB-INF/lib/src/dbforms2.5-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/curvesapi-1.06.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/dbforms2.5.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/dom4j-2.1.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/dsn.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/duckdb_jdbc-1.2.1.jar" sourcepath="superx/WEB-INF/lib/src/duckdb_jdbc-1.2.1-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/duckdb_jdbc-1.2.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/eigenbase-properties-1.1.0.10924.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/eigenbase-resgen-1.3.0.11873.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/eigenbase-xom-1.3.0.11999.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/enunciate-core-annotations-1.28.jar" sourcepath="superx/WEB-INF/lib/src/enunciate-core-annotations-1.28-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/enunciate-core-rt-1.28.jar" sourcepath="superx/WEB-INF/lib/src/enunciate-core-rt-1.28-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/enunciate-core-annotations-1.28.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/enunciate-core-rt-1.28.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/enunciate-jaxws-ri-rt-1.28.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/enunciate-jersey-rt-1.28.jar" sourcepath="superx/WEB-INF/lib/src/enunciate-jersey-rt-1.28-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/enunciate-jersey-rt-1.28.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/enunciate-rt-1.28.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/enunciate-spring-jaxws-rt-1.28.jar" sourcepath="superx/WEB-INF/lib/src/enunciate-spring-jaxws-rt-1.28-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/enunciate-spring3-app-rt-1.28.jar" sourcepath="superx/WEB-INF/lib/src/enunciate-spring3-app-rt-1.28-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/failureaccess-1.0.1.jar" sourcepath="superx/WEB-INF/lib/src/failureaccess-1.0.1-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/fop-2.2.jar" sourcepath="superx/WEB-INF/lib/src/fop-2.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/freemarker-2.3.33.jar" sourcepath="superx/WEB-INF/lib/src/freemarker-2.3.33-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/groovy-all-2.4.21.jar" sourcepath="superx/WEB-INF/lib/src/groovy-all-2.4.21-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/groovy-all-2.4.21.jar/../javadoc/groovy-all-2.4.21-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/guava-33.2.1-jre.jar" sourcepath="superx/WEB-INF/lib/src/guava-33.2.1-jre-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/guice-3.0.jar" sourcepath="superx/WEB-INF/lib/src/guice-3.0-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/h2-2.3.232.jar" sourcepath="superx/WEB-INF/lib/src/h2-2.3.232-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/enunciate-spring3-app-rt-1.28.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/enunciate-spring-jaxws-rt-1.28.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/failureaccess-1.0.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/fontbox-2.0.0-SNAPSHOT.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/fop-2.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/freemarker-2.3.33.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/groovy-all-2.4.21.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/guava-33.2.1-jre.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/guice-3.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/h2-2.3.232.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/HikariCP-4.0.3.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/his-jackson-annotations-2.17.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/his-jackson-core-2.17.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/his-jackson-databind-2.12.0.jar"/>
@ -96,270 +78,171 @@ @@ -96,270 +78,171 @@
<classpathentry kind="lib" path="superx/WEB-INF/lib/his-jackson-jaxrs-base-2.12.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/his-jackson-jaxrs-json-provider-2.12.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/his-jackson-module-jaxb-annotations-2.12.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/htmlcleaner-2.29.jar" sourcepath="superx/WEB-INF/lib/src/htmlcleaner-2.29-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/httpclient-4.2.5.jar" sourcepath="superx/WEB-INF/lib/src/httpclient-4.2.5-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/httpcore-4.3-alpha1.jar" sourcepath="superx/WEB-INF/lib/src/httpcore-4.3-alpha1-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/httpmime-4.3.2.jar" sourcepath="superx/WEB-INF/lib/src/httpmime-4.3.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/htmlcleaner-2.29.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/httpclient-4.2.5.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/httpcore-4.3-alpha1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/httpmime-4.3.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/httpunit.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/ifxjdbc-4.50.12.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/inspector.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackrabbit-core-2.22.3.jar" sourcepath="superx/WEB-INF/lib/src/jackrabbit-core-2.22.3-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackrabbit-data-2.22.3.jar" sourcepath="superx/WEB-INF/lib/src/jackrabbit-data-2.22.3-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackrabbit-jcr-commons-2.22.3.jar" sourcepath="superx/WEB-INF/lib/src/jackrabbit-jcr-commons-2.22.3-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackrabbit-spi-2.22.3.jar" sourcepath="superx/WEB-INF/lib/src/jackrabbit-spi-2.22.3-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackrabbit-spi-commons-2.22.3.jar" sourcepath="superx/WEB-INF/lib/src/jackrabbit-spi-commons-2.22.3-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackson-annotations-2.18.8.jar" sourcepath="superx/WEB-INF/lib/src/jackson-annotations-2.18.8-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/jackson-annotations-2.18.8.jar/../javadoc/jackson-annotations-2.18.8-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackson-core-2.18.8.jar" sourcepath="superx/WEB-INF/lib/src/jackson-core-2.18.8-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/jackson-core-2.18.8.jar/../javadoc/jackson-core-2.18.8-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackson-databind-2.18.8.jar" sourcepath="superx/WEB-INF/lib/src/jackson-databind-2.18.8-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/jackson-databind-2.18.8.jar/../javadoc/jackson-databind-2.18.8-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackson-dataformat-yaml-2.17.2.jar" sourcepath="superx/WEB-INF/lib/src/jackson-dataformat-yaml-2.17.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackson-datatype-jsr310-2.18.8.jar" sourcepath="superx/WEB-INF/lib/src/jackson-datatype-jsr310-2.18.8-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/jackson-datatype-jsr310-2.18.8.jar/../javadoc/jackson-datatype-jsr310-2.18.8-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackson-jaxrs-base-2.18.8.jar" sourcepath="superx/WEB-INF/lib/src/jackson-jaxrs-base-2.18.8-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/jackson-jaxrs-base-2.18.8.jar/../javadoc/jackson-jaxrs-base-2.18.8-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackson-jaxrs-json-provider-2.18.8.jar" sourcepath="superx/WEB-INF/lib/src/jackson-jaxrs-json-provider-2.18.8-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/jackson-jaxrs-json-provider-2.18.8.jar/../javadoc/jackson-jaxrs-json-provider-2.18.8-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackson-module-jaxb-annotations-2.18.8.jar" sourcepath="superx/WEB-INF/lib/src/jackson-module-jaxb-annotations-2.18.8-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/jackson-module-jaxb-annotations-2.18.8.jar/../javadoc/jackson-module-jaxb-annotations-2.18.8-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jakarta-regexp-1.5.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackrabbit-core-2.22.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackrabbit-data-2.22.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackrabbit-jcr-commons-2.22.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackrabbit-spi-2.22.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackrabbit-spi-commons-2.22.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackson-annotations-2.17.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackson-core-2.17.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackson-databind-2.17.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackson-dataformat-yaml-2.17.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackson-datatype-jsr310-2.17.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackson-jaxrs-base-2.17.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackson-jaxrs-json-provider-2.17.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jackson-module-jaxb-annotations-2.17.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jakarta.activation-api-2.1.3.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jakarta.mail-api-2.1.3.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jakarta.persistence-api-2.2.jar" sourcepath="superx/WEB-INF/lib/src/jakarta.persistence-api-2.2-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/jakarta.persistence-api-2.2.jar/../javadoc/jakarta.persistence-api-2.2-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib_ext/jakarta.servlet-api-6.0.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jakarta.transaction-api-2.0.1.jar" sourcepath="superx/WEB-INF/lib/src/jakarta.transaction-api-2.0.1-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/janino-2.5.16.jar" sourcepath="superx/WEB-INF/lib/src/janino-2.5.16-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jasperreports-6.21.3.jar" sourcepath="superx/WEB-INF/lib/src/jasperreports-6.21.3-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/javacc-5.0.jar" sourcepath="superx/WEB-INF/lib/src/javacc-5.0-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jakarta.persistence-api-2.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jakarta-regexp-1.5.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/janino-2.5.16.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jasperreports-6.21.3.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jasperreports-fonts-6.21.3.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jasperreports-functions-6.21.3.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/javacc-5.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/javacup-10k.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/javassist-3.20.0-GA.jar" sourcepath="superx/WEB-INF/lib/src/javassist-3.20.0-GA-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/javax.batch-api-1.0.1.jar" sourcepath="superx/WEB-INF/lib/src/javax.batch-api-1.0.1-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/javax.batch-api-1.0.1.jar/../javadoc/javax.batch-api-1.0.1-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/javassist-3.20.0-GA.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/javax.batch-api-1.0.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/javax.mail.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/javax.xml.soap-api-1.4.0.jar" sourcepath="superx/WEB-INF/lib/src/javax.xml.soap-api-1.4.0-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/javax.xml.soap-api-1.4.0.jar/../javadoc/javax.xml.soap-api-1.4.0-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jaxb-api-2.2.2.jar" sourcepath="superx/WEB-INF/lib/src/jaxb-api-2.2.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jaxb-impl-2.2.3-1.jar" sourcepath="superx/WEB-INF/lib/src/jaxb-impl-2.2.3-1-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jaxen-1.2.0.jar" sourcepath="superx/WEB-INF/lib/src/jaxen-1.2.0-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jaxws-api-2.1.jar" sourcepath="superx/WEB-INF/lib/src/jaxws-api-2.1-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jaxws-rt-2.1.7.jar" sourcepath="superx/WEB-INF/lib/src/jaxws-rt-2.1.7-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/javax.xml.soap-api-1.4.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jaxb-api-2.2.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jaxb-impl-2.2.3-1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jaxen-1.2.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jaxws-api-2.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jaxws-rt-2.1.7.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jaxws-spring-1.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jcommon-1.0.23.jar" sourcepath="superx/WEB-INF/lib/src/jcommon-1.0.23-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jcr-2.0.jar" sourcepath="superx/WEB-INF/lib/src/jcr-2.0-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jdom2-2.0.6.1.jar" sourcepath="superx/WEB-INF/lib/src/jdom2-2.0.6.1-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jersey-bundle-1.19.4.jar" sourcepath="superx/WEB-INF/lib/src/jersey-bundle-1.19.4-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/jersey-bundle-1.19.4.jar/../javadoc/jersey-bundle-1.19.4-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jersey-container-servlet-3.0.0.jar" sourcepath="superx/WEB-INF/lib/src/jersey-container-servlet-3.0.0-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jettison-1.5.4.jar" sourcepath="superx/WEB-INF/lib/src/jettison-1.5.4-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jcommon-1.0.23.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jcr-2.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jdom2-2.0.6.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jersey-bundle-1.19.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jersey-common-3.0.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jersey-container-servlet-3.0.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jersey-core-1.19.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jersey-json-1.19.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jersey-media-jaxb-3.1.10.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jersey-media-multipart-3.1.10.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jersey-multipart-1.19.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jersey-server-3.0.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jersey-servlet-1.19.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jersey-spring-1.19.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jettison-1.5.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jfor-0.7.2rc1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jfreechart-1.0.19.jar" sourcepath="superx/WEB-INF/lib/src/jfreechart-1.0.19-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jfreechart-1.0.19.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jh.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/joda-time-2.4.jar" sourcepath="superx/WEB-INF/lib/src/joda-time-2.4-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jlfgr-1.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jline-2.14.6.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/joda-time-2.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/js-1.7R3.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jsch-0.1.54.jar" sourcepath="superx/WEB-INF/lib/src/jsch-0.1.54-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/json-20240303.jar" sourcepath="superx/WEB-INF/lib/src/json-20240303-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/json-20240303.jar/../javadoc/json-20240303-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jsqlparser-5.3.jar" sourcepath="superx/WEB-INF/lib/src/jsqlparser-5.3-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jsr311-api-1.1.1.jar" sourcepath="superx/WEB-INF/lib/src/jsr311-api-1.1.1-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/junit-4.13.1.jar" sourcepath="superx/WEB-INF/lib/src/junit-4.13.1-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/junit-4.13.1.jar/../javadoc/junit-4.13.1-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/kettle-core-8.3.0.0-SNAPSHOT.jar" sourcepath="superx/WEB-INF/lib/src/kettle-core-8.3.0.0-SNAPSHOT-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/kettle-plugins/kettle-dummy-plugin/kettle-dummy-plugin-core-8.3.0.0-SNAPSHOT.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/kettle-engine-8.3.0.0-SNAPSHOT.jar" sourcepath="superx/WEB-INF/lib/src/kettle-engine-8.3.0.0-SNAPSHOT-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/kettle-plugins/kettle-version-checker/kettle-version-checker-core-8.3.0.0-SNAPSHOT.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/libbase-8.3.0.0-371.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/JSAP-2.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jsch-0.1.54.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/json-20240303.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jsqlparser-5.3.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jsr311-api-1.1.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jug-lgpl-2.0.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/junit-4.13.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/kettle-core-8.3.0.0-SNAPSHOT.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/kettle-engine-8.3.0.0-SNAPSHOT.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/libbase-6.0.0.0-353.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/liberation-fonts-jasperreports_extension.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/liberation-fonts-ttf.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/libformula-8.3.0.0-371.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/log4jdbc-1.2.jar" sourcepath="superx/WEB-INF/lib/src/log4jdbc-1.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/logkit-1.0.1.jar" sourcepath="superx/WEB-INF/lib/src/logkit-1.0.1-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/ltslog-1.2.19.jar" sourcepath="superx/WEB-INF/lib/src/ltslog-1.2.19-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/lucene-core-3.6.0.jar" sourcepath="superx/WEB-INF/lib/src/lucene-core-3.6.0-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/libformula-6.0.0.0-353.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/log4jdbc-1.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/logkit-1.0.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/ltslog-1.2.19.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/lucene-core-3.6.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/maxq-0.95dev.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/metastore-6.0.0.0-353.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/micrometer-commons-1.12.5.jar" sourcepath="superx/WEB-INF/lib/src/micrometer-commons-1.12.5-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/micrometer-core-1.12.5.jar" sourcepath="superx/WEB-INF/lib/src/micrometer-core-1.12.5-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/micrometer-observation-1.12.5.jar" sourcepath="superx/WEB-INF/lib/src/micrometer-observation-1.12.5-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/mimepull-1.3.jar" sourcepath="superx/WEB-INF/lib/src/mimepull-1.3-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/micrometer-commons-1.12.5.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/micrometer-core-1.12.5.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/micrometer-observation-1.12.5.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/mimepull-1.3.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/miredot-annotations-1.3.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/mockito-all-1.10.19.jar" sourcepath="superx/WEB-INF/lib/src/mockito-all-1.10.19-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/mondrian-4.7.0.0-12.jar" sourcepath="superx/WEB-INF/lib/src/mondrian-4.7.0.0-12-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/oak-jackrabbit-api-1.42.0.jar" sourcepath="superx/WEB-INF/lib/src/oak-jackrabbit-api-1.42.0-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/mockito-all-1.10.19.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/mondrian-4.7.0.0-12.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/oak-jackrabbit-api-1.42.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/olap4j-TRUNK-SNAPSHOT.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/olap4j-xmlaserver-1.2.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/olap4j-xmla-TRUNK-SNAPSHOT.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/olap4j-xmlaserver-1.2.0.jar" sourcepath="superx/WEB-INF/lib/src/olap4j-xmlaserver-1.2.0-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/openpdf-2.0.3.jar" sourcepath="superx/WEB-INF/lib/src/openpdf-2.0.3-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/pdfbox-2.0.32.jar" sourcepath="superx/WEB-INF/lib/src/pdfbox-2.0.32-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/kettle-plugins/pdi-core-plugins/pdi-core-plugins-impl-8.3.0.0-SNAPSHOT.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/kettle-plugins/platform-utils-plugin/pdi-platform-utils-plugin-core-8.3.0.0-SNAPSHOT.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/kettle-plugins/pdi-xml-plugin/pdi-xml-plugin-core-8.3.0.0-SNAPSHOT.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/opencsv-2.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/openpdf-2.0.3.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/pdfbox-2.0.32.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/pl_generator.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/poi-4.1.2.jar" sourcepath="superx/WEB-INF/lib/src/poi-4.1.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/poi-examples-4.1.2.jar" sourcepath="superx/WEB-INF/lib/src/poi-examples-4.1.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/poi-excelant-4.1.2.jar" sourcepath="superx/WEB-INF/lib/src/poi-excelant-4.1.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/poi-ooxml-4.1.2.jar" sourcepath="superx/WEB-INF/lib/src/poi-ooxml-4.1.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/poi-4.1.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/poi-examples-4.1.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/poi-excelant-4.1.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/poi-ooxml-4.1.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/poi-ooxml-schemas-4.1.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/poi-scratchpad-4.1.2.jar" sourcepath="superx/WEB-INF/lib/src/poi-scratchpad-4.1.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/postgresql-42.7.2.jar" sourcepath="superx/WEB-INF/lib/src/postgresql-42.7.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/quartz-2.5.0.jar" sourcepath="superx/WEB-INF/lib/src/quartz-2.5.0-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/resolver-20050927.jar" sourcepath="superx/WEB-INF/lib/src/resolver-20050927-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/saiku-olap-util-3.90.jar" sourcepath="superx/WEB-INF/lib/src/saiku-olap-util-3.90-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/poi-scratchpad-4.1.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/postgresql-42.7.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/quartz-2.5.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/quartz-jobs-2.5.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/resolver-20050927.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/saiku-olap-util-3.90.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/saiku-query-0.4-SNAPSHOT-HIS.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/saiku-service-3.90.jar" sourcepath="superx/WEB-INF/lib/src/saiku-service-3.90-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/saiku-web-3.90.jar" sourcepath="superx/WEB-INF/lib/src/saiku-web-3.90-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/saxon-he-10.5.jar" sourcepath="superx/WEB-INF/lib/src/saxon-he-10.5-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/saiku-service-3.90.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/saiku-web-3.90.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/saxon-he-10.5.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/saxon-he-test-10.5.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/saxon-xqj-10.5.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/scannotation-1.0.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/sdf_generator.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/serializer-2.7.3.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/sis_util.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/slf4j-api-1.6.4.jar" sourcepath="superx/WEB-INF/lib/src/slf4j-api-1.6.4-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/slf4j-log4j12-1.6.4.jar" sourcepath="superx/WEB-INF/lib/src/slf4j-log4j12-1.6.4-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/snakeyaml-2.2.jar" sourcepath="superx/WEB-INF/lib/src/snakeyaml-2.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-aop-6.2.19.jar" sourcepath="superx/WEB-INF/lib/src/spring-aop-6.2.19-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/spring-aop-6.2.19.jar/../javadoc/spring-aop-6.2.19-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-batch-core-5.2.2.jar" sourcepath="superx/WEB-INF/lib/src/spring-batch-core-5.2.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-batch-infrastructure-5.2.2.jar" sourcepath="superx/WEB-INF/lib/src/spring-batch-infrastructure-5.2.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-batch-integration-5.2.2.jar" sourcepath="superx/WEB-INF/lib/src/spring-batch-integration-5.2.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-batch-test-5.2.2.jar" sourcepath="superx/WEB-INF/lib/src/spring-batch-test-5.2.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-beans-6.2.19.jar" sourcepath="superx/WEB-INF/lib/src/spring-beans-6.2.19-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/spring-beans-6.2.19.jar/../javadoc/spring-beans-6.2.19-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-context-6.2.19.jar" sourcepath="superx/WEB-INF/lib/src/spring-context-6.2.19-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/spring-context-6.2.19.jar/../javadoc/spring-context-6.2.19-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-context-support-6.2.19.jar" sourcepath="superx/WEB-INF/lib/src/spring-context-support-6.2.19-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/spring-context-support-6.2.19.jar/../javadoc/spring-context-support-6.2.19-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-core-6.2.19.jar" sourcepath="superx/WEB-INF/lib/src/spring-core-6.2.19-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/spring-core-6.2.19.jar/../javadoc/spring-core-6.2.19-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-data-commons-3.2.8.jar" sourcepath="superx/WEB-INF/lib/src/spring-data-commons-3.2.8-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-data-jdbc-3.2.8.jar" sourcepath="superx/WEB-INF/lib/src/spring-data-jdbc-3.2.8-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-data-relational-3.2.8.jar" sourcepath="superx/WEB-INF/lib/src/spring-data-relational-3.2.8-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-expression-6.2.19.jar" sourcepath="superx/WEB-INF/lib/src/spring-expression-6.2.19-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/spring-expression-6.2.19.jar/../javadoc/spring-expression-6.2.19-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-jdbc-6.2.19.jar" sourcepath="superx/WEB-INF/lib/src/spring-jdbc-6.2.19-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/spring-jdbc-6.2.19.jar/../javadoc/spring-jdbc-6.2.19-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-orm-6.2.19.jar" sourcepath="superx/WEB-INF/lib/src/spring-orm-6.2.19-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/spring-orm-6.2.19.jar/../javadoc/spring-orm-6.2.19-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-security-acl-6.4.13.jar" sourcepath="superx/WEB-INF/lib/src/spring-security-acl-6.4.13-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/spring-security-acl-6.4.13.jar/../javadoc/spring-security-acl-6.4.13-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-security-cas-6.4.13.jar" sourcepath="superx/WEB-INF/lib/src/spring-security-cas-6.4.13-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/spring-security-cas-6.4.13.jar/../javadoc/spring-security-cas-6.4.13-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-security-config-6.4.13.jar" sourcepath="superx/WEB-INF/lib/src/spring-security-config-6.4.13-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/spring-security-config-6.4.13.jar/../javadoc/spring-security-config-6.4.13-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-security-core-6.4.13.jar" sourcepath="superx/WEB-INF/lib/src/spring-security-core-6.4.13-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/spring-security-core-6.4.13.jar/../javadoc/spring-security-core-6.4.13-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-security-crypto-6.4.13.jar" sourcepath="superx/WEB-INF/lib/src/spring-security-crypto-6.4.13-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-security-taglibs-6.4.13.jar" sourcepath="superx/WEB-INF/lib/src/spring-security-taglibs-6.4.13-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/spring-security-taglibs-6.4.13.jar/../javadoc/spring-security-taglibs-6.4.13-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-security-web-6.4.13.jar" sourcepath="superx/WEB-INF/lib/src/spring-security-web-6.4.13-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/spring-security-web-6.4.13.jar/../javadoc/spring-security-web-6.4.13-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-test-6.2.19.jar" sourcepath="superx/WEB-INF/lib/src/spring-test-6.2.19-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/spring-test-6.2.19.jar/../javadoc/spring-test-6.2.19-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-tx-6.2.19.jar" sourcepath="superx/WEB-INF/lib/src/spring-tx-6.2.19-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/spring-tx-6.2.19.jar/../javadoc/spring-tx-6.2.19-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-web-6.2.19.jar" sourcepath="superx/WEB-INF/lib/src/spring-web-6.2.19-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/spring-web-6.2.19.jar/../javadoc/spring-web-6.2.19-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-webmvc-6.2.19.jar" sourcepath="superx/WEB-INF/lib/src/spring-webmvc-6.2.19-sources.jar">
<attributes>
<attribute name="javadoc_location" value="jar:platform:/resource/superx/WEB-INF/lib/spring-webmvc-6.2.19.jar/../javadoc/spring-webmvc-6.2.19-javadoc.jar!/"/>
</attributes>
</classpathentry>
<classpathentry kind="lib" path="superx/WEB-INF/lib_ext/tomcat-api-10.1.55.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib_ext/tomcat-catalina-10.1.55.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib_ext/tomcat-el-api-10.1.55.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib_ext/tomcat-jasper-10.1.55.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib_ext/tomcat-jsp-api-10.1.55.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/xbean-spring-2.7.jar" sourcepath="superx/WEB-INF/lib/src/xbean-spring-2.7-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/xercesImpl-2.12.2.jar" sourcepath="superx/WEB-INF/lib/src/xercesImpl-2.12.2-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/slf4j-api-1.6.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/slf4j-log4j12-1.6.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/snakeyaml-2.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/SparseBitSet-1.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-aop-6.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-batch-core-5.2.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-batch-infrastructure-5.2.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-batch-integration-5.2.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-batch-test-5.2.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-beans-6.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-context-6.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-context-support-6.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-core-6.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-data-commons-3.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-data-jdbc-3.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-data-relational-3.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-expression-6.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-jdbc-6.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-orm-6.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-security-acl-6.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-security-cas-6.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-security-config-6.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-security-core-6.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-security-crypto-6.3.10.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-security-taglibs-6.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-security-web-6.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-test-6.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-tx-6.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-web-6.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/spring-webmvc-6.2.8.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/ST4-4.0.4.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/streambuffer-0.9.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/superx6.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/tika-core-2.9.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/xalan-2.7.3.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/xbean-spring-2.7.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/xercesImpl-2.12.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/xml-apis-ext-1.4.01.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/xmlbeans-3.1.0.jar" sourcepath="superx/WEB-INF/lib/src/xmlbeans-3.1.0-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/xmlgraphics-commons-2.9.jar" sourcepath="superx/WEB-INF/lib/src/xmlgraphics-commons-2.9-sources.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/xmlbeans-3.1.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/xmlgraphics-commons-2.9.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/xml-resolver-1.2.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib_ext/jakarta.servlet-api-6.0.0.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib_ext/ant-contrib-1.0b3.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib_ext/tomcat-api-10.1.44.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib_ext/tomcat-catalina-10.1.44.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib_ext/tomcat-el-api-10.1.44.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib_ext/tomcat-jasper-10.1.44.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib_ext/tomcat-jsp-api-10.1.44.jar"/>
<classpathentry kind="lib" path="/usr/lib/jvm/java-17-openjdk-amd64/lib/jrt-fs.jar"/>
<classpathentry kind="output" path="superx/WEB-INF/classes"/>
</classpath>

16
.externalToolBuilders/Ant Superx Version.launch

@ -1,16 +0,0 @@ @@ -1,16 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.ant.AntBuilderLaunchConfigurationType">
<stringAttribute key="org.eclipse.ant.ui.ATTR_ANT_AFTER_CLEAN_TARGETS" value="get_git_info,"/>
<stringAttribute key="org.eclipse.ant.ui.ATTR_ANT_MANUAL_TARGETS" value="get_git_info,"/>
<booleanAttribute key="org.eclipse.ant.ui.ATTR_TARGETS_UPDATED" value="true"/>
<booleanAttribute key="org.eclipse.ant.ui.DEFAULT_VM_INSTALL" value="false"/>
<booleanAttribute key="org.eclipse.debug.core.ATTR_REFRESH_RECURSIVE" value="false"/>
<stringAttribute key="org.eclipse.debug.core.ATTR_REFRESH_SCOPE" value="${working_set:&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;&#10;&lt;resources/&gt;}"/>
<booleanAttribute key="org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="org.eclipse.ant.ui.AntClasspathProvider"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="true"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="superx"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_LOCATION" value="/home/superx/git/kern/superx-build/build.xml"/>
<stringAttribute key="org.eclipse.ui.externaltools.ATTR_RUN_BUILD_KINDS" value="full,incremental,"/>
<booleanAttribute key="org.eclipse.ui.externaltools.ATTR_TRIGGERS_CONFIGURED" value="true"/>
</launchConfiguration>

294
.gitignore vendored

@ -1,270 +1,46 @@ @@ -1,270 +1,46 @@
# agent and copilot context - local/temporary files
.github/
# ignore all log files
*.log
*.err
*.log.*
# Gradle build output and wrapper cache
build/
.gradle/
# ignore /dist/*
*dist/
# ignore swap files
superx/WEB-INF/reports/swap_*
# ignore eclipse settings
.settings/org.eclipse.ltk.core.refactoring.prefs
# vim .swp files
*.swp
# ignore gradle bin
scripts/gradle/bin/
scripts/gradle/.gradle/
# cobertura
superx-build/cobertura*
# classes
superx/WEB-INF/classes/
# tag files
**/tags
# .orig files
**/*.orig
# datum.alt files
**/superx.datum.alt
# script artefacts
scripts/**/build/
scripts/**/log/
# build artefacts
superx-build/declared-jars.txt
superx-build/cobertura_build_tests/
superx-build/cobertura_build_classes/
superx-build/coveragereport/
superx-build/results/
superx-build/tmp_build/
# Eclipse launch configurations are causing conflicts on 'pull' if you've got local ones; so they should not be under SCM
scripts/eclipseExternalTools/
# extracted h1-modules. These should only be links to folders in cs.bia.his_components
src-modules/module/prom/
src-modules/module/res/
src-modules/module/astat/
src-modules/module/tt/
src-modules/module/star/
src-modules/module/kern_s/
src-modules/module/res_s/
# also ignore symlinks on linux
src-modules/module/prom
src-modules/module/res
src-modules/module/astat
src-modules/module/tt
src-modules/module/star
src-modules/module/kern_s
src-modules/module/res_s
src-modules/module/biad
# artifact folders for reduced/cleaned modules
superx/WEB-INF/conf/edustore/db/module/prom/
superx/WEB-INF/conf/edustore/db/module/res/
superx/WEB-INF/conf/edustore/db/module/zul/
superx/WEB-INF/conf/edustore/db/module/sos/
superx/WEB-INF/conf/edustore/db/module/sva/
superx/WEB-INF/conf/edustore/db/module/ivs/
superx/WEB-INF/conf/edustore/db/module/astat/
superx/WEB-INF/conf/edustore/db/module/tt/
superx/WEB-INF/conf/edustore/db/module/lm/
superx/WEB-INF/conf/edustore/db/module/etl/
superx/WEB-INF/conf/edustore/db/module/star/
superx/WEB-INF/conf/edustore/db/module/cob/
superx/WEB-INF/conf/edustore/db/module/kern_s/
superx/WEB-INF/conf/edustore/db/module/res_s/
superx/WEB-INF/conf/edustore/db/module/bau/
superx/WEB-INF/conf/edustore/db/module/fin/
superx/WEB-INF/conf/edustore/db/module/biad/
superx/WEB-INF/conf/edustore/db/module/gang/
superx/WEB-INF/conf/edustore/db/module/erfolg/
superx/WEB-INF/conf/edustore/db/module/man/
superx/WEB-INF/conf/edustore/db/module/kenn/
superx/WEB-INF/conf/edustore/db/module/kennx/
# module generation artifacts for module fin
src/edustore/edustore_fin.xml
superx/WEB-INF/fin_dbforms-config_ids.xml
superx/WEB-INF/fin_dbforms-config_pg.xml
# module generation artifacts for module bau
src/edustore/edustore_bau.xml
superx/WEB-INF/bau_dbforms-config_ids.xml
superx/WEB-INF/bau_dbforms-config_pg.xml
# module generation artifacts for module prom
src/edustore/edustore_prom.xml
superx/WEB-INF/prom_dbforms-config_ids.xml
superx/WEB-INF/prom_dbforms-config_pg.xml
superx/edit/prom/
# module generation artifacts for module res
src/edustore/edustore_res.xml
superx/WEB-INF/res_dbforms-config_ids.xml
superx/WEB-INF/res_dbforms-config_pg.xml
# module generation artifacts for module gang
src/edustore/edustore_gang.xml
superx/WEB-INF/gang_dbforms-config_ids.xml
superx/WEB-INF/gang_dbforms-config_pg.xml
superx/edit/gang/*edit.jsp
superx/edit/gang/*list.jsp
# ignore all CVS + sub dirs
*CVS/
# ignore java classes
*classes/
# module generation artifacts for module kenn
src/edustore/edustore_kenn.xml
superx/WEB-INF/kenn_dbforms-config_ids.xml
superx/WEB-INF/kenn_dbforms-config_pg.xml
superx/edit/kenn/*edit.jsp
superx/edit/kenn/*list.jsp
# ignore files in directory rohdaten
superx/WEB-INF/conf/edustore/db/module/*/rohdaten/unl/*.unl
superx/WEB-INF/conf/edustore/db/install/rohdaten/unl/*.unl
# module generation artifacts for module kennx
src/edustore/edustore_kennx.xml
superx/WEB-INF/kennx_dbforms-config_ids.xml
superx/WEB-INF/kennx_dbforms-config_pg.xml
superx/edit/kennx/*edit.jsp
superx/edit/kennx/*list.jsp
# ignore temporary files
*.tmp
*.tmp.sql
temp.xml
tmp.fo
# ignore auto generated files
db.properties
dbforms-config.xml
superx/WEB-INF/dbforms-config.old
superx.datum
# module generation artifacts for module man
src/edustore/edustore_man.xml
superx/WEB-INF/man_dbforms-config_ids.xml
superx/WEB-INF/man_dbforms-config_pg.xml
superx/edit/man/catdesc.jsp
superx/edit/man/cat.jsp
superx/edit/man/grouping.jsp
superx/edit/man/man_reports.jsp
superx/edit/man/organigramm_dialog.jsp
superx/edit/man/rpt.jsp
superx/edit/man/*edit.jsp
superx/edit/man/*list.jsp
# module generation artifacts for module erfolg
src/edustore/edustore_erfolg.xml
superx/WEB-INF/erfolg_dbforms-config_ids.xml
superx/WEB-INF/erfolg_dbforms-config_pg.xml
# module generation artifacts for module sos
src/edustore/edustore_sos.xml
superx/WEB-INF/sos_dbforms-config_ids.xml
superx/WEB-INF/sos_dbforms-config_pg.xml
superx/edit/sos/hoererstatus_edit.jsp
superx/edit/sos/koepfe_oder_faelle_edit.jsp
superx/edit/sos/lehr_stg_ab_edit.jsp
superx/edit/sos/semester_edit.jsp
superx/edit/sos/semester_list.jsp
superx/edit/sos/sos_status_edit.jsp
superx/edit/sos/sos_stichtag_edit.jsp
superx/edit/sos/sos_stichtag_list.jsp
superx/edit/sos/statistik_edit.jsp
superx/edit/sos/studierendenuebertragung_edit.jsp
# module generation artifacts for module sva
src/edustore/edustore_sva.xml
superx/WEB-INF/sva_dbforms-config_ids.xml
superx/WEB-INF/sva_dbforms-config_pg.xml
superx/edit/sva/konstanten_sva_edit.jsp
superx/edit/sva/sichten_sva_dienstart_edit.jsp
superx/edit/sva/sichten_sva_klr_geldgeber_kurz_edit.jsp
superx/edit/sva/sva_stichtag_list.jsp
superx/edit/sva/sx_repository_sva_edit.jsp
superx/edit/sva/user_sva.jsp
superx/edit/sva/user_sva_manuell.jsp
# module generation artifacts for module ivs
src/edustore/edustore_ivs.xml
superx/edit/ivs/user_inventar_edit.jsp
superx/WEB-INF/ivs_dbforms-config_ids.xml
superx/WEB-INF/ivs_dbforms-config_pg.xml
# module generation artifacts for module zul
src/edustore/edustore_zul.xml
superx/WEB-INF/zul_dbforms-config_ids.xml
superx/WEB-INF/zul_dbforms-config_pg.xml
# module generation artifacts for module astat
src/edustore/edustore_astat.xml
superx/WEB-INF/astat_dbforms-config_ids.xml
superx/WEB-INF/astat_dbforms-config_pg.xml
# module generation artifacts for module tt
src/edustore/edustore_tt.xml
superx/WEB-INF/tt_dbforms-config_ids.xml
superx/WEB-INF/tt_dbforms-config_pg.xml
superx/edit/tt/
# module generation artifacts for module lm
src/edustore/edustore_lm.xml
superx/WEB-INF/lm_dbforms-config_ids.xml
superx/WEB-INF/lm_dbforms-config_pg.xml
superx/edit/lm/
# module generation artifacts for module etl
src/edustore/edustore_etl.xml
superx/WEB-INF/etl_dbforms-config_ids.xml
superx/WEB-INF/etl_dbforms-config_pg.xml
superx/edit/etl/etl_job_edit.jsp
superx/edit/etl/etl_step_edit.jsp
superx/edit/etl/etl_step_type_list.jsp
# module generation artifacts for module star
src/edustore/edustore_star.xml
superx/WEB-INF/star_dbforms-config_ids.xml
superx/WEB-INF/star_dbforms-config_pg.xml
# module generation artifacts for module cob
src/edustore/edustore_cob.xml
superx/WEB-INF/cob_dbforms-config_ids.xml
superx/WEB-INF/cob_dbforms-config_pg.xml
superx/edit/cob/sx_repository_cob_edit.jsp
# module generation artifacts for module kern_s
src/edustore/edustore_kern_s.xml
superx/WEB-INF/kern_s_dbforms-config_ids.xml
superx/WEB-INF/kern_s_dbforms-config_pg.xml
# module generation artifacts for module res_s
src/edustore/edustore_res_s.xml
superx/WEB-INF/res_s_dbforms-config_ids.xml
superx/WEB-INF/res_s_dbforms-config_pg.xml
# module generation artifacts for module biad
src/edustore/edustore_biad.xml
superx/WEB-INF/biad_dbforms-config_ids.xml
superx/WEB-INF/biad_dbforms-config_pg.xml
# dbt project generation backups
superx/dbt/projects/*_backup/
# Saiku his icons
superx/saiku/css/saiku/src/saiku_his.css
# Dateifunktionalität im kenn und kennx Modul
superx\WEB-INF\downloads
# ignore iReport Designer
*.jasper
# vscode personal settings
.vscode/
.metadata/
# ignore /dist/*
*dist/
# edustore.xml soll nicht mehr ausgeliefert werden siehe #313658
src/edustore/edustore.xml
# ignore swap files
superx/WEB-INF/reports/swap_*
# package folder und config file für statisch generierte JSPs
src/de/superx/jsp/
superx/WEB-INF/jsp_servlet_config.xml
src/edustore/edustore_*.xml
# jaspers werden nicht mehr commitet, sondern im build gebaut
*.jasper
# updateUnmodifiedJars backup
superx/WEB-INF/lib/backup/
# ignore eclipse settings
.settings/org.eclipse.ltk.core.refactoring.prefs
#rpta files:
superx/WEB-INF/reports/simple_table.jrtx
superx/WEB-INF/reports/tabelle_jrxml_a4_quer.xsl.jrxml
superx/xml/jr_templates.xsl
superx/xml/rpta_assistent.xsl

28
.project

@ -11,24 +11,20 @@ @@ -11,24 +11,20 @@
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
<triggers>full,incremental,</triggers>
<name>org.eclipse.babel.editor.rbeBuilder</name>
<arguments>
<dictionary>
<key>LaunchConfigHandle</key>
<value>&lt;project&gt;/.externalToolBuilders/Ant Superx Version.launch</value>
</dictionary>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.groovy.core.groovyNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>com.sysdeo.eclipse.tomcat.tomcatnature</nature>
<nature>com.jaspersoft.studio.babel.editor.rbeNature</nature>
</natures>
<filteredResources>
<filter>
<id>1617792163171</id>
<id>1459344926910</id>
<name></name>
<type>10</type>
<matcher>
@ -36,23 +32,5 @@ @@ -36,23 +32,5 @@
<arguments>1.0-name-matches-false-false-dist</arguments>
</matcher>
</filter>
<filter>
<id>1617792163186</id>
<name></name>
<type>6</type>
<matcher>
<id>org.eclipse.ui.ide.multiFilter</id>
<arguments>1.0-name-matches-false-true-.+\.class</arguments>
</matcher>
</filter>
<filter>
<id>1687335947806</id>
<name></name>
<type>30</type>
<matcher>
<id>org.eclipse.core.resources.regexFilterMatcher</id>
<arguments>node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>

356
lizenz.txt

@ -1,186 +1,248 @@ @@ -1,186 +1,248 @@
Mit CampusSource wird die Nutzung universitärer Entwicklungen durch
Dritte zu den Bedingungen der General GNU Public Licence (GPL)
ermöglicht, die eine der bekanntesten Open Source-Lizenzen ist.
CampusSource · AGB und Lizenz
AllgemeineGeschäftsbedingungenfürdieNutzungderCampusSource-Software
1. Vorbemerkung
Diese Allgemeinen Geschäftsbedingungen regeln die Rechtsbeziehungen
zwischen dem Lizenzgeber der jew. Software und dem Nutzer (im Folgenden
„Lizenznehmer“ genannt) der CampusSource-Software. Sie sind ebenso wie
die GNU General Public License (siehe dazu Abschnitt 4 „Lizenz“)
Bestandteil des zwischen dem Lizenzgeber und dem Lizenznehmer
geschlossenen Vertrages.
Die GNU General Public License (im Folgenden GPL genannt) finden Sie im
Internet unter www.gnu.org/copyleft/gpl.html <http://www.gnu.org/
copyleft/gpl.html>, eine deutsche Übersetzung unter www.gnu.de/gpl-
ger.html <http://www.gnu.de/gpl-ger.html>.
2. Vertragsgegenstand
Allgemeine Geschäftsbedingungen (AGB) und Lizenz
Gegenstand des Vertragsangebotes des Lizenzgebers ist die auf diesem
Server befindliche Software des CampusSource-Projektes (im Folgenden
„Software“ genannt) und dazugehöriges Begleitmaterial.
Der Lizenzgeber bietet dem Lizenznehmer nach erfolgter Registrierung die
folgenden Leistungen an:
|Der Lizenzgeber verschafft dem Lizenznehmer die Möglichkeit, auf elektronischem Weg Zugang zur Software, deren Dokumentation und zu Erfahrungsberichten zu erhalten und sich einen Überblick über das Softwareangebot zu verschaffen.
Der Lizenzgeber gestattet dem Lizenznehmer, die Software physikalisch downzuloaden.
Der Lizenzgeber überträgt die in Abschnitt 4 "Lizenz" näher bezeichneten Nutzungsrechte auf den Lizenznehmer.
|
Lizenzgeber und Lizenznehmer sind sich einig darüber, dass die
Inanspruchnahme der unter 1.) bis 3.) angebotenen Leistungen
unentgeltlich, schenkungsweise erfolgen soll. Dies bedeutet jedoch
nicht, dass der Lizenzgeber irgendwelche durch die Nutzung des Angebots
entstandenen Kosten übernimmt.
Sofern der Lizenznehmer die Software bearbeitet und diese Bearbeitung
Dritten zugänglich macht, ist er verpflichtet, dem Lizenzgeber auch eine
Kopie der Bearbeitung kostenlos zukommen zu lassen, oder, sofern die
Bearbeitung öffentlich und kostenlos zugänglich ist, dem Lizenzgeber die
Quelle mitzuteilen.
Die in diesen Allgemeinen Geschäftsbedingungen festgelegten
Nebenpflichten stellen keine Gegenleistung im Sinne des Bürgerlichen
Rechts dar und sind für den Lizenznehmer verbindlich. Nicht Gegenstand
des Vertrages sind irgendeine Form von Softwareinstallation,
Softwarepflege oder Beratung im Zusammenhang mit der Software.
Insbesondere wird durch die mit der Software beigefügte oder für die
Software bereit gestellte Information oder Dokumentation kein
Beratungsvertrag angeboten. Wenn Sie solche Dienstleistungen wünschen,
wenden Sie sich an die CampusSource Geschäftsstelle.
Der Lizenzgeber behält sich vor, das Leistungsangebot jederzeit
einzustellen. Bezüglich bereits empfangener Leistungen bleiben die
Verpflichtungen beider Parteien hiervon unberührt, insbesondere
entfallen dadurch nicht die in diesen Allgemeinen Geschäftsbedingungen
festgelegten Nebenpflichten des Lizenznehmers.
Diese Allgemeinen Geschäftsbedingungen gelten auch dann, wenn der
Lizenznehmer das oben genannte Leistungspaket nur teilweise in Anspruch
nimmt.
Mit CampusSource wird die Nutzung universitärer Entwicklungen durch Dritte zu
den Bedingungen der General GNU Public Licence (GPL) ermöglicht, die eine der
bekanntesten Opensource-Lizenzen ist.
Die GPL ist eine Lizenz, die dem amerikanischen Recht, nicht jedoch dem
deutschen Recht genügt. So sind einige Passagen der GPL nach dem deutschen Recht
nicht wirksam. CampusSource hat aus diesem Grunde Allgemeine
Geschäftsbedingungen formuliert, die die Interpretation der GPL unter deutschem
Recht vornimmt und ergänzt.
3. Sorgfaltspflichten des Lizenznehmers
Der Lizenznehmer ist verpflichtet, sein Passwort sorgfältig
aufzubewahren und Dritten nicht zugänglich zu machen. Der Lizenznehmer
haftet für alle Schäden, die aus der Verletzung dieser Sorgfaltspflicht
entstehen.
4. Lizenz
Allgemeine Geschäftsbedingungen für die
Nutzung der Software der Initiative CampusSource
Die Nutzungsrechte, welche der Lizenznehmer erhält, ergeben sich aus der
GNU General Public License. Diese Nutzungsrechte sind dinglich – im
Sinne des Urheberrechts – mit der Software verknüpft und gelten auch
dann, wenn der Lizenznehmer keine Kenntnis davon nimmt. Die GNU General
Public License (im Folgenden GPL genannt) finden Sie im Internet unter
www.gnu.org/copyleft/gpl.html <http://www.gnu.org/copyleft/gpl.html>,
eine deutsche Übersetzung unter www.gnu.de/gpl-ger.html <http://
www.gnu.de/gpl-ger.html>.
Die GPL ist zu dem Zweck entworfen worden, dass Sie die unter diese
Lizenz gestellte Software weitergeben und verändern dürfen. Wenn Sie die
Software verändern und weitergeben, müssen Sie den Quellcode der
bearbeiteten Software wieder unter die GPL stellen und den Quellcode
zugänglich machen, so dass auch andere von Ihrem Werk profitieren, wie
auch Sie von der erhaltenen Software profitiert haben. Auf diese Art und
Weise soll ein System von jedermann frei zugänglicher Software
geschaffen werden.
1. Vorbemerkung
Diese Allgemeinen Geschäftsbedingungen regeln die Rechtsbeziehungen zwischen dem
Land Nordrhein-Westfalen, vertreten durch die FernUniversität Hagen, diese
wiederum vertreten durch die Geschäftsstelle der Initiative CampusSource bei der
FernUniversität Hagen, Universitätsstraße 11, D-58097 Hagen (im Folgenden
»Lizenzgeber« genannt) und dem Nutzer (im Folgenden »Lizenznehmer« genannt) der
CampusSource-Software. Sie sind ebenso wie die GNU General Public License (siehe
dazu Abschnitt 4 »Lizenz«) Bestandteil des zwischen dem Lizenzgeber und dem
Lizenznehmer geschlossenen Vertrages.
Die GNU General Public License (im Folgenden GPL genannt) finden Sie im Internet
unter www.gnu.org/copyleft/gpl.html, eine deutsche Übersetzung unter
www.gnu.de/gpl-ger.html.
Der Lizenzgeber weist den Lizenznehmer darauf hin, dass die GPL in den
USA entworfen wurde und daher einige Bestimmungen nach deutschem Recht
nicht wirksam sind oder in Deutschland rechtlich anders beurteilt werden
als in den USA:
Die Formulierung "You may charge a fee for the physical act of transferring a copy" in Abschnitt 1 der GPL ist nach deutschem Recht so zu verstehen, dass nur eine angemessene, marktübliche Gegenleistung für die Anfertigung einer Kopie verlangt werden darf. Sofern eine das marktübliche überschreitende Gegenleistung für das Anfertigen von Kopien verlangt werden würde, hätte dies neben einer möglichen Lizenzverletzung zur Folge, dass die durch die kostenlose Weitergabe bestehende Haftungsprivilegierung wegfallen könnte und der Lizenznehmer wie ein Verkäufer oder Unternehmer (Werkvertrag) bei Mängeln auf Schadensersatz haftet.
Abschnitt 11 und 12 der GPL (Haftungsausschluss) verstoßen gegen das "Gesetz zur Regelung des Rechts der Allgemeinen Geschäftsbedingungen" (AGBG) und sind nach deutschem Recht unwirksam. An ihre Stelle treten die entsprechenden Bestimmungen des Bürgerlichen Rechts §§ 521ff. (Haftung des Schenkers).
2. Vertragsgegenstand
Gegenstand des Vertragsangebotes des Lizenzgebers ist die auf diesem Server
befindliche Software des CampusSource-Projektes (im Folgenden »Software«
genannt) und dazugehöriges Begleitmaterial.
Der Lizenzgeber bietet dem Lizenznehmer nach erfolgter Registrierung die
folgenden Leistungen an:
Der Lizenzgeber verschafft dem Lizenznehmer die Möglichkeit, auf
elektronischem Weg Zugang zur Software, deren Dokumentation und zu
Erfahrungsberichten zu erhalten und sich einen Überblick über das
Softwareangebot zu verschaffen.
Der Lizenzgeber gestattet dem Lizenznehmer, die Software physikalisch
downzuloaden.
Der Lizenzgeber überträgt die in Abschnitt 4 »Lizenz« näher bezeichneten
Nutzungsrechte auf den Lizenznehmer.
Lizenzgeber und Lizenznehmer sind sich einig darüber, dass die Inanspruchnahme
der unter 1.) bis 3.) angebotenen Leistungen unentgeltlich, schenkungsweise
erfolgen soll. Dies bedeutet jedoch nicht, dass der Lizenzgeber irgendwelche
durch die Nutzung des Angebots entstandenen Kosten übernimmt.
Sofern der Lizenznehmer die Software bearbeitet und diese Bearbeitung Dritten
zugänglich macht, ist er verpflichtet, dem Lizenzgeber auch eine Kopie der
Bearbeitung kostenlos zukommen zu lassen, oder, sofern die Bearbeitung
öffentlich und kostenlos zugänglich ist, dem Lizenzgeber die Quelle mitzuteilen.
Die in diesen Allgemeinen Geschäftsbedingungen festgelegten Nebenpflichten
stellen keine Gegenleistung im Sinne des Bürgerlichen Rechts dar und sind für
den Lizenznehmer verbindlich. Nicht Gegenstand des Vertrages sind irgendeine
Form von Softwareinstallation, Softwarepflege oder Beratung im Zusammenhang mit
der Software. Insbesondere wird durch die mit der Software beigefügte oder für
die Software bereitgestellte Information oder Dokumentation kein
Beratungsvertrag angeboten. Wenn Sie solche Dienstleistungen wünschen, wenden
Sie sich an die Geschäftsstelle der Initiative CampusSource.
Der Lizenzgeber behält sich vor, das Leistungsangebot jederzeit einzustellen.
Bezüglich bereits empfangener Leistungen bleiben die Verpflichtungen beider
Parteien hiervon unberührt, insbesondere entfallen dadurch nicht die in diesen
Allgemeinen Geschäftsbedingungen festgelegten Nebenpflichten des Lizenznehmers.
Diese Allgemeinen Geschäftsbedingungen gelten auch dann, wenn der Lizenznehmer
das oben genannte Leistungspaket nur teilweise in Anspruch nimmt.
3. Sorgfaltspflichten des Lizenznehmers
Der Lizenznehmer ist verpflichtet, sein Passwort sorgfältig aufzubewahren und
Dritten nicht zugänglich zu machen. Der Lizenznehmer haftet für alle Schäden,
die aus der Verletzung dieser Sorgfaltspflicht entstehen.
4. Lizenz
Die Nutzungsrechte, welche der Lizenznehmer erhält, ergeben sich aus der GNU
General Public License. Diese Nutzungsrechte sind dinglich - im Sinne des
Urheberrechts - mit der Software verknüpft und gelten auch dann, wenn der
Lizenznehmer keine Kenntnis davon nimmt. Die GNU General Public License (im
Folgenden GPL genannt) finden Sie im Internet unter
www.gnu.org/copyleft/gpl.html, eine deutsche Übersetzung unter
www.gnu.de/gpl-ger.html.
Die GPL ist zu dem Zweck entworfen worden, dass Sie die unter diese Lizenz
gestellte Software weitergeben und verändern dürfen. Wenn Sie die Software
verändern und weitergeben, müssen Sie den Quellcode der bearbeiteten Software
wieder unter die GPL stellen und den Quellcode zugänglich machen, so dass auch
andere von Ihrem Werk profitieren, wie auch Sie von der erhaltenen Software
profitiert haben. Auf diese Art und Weise soll ein System von jedermann frei
zugänglicher Software geschaffen werden.
Der Lizenzgeber weist den Lizenznehmer darauf hin, dass die GPL in den USA
entworfen wurde und daher einige Bestimmungen nach deutschem Recht nicht wirksam
sind oder in Deutschland rechtlich anders beurteilt werden als in den USA:
Die Formulierung »You may charge a fee for the physical act of transferring a
copy« in Abschnitt 1 der GPL ist nach deutschem Recht so zu verstehen, dass
nur eine angemessene, marktübliche Gegenleistung für die Anfertigung einer
Kopie verlangt werden darf. Sofern eine das marktübliche überschreitende
Gegenleistung für das Anfertigen von Kopien verlangt werden würde, hätte dies
neben einer möglichen Lizenzverletzung zur Folge, dass die durch die
kostenlose Weitergabe bestehende Haftungsprivilegierung wegfallen könnte und
der Lizenznehmer wie ein Verkäufer oder Unternehmer (Werkvertrag) bei Mängeln
auf Schadensersatz haftet.
Abschnitt 11 und 12 der GPL (Haftungsausschluss) verstoßen gegen das »Gesetz
zur Regelung des Rechts der Allgemeinen Geschäftsbedingungen« (AGBG) und sind
nach deutschem Recht unwirksam. An ihre Stelle treten die entsprechenden
Bestimmungen des Bürgerlichen Rechts §§ 521ff. (Haftung des Schenkers).
Es folgt eine kurze unvollständige Zusammenfassung der GPL. Der Lizenznehmer ist
verpflichtet, die weiterführenden und präziseren Bestimmungen der GPL zu
beachten. Der Lizenznehmer wird darauf hingewiesen, dass die GPL einige
(auflösende) Bedingungen enthält, bei deren Verletzung die dem Lizenznehmer
übertragenen Nutzungsrechte automatisch ohne jeden Widerruf erlöschen und eine
weitere Nutzung des Programms zu einer (strafbaren) Urheberrechtsverletzung
wird.
Die Lizenz erlaubt dem Lizenznehmer das Ausführen der Programme zu jedem
Zweck. Gesetzliche Einschränkungen werden hiervon nicht berührt.
Der Lizenznehmer darf unveränderte Kopien des Quellcodes anfertigen und
weiterverbreiten, unter der Bedingung, dass mit der Kopie ein entsprechender
Urheberrechtsvermerk sowie ein Haftungsausschluß veröffentlicht wird und dass
alle die GPL betreffenden Hinweise unverändert weitergegeben werden. Ein
Entgelt darf nur für die Anfertigung von Kopien oder für das Anbieten einer
Garantie genommen werden. Näheres enthält § 1 GPL.
Der Lizenznehmer darf das Programm verändern und die so entstandene
Bearbeitung unter der Bedingung vervielfältigen und verbreiten, dass er einen
auffälligen Vermerk über die vorgenommenen Modifizierungen anbringt, die
Kopien der Bearbeitung ohne Lizenzgebühren unter den Bedingungen der GPL
verbreitet und dafür sorgt, dass das Programm bei interaktiver Nutzung einen
Urheberrechtsvermerk ausgibt. Näheres regelt § 2 GPL.
Der Lizenznehmer darf das Programm oder eine Bearbeitung als Objectcode oder
in ausführbarer Form unter Berücksichtigung der letzten beiden Abschnitte
unter der Bedingung vervielfältigen und verbreiten, dass er den Quelltext
beifügt oder eine der in § 3 GPL genannten Alternativen erfüllt. Näheres
regelt § 3 GPL.
Sollte dem Lizenznehmer infolge eines Gerichtsurteils oder durch einen
gerichtlichen Vergleich Bedingungen auferlegt werden, die der GPL
widersprechen, so entbindet dies den Lizenznehmer nicht von der Einhaltung der
GPL. Näheres regelt § 7 GPL.
Wenn die Verbreitung oder die Benutzung des Programms in bestimmten Staaten
durch Patent- oder Urheberrecht eingeschränkt ist, kann der Lizenznehmer bei
der Verbreitung des Programms durch einen entsprechenden Vermerk bestimmen,
dass die Verbreitung des Programms in bestimmten Staaten ausgeschlossen ist.
Näheres regelt § 8 GPL.
Es folgt eine kurze unvollständige Zusammenfassung der GPL. Der
Lizenznehmer ist verpflichtet, die weiterführenden und präziseren
Bestimmungen der GPL zu beachten. Der Lizenznehmer wird darauf
hingewiesen, dass die GPL einige (auflösende) Bedingungen enthält, bei
deren Verletzung die dem Lizenznehmer übertragenen Nutzungsrechte
automatisch ohne jeden Widerruf erlöschen und eine weitere Nutzung des
Programms zu einer (strafbaren) Urheberrechtsverletzung wird.
Die Lizenz erlaubt dem Lizenznehmer das Ausführen der Programme zu jedem Zweck. Gesetzliche Einschränkungen werden hiervon nicht berührt.
Der Lizenznehmer darf unveränderte Kopien des Quellcodes anfertigen und weiter verbreiten, unter der Bedingung, dass mit der Kopie ein entsprechender Urheberrechtsvermerk sowie ein Haftungsausschluss veröffentlicht wird und dass alle die GPL betreffenden Hinweise unverändert weitergegeben werden. Ein Entgelt darf nur für die Anfertigung von Kopien oder für das Anbieten einer Garantie genommen werden. Näheres enthält § 1 GPL.
Der Lizenznehmer darf das Programm verändern und die so entstandene Bearbeitung unter der Bedingung vervielfältigen und verbreiten, dass er einen auffälligen Vermerk über die vorgenommenen Modifizierungen anbringt, die Kopien der Bearbeitung ohne Lizenzgebühren unter den Bedingungen der GPL verbreitet und dafür sorgt, dass das Programm bei interaktiver Nutzung einen Urheberrechtsvermerk ausgibt. Näheres regelt § 2 GPL.
Der Lizenznehmer darf das Programm oder eine Bearbeitung als Objectcode oder in ausführbarer Form unter Berücksichtigung der letzten beiden Abschnitte unter der Bedingung vervielfältigen und verbreiten, dass er den Quelltext beifügt oder eine der in § 3 GPL genannten Alternativen erfüllt. Näheres regelt § 3 GPL.
Sollte dem Lizenznehmer infolge eines Gerichtsurteils oder durch einen gerichtlichen Vergleich Bedingungen auferlegt werden, die der GPL widersprechen, so entbindet dies den Lizenznehmer nicht von der Einhaltung der GPL. Näheres regelt § 7 GPL.
Wenn die Verbreitung oder die Benutzung des Programms in bestimmten Staaten durch Patent- oder Urheberrecht eingeschränkt ist, kann der Lizenznehmer bei der Verbreitung des Programms durch einen entsprechenden Vermerk bestimmen, dass die Verbreitung des Programms in bestimmten Staaten ausgeschlossen ist. Näheres regelt § 8 GPL.
5. Schutzrechte Dritter
Der Lizenzgeber geht davon aus, dass der Besitz und der vertragsgemäße Gebrauch
der Software keine Schutzrechte Dritter für den Bereich der BRD beeinträchtigt.
Im Zusammenhang mit einer möglichen Beeinträchtigung der Schutzrechte Dritter
werden die folgenden Nebenpflichten vereinbart:
Der Lizenznehmer verpflichtet sich, dass er weder für sich noch im Auftrag
eines Dritten die Software zu dem Zweck verwendet, diese nach
Schutzrechtsverletzungen zu untersuchen oder untersuchen zu lassen.
5. Schutzrechte Dritter
Der Lizenzgeber geht davon aus, dass der Besitz und der vertragsgemäße
Gebrauch der Software keine Schutzrechte Dritter für den Bereich der BRD
beeinträchtigt. Im Zusammenhang mit einer möglichen Beeinträchtigung der
Schutzrechte Dritter werden die folgenden Nebenpflichten vereinbart:
Der Lizenznehmer verpflichtet sich, den Lizenzgeber unverzüglich zu
benachrichtigen, wenn Dritte Schutzrechtsverletzungen geltend machen.
|Der Lizenznehmer verpflichtet sich, dass er weder für sich noch im Auftrag eines Dritten die Software zu dem Zweck verwendet, diese nach Schutzrechtsverletzungen zu untersuchen oder untersuchen zu lassen.
Der Lizenznehmer verpflichtet sich, den Lizenzgeber unverzüglich zu benachrichtigen, wenn Dritte Schutzrechtsverletzungen geltend machen.
Hat der Lizenznehmer den Eindruck, dass die Software Patente oder andere Schutzrechte Dritter verletzt, so ist er verpflichtet, den Lizenzgeber unverzüglich schriftlich unter Beifügung einer genauen Beschreibung der Verletzungshandlung zu unterrichten. Es ist dem Lizenznehmer untersagt, andere natürliche oder juristische Personen ohne schriftliches Einverständnis des Lizenzgebers zu informieren.
Hat der Lizenznehmer den Eindruck, dass die Software Patente oder andere
Schutzrechte Dritter verletzt, so ist er verpflichtet, den Lizenzgeber
unverzüglich schriftlich unter Beifügung einer genauen Beschreibung der
Verletzungshandlung zu unterrichten. Es ist dem Lizenznehmer untersagt, andere
natürliche oder juristische Personen ohne schriftliches Einverständnis des
Lizenzgebers zu informieren.
Bei Verletzung einer der obigen Nebenpflichten verpflichtet sich der
Lizenznehmer, dem Lizenzgeber Schadensersatz für alle durch die
Verletzung entstandenen Schäden (einschließlich der Prozesskosten) zu
leisten. Ist die Verletzung einer solchen Nebenpflicht festgestellt, so
genügt es, wenn der Lizenzgeber plausibel darlegt, dass der Schaden
durch die Verletzung entstanden ist. Den Lizenznehmer trifft die volle
Beweislast für das Gegenteil. Der Lizenznehmer verpflichtet sich, dem
Lizenzgeber alle Auskünfte im Zusammenhang mit der Verletzung einer der
obigen Nebenpflichten zu erteilen.
Der Lizenzgeber weist darauf hin, dass zur CampusSource-Software nicht
die Software anderer Hersteller gehört, mit der die CampusSource-
Software zusammen arbeiten kann oder die für den Betrieb der
CampusSource-Software notwendigerweise vorhanden sein muss, wie z. B.
WWW-Server, Funktionsbibliotheken, Werkzeugsysteme und
Datenbankmanagementsysteme. Die Lizenzen für diese Software müssen vom
jeweiligen Hersteller separat erworben werden. Die GPL gilt für diese
Software in der Regel nicht.
6. Datenschutz
Lizenznehmer, dem Lizenzgeber Schadensersatz für alle durch die Verletzung
entstandenen Schäden (einschließlich der Prozeßkosten) zu leisten. Ist die
Verletzung einer solchen Nebenpflicht festgestellt, so genügt es, wenn der
Lizenzgeber plausibel darlegt, dass der Schaden durch die Verletzung entstanden
ist. Den Lizenznehmer trifft die volle Beweislast für das Gegenteil. Der
Lizenznehmer verpflichtet sich, dem Lizenzgeber alle Auskünfte im Zusammenhang
mit der Verletzung einer der obigen Nebenpflichten zu erteilen.
Der Lizenzgeber weist darauf hin, dass zur CampusSource-Software nicht die
Software anderer Hersteller gehört, mit der die CampusSource-Software
zusammenarbeiten kann oder die für den Betrieb der CampusSource-Software
notwendigerweise vorhanden sein muss, wie z. B. WWW-Server,
Funktionsbibliotheken, Werkzeugsysteme und Datenbankmanagementsysteme. Die
Lizenzen für diese Software müssen vom jeweiligen Hersteller separat erworben
werden. Die GPL gilt für diese Software in der Regel nicht.
6. Datenschutz
Der Lizenzgeber verpflichtet sich, bezüglich der bei der Registrierung
angegebenen Daten die einschlägigen landes- und bundesrechtlichen
Datenschutzbestimmungen einzuhalten. Alle Verbindungen zu diesem Server
im Download- und Registrierbereich werden in einem LOG-File
aufgezeichnet. Bitte beachten Sie dazu unsere Datenschutzerklärung.
Datenschutzbestimmungen einzuhalten. Alle Verbindungen zu diesem Server im
Download- und Registrierbereich werden in einem LOG-File aufgezeichnet.
7. Schriftform
7. Schriftform
Alle Nebenabreden, die zwischen dem Lizenzgeber und dem Lizenznehmer
abgeschlossen werden, bedürfen der Schriftform. Eine Abänderung oder
Aufhebung dieser Klausel bedarf ebenfalls der Schriftform.
abgeschlossen werden, bedürfen der Schriftform. Eine Abänderung oder Aufhebung
dieser Klausel bedarf ebenfalls der Schriftform.
8. Gerichtsstand
Gerichtsstand für alle Streitigkeiten aus diesem Vertrag ist Duisburg,
sofern der Lizenznehmer Kaufmann, eine juristische Person des
öffentlichen Rechts oder ein öffentlich-rechtliches Sondervermögen ist.
8. Gerichtsstand
Gerichtsstand für alle Streitigkeiten aus diesem Vertrag ist Hagen, sofern der
Lizenznehmer Kaufmann, eine juristische Person des öffentlichen Rechts oder ein
öffentlich-rechtliches Sondervermögen ist.
Die Parteien vereinbaren die Anwendung deutschen Rechts. Sollte nach
Internationalem Verfahrensrecht die Zuständigkeit eines deutschen
Gerichts möglich sein, so vereinbaren die Parteien die Zuständigkeit der
deutschen Gerichtsbarkeit und innerhalb Deutschlands die Zuständigkeit
des Amtsgerichtes bzw. Landgerichtes Hagen. Bezüglich der in Abschnitt 5
„Schutzrechte Dritter“ festgelegten Nebenpflichten des Lizenznehmers
kann der Lizenzgeber abweichend von Satz 3 ein beliebiges international
zuständiges Gericht anrufen.
Copyright © 2026 Förderverein CampusSource e.V. | Impressum <http://
www.campussource.de/impressum/> | Datenschutz <https://
www.campussource.de/datenschutz/>
Internationalem Verfahrensrecht die Zuständigkeit eines deutschen Gerichts
möglich sein, so vereinbaren die Parteien die Zuständigkeit der deutschen
Gerichtsbarkeit und innerhalb Deutschlands die Zuständigkeit des Amtsgerichtes
bzw. Landgerichtes Hagen. Bezüglich der in Abschnitt 5 »Schutzrechte Dritter«
festgelegten Nebenpflichten des Lizenznehmers kann der Lizenzgeber abweichend
von Satz 3 ein beliebiges international zuständiges Gericht anrufen.
© 2000 CampusSource Alle Rechte vorbehalten

901
src-patches/com/sun/jersey/spi/container/servlet/WebComponent.java

@ -1,901 +0,0 @@ @@ -1,901 +0,0 @@
/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2010-2013 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can
* obtain a copy of the License at
* http://glassfish.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
/* gepatchtes class-File:
* im Rahmen von #331613 muss mit dem Update auf Tomcat 10 auch die Anwendung von Serlvet API 4.0 auf 6.0 umgestellt werden.
* Dies leistet ein Migrationstool, welches in erster Linie den Packagenamen von 'javax' auf 'jakarta' umstellt.
* Allerdings bestand danach immer noch in der 'jersey-bunde-1.19.4.jar' ein Zugriff auf die entfernte Methode
* 'HttpServletResponse.setStatus(int, String)', welcher beim Laden von Saiku zu einer Exception geführt hat,
* wodurch das Frontend in der Ladeanzeige geblieben ist.
* Der Patch ändert die Klasse derart, dass sie die noch vorhandene Methode 'HttpServletResponse.setStatus(int)' aufruft,
* was zu funktionieren scheint.
* Ursprung entspricht https://github.com/javaee/jersey-1.x/blob/864a01d7be490ab93d2424da3e446ad8eb84b1e8/jersey-servlet/src/main/java/com/sun/jersey/spi/container/servlet/WebComponent.java
*/
package com.sun.jersey.spi.container.servlet;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.net.URI;
import java.security.Principal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.GenericEntity;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import javax.naming.NamingException;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import com.sun.jersey.api.container.ContainerException;
import com.sun.jersey.api.container.MappableContainerException;
import com.sun.jersey.api.core.ApplicationAdapter;
import com.sun.jersey.api.core.ClasspathResourceConfig;
import com.sun.jersey.api.core.PackagesResourceConfig;
import com.sun.jersey.api.core.ResourceConfig;
import com.sun.jersey.api.core.TraceInformation;
import com.sun.jersey.api.core.servlet.WebAppResourceConfig;
import com.sun.jersey.api.representation.Form;
import com.sun.jersey.core.header.InBoundHeaders;
import com.sun.jersey.core.header.MediaTypes;
import com.sun.jersey.core.reflection.ReflectionHelper;
import com.sun.jersey.core.util.ReaderWriter;
import com.sun.jersey.server.impl.InitialContextHelper;
import com.sun.jersey.server.impl.ThreadLocalInvoker;
import com.sun.jersey.server.impl.application.DeferredResourceConfig;
import com.sun.jersey.server.impl.cdi.CDIComponentProviderFactoryInitializer;
import com.sun.jersey.server.impl.container.servlet.JSPTemplateProcessor;
import com.sun.jersey.server.impl.ejb.EJBComponentProviderFactoryInitilizer;
import com.sun.jersey.server.impl.managedbeans.ManagedBeanComponentProviderFactoryInitilizer;
import com.sun.jersey.server.impl.model.method.dispatch.FormDispatchProvider;
import com.sun.jersey.server.impl.monitoring.GlassFishMonitoringInitializer;
import com.sun.jersey.server.probes.UriRuleProbeProvider;
import com.sun.jersey.spi.container.ContainerListener;
import com.sun.jersey.spi.container.ContainerNotifier;
import com.sun.jersey.spi.container.ContainerRequest;
import com.sun.jersey.spi.container.ContainerResponse;
import com.sun.jersey.spi.container.ContainerResponseWriter;
import com.sun.jersey.spi.container.ReloadListener;
import com.sun.jersey.spi.container.WebApplication;
import com.sun.jersey.spi.container.WebApplicationFactory;
import com.sun.jersey.spi.inject.SingletonTypeInjectableProvider;
import java.security.AccessController;
import java.security.PrivilegedActionException;
/**
* An abstract Web component that may be extended a Servlet and/or
* Filter implementation, or encapsulated by a Servlet or Filter implementation.
*
* @author Paul Sandoz (paul.sandoz at oracle.com)
*/
public class WebComponent implements ContainerListener {
/**
* The servlet initialization property whose value is a fully qualified
* class name of a class that implements {@link ResourceConfig} or
* {@link Application}.
*/
public static final String APPLICATION_CONFIG_CLASS =
"javax.ws.rs.Application";
/**
* The servlet initialization property whose value is a fully qualified
* class name of a class that implements {@link ResourceConfig} or
* {@link Application}.
*/
public static final String RESOURCE_CONFIG_CLASS =
"com.sun.jersey.config.property.resourceConfigClass";
/**
* The base path in the Web Pages where JSP templates, associated with
* viewables of resource classes, are located.
* <p/>
* If this property is not set then the base path will be the root path
* of the Web Pages.
*/
public static final String JSP_TEMPLATES_BASE_PATH =
"com.sun.jersey.config.property.JSPTemplatesBasePath";
private static final Logger LOGGER =
Logger.getLogger(WebComponent.class.getName());
private final ThreadLocalInvoker<HttpServletRequest> requestInvoker =
new ThreadLocalInvoker<HttpServletRequest>();
private final ThreadLocalInvoker<HttpServletResponse> responseInvoker =
new ThreadLocalInvoker<HttpServletResponse>();
private WebConfig config;
private ResourceConfig resourceConfig;
private WebApplication application;
public WebComponent() {
}
public WebComponent(Application app) {
if (app == null)
throw new IllegalArgumentException();
if (app instanceof ResourceConfig) {
resourceConfig = (ResourceConfig) app;
} else {
resourceConfig = new ApplicationAdapter(app);
}
}
/**
* Get the Web configuration.
*
* @return the Web configuration.
*/
public WebConfig getWebConfig() {
return config;
}
/**
* Get the resource configuration.
*
* @return the resource configuration.
*/
public ResourceConfig getResourceConfig() {
return resourceConfig;
}
/**
* Initiate the Web component.
*
* @param webConfig the Web configuration.
* @throws javax.servlet.ServletException in case of any initialization error
*/
public void init(WebConfig webConfig) throws ServletException {
config = webConfig;
if (resourceConfig == null)
resourceConfig = createResourceConfig(config);
load();
Object o = resourceConfig.getProperties().get(
ResourceConfig.PROPERTY_CONTAINER_NOTIFIER);
if (o instanceof List) {
List list = (List) o;
for (Object elem : list) {
if (elem instanceof ContainerNotifier) {
ContainerNotifier crf = (ContainerNotifier) elem;
crf.addListener(this);
}
}
} else if (o instanceof ContainerNotifier) {
ContainerNotifier crf = (ContainerNotifier) o;
crf.addListener(this);
}
}
/**
* Destroy this Web component.
* <p/>
* This will destroy the Web application created by this this Web component.
*/
public void destroy() {
if (application != null)
application.destroy();
}
private final static class Writer extends OutputStream implements ContainerResponseWriter {
final HttpServletResponse response;
ContainerResponse cResponse;
long contentLength;
OutputStream out;
boolean statusAndHeadersWritten = false;
Writer(HttpServletResponse response) {
this.response = response;
}
public OutputStream writeStatusAndHeaders(long contentLength,
ContainerResponse cResponse) throws IOException {
this.contentLength = contentLength;
this.cResponse = cResponse;
this.statusAndHeadersWritten = false;
return this;
}
public void finish() throws IOException {
if (statusAndHeadersWritten)
return;
// Note that the writing of headers MUST be performed before
// the invocation of sendError as on some Servlet implementations
// modification of the response headers will have no effect
// after the invocation of sendError.
writeHeaders();
writeStatus();
}
private void writeStatus() {
Response.StatusType statusType = cResponse.getStatusType();
final String reasonPhrase = statusType.getReasonPhrase();
// if (reasonPhrase != null) {
// response.setStatus(statusType.getStatusCode(), reasonPhrase);
// } else {
response.setStatus(statusType.getStatusCode());
// }
}
public void write(int b) throws IOException {
initiate();
out.write(b);
}
@Override
public void write(byte b[]) throws IOException {
if (b.length > 0) {
initiate();
out.write(b);
}
}
@Override
public void write(byte b[], int off, int len) throws IOException {
if (len > 0) {
initiate();
out.write(b, off, len);
}
}
@Override
public void flush() throws IOException {
writeStatusAndHeaders();
if (out != null)
out.flush();
}
@Override
public void close() throws IOException {
initiate();
out.close();
}
void initiate() throws IOException {
if (out == null) {
writeStatusAndHeaders();
out = response.getOutputStream();
}
}
void writeStatusAndHeaders() {
if (statusAndHeadersWritten)
return;
writeHeaders();
writeStatus();
statusAndHeadersWritten = true;
}
void writeHeaders() {
if (contentLength != -1 && contentLength < Integer.MAX_VALUE)
response.setContentLength((int) contentLength);
MultivaluedMap<String, Object> headers = cResponse.getHttpHeaders();
for (Map.Entry<String, List<Object>> e : headers.entrySet()) {
for (Object v : e.getValue()) {
response.addHeader(e.getKey(), ContainerResponse.getHeaderValue(v));
}
}
}
}
/**
* Dispatch client requests to a resource class.
*
* @param baseUri the base URI of the request.
* @param requestUri the URI of the request.
* @param request the {@link HttpServletRequest} object that
* contains the request the client made to
* the Web component.
* @param response the {@link HttpServletResponse} object that
* contains the response the Web component returns
* to the client.
* @return the status code of the response.
* @throws IOException if an input or output error occurs
* while the Web component is handling the
* HTTP request.
* @throws ServletException if the HTTP request cannot
* be handled.
*/
public int service(URI baseUri, URI requestUri,
final HttpServletRequest request,
final HttpServletResponse response)
throws ServletException, IOException {
// Copy the application field to local instance to ensure that the
// currently loaded web application is used to process
// request
final WebApplication _application = application;
final ContainerRequest cRequest = createRequest(
_application,
request,
baseUri,
requestUri);
cRequest.setSecurityContext(new SecurityContext() {
public Principal getUserPrincipal() {
return request.getUserPrincipal();
}
public boolean isUserInRole(String role) {
return request.isUserInRole(role);
}
public boolean isSecure() {
return request.isSecure();
}
public String getAuthenticationScheme() {
return request.getAuthType();
}
});
try {
// Check if any servlet filters have consumed a request entity
// of the media type application/x-www-form-urlencoded
// This can happen if a filter calls request.getParameter(...)
filterFormParameters(request, cRequest);
UriRuleProbeProvider.requestStart(requestUri);
requestInvoker.set(request);
responseInvoker.set(response);
final Writer w = new Writer(response);
_application.handleRequest(cRequest, w);
return w.cResponse.getStatus();
} catch (WebApplicationException ex) {
final Response exResponse = ex.getResponse();
final String entity = exResponse.getEntity() != null ? exResponse.getEntity().toString() : null;
response.sendError(exResponse.getStatus(), entity);
return exResponse.getStatus();
} catch (MappableContainerException ex) {
traceOnException(cRequest, response);
throw new ServletException(ex.getCause());
} catch (ContainerException ex) {
traceOnException(cRequest, response);
throw new ServletException(ex);
} catch (RuntimeException ex) {
traceOnException(cRequest, response);
throw ex;
} finally {
UriRuleProbeProvider.requestEnd();
requestInvoker.set(null);
responseInvoker.set(null);
}
}
/**
* Extension point for creating your custom container request.
*
* @param app the web app
* @param request the current servlet api request
* @param baseUri the base uri
* @param requestUri the request uri
* @return the request container
* @throws IOException if any error occurs when getting the input stream
*/
protected ContainerRequest createRequest(WebApplication app,
HttpServletRequest request, URI baseUri, URI requestUri) throws IOException {
return new ContainerRequest(
app,
request.getMethod(),
baseUri,
requestUri,
getHeaders(request),
request.getInputStream());
}
private void traceOnException(final ContainerRequest cRequest, final HttpServletResponse response) {
if (cRequest.isTracingEnabled()) {
final TraceInformation ti = (TraceInformation) cRequest.getProperties().
get(TraceInformation.class.getName());
ti.addTraceHeaders(new TraceInformation.TraceHeaderListener() {
public void onHeader(String name, String value) {
response.addHeader(name, value);
}
});
}
}
/**
* Create a new instance of a {@link WebApplication}.
*
* @return the {@link WebApplication} instance.
*/
protected WebApplication create() {
return WebApplicationFactory.createWebApplication();
}
/**
* A helper class for creating an injectable provider that supports
* {@link Context} with a type and constant value.
*
* @param <T> the type of the constant value.
*/
protected static class ContextInjectableProvider<T> extends
SingletonTypeInjectableProvider<Context, T> {
/**
* Create a new instance.
*
* @param type the type of the constant value.
* @param instance the constant value.
*/
protected ContextInjectableProvider(Type type, T instance) {
super(type, instance);
}
}
/**
* Configure the {@link ResourceConfig}.
* <p/>
* The {@link ResourceConfig} is configured such that the following classes
* may be injected onto the field of a root resource class or a parameter
* of a method of root resource class that is annotated with
* {@link javax.ws.rs.core.Context}: {@link HttpServletRequest}, {@link HttpServletResponse}
* , {@link ServletContext} and {@link WebConfig}.
* <p/>
* Any root resource class in registered in the resource configuration
* that is an interface is processed as follows.
* If the class is an interface and there exists a JNDI named object
* with the fully qualified class name as the JNDI name then that named
* object is added as a singleton root resource and the class is removed
* from the set of root resource classes.
* <p/>
* An inheriting class may override this method to configure the
* {@link ResourceConfig} to provide alternative or additional instances
* that are resource or provider classes or instances, and may modify the
* features and properties of the {@link ResourceConfig}. For an inheriting
* class to extend configuration behaviour the overriding method MUST call
* <code>super.configure(servletConfig, rc, wa)</code> as the first statement
* of that method.
* <p/>
* This method will be called only once at initiation. Subsequent
* reloads of the Web application will not result in subsequence calls to
* this method.
*
* @param wc the Web configuration
* @param rc the Resource configuration
* @param wa the Web application
*/
protected void configure(WebConfig wc, ResourceConfig rc, WebApplication wa) {
configureJndiResources(rc);
rc.getSingletons().add(new ContextInjectableProvider<HttpServletRequest>(
HttpServletRequest.class,
(HttpServletRequest) Proxy.newProxyInstance(
this.getClass().getClassLoader(),
new Class[]{HttpServletRequest.class},
requestInvoker)));
rc.getSingletons().add(new ContextInjectableProvider<HttpServletResponse>(
HttpServletResponse.class,
(HttpServletResponse) Proxy.newProxyInstance(
this.getClass().getClassLoader(),
new Class[]{HttpServletResponse.class},
responseInvoker)));
GenericEntity<ThreadLocal<HttpServletRequest>> requestThreadLocal =
new GenericEntity<ThreadLocal<HttpServletRequest>>(requestInvoker.getImmutableThreadLocal()) {
};
rc.getSingletons().add(new ContextInjectableProvider<ThreadLocal<HttpServletRequest>>(
requestThreadLocal.getType(), requestThreadLocal.getEntity()));
GenericEntity<ThreadLocal<HttpServletResponse>> responseThreadLocal =
new GenericEntity<ThreadLocal<HttpServletResponse>>(responseInvoker.getImmutableThreadLocal()) {
};
rc.getSingletons().add(new ContextInjectableProvider<ThreadLocal<HttpServletResponse>>(
responseThreadLocal.getType(), responseThreadLocal.getEntity()));
rc.getSingletons().add(new ContextInjectableProvider<ServletContext>(
ServletContext.class,
wc.getServletContext()));
rc.getSingletons().add(new ContextInjectableProvider<WebConfig>(
WebConfig.class,
wc));
rc.getClasses().add(JSPTemplateProcessor.class);
// TODO
// If CDI is enabled should EJB injection be disabled?
EJBComponentProviderFactoryInitilizer.initialize(rc);
CDIComponentProviderFactoryInitializer.initialize(wc, rc, wa);
// TODO
// If CDI is enabled then no need to initialize managed beans
ManagedBeanComponentProviderFactoryInitilizer.initialize(rc);
GlassFishMonitoringInitializer.initialize();
}
/**
* Initiate the {@link WebApplication}.
* <p/>
* This method will be called once at initiation and for
* each reload of the Web application.
* <p/>
* An inheriting class may override this method to initiate the
* Web application with different parameters.
*
* @param rc the Resource configuration
* @param wa the Web application
*/
protected void initiate(ResourceConfig rc, WebApplication wa) {
wa.initiate(rc);
}
/**
* Load the Web application. This will create, configure and initiate
* the web application.
*/
public void load() {
WebApplication _application = create();
configure(config, resourceConfig, _application);
initiate(resourceConfig, _application);
application = _application;
}
/**
* Get the default resource configuration if one is not declared in the
* web.xml.
* <p/>
* This implementation returns an instance of {@link WebAppResourceConfig}
* that scans in files and directories as declared by the
* {@link ClasspathResourceConfig#PROPERTY_CLASSPATH} if present, otherwise
* in the "WEB-INF/lib" and "WEB-INF/classes" directories.
* <p/>
* An inheriting class may override this method to supply a different
* default resource configuration implementation.
*
* @param props the properties to pass to the resource configuration.
* @param wc the web configuration.
* @return the default resource configuration.
* @throws javax.servlet.ServletException in case of any issues with providing \
* the default resource configuration
*/
protected ResourceConfig getDefaultResourceConfig(Map<String, Object> props,
WebConfig wc) throws ServletException {
return getWebAppResourceConfig(props, wc);
}
// ContainerListener
/**
* Reload the Web application. This will create and initiate the web
* application using the same {@link ResourceConfig} implementation
* that was used to load the Web application.
* <p/>
* This method may be called at runtime, more than once, to reload the
* Web application. For example, if a {@link ResourceConfig} implementation
* is capable of detecting changes to resource classes (addition or removal)
* or providers then this method may be invoked to reload the web
* application for such changes to take effect.
* <p/>
* If this method is called when there are pending requests then such
* requests will be processed using the previously loaded web application.
*/
public void onReload() {
WebApplication oldApplication = application;
WebApplication newApplication = create();
initiate(resourceConfig, newApplication);
application = newApplication;
if (resourceConfig instanceof ReloadListener)
((ReloadListener) resourceConfig).onReload();
oldApplication.destroy();
}
//
/* package */ ResourceConfig getWebAppResourceConfig(Map<String, Object> props,
WebConfig webConfig) throws ServletException {
// Default to using Web app resource config
return new WebAppResourceConfig(props, webConfig.getServletContext());
}
private ResourceConfig createResourceConfig(WebConfig webConfig)
throws ServletException {
final Map<String, Object> props = getInitParams(webConfig);
final ResourceConfig rc = createResourceConfig(webConfig, props);
rc.setPropertiesAndFeatures(props);
return rc;
}
private ResourceConfig createResourceConfig(WebConfig webConfig, Map<String, Object> props)
throws ServletException {
// Check if the resource config class property is present
String resourceConfigClassName = webConfig.getInitParameter(RESOURCE_CONFIG_CLASS);
// Otherwise check if the JAX-RS application config class property is
// present
if (resourceConfigClassName == null)
resourceConfigClassName = webConfig.getInitParameter(APPLICATION_CONFIG_CLASS);
// If no resource config class property is present
if (resourceConfigClassName == null) {
// If the packages property is present then
// use the packages resource config
String packages = webConfig.getInitParameter(
PackagesResourceConfig.PROPERTY_PACKAGES);
if (packages != null) {
props.put(PackagesResourceConfig.PROPERTY_PACKAGES, packages);
return new PackagesResourceConfig(props);
}
ResourceConfig defaultConfig = webConfig.getDefaultResourceConfig(props);
if (defaultConfig != null)
return defaultConfig;
return getDefaultResourceConfig(props, webConfig);
}
try {
Class<?> resourceConfigClass = AccessController.doPrivileged(ReflectionHelper.
classForNameWithExceptionPEA(resourceConfigClassName));
// TODO add support for WebAppResourceConfig
if (resourceConfigClass == ClasspathResourceConfig.class) {
String[] paths = getPaths(webConfig.getInitParameter(
ClasspathResourceConfig.PROPERTY_CLASSPATH));
props.put(ClasspathResourceConfig.PROPERTY_CLASSPATH, paths);
return new ClasspathResourceConfig(props);
} else if (ResourceConfig.class.isAssignableFrom(resourceConfigClass)) {
try {
Constructor constructor = resourceConfigClass.getConstructor(Map.class);
if (ClasspathResourceConfig.class.isAssignableFrom(resourceConfigClass)) {
String[] paths = getPaths(webConfig.getInitParameter(
ClasspathResourceConfig.PROPERTY_CLASSPATH));
props.put(ClasspathResourceConfig.PROPERTY_CLASSPATH, paths);
}
return (ResourceConfig) constructor.newInstance(props);
} catch (NoSuchMethodException ex) {
// Pass through and try the default constructor
} catch (Exception e) {
throw new ServletException(e);
}
return new DeferredResourceConfig(resourceConfigClass.asSubclass(ResourceConfig.class));
} else if (Application.class.isAssignableFrom(resourceConfigClass)) {
return new DeferredResourceConfig(resourceConfigClass.asSubclass(Application.class));
} else {
String message = "Resource configuration class, " + resourceConfigClassName +
", is not a super class of " + Application.class;
throw new ServletException(message);
}
} catch (ClassNotFoundException e) {
String message = "Resource configuration class, " + resourceConfigClassName +
", could not be loaded";
throw new ServletException(message, e);
} catch (PrivilegedActionException e) {
String message = "Resource configuration class, " + resourceConfigClassName +
", could not be loaded";
throw new ServletException(message, e.getCause());
}
}
private Map<String, Object> getInitParams(WebConfig webConfig) {
Map<String, Object> props = new HashMap<String, Object>();
Enumeration names = webConfig.getInitParameterNames();
while (names.hasMoreElements()) {
String name = (String) names.nextElement();
props.put(name, webConfig.getInitParameter(name));
}
return props;
}
private String[] getPaths(String classpath) throws ServletException {
final ServletContext context = config.getServletContext();
if (classpath == null) {
String[] paths = {
context.getRealPath("/WEB-INF/lib"),
context.getRealPath("/WEB-INF/classes")
};
if (paths[0] == null && paths[1] == null) {
String message = "The default deployment configuration that scans for " +
"classes in /WEB-INF/lib and /WEB-INF/classes is not supported " +
"for the application server." +
"Try using the package scanning configuration, see the JavaDoc for " +
PackagesResourceConfig.class.getName() + " and the property " +
PackagesResourceConfig.PROPERTY_PACKAGES + ".";
throw new ServletException(message);
}
return paths;
} else {
String[] virtualPaths = classpath.split(";");
List<String> resourcePaths = new ArrayList<String>();
for (String virtualPath : virtualPaths) {
virtualPath = virtualPath.trim();
if (virtualPath.length() == 0) continue;
String path = context.getRealPath(virtualPath);
if (path != null) resourcePaths.add(path);
}
if (resourcePaths.isEmpty()) {
String message = "None of the declared classpath locations, " +
classpath +
", could be resolved. " +
"This could be because the default deployment configuration that scans for " +
"classes in classpath locations is not supported. " +
"Try using the package scanning configuration, see the JavaDoc for " +
PackagesResourceConfig.class.getName() + " and the property " +
PackagesResourceConfig.PROPERTY_PACKAGES + ".";
throw new ServletException(message);
}
return resourcePaths.toArray(new String[resourcePaths.size()]);
}
}
private void configureJndiResources(ResourceConfig rc) {
// Obtain any instances that are registered in JNDI
// Assumes such instances are singletons
// Registered classes have to be interfaces
javax.naming.Context x = InitialContextHelper.getInitialContext();
if (x != null) {
Iterator<Class<?>> i = rc.getClasses().iterator();
while (i.hasNext()) {
Class<?> c = i.next();
if (!c.isInterface()) continue;
try {
Object o = x.lookup(c.getName());
if (o != null) {
i.remove();
rc.getSingletons().add(o);
LOGGER.log(Level.INFO,
"An instance of the class " + c.getName() +
" is found by JNDI look up using the class name as the JNDI name. " +
"The instance will be registered as a singleton.");
}
} catch (NamingException ex) {
LOGGER.log(Level.CONFIG,
"JNDI lookup failed for Jersey application resource " + c.getName(),
ex);
}
}
}
}
private void filterFormParameters(HttpServletRequest servletRequest, ContainerRequest containerRequet) throws IOException {
if (MediaTypes.typeEquals(MediaType.APPLICATION_FORM_URLENCODED_TYPE, containerRequet.getMediaType())
&& !isEntityPresent(containerRequet)) {
Form f = new Form();
Enumeration e = servletRequest.getParameterNames();
while (e.hasMoreElements()) {
String name = (String) e.nextElement();
String[] values = servletRequest.getParameterValues(name);
f.put(name, Arrays.asList(values));
}
if (!f.isEmpty()) {
containerRequet.getProperties().put(FormDispatchProvider.FORM_PROPERTY, f);
if (LOGGER.isLoggable(Level.WARNING)) {
LOGGER.log(Level.WARNING,
"A servlet request, to the URI " + containerRequet.getRequestUri() + ", " +
"contains form parameters in " +
"the request body but the request body has been consumed " +
"by the servlet or a servlet filter accessing the request " +
"parameters. Only resource methods using @FormParam " +
"will work as expected. Resource methods consuming the " +
"request body by other means will not work as expected.");
}
}
}
}
private boolean isEntityPresent(ContainerRequest cr) throws IOException {
InputStream in = cr.getEntityInputStream();
if (!in.markSupported()) {
in = new BufferedInputStream(in, ReaderWriter.BUFFER_SIZE);
cr.setEntityInputStream(in);
}
in.mark(1);
if (in.read() == -1)
return false;
else {
in.reset();
return true;
}
}
private InBoundHeaders getHeaders(HttpServletRequest request) {
InBoundHeaders rh = new InBoundHeaders();
for (Enumeration<String> names = request.getHeaderNames(); names.hasMoreElements();) {
String name = names.nextElement();
List<String> valueList = new LinkedList<String>();
for (Enumeration<String> values = request.getHeaders(name); values.hasMoreElements();) {
valueList.add(values.nextElement());
}
rh.put(name, valueList);
}
return rh;
}
}

366
src-patches/org/dbforms/util/MultipartRequest.java

@ -1,366 +0,0 @@ @@ -1,366 +0,0 @@
/*
* $Header: /cvsroot/jdbforms/dbforms/src/org/dbforms/util/MultipartRequest.java,v 1.19 2005/02/19 21:26:32 hkollmann Exp $
* $Revision: 1.19 $
* $Date: 2005/02/19 21:26:32 $
*
* DbForms - a Rapid Application Development Framework
* Copyright (C) 2001 Joachim Peer <joepeer@excite.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package org.dbforms.util;
import org.apache.commons.fileupload.DiskFileUpload;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletRequestWrapper;
import jakarta.servlet.http.HttpServletRequest;
import org.dbforms.util.external.FileUtil;
import de.superx.util.multipart.support.DefaultMultipartHttpServletRequest;
/**
* A utility class to handle <code>multipart/form-data</code> requests, the
* kind of requests that support file uploads. This class emulates the interface
* of <code>HttpServletRequest</code>, making it familiar to use. It uses a
* "push" model where any incoming files are read and saved directly to disk in
* the constructor. If you wish to have more flexibility, e.g. write the files
* to a database, use the "pull" model <code>MultipartParser</code> instead.
* <p>
* This class can receive arbitrarily large files (up to an artificial limit you
* can set), and fairly efficiently too. It cannot handle nested data (multipart
* content within multipart content) or internationalized content (such as non
* Latin-1 filenames).
* </p>
* <p>
* See the included <a href="upload.war">upload.war </a> for an example of how
* to use this class.
* <p>
* The full file upload specification is contained in experimental RFC 1867,
* available at <a href="http://www.ietf.org/rfc/rfc1867.txt"
* http://www.ietf.org/rfc/rfc1867.txt </a>.
* </p>
* <p>
* PLEASE NOTE: this class uses Multipart Support Classes by Jason Hunter.
* Copyright (C) 1998 by Jason Hunter. All rights reserved.
* Use of this class is limited. Please see the LICENSE for more information.
* Make sure that you project meets the requierements defined by Jason Hunter
* respective O'reilly!
* </p>
*
* @see MultipartParser
*
* @author Jason Hunter
* @author Geoff Soutter
* @author Joe Peer - _changed_ it for use in DbForms (i apologize)
*/
/* Witt: am 28.05.2026 als Basis für gepatchte Datei nach 'src-patches' übernommen, Basis waren meine
* lokalen 'dbforms_2.5_20050226' Sourcen */
public class MultipartRequest {
private static Log logCat = LogFactory.getLog(MultipartRequest.class); // logging category
private Hashtable files = new Hashtable(); // name - UploadedFile
// for this class
//private File dir;
private Hashtable parameters = new Hashtable(); // name - Vector of values
/**
* Constructs a new MultipartRequest to handle the specified request,
* {saving any uploaded files to the given directory}, and limiting the
* upload size to the specified length. If the content is too large, an
* IOException is thrown. This constructor actually parses the
* <tt>multipart/form-data</tt> and throws an IOException if there's any
* problem reading or parsing the request.
*
* @param request
* the servlet request.
* @param maxPostSize
* the maximum size of the POST content.
* @exception IOException
* if the uploaded content is larger than
* <tt>maxPostSize</tt> or there's a problem reading or
* parsing the request.
*/
public MultipartRequest(HttpServletRequest request,
int maxPostSize)
throws IOException {
// Sanity check values
if (request == null) {
throw new IllegalArgumentException("request cannot be null");
}
// Create a new file upload handler
DiskFileUpload upload = new DiskFileUpload();
// Set upload parameters
upload.setSizeMax(maxPostSize);
String tmpDir = System.getProperty("java.io.tmpdir");
upload.setRepositoryPath(tmpDir);
// Parse the request
try {
// we initilize this list with 'null', as the following unwrapping code should do the work - contrary to the 'org.dbforms.util.MultipartRequest code, from which this is copied
List<FileItem> items = null; //upload.parseRequest(request);
if (items == null || items.size() == 0) {
ServletRequest originalRequest = request;
DefaultMultipartHttpServletRequest dmhsr = null;
while (originalRequest instanceof ServletRequestWrapper) {
if (originalRequest instanceof DefaultMultipartHttpServletRequest) {
dmhsr = (DefaultMultipartHttpServletRequest) originalRequest;
items = dmhsr.getFileItems();
break;
}
originalRequest = ((ServletRequestWrapper) originalRequest).getRequest();
}
}
Iterator iter = items.iterator();
while (iter.hasNext()) {
FileItem item = (FileItem) iter.next();
String name = item.getFieldName();
if (item.isFormField()) {
String value = item.getString();
// plain parameters get allways into parameters structure
Vector existingValues = (Vector) parameters.get(name);
if (existingValues == null) {
existingValues = new Vector();
parameters.put(name, existingValues);
}
existingValues.addElement(value);
} else {
String fileName = item.getName();
if (!Util.isNull(fileName)) {
File f = new File(fileName);
// 2004-08-05-HKK/Dziugas Baltrunas:
// FileItem always returns the full pathname!
fileName = FileUtil.filename(f.getName());
// The part actually contained a file
// #changes by joe peer:
// we must delay storing the file-inputstream (into
// database, filesystem or whatever)
// until we know exactly what should happen with it
FileHolder fileHolder = new FileHolder(fileName, item);
files.put(name, fileHolder);
logCat.info("buffered and now added as " + name
+ " the following fileHolder:"
+ fileHolder.getFileName());
// #changes by joe peer:
// if a file parameter is not null it gets into the
// parameters structures as well
// this is important to provide a simple access to
// request (no distinction between files and plain
// params)
Vector existingValues = (Vector) parameters.get(name);
if (existingValues == null) {
existingValues = new Vector();
parameters.put(name, existingValues);
}
existingValues.addElement(fileName);
}
}
}
} catch (Exception e) {
logCat.error("MultipartRequest", e);
}
}
/**
* Returns the content type of the specified file (as supplied by the client
* browser), or null if the file was not included in the upload.
*
* @param name
* the file name.
* @return the content type of the file.
*/
public String getContentType(String name) {
try {
//#changed by joe peer
//UploadedFile file = (UploadedFile)files.get(name);
FileHolder filePart = (FileHolder) files.get(name);
return filePart.getContentType(); // may be null
} catch (Exception e) {
return null;
}
}
/**
* Returns a FilePart object for the specified file this method was added by
* joe peer
*
* @param name
* the file name.
* @return a FilePart object for the named file.
*/
public FileHolder getFileHolder(String name) {
try {
return (FileHolder) files.get(name);
} catch (Exception e) {
return null;
}
}
/**
* Returns a InputStream object for the specified file saved on the server's
* filesystem, or null if the file was not included in the upload.
*
* @param name
* the file name.
* @return a InputStream object for the named file.
*/
public InputStream getFileInputStream(String name) {
try {
//#changed by joe peer
//UploadedFile file = (UploadedFile)files.get(name);
FileHolder filePart = (FileHolder) files.get(name);
return filePart.getInputStreamFromBuffer(); // may be null
} catch (Exception e) {
return null;
}
}
/**
* Returns the names of all the uploaded files as an Enumeration of Strings.
* It returns an empty Enumeration if there are no uploaded files. Each file
* name is the name specified by the form, not by the user.
*
* @return the names of all the uploaded files as an Enumeration of Strings.
*/
public Enumeration getFileNames() {
return files.keys();
}
/**
* Returns the filesystem name of the specified file, or null if the file
* was not included in the upload. A filesystem name is the name specified
* by the user. It is also the name under which the file is actually saved.
*
* @param name
* the file name.
* @return the filesystem name of the file.
*/
public String getFilesystemName(String name) {
try {
//#changed by joe peer
//UploadedFile file = (UploadedFile)files.get(name);
FileHolder filePart = (FileHolder) files.get(name);
return filePart.getFileName(); // may be null
} catch (Exception e) {
return null;
}
}
/**
* Returns the value of the named parameter as a String, or null if the
* parameter was not sent or was sent without a value. The value is
* guaranteed to be in its normal, decoded form. If the parameter has
* multiple values, only the last one is returned (for backward
* compatibility). For parameters with multiple values, it's possible the
* last "value" may be null.
*
* @param name
* the parameter name.
* @return the parameter value.
*/
public String getParameter(String name) {
try {
Vector values = (Vector) parameters.get(name);
if ((values == null) || (values.size() == 0)) {
return null;
}
String value = (String) values.elementAt(0);
return value;
} catch (Exception e) {
return null;
}
}
/**
* Returns the names of all the parameters as an Enumeration of Strings. It
* returns an empty Enumeration if there are no parameters.
*
* @return the names of all the parameters as an Enumeration of Strings.
*/
public Enumeration getParameterNames() {
return parameters.keys();
}
/**
* Returns the values of the named parameter as a String array, or null if
* the parameter was not sent. The array has one entry for each parameter
* field sent. If any field was sent without a value that entry is stored in
* the array as a null. The values are guaranteed to be in their normal,
* decoded form. A single value is returned as a one-element array.
*
* @param name
* the parameter name.
* @return the parameter values.
*/
public String[] getParameterValues(String name) {
try {
Vector values = (Vector) parameters.get(name);
if ((values == null) || (values.size() == 0)) {
return null;
}
String[] valuesArray = new String[values.size()];
values.copyInto(valuesArray);
return valuesArray;
} catch (Exception e) {
return null;
}
}
}

699
src-patches/org/springframework/security/web/util/OnCommittedResponseWrapper.java

@ -1,699 +0,0 @@ @@ -1,699 +0,0 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* taken/copied from 'spring-security-web-6.2.8.jar' as a base for patching */
package org.springframework.security.web.util;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Locale;
import jakarta.servlet.ServletOutputStream;
import jakarta.servlet.WriteListener;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpServletResponseWrapper;
/**
* Base class for response wrappers which encapsulate the logic for handling an event when
* the {@link jakarta.servlet.http.HttpServletResponse} is committed.
*
* @author Rob Winch
* @since 4.0.2
*/
public abstract class OnCommittedResponseWrapper extends HttpServletResponseWrapper {
private boolean disableOnCommitted;
/**
* The Content-Length response header. If this is greater than 0, then once
* {@link #contentWritten} is larger than or equal the response is considered
* committed.
*/
private long contentLength;
/**
* The size of data written to the response body. The field will only be updated when
* {@link #disableOnCommitted} is false.
*/
private long contentWritten;
/**
* @param response the response to be wrapped
*/
public OnCommittedResponseWrapper(HttpServletResponse response) {
super(response);
}
@Override
public void addHeader(String name, String value) {
checkContentLengthHeader(name, value);
super.addHeader(name, value);
}
@Override
public void addIntHeader(String name, int value) {
checkContentLengthHeader(name, value);
super.addIntHeader(name, value);
}
@Override
public void setHeader(String name, String value) {
checkContentLengthHeader(name, value);
super.setHeader(name, value);
}
@Override
public void setIntHeader(String name, int value) {
checkContentLengthHeader(name, value);
super.setIntHeader(name, value);
}
private void checkContentLengthHeader(String name, int value) {
if ("Content-Length".equalsIgnoreCase(name)) {
setContentLength(value);
}
}
private void checkContentLengthHeader(String name, String value) {
if ("Content-Length".equalsIgnoreCase(name)) {
setContentLength(Long.parseLong(value));
}
}
@Override
public void setContentLength(int len) {
setContentLength((long) len);
super.setContentLength(len);
}
@Override
public void setContentLengthLong(long len) {
setContentLength(len);
super.setContentLengthLong(len);
}
private void setContentLength(long len) {
this.contentLength = len;
checkContentLength(0);
}
/**
* Invoke this method to disable invoking
* {@link OnCommittedResponseWrapper#onResponseCommitted()} when the
* {@link jakarta.servlet.http.HttpServletResponse} is committed. This can be useful
* in the event that Async Web Requests are made.
*/
protected void disableOnResponseCommitted() {
this.disableOnCommitted = true;
}
/**
* Returns true if {@link #onResponseCommitted()} will be invoked when the response is
* committed, else false.
* @return if {@link #onResponseCommitted()} is enabled
*/
protected boolean isDisableOnResponseCommitted() {
return this.disableOnCommitted;
}
/**
* Implement the logic for handling the
* {@link jakarta.servlet.http.HttpServletResponse} being committed
*/
protected abstract void onResponseCommitted();
/**
* Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked
* before calling the superclass <code>sendError()</code>
*/
@Override
public final void sendError(int sc) throws IOException {
doOnResponseCommitted();
super.sendError(sc);
}
/**
* Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked
* before calling the superclass <code>sendError()</code>
*/
@Override
public final void sendError(int sc, String msg) throws IOException {
doOnResponseCommitted();
super.sendError(sc, msg);
}
/**
* Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked
* before calling the superclass <code>sendRedirect()</code>
*/
@Override
public final void sendRedirect(String location) throws IOException {
doOnResponseCommitted();
super.sendRedirect(location);
}
/**
* Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked
* before calling the calling <code>getOutputStream().close()</code> or
* <code>getOutputStream().flush()</code>
*/
@Override
public ServletOutputStream getOutputStream() throws IOException {
return new SaveContextServletOutputStream(super.getOutputStream());
}
/**
* Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked
* before calling the <code>getWriter().close()</code> or
* <code>getWriter().flush()</code>
*/
@Override
public PrintWriter getWriter() throws IOException {
return new SaveContextPrintWriter(super.getWriter());
}
/**
* Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked
* before calling the superclass <code>flushBuffer()</code>
*/
@Override
public void flushBuffer() throws IOException {
doOnResponseCommitted();
super.flushBuffer();
}
private void trackContentLength(boolean content) {
if (!this.disableOnCommitted) {
checkContentLength(content ? 4 : 5); // TODO Localization
}
}
private void trackContentLength(char content) {
if (!this.disableOnCommitted) {
checkContentLength(1);
}
}
private void trackContentLength(Object content) {
if (!this.disableOnCommitted) {
trackContentLength(String.valueOf(content));
}
}
private void trackContentLength(byte[] content) {
if (!this.disableOnCommitted) {
checkContentLength((content != null) ? content.length : 0);
}
}
private void trackContentLength(char[] content) {
if (!this.disableOnCommitted) {
checkContentLength((content != null) ? content.length : 0);
}
}
private void trackContentLength(int content) {
if (!this.disableOnCommitted) {
trackContentLength(String.valueOf(content));
}
}
private void trackContentLength(float content) {
if (!this.disableOnCommitted) {
trackContentLength(String.valueOf(content));
}
}
private void trackContentLength(double content) {
if (!this.disableOnCommitted) {
trackContentLength(String.valueOf(content));
}
}
private void trackContentLengthLn() {
if (!this.disableOnCommitted) {
trackContentLength("\r\n");
}
}
private void trackContentLength(String content) {
if (!this.disableOnCommitted) {
int contentLength = (content != null) ? content.length() : 4;
checkContentLength(contentLength);
}
}
/**
* Adds the contentLengthToWrite to the total contentWritten size and checks to see if
* the response should be written.
* @param contentLengthToWrite the size of the content that is about to be written.
*/
private void checkContentLength(long contentLengthToWrite) {
this.contentWritten += contentLengthToWrite;
boolean isBodyFullyWritten = this.contentLength > 0 && this.contentWritten >= this.contentLength;
int bufferSize = getBufferSize();
boolean requiresFlush = bufferSize > 0 && this.contentWritten >= bufferSize;
if (isBodyFullyWritten || requiresFlush) {
doOnResponseCommitted();
}
}
/**
* Calls <code>onResponseCommmitted()</code> with the current contents as long as
* {@link #disableOnResponseCommitted()} was not invoked.
*/
private void doOnResponseCommitted() {
if (!this.disableOnCommitted) {
onResponseCommitted();
disableOnResponseCommitted();
}
}
/**
* Ensures {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked before
* calling the prior to methods that commit the response. We delegate all methods to
* the original {@link java.io.PrintWriter} to ensure that the behavior is as close to
* the original {@link java.io.PrintWriter} as possible. See SEC-2039
*
* @author Rob Winch
*/
private class SaveContextPrintWriter extends PrintWriter {
private final PrintWriter delegate;
SaveContextPrintWriter(PrintWriter delegate) {
super(delegate);
this.delegate = delegate;
}
@Override
public void flush() {
doOnResponseCommitted();
this.delegate.flush();
}
@Override
public void close() {
doOnResponseCommitted();
this.delegate.close();
}
@Override
public boolean equals(Object obj) {
return this.delegate.equals(obj);
}
@Override
public int hashCode() {
return this.delegate.hashCode();
}
@Override
public String toString() {
return getClass().getName() + "[delegate=" + this.delegate.toString() + "]";
}
@Override
public boolean checkError() {
return this.delegate.checkError();
}
@Override
public void write(int c) {
trackContentLength(c);
this.delegate.write(c);
}
@Override
public void write(char[] buf, int off, int len) {
checkContentLength(len);
this.delegate.write(buf, off, len);
}
@Override
public void write(char[] buf) {
trackContentLength(buf);
this.delegate.write(buf);
}
@Override
public void write(String s, int off, int len) {
checkContentLength(len);
this.delegate.write(s, off, len);
}
@Override
public void write(String s) {
trackContentLength(s);
this.delegate.write(s);
}
@Override
public void print(boolean b) {
trackContentLength(b);
this.delegate.print(b);
}
@Override
public void print(char c) {
trackContentLength(c);
this.delegate.print(c);
}
@Override
public void print(int i) {
trackContentLength(i);
this.delegate.print(i);
}
@Override
public void print(long l) {
trackContentLength(l);
this.delegate.print(l);
}
@Override
public void print(float f) {
trackContentLength(f);
this.delegate.print(f);
}
@Override
public void print(double d) {
trackContentLength(d);
this.delegate.print(d);
}
@Override
public void print(char[] s) {
trackContentLength(s);
this.delegate.print(s);
}
@Override
public void print(String s) {
trackContentLength(s);
this.delegate.print(s);
}
@Override
public void print(Object obj) {
trackContentLength(obj);
this.delegate.print(obj);
}
@Override
public void println() {
trackContentLengthLn();
this.delegate.println();
}
@Override
public void println(boolean x) {
trackContentLength(x);
trackContentLengthLn();
this.delegate.println(x);
}
@Override
public void println(char x) {
trackContentLength(x);
trackContentLengthLn();
this.delegate.println(x);
}
@Override
public void println(int x) {
trackContentLength(x);
trackContentLengthLn();
this.delegate.println(x);
}
@Override
public void println(long x) {
trackContentLength(x);
trackContentLengthLn();
this.delegate.println(x);
}
@Override
public void println(float x) {
trackContentLength(x);
trackContentLengthLn();
this.delegate.println(x);
}
@Override
public void println(double x) {
trackContentLength(x);
trackContentLengthLn();
this.delegate.println(x);
}
@Override
public void println(char[] x) {
trackContentLength(x);
trackContentLengthLn();
this.delegate.println(x);
}
@Override
public void println(String x) {
trackContentLength(x);
trackContentLengthLn();
this.delegate.println(x);
}
@Override
public void println(Object x) {
trackContentLength(x);
trackContentLengthLn();
this.delegate.println(x);
}
@Override
public PrintWriter printf(String format, Object... args) {
return this.delegate.printf(format, args);
}
@Override
public PrintWriter printf(Locale l, String format, Object... args) {
return this.delegate.printf(l, format, args);
}
@Override
public PrintWriter format(String format, Object... args) {
return this.delegate.format(format, args);
}
@Override
public PrintWriter format(Locale l, String format, Object... args) {
return this.delegate.format(l, format, args);
}
@Override
public PrintWriter append(CharSequence csq) {
checkContentLength(csq.length());
return this.delegate.append(csq);
}
@Override
public PrintWriter append(CharSequence csq, int start, int end) {
checkContentLength(end - start);
return this.delegate.append(csq, start, end);
}
@Override
public PrintWriter append(char c) {
trackContentLength(c);
return this.delegate.append(c);
}
}
/**
* Ensures{@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked before
* calling methods that commit the response. We delegate all methods to the original
* {@link jakarta.servlet.ServletOutputStream} to ensure that the behavior is as close
* to the original {@link jakarta.servlet.ServletOutputStream} as possible. See
* SEC-2039
*
* @author Rob Winch
*/
private class SaveContextServletOutputStream extends ServletOutputStream {
private final ServletOutputStream delegate;
SaveContextServletOutputStream(ServletOutputStream delegate) {
this.delegate = delegate;
}
@Override
public void write(int b) throws IOException {
trackContentLength(b);
this.delegate.write(b);
}
@Override
public void flush() throws IOException {
doOnResponseCommitted();
this.delegate.flush();
}
@Override
public void close() throws IOException {
doOnResponseCommitted();
this.delegate.close();
}
@Override
public void print(boolean b) throws IOException {
trackContentLength(b);
this.delegate.print(b);
}
@Override
public void print(char c) throws IOException {
trackContentLength(c);
this.delegate.print(c);
}
@Override
public void print(double d) throws IOException {
trackContentLength(d);
this.delegate.print(d);
}
@Override
public void print(float f) throws IOException {
trackContentLength(f);
this.delegate.print(f);
}
@Override
public void print(int i) throws IOException {
trackContentLength(i);
this.delegate.print(i);
}
@Override
public void print(long l) throws IOException {
trackContentLength(l);
this.delegate.print(l);
}
@Override
public void print(String s) throws IOException {
trackContentLength(s);
this.delegate.print(s);
}
@Override
public void println() throws IOException {
trackContentLengthLn();
this.delegate.println();
}
@Override
public void println(boolean b) throws IOException {
trackContentLength(b);
trackContentLengthLn();
this.delegate.println(b);
}
@Override
public void println(char c) throws IOException {
trackContentLength(c);
trackContentLengthLn();
this.delegate.println(c);
}
@Override
public void println(double d) throws IOException {
trackContentLength(d);
trackContentLengthLn();
this.delegate.println(d);
}
@Override
public void println(float f) throws IOException {
trackContentLength(f);
trackContentLengthLn();
this.delegate.println(f);
}
@Override
public void println(int i) throws IOException {
trackContentLength(i);
trackContentLengthLn();
this.delegate.println(i);
}
@Override
public void println(long l) throws IOException {
trackContentLength(l);
trackContentLengthLn();
this.delegate.println(l);
}
@Override
public void println(String s) throws IOException {
trackContentLength(s);
trackContentLengthLn();
this.delegate.println(s);
}
@Override
public void write(byte[] b) throws IOException {
trackContentLength(b);
this.delegate.write(b);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
checkContentLength(len);
this.delegate.write(b, off, len);
}
@Override
public boolean isReady() {
return this.delegate.isReady();
}
@Override
public void setWriteListener(WriteListener writeListener) {
this.delegate.setWriteListener(writeListener);
}
@Override
public boolean equals(Object obj) {
return this.delegate.equals(obj);
}
@Override
public int hashCode() {
return this.delegate.hashCode();
}
@Override
public String toString() {
return getClass().getName() + "[delegate=" + this.delegate.toString() + "]";
}
}
}

10
src/de/memtext/util/CryptUtils.java

@ -7,7 +7,6 @@ import java.util.StringTokenizer; @@ -7,7 +7,6 @@ import java.util.StringTokenizer;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.digest.Crypt;
/**
*
@ -78,15 +77,6 @@ public class CryptUtils { @@ -78,15 +77,6 @@ public class CryptUtils {
}
return encrypted.toString();
}
public static String encodeSHA512(String txt) {
if (txt ==null || txt.length() > 1000) {
throw new RuntimeException("Passwort ist ungültig");
}
byte txtArray[] = txt.getBytes();
return Crypt.crypt(txtArray, "$6$0q5233a66a3e9bea16f3139bfe4f6ce50ced591deafbc4b9ef56d6ae60fe9c4b22aa78dbd704bde57c");
}
public static String encryptSimple(String s) {
int i = 0;

94
src/de/statspez/plausi/astat/csv/Studienfach_Land.csv

@ -5825,9 +5825,9 @@ M05;20252;12;0;1332108;99999;Angewandte Psychologie;311;132 @@ -5825,9 +5825,9 @@ M05;20252;12;0;1332108;99999;Angewandte Psychologie;311;132
254;20062;04;0;0116790;99999;Sachunterricht (einschl.Schulgarten);142;254
255;20072;04;0;0329200;99999;Archiv- und Bibliothekswesen;322;255
256;20072;04;1;0329650;20251;Rechtspflege;380;256
256;20252;04;1;0329650;99999;Rechtspflege;380;-1
256;20252;04;1;0329650;20251;Rechtspflege;380;-1
258;20072;04;1;0329150;20251;Arbeitsverwaltung;345;258
258;20252;04;0;0329150;99999;Arbeitsverwaltung;345;
258;20252;04;0;0329150;20251;Arbeitsverwaltung;345;
261;20062;04;0;0329190;99999;Innere Verwaltung;345;261
262;20072;04;1;0329350;20251;Bibliothekswesen;322;262
262;20252;04;0;0329350;99999;Bibliothekswesen;322;022
@ -5835,7 +5835,7 @@ M05;20252;12;0;1332108;99999;Angewandte Psychologie;311;132 @@ -5835,7 +5835,7 @@ M05;20252;12;0;1332108;99999;Angewandte Psychologie;311;132
263;20072;04;0;0329600;99999;Bundeswehr, Justiz und Polizei;861;263
264;20171;04;0;5070359;99999;Arbeits- und Sozialverwaltung, Berufsberatung;343;264
265;20112;04;1;0329300;20251;Bankwesen;343;265
265;20252;04;0;0329300;99999;Bankwesen;343;
265;20252;04;0;0329300;20251;Bankwesen;343;
266;20062;04;1;0329180;20251;Finanzverwaltung;344;266
266;20252;04;0;0329180;99999;Finanzverwaltung;344;269
268;20072;04;0;0329750;99999;Verkehrswesen;840;268
@ -6391,7 +6391,7 @@ L40;20201;04;0;0330150;99999;Logistikmanangement (duales Studium);340;021 @@ -6391,7 +6391,7 @@ L40;20201;04;0;0330150;99999;Logistikmanangement (duales Studium);340;021
167;20072;06;0;0330200;20251;Europäische Wirtschaft;340;167
167;20252;06;1;1230200;99999;Europäische Wirtschaft;340;167
168;20072;06;1;0329550;20251;Justizvollzug;861;168
168;20252;06;1;1129550;99999;Justizvollzug;861;168
168;20252;06;1;1129550;20251;Justizvollzug;861;168
169;20072;06;0;0104100;99999;Ethik;226;169
170;20072;06;1;0117900;20151;Verhaltensgestörtenpädagogik;141;170
171;20072;06;0;0869200;99999;Vermessungswesen (Geodäsie);581;171
@ -6483,27 +6483,27 @@ L40;20201;04;0;0330150;99999;Logistikmanangement (duales Studium);340;021 @@ -6483,27 +6483,27 @@ L40;20201;04;0;0330150;99999;Logistikmanangement (duales Studium);340;021
255;20072;06;0;0329200;20251;Archivwesen;322;255
255;20252;06;1;1129150;99999;Archiv- und Bibliothekswesen;322;255
256;20072;06;1;0329650;20251;Rechtspflege;380;256
256;20252;06;1;1129650;99999;Rechtspflege;380;256
256;20252;06;1;1129650;20251;Rechtspflege;380;256
257;20072;06;1;0329100;20251;Arbeits- und Berufsberatung;762;257
257;20252;06;1;1129100;99999;Arbeits- und Berufsberatung;762;257
257;20252;06;1;1129100;20251;Arbeits- und Berufsberatung;762;257
258;20072;06;1;0329150;20251;Arbeitsverwaltung;345;258
258;20252;06;1;1129150;99999;Arbeitsverwaltung;345;258
258;20252;06;1;1129150;20251;Arbeitsverwaltung;345;258
259;20072;06;0;0329250;20251;Auswärtige Angelegenheiten;345;259
259;20252;06;1;1129250;99999;Auswärtige Angelegenheiten;345;259
260;20072;06;1;0329400;20251;Bundeswehrverwaltung;345;260
260;20252;06;1;1129400;99999;Bundeswehrverwaltung;345;260
260;20252;06;1;1129400;20251;Bundeswehrverwaltung;345;260
261;20072;06;0;0329500;20251;Innere Verwaltung;345;261
261;20252;06;1;1129500;99999;Innere Verwaltung;345;261
262;20072;06;1;0329350;20251;Bibliothekswesen;322;262
262;20252;06;1;1129350;99999;Bibliothekswesen;322;262
262;20252;06;1;1129350;20251;Bibliothekswesen;322;262
263;20072;06;0;0329600;20251;Polizei/Verfassungsschutz;861;263
263;20252;06;1;1129300;99999;Bundeswehr, Justiz und Polizei;861;263
264;20072;06;0;0329700;20251;Sozialversicherung;343;264
264;20252;06;1;1129100;99999;Arbeits- und Sozialverwaltung, Berufsberatung;343;264
265;20072;06;1;0329300;20251;Bankwesen;343;265
265;20252;06;1;1129300;99999;Bankwesen;343;265
265;20252;06;1;1129300;20251;Bankwesen;343;265
266;20072;06;1;0329450;20251;Finanzverwaltung;344;266
266;20252;06;1;1129450;99999;Finanzverwaltung;344;266
266;20252;06;1;1129450;20251;Finanzverwaltung;344;266
268;20072;06;0;0329750;20251;Verkehrswesen;840;268
268;20252;06;1;1129750;99999;Verkehrswesen;840;268
269;20072;06;0;0329850;20251;Zoll- und Steuerverwaltung;344;269
@ -7148,29 +7148,29 @@ a51;20252;06;1;1333409;99999;Bildungswissenschaften: Bildung im globalen Technis @@ -7148,29 +7148,29 @@ a51;20252;06;1;1333409;99999;Bildungswissenschaften: Bildung im globalen Technis
a52;20112;06;0;0330186;20251;Quant. Economics, Quant. Finance, Quant. Management, Quant. Marketing and Law a. Quant. Economics;340;021
a52;20252;06;1;1230186;99999;Quant. Economics, Quant. Finance, Quant. Management, Quant. Marketing and Law a. Quant. Economics;340;021
a53;20112;06;1;0330187;20122;Quantitative Economics;340;021
a53;20252;06;1;1230187;99999;Quantitative Economics;340;021
a53;20252;06;1;1230187;20122;Quantitative Economics;340;021
a54;20112;06;1;0330188;20122;Quantitative Finance;340;021
a54;20252;06;1;1230188;99999;Quantitative Finance;340;021
a54;20252;06;1;1230188;20122;Quantitative Finance;340;021
a55;20112;06;1;0330189;20122;Quantitative Management;340;021
a55;20252;06;1;1230189;99999;Quantitative Management;340;021
a55;20252;06;1;1230189;20122;Quantitative Management;340;021
a56;20112;06;1;0330190;20122;Quantitative Marketing;340;021
a56;20252;06;1;1230190;99999;Quantitative Marketing;340;021
a56;20252;06;1;1230190;20122;Quantitative Marketing;340;021
a57;20112;06;1;0330191;20122;Law and Quantitative Economics;340;021
a57;20252;06;1;1230191;99999;Law and Quantitative Economics;340;021
a57;20252;06;1;1230191;20122;Law and Quantitative Economics;340;021
a58;20112;06;0;0868113;99999;Bauingenieurwesen B07M03;582;017
a59;20112;06;0;0438704;20151;Wirtschaftsinformatik B08M02;481;277
a59;20152;06;0;0871704;99999;Wirtschaftsinformatik B08M02;481;277
a60;20112;06;0;0101107;99999;Empirische Sprachwissenschaften B08M02;220;004
a61;20112;06;1;0330192;20122;Ph.D. in Economics;340;021
a61;20252;06;1;1230192;99999;Ph.D. in Economics;340;021
a61;20252;06;1;1230192;20122;Ph.D. in Economics;340;021
a62;20112;06;1;0330193;20122;Ph.D. in Finance;340;021
a62;20252;06;1;1230193;99999;Ph.D. in Finance;340;021
a62;20252;06;1;1230193;20122;Ph.D. in Finance;340;021
a63;20112;06;1;0330194;20251;Ph.D. in Management;340;021
a63;20252;06;1;1230194;99999;Ph.D. in Management;340;021
a64;20112;06;1;0330195;20122;Ph.D. in Marketing;340;021
a64;20252;06;1;1230195;99999;Ph.D. in Marketing;340;021
a64;20252;06;1;1230195;20122;Ph.D. in Marketing;340;021
a65;20112;06;1;0330196;20122;Ph.D. in Law and Economics;340;021
a65;20252;06;1;1230196;99999;Ph.D. in Law and Economics;340;021
a65;20252;06;1;1230196;20122;Ph.D. in Law and Economics;340;021
a66;20111;06;0;0107401;99999;Gebärdensprachdolmetschen;220;018
a67;20111;06;0;0116408;20151;Frühpädagogik – Leitung und Management von Kindertageseinrichtungen;142;052
a67;20152;06;0;0333408;20251;Frühpädagogik – Leitung und Management von Kindertageseinrichtungen;142;052
@ -7392,8 +7392,8 @@ c10;20252;06;1;1128104;99999;Wirtschaftsrecht – Nachhaltigkeit und Ethik;380;1 @@ -7392,8 +7392,8 @@ c10;20252;06;1;1128104;99999;Wirtschaftsrecht – Nachhaltigkeit und Ethik;380;1
c11;20142;06;0;0330293;20251;Accounting, Finance, Controlling;340;021
c11;20252;06;1;1230293;99999;Accounting, Finance, Controlling;340;021
c12;20142;06;2;0331210;20151;LifeCycle Catering;621;125
c12;20152;06;2;0758210;20251;LifeCycle Catering;621;125
c12;20252;06;1;1231210;99999;LifeCycle Catering;621;125
c12;20152;06;2;0758210;99999;LifeCycle Catering;621;125
c12;20252;06;1;1231210;20151;LifeCycle Catering;621;125
c13;20142;06;0;0327308;20251;Systementwicklung Inklusion;760;253
c13;20252;06;1;1327308;99999;Systementwicklung Inklusion;762;253
c14;20142;06;0;0758552;99999;International Wine Business;621;227
@ -8718,13 +8718,13 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 @@ -8718,13 +8718,13 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113
081;20062;03;0;0113400;20251;Iranistik;222;081
081;20252;03;0;0113400;99999;Iranistik;310;081
082;20062;03;0;0863460;99999;Holz-/Fasertechnik;543;082
083;20062;03;0;0113450;20251;Islamwissenschaft;222;083
083;20062;03;0;0113450;99999;Islamwissenschaft;222;083
083;20252;03;0;0113450;99999;Islamwissenschaft;310;083
084;20062;03;0;0111200;99999;Italienisch;222;084
085;20062;03;0;0113500;99999;Japanologie;222;085
086;20062;03;0;0103300;99999;Kath. Theologie, -Religionslehre;221;086
087;20062;03;1;0117500;20151;Körperbehindertenpädagogik;141;087
088;20062;03;0;0864600;20251;Optoelektronik;524;088
088;20062;03;0;0864600;99999;Optoelektronik;524;088
088;20252;03;0;0864600;99999;Optoelektronik;523;088
089;20062;03;0;0865500;99999;Verkehrsingenieurwesen;525;089
090;20062;03;1;0101200;20201;Lernbereich Sprach- und Kulturwissenschaften;142;090
@ -8762,7 +8762,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 @@ -8762,7 +8762,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113
115;20252;03;0;1333500;99999;Grundschul-/Primarstufenpädagogik;144;115
116;20062;03;0;0976500;99999;Textilgestaltung;214;116
117;20062;03;1;0116100;20151;Ausländerpädagogik;142;117
117;20152;03;0;0333100;20251;Ausländerpädagogik;142;117
117;20152;03;0;0333100;99999;Ausländerpädagogik;142;117
117;20252;03;0;1333100;99999;Ausländerpädagogik;142;117
118;20062;03;0;0437300;99999;Technomathematik;461;118
119;20062;03;0;0109600;99999;Niederländisch;223;119
@ -8787,7 +8787,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 @@ -8787,7 +8787,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113
132;20252;03;0;1332100;99999;Psychologie;311;132
133;20062;03;3;0106400;20081;Medienkunde/Kommunikations-/Informationswissenschaft;321;133
134;20062;03;0;0867100;99999;Raumplanung;581;134
135;20062;03;0;0328100;20251;Rechtswissenschaft;380;135
135;20062;03;0;0328100;99999;Rechtswissenschaft;380;135
135;20252;03;0;1128100;99999;Rechtswissenschaft;380;135
136;20062;03;0;0104300;99999;Religionswissenschaft;221;136
137;20062;03;0;0111400;99999;Romanistik (Romanische Philologie, Einzelsprachen a.n.g.);222;137
@ -8842,11 +8842,11 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 @@ -8842,11 +8842,11 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113
169;20062;03;0;0104100;99999;Ethik;226;169
170;20062;03;1;0117900;20151;Verhaltensgestörtenpädagogik;141;170
171;20062;03;0;0869200;99999;Vermessungswesen (Geodäsie);581;171
172;20062;03;0;0329800;20251;Verwaltungswissenschaft/-wesen;345;172
172;20062;03;0;0329800;99999;Verwaltungswissenschaft/-wesen;345;172
172;20252;03;0;1129800;99999;Verwaltungswissenschaft/-wesen;345;172
173;20062;03;0;0114200;99999;Ethnologie;312;173
174;20062;03;0;0114300;99999;Volkskunde;312;174
175;20062;03;0;0330450;20251;Volkswirtschaftslehre;314;175
175;20062;03;0;0330450;99999;Volkswirtschaftslehre;314;175
175;20252;03;0;1230450;99999;Volkswirtschaftslehre;314;175
176;20062;03;0;0976600;20251;Werkerziehung;142;176
176;20252;03;0;0976600;99999;Werkerziehung;145;176
@ -8857,12 +8857,12 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 @@ -8857,12 +8857,12 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113
179;20092;03;0;0331200;20251;Wirtschaftsingenieurwesen mit wirtschaftswissenschaftlichem Schwerpunkt;340;179
179;20252;03;0;1231200;99999;Wirtschaftsingenieurwesen mit wirtschaftswissenschaftlichem Schwerpunkt;340;179
180;20062;03;0;0113550;99999;Kaukasistik;222;180
181;20062;03;0;0330500;20251;Wirtschaftspädagogik;142;181
181;20062;03;0;0330500;99999;Wirtschaftspädagogik;142;181
181;20252;03;0;1230500;99999;Wirtschaftspädagogik;145;181
182;20062;03;0;0330250;20251;Intern. Betriebswirtschaft/Management;340;182
182;20252;03;0;1230250;99999;Intern. Betriebswirtschaft/Management;340;182
183;20062;03;0;0105600;99999;Wirtschafts-/Sozialgeschichte;225;183
184;20062;03;0;0330550;20251;Wirtschaftswissenschaften;340;184
184;20062;03;0;0330550;99999;Wirtschaftswissenschaften;340;184
184;20252;03;0;1230550;99999;Wirtschaftswissenschaften;340;184
185;20062;03;0;0550100;99999;Zahnmedizin;724;185
186;20062;03;0;0436300;20251;Lernbereich Naturwissenschaft/Sachunterricht;142;186
@ -8944,7 +8944,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 @@ -8944,7 +8944,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113
256;20062;03;1;0329650;20251;Rechtspflege;380;256
257;20062;03;1;0329100;20251;Arbeits- und Berufsberatung;762;257
258;20062;03;1;0329150;20251;Arbeitsverwaltung;345;258
259;20062;03;0;0329250;20251;Auswärtige Angelegenheiten;345;259
259;20062;03;0;0329250;99999;Auswärtige Angelegenheiten;345;259
259;20252;03;0;1129250;99999;Auswärtige Angelegenheiten;345;259
260;20062;03;1;0329400;20251;Bundeswehrverwaltung;345;260
261;20062;03;0;0329500;20251;Innere Verwaltung;345;261
@ -8968,7 +8968,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 @@ -8968,7 +8968,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113
271;20252;03;0;0109200;99999;Deutsch als Fremdsprache oder als Zweitsprache;222;271
272;20062;03;0;0105100;99999;Alte Geschichte;225;272
273;20062;03;0;0105400;99999;Mittlere und neuere Geschichte;225;273
274;20062;03;0;0330350;20251;Tourismuswirtschaft;812;274
274;20062;03;0;0330350;99999;Tourismuswirtschaft;812;274
274;20252;03;0;1230350;99999;Tourismuswirtschaft;812;274
275;20072;03;1;0436102;20201;Geschichte der Mathematik und Naturwissenschaften;142;275
275;20202;03;2;0105700;99999;Wissenschaftsgeschichte/Technikgeschichte;225;275
@ -9018,7 +9018,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 @@ -9018,7 +9018,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113
361;20152;03;0;0333700;20251;Schulpädagogik;142;361
361;20252;03;0;1333700;99999;Schulpädagogik;142;361
365;20122;03;2;0116450;20151;Pädagogik der frühen Kindheit;142;365
365;20152;03;1;0333450;20251;Pädagogik der frühen Kindheit;142;365
365;20152;03;1;0333450;99999;Pädagogik der frühen Kindheit;142;365
365;20252;03;0;1333550;99999;Kindheitspädagogik;142;365
370;20092;03;0;0870100;99999;Wirtschaftsingenieurwesen mit ingenieurwissenschaftlichem Schwerpunkt;520;370
371;20062;03;0;0758500;99999;Tierproduktion;621;371
@ -9159,19 +9159,19 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 @@ -9159,19 +9159,19 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113
681;20062;03;1;0439201;20121;Regen.Energiequellen(Erg.);441;128
681;20222;03;2;0439500;99999;Quantum Engineering;441;128
682;20062;03;0;0862302;99999;Steine,Erden;521;076
683;20062;03;0;0323101;20251;Umweltrecht/-ökonomie(WB);142;030
683;20062;03;0;0323101;99999;Umweltrecht/-ökonomie(WB);142;030
683;20252;03;0;1128102;99999;Nachhaltigkeitsrecht - Energie, Ressourcen, Umwelt;380;135
684;20112;03;0;0101104;20251;Angewandte Kulturwissenschaften-Kulturinformatik(NF);142;004
684;20252;03;0;0101104;99999;Kulturwiss., Angew., ~, Kritische, Kulturanalysen: Repräsentation, Performativität u. Gender, Interk;220;004
685;20062;03;0;0326201;20251;Sozial-und Kulturgeschichte;312;148
685;20062;03;0;0326201;99999;Sozial-und Kulturgeschichte;312;148
685;20252;03;0;1326201;99999;Kulturwiss., Ang. -Sozial- u. -Kulturgeschichte-, Sozialwissenschaftliche Diversitätsforschung;312;148
686;20062;03;0;0222201;20251;Spiel-u.Bewegungskultur;813;029
686;20252;03;0;1335201;99999;Kulturwiss., Ang. -Spiel- und -Bewegungskultur;813;029
687;20062;03;0;0107301;20251;Sprache u. Kommunikation;220;284
687;20062;03;0;0107301;99999;Sprache u. Kommunikation;220;284
687;20252;03;0;0107301;99999;Kulturwiss., Ang. -Sprache und Kommunikation-, Medientext und Medienübersetzung;223;284
688;20112;03;1;0438302;20151;Umweltinformatik(NF);481;079
688;20152;03;2;0871302;20231;Umweltinformatik(NF);481;079
688;20232;03;1;0328201;20251;Tax Law - Steuerrecht;380;042
688;20232;03;1;0328201;99999;Tax Law - Steuerrecht;380;042
688;20252;03;0;1128201;99999;Tax Law - Steuerrecht, Taxation;380;042
689;20062;03;0;0326202;20251;Europäische Studien;312;148
689;20252;03;0;1326202;99999;Europäische Studien, Eur. Integration/Eur. Studies, Euroculture, Europäisches Regieren;312;148
@ -9179,7 +9179,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 @@ -9179,7 +9179,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113
690;20232;03;0;0758301;99999;International horticulture;622;060
691;20062;03;0;0548202;99999;Kosmetologie(Körperpflege);345;232
692;20062;03;1;0760201;99999;Lebensmittelwiss., Agrar- und Lebensmittelwirtschaft, Food Research and Development;814;071
693;20062;03;0;0548203;20251;Gesundheit;345;232
693;20062;03;0;0548203;99999;Gesundheit;345;232
693;20252;03;0;0548251;99999;Midwifery;723;550
694;20062;03;1;0866101;99999;Bautechnik, Wirtschaftsinformatik-Bau;581;013
695;20062;03;0;0861301;20251;Farbtechnik und Raumgestaltung (Gestaltungstechnik);142;199
@ -9194,7 +9194,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 @@ -9194,7 +9194,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113
701;20101;03;3;0334200;20201;Internationales Informationsmanagement;321;303
701;20202;03;2;0334200;20251;Internationales Informationsmanagement;321;303
701;20252;03;0;1334200;99999;Informationsmanagement (int.), Informations- und Wissensmanagement, ,Int Informationsmgt-Information;321;303
702;20062;03;0;0330154;20251;Krankenversicherungsmanagement;340;021
702;20062;03;0;0330154;99999;Krankenversicherungsmanagement;340;021
702;20252;03;0;1230154;99999;Krankenversicherung/-management;340;021
703;20062;03;1;0757101;99999;Landesentwicklung/Landschaftsentwicklung, European Master in Territorial Development;581;093
706;20062;03;0;0331202;20251;Industrial Business System (IBS );340;179
@ -9216,24 +9216,24 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 @@ -9216,24 +9216,24 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113
719;20252;03;0;0861201;99999;Hörtechnik u. Audiologie;520;072
721;20062;03;1;0865504;99999;Logistik und Informationsmanagement im Praxisverbund;525;089
722;20072;03;1;0869201;99999;Geoinformatik/-informationswesen, Navigation und Umweltrobotik;581;171
723;20072;03;0;0330155;20251;Betr.Wirtsch.-Automobilwirtschaft;340;021
723;20072;03;0;0330155;99999;Betr.Wirtsch.-Automobilwirtschaft;340;021
723;20252;03;0;1230155;99999;Automobilwirtschaft;;
724;20062;03;0;0330552;20251;Organization Studies;340;184
724;20062;03;0;0330552;99999;Organization Studies;340;184
724;20252;03;0;1230552;99999;Organisation Studies, Performance Leadership, Führung und Organisation, WiPäd u. Personalentwicklung;340;184
725;20062;03;1;0330553;20072;Ergotherapie/Logopädie/Physiotherapie/Medizinalfachberufe;340;184
725;20081;03;1;0548301;20251;Ergotherapie/Logopädie/Physiotherapie/Medizinalfachberufe;722;233
725;20252;03;0;0548301;99999;Ergotherapie, Logopädie, Physiotherapie, Medizinalfachberufe, Berufspäd. u. Mgt. im Rettungsdienst;345;232
726;20062;03;0;0330156;20251;Reedereilogistik;340;021
726;20062;03;0;0330156;99999;Reedereilogistik;340;021
726;20252;03;0;1230156;99999;Schiffs- und Reedereimanagement, Maritime Technology and Shipping Management;340;021
727;20062;03;1;0330157;20111;Umwelt-u.Qualitätsmanagement;340;021
727;20112;03;0;0330557;20251;Umwelt-u.Qualitätsmanagement;340;184
727;20112;03;0;0330557;99999;Umwelt-u.Qualitätsmanagement;340;184
727;20252;03;0;1230557;99999;Umwelt-u.Qualitätsmanagement;340;184
728;20062;03;0;0863823;20251;Environmental Engineering (Internat.Umwelt-Verfahrenstechnik),Bio-u.Enviromental Engineering;850;457
728;20062;03;0;0863823;99999;Environmental Engineering (Internat.Umwelt-Verfahrenstechnik),Bio-u.Enviromental Engineering;850;457
728;20252;03;0;0863823;99999;Bio- und Umwelttechnik, Geoenvironmental Engineering;851;457
729;20062;03;0;0101101;20251;Kognitive Science;142;004
729;20252;03;0;0101101;99999;Cognitive Science;220;004
731;20062;03;1;0109601;99999;Niederlande-Studien;223;119
732;20062;03;0;0326203;20251;Frauen-u.Geschlechterstudien,Geschlechterforschung;312;148
732;20062;03;0;0326203;99999;Frauen-u.Geschlechterstudien,Geschlechterforschung;312;148
732;20252;03;0;1326203;99999;Frauen- u. Geschlechterstudien, Geschlechterforschung, Gender Studies;312;148
733;20062;03;0;0101102;20251;Kulturwissenschaftliche Geschlechterstudien;142;004
733;20252;03;0;0101102;99999;Kulturwiss. Geschlechterstudien, Combines Studies, Kultureller Wandel, KuWi: Kritik der Gegenwart - ;220;004
@ -9243,7 +9243,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 @@ -9243,7 +9243,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113
736;20062;03;1;0115102;20151;Kinder-und Jugendlichenpsychotherapie;311;132
736;20152;03;0;0332102;20251;Kinder-und Jugendlichenpsychotherapie;311;132
736;20252;03;0;1332102;99999;Kinder- u. Jugendpsychiatrie/Jugendlichenpsychotherapie;311;132
737;20072;03;0;0331203;20251;Kreislaufwirtschaft;340;179
737;20072;03;0;0331203;99999;Kreislaufwirtschaft;340;179
737;20252;03;0;1231203;99999;Kreislaufwirtschaft;340;179
738;20072;03;0;0327304;20252;Supervision u.Organisationsberatung;760;253
738;20252;03;0;1327304;99999;Supervision u.Organisationsberatung;762;253
@ -9342,7 +9342,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113 @@ -9342,7 +9342,7 @@ l11;20252;06;1;0980405;99999;Langfach Musik;145;113
798;20072;03;0;0863597;99999;Industrieinformatik im Praxisverbund;520;104
799;20062;03;0;0437105;99999;Mathematik und ihre Vermittlung/Elementarmathematik;461;105
800;20062;03;1;0442203;99999;Biologische Diversität u. Ökologie, Biodiversity: Ecology, Evolution, and Conservation;421;026
801;20062;03;0;0330158;20251;Bildungsmanagement und Schulentwicklung;340;021
801;20062;03;0;0330158;99999;Bildungsmanagement und Schulentwicklung;340;021
801;20252;03;0;1230158;99999;Bildungsmanagement u. Schulentwicklg, Business Admin. in Educational Management, Bildungs- u. Wissen;340;021
802;20062;03;0;0331212;20251;Wirtschaftsingenieur / Techn. Vertrieb;340;179
802;20252;03;0;1231212;99999;Technischer Vertrieb im Praxisverbund, Management betrieblicher Systeme im Praxisverbund;340;179

Can't render this file because it is too large.

41
src/de/superx/access/DbSqlRestrictionProvider.java

@ -1,8 +1,5 @@ @@ -1,8 +1,5 @@
package de.superx.access;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.beans.factory.InitializingBean;
@ -27,37 +24,27 @@ public class DbSqlRestrictionProvider implements SqlRestrictionProvider, Initial @@ -27,37 +24,27 @@ public class DbSqlRestrictionProvider implements SqlRestrictionProvider, Initial
@Override
public Select getRestriction(String username, String userrole, String schemaName, String tableName, int orgunitLid) {
try {
String lookupSql =
"SELECT granted_ids, join_column FROM user_dimension_grants"
+ " WHERE user_name = ? AND role_name = ?"
+ " AND schema_name = ? AND table_name = ?"
+ " AND orgunit_lid = ?";
SqlRowSet result = jt.queryForRowSet(lookupSql, username, userrole, schemaName, tableName, Integer.valueOf(orgunitLid));
String lookupSql = String.format(
"SELECT restriction FROM view_rechte"
+ " WHERE user_name = '%s' AND role_name = '%s'"
+ " AND schema_name = '%s' AND table_name = '%s'"
+ " AND orgunit_lid = '%d';",
username, userrole, schemaName, tableName, Integer.valueOf(orgunitLid));
List<String> restrictions = new ArrayList<>();
SqlRowSet result = jt.queryForRowSet(lookupSql);
result.beforeFirst();
String restriction = "";
while (result.next()) {
String grantedIds = result.getString("granted_ids");
String joinColumn = result.getString("join_column");
if (grantedIds == null || grantedIds.isBlank()) {
restrictions.add("1 = 0");
} else {
restrictions.add(String.format("%s IN (%s)", joinColumn, grantedIds));
}
}
String whereClause = "";
if (!restrictions.isEmpty()) {
whereClause = " WHERE " + String.join(" AND ", restrictions);
restriction = result.getString("restriction");
}
String resultSql = String.format(
"(SELECT * FROM %s.%s%s) AS %s",
schemaName, tableName, whereClause, tableName);
return (Select) CCJSqlParserUtil.parse(resultSql);
"(SELECT * FROM %s.%s %s) AS %s",
schemaName, tableName, restriction, tableName);
Select select = (Select)CCJSqlParserUtil.parse(resultSql);
return select;
} catch (DataAccessException e) {
throw new IllegalStateException("Restriction lookup failed", e);
} catch (JSQLParserException e) {

5
src/de/superx/access/InlineSelectVisitor.java

@ -47,11 +47,6 @@ public class InlineSelectVisitor implements net.sf.jsqlparser.statement.select.S @@ -47,11 +47,6 @@ public class InlineSelectVisitor implements net.sf.jsqlparser.statement.select.S
Set<String> cteNames = new HashSet<>();
if (plainSelect.getWithItemsList() != null) {
for (WithItem<?> withItem : plainSelect.getWithItemsList()) {
PlainSelect cteSelect = withItem.getSelect().getPlainSelect(); // inner SELECT of the CTE
FromItem innerFrom = cteSelect.getFromItem(); // FROM inside the CTE
if(innerFrom instanceof Table) {
cteSelect.setFromItem(editFromItem((Table)innerFrom));
}
cteNames.add(withItem.getAlias() != null
? withItem.getAlias().getName()
: withItem.getAliasName());

13
src/de/superx/bianalysis/ExcelSheetBuilder.java

@ -369,22 +369,11 @@ public class ExcelSheetBuilder { @@ -369,22 +369,11 @@ public class ExcelSheetBuilder {
Column column= this.visibleColumns.get(i);
String[] columnHeader = column.header.split("\\|\\|");
boolean isLeftDimensionAttributeColumn = (columnHeader.length == 1 && !columnHeader[0].contains("|"))? true : false;
// sort headers according to headerOrder for top dimension columns
String[] columnHeaderSorted = new String[columnHeader.length];
for (int ii = 0; !isLeftDimensionAttributeColumn && ii < result.headerOrder.size(); ii++) {
String header = result.headerOrder.get(ii);
for (int iii = 0; iii < result.headerOrder.size(); iii++) {
if (columnHeader[iii].trim().startsWith(header)) {
columnHeaderSorted[ii] = columnHeader[iii];
continue;
}
}
}
for(int j = 0; j < rowSize; j++) {
if(isLeftDimensionAttributeColumn) {
grid[j][i] = columnHeader[0];
}else {
String header = columnHeaderSorted[j];
String header = columnHeader[j];
String[] headerValues = header.split("\\|");
grid[j][i] = headerValues[1];
}

5
src/de/superx/bianalysis/ReportDefinition.java

@ -9,7 +9,7 @@ import de.superx.bianalysis.service.DbMetaAdapter; @@ -9,7 +9,7 @@ import de.superx.bianalysis.service.DbMetaAdapter;
public class ReportDefinition {
public Identifier factTableId;
public List<Identifier> factTableIds;
public List<Identifier> leftDimensionAttributeIds;
public List<Identifier> topDimensionAttributeIds;
public List<Identifier> measureIds;
@ -18,6 +18,7 @@ public class ReportDefinition { @@ -18,6 +18,7 @@ public class ReportDefinition {
public boolean hideEmptyColumns;
public ReportDefinition() {
this.factTableIds = new ArrayList<>();
this.leftDimensionAttributeIds = new ArrayList<>();
this.topDimensionAttributeIds = new ArrayList<>();
this.measureIds = new ArrayList<>();
@ -27,7 +28,7 @@ public class ReportDefinition { @@ -27,7 +28,7 @@ public class ReportDefinition {
public ReportDefinition(ReportDefinition definition) {
super();
this.factTableId = definition.factTableId;
this.factTableIds = definition.factTableIds;
this.topDimensionAttributeIds = definition.topDimensionAttributeIds;
this.measureIds = definition.measureIds;
this.filters = definition.filters;

47
src/de/superx/bianalysis/ReportMetadata.java

@ -135,9 +135,9 @@ public class ReportMetadata { @@ -135,9 +135,9 @@ public class ReportMetadata {
List<DimensionAttribute> leftAttributes = new ArrayList<>();
List<DimensionAttribute> rightAttributes = new ArrayList<>();
List<Filter> filterAttributes = new ArrayList<>();
List<Measure> reportMeasures = dbMetaAdapter.getMeasureMetadata(allMeasures);
List<Measure> measures2 = dbMetaAdapter.getMeasureMetadata(allMeasures);
for (Measure measure : reportMeasures) {
for (Measure measure : measures2) {
if(!measure.getFacttableId().equals(secondaryFacttable.factId)) {
measure.isNullMeasure = true;
}
@ -190,29 +190,16 @@ public class ReportMetadata { @@ -190,29 +190,16 @@ public class ReportMetadata {
}
}
ReportMetadata secondaryFacttableMetadata = new ReportMetadata(secondaryFacttable.factTable, leftAttributes, rightAttributes, reportMeasures, filterAttributes, dbMetaAdapter);
secondaryFacttableMetadata.hideEmptyColumns = primaryReportMetadata.hideEmptyColumns;
ReportMetadata secondaryFacttableMetadata = new ReportMetadata(secondaryFacttable.factTable, leftAttributes, rightAttributes, measures2, filterAttributes, dbMetaAdapter);
// TODO all values from all facttables
secondaryFacttableMetadata.setTopDimensionAttributeValues(dbMetaAdapter, secondaryFacttableMetadata.factTable.getId());
secondaryFacttableMetadata.isOuter = isOuter;
factMetadatas.put(secondaryFacttable.factId.composedId, secondaryFacttableMetadata);
boolean onlyNullAttributes = true;
for (DimensionAttribute attribute : leftAttributes) {
if(!attribute.isNullAttribute()) {
onlyNullAttributes = false;
break;
}
}
if(onlyNullAttributes) {
for (Measure measure : reportMeasures) {
measure.isNullMeasure = true;
}
}
}
// set top dimension attribute filter values
for (DimensionAttribute attribute : topDimensionAttributes) {
DimensionAttribute confAttr =
@ -231,21 +218,10 @@ public class ReportMetadata { @@ -231,21 +218,10 @@ public class ReportMetadata {
}
}
// TODO workaround to have the correct topDimensionAttributes values in the first
// report metadata
for (DimensionAttribute a : this.topDimensionAttributes) {
for (DimensionAttribute att : primaryReportMetadata.topDimensionAttributes) {
if(a.getId().equals(att.getId())) {
a.setDimensionAttributeValues(att.getDimensionAttributeValues());
}
}
}
return factMetadatas;
}
public ReportMetadata(ReportMetadata metadata, List<SecondaryFacttable> secondary, List<Identifier> measureIds) {
this.hideEmptyColumns = metadata.hideEmptyColumns;
this.dbMetaAdapter = metadata.dbMetaAdapter;
this.factTable = metadata.factTable;
this.sachgebiet = metadata.sachgebiet;
@ -279,6 +255,7 @@ public class ReportMetadata { @@ -279,6 +255,7 @@ public class ReportMetadata {
for (DimensionAttribute attribute : this.leftDimensionAttributes) {
attribute.setGrouping(true);
}
this.hideEmptyColumns = metadata.hideEmptyColumns;
this.secondaryFacttables = secondaryFacttables;
}
@ -497,25 +474,21 @@ public class ReportMetadata { @@ -497,25 +474,21 @@ public class ReportMetadata {
for (SecondaryFacttable secondaryFacttable: secondaryFacttables) {
String label = "";
if(secondaryFacttable.join.equals("left")) {
label+= "Inklusion ";
label+= "Inklusion ";
} else if(secondaryFacttable.join.equals("inner")) {
label+= "Schnittmenge ";
label+= "Schnittmenge ";
} else if(secondaryFacttable.join.equals("union")) {
label+= "Vereinigung ";
label+= "Vereinigung ";
}
label+= "mit ";
label+=(secondaryFacttable.factTable.getCaption());
label+= " über (";
label+= " über ";
for( int i = 0; i < secondaryFacttable.joinCriterias.size(); i++) {
label+=(secondaryFacttable.joinCriterias.get(i).leadingDimension.getCaption());
label+= " = ";
label+=(secondaryFacttable.joinCriterias.get(i).secondaryDimension.getCaption());
if(i < secondaryFacttable.joinCriterias.size() -1) {
label+= " und ";
}
}
label+= "); ";
secondaryFacttableList.add(label);
}
return secondaryFacttableList;

54
src/de/superx/bianalysis/bin/BiAnalysisCLI.java

@ -7,7 +7,6 @@ import java.io.IOException; @@ -7,7 +7,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
@ -268,7 +267,7 @@ public class BiAnalysisCLI { @@ -268,7 +267,7 @@ public class BiAnalysisCLI {
private static void validateMetadataAgainstSchema() {
SuperXManager.setWEB_INFPfad(PathAndFileUtils.getWebinfPath());
Path metadataFolder = DbtUtils.getBiAnalysisMetadataFolder("hisinone");
Path metadataFolder = DbtUtils.getBiAnalysisMetadataFolder();
File directory = metadataFolder.toFile();
File[] files = directory.listFiles();
@ -349,42 +348,7 @@ public class BiAnalysisCLI { @@ -349,42 +348,7 @@ public class BiAnalysisCLI {
String docDirectory = PathAndFileUtils.getModulePath("biad");
docDirectory = String.join(File.separator, docDirectory, "conf", "his1", "edustore_doc");
String filepathAttributes = docDirectory + File.separator + "biad_attributes_mediawiki.txt";
try (PrintWriter writer = new PrintWriter(filepathAttributes, StandardCharsets.UTF_8)) {
writer.println("{| class=\"wikitable sortable\"");
writer.println("! Attributsbezeichnung !! A. Beschreibung !! A. Spalte !! Dimension !! D. Tabelle !! Thema !! Faktentabelle");
for (MetaObject obj : importer.getAllMetaObjects()) {
if (!(obj instanceof MetaDimensionAttribute a)) {
continue;
}
MetaDimension d = a.getDimension();
MetaFact f = d == null ? null : (MetaFact) d.getFact();
MetaDimensionAttribute attributeRef = a.getConfDimAttrRef();
MetaDimension conformedDimension = d == null ? null : d.getConformedDimension();
String attributeLine = """
|-
| %s || %s || %s || %s || %s || %s || %s
""".formatted(
empty(firstNonNull(a.getCaption(), attributeRef == null ? null : attributeRef.getCaption())),
empty(firstNonNull(a.getDescription(), attributeRef == null ? null : attributeRef.getDescription())),
empty(firstNonNull(a.getDimColumn(), attributeRef == null ? null : attributeRef.getDimColumn())),
empty(firstNonNull(d == null ? null : d.getCaption(), conformedDimension == null ? null : conformedDimension.getCaption())),
empty(firstNonNull(d == null ? null : d.getDimension(), conformedDimension == null ? null : conformedDimension.getDimension())),
empty(f == null ? null : f.getCaption()),
empty(f == null ? null : f.getFacttable())
);
writer.println(attributeLine);
}
writer.println("|}");
System.out.println("Written to file " + filepathAttributes);
}
for(MetaFact fact : importer.getAllFactTables()) {
if("all".equals(facttable) || fact.getFacttable().equals(facttable)) {
@ -444,8 +408,7 @@ public class BiAnalysisCLI { @@ -444,8 +408,7 @@ public class BiAnalysisCLI {
for(MetaDimensionAttribute a : attributes) {
String caption = a.getCaption() == null ? a.getConfDimAttrRef().getCaption() : a.getCaption();
String isHidden = a.isHidden() ? " (Standardmäßig ausgeblendet)" : "";
writer.println("*"+caption + isHidden);
writer.println("*"+caption);
try {
String desc = a.getDescription() == null ? a.getConfDimAttrRef().getDescription() : a.getDescription();
if(!desc.equals("null")) {
@ -464,10 +427,6 @@ public class BiAnalysisCLI { @@ -464,10 +427,6 @@ public class BiAnalysisCLI {
}
private static String getEmptyStringIfNull(String value) {
return value == null ? "" : value;
}
private static void generateYmlForJsonFile(CommandLine parsedArgs) {
String[] dirs = parsedArgs.getOptionValues("c");
String filename = parsedArgs.getOptionValue("y");
@ -806,13 +765,4 @@ public class BiAnalysisCLI { @@ -806,13 +765,4 @@ public class BiAnalysisCLI {
return list == null || list.isEmpty();
}
private static String firstNonNull(String value, String fallback) {
return value != null ? value : fallback;
}
private static String empty(String value) {
return value == null ? "" : value;
}
}

3
src/de/superx/bianalysis/jasper/JasperReportJson.java

@ -105,9 +105,6 @@ public class JasperReportJson { @@ -105,9 +105,6 @@ public class JasperReportJson {
for (Map<String, Object> cell : cells) {
Map<String, Object> newRow = new HashMap<>();
for (Map.Entry<String, Object> entry : cell.entrySet()) {
if(entry.getKey().endsWith("_sorting")) {
break;
}
String fieldCaptionWithoutBrackets = fieldCaptionMap.get(entry.getKey())
.replace("[", "_")
.replace("]", "_")

5
src/de/superx/bianalysis/metadata/MetadataImporter.java

@ -51,7 +51,7 @@ import de.superx.util.PathAndFileUtils; @@ -51,7 +51,7 @@ import de.superx.util.PathAndFileUtils;
* dimension.
*
* To learn more about the metadata concept for the BIAnalysis Tool see:
* doc\bi_analysis\metadaten.adoc
* doc\bi_analysis\report_wizard\metadaten.adoc
*
*/
public final class MetadataImporter {
@ -405,7 +405,7 @@ public final class MetadataImporter { @@ -405,7 +405,7 @@ public final class MetadataImporter {
}
public void updateMetadataForH2Database(DataSource dataSource) throws Exception {
String metaFilesDir = String.join(File.separator, new String[] {"test", "resources", "db", "fixtures", "bianalysis", "metadata"});
String metaFilesDir = String.join(File.separator, new String[] {"test", "resources", "db", "fixtures", "reportwizard", "metadata"});
deserializeMetadataFromJsonFiles(metaFilesDir);
JdbcTemplate jt = new JdbcTemplate(dataSource);
String upserts = String.join("\n", getAllUpsertStrings(false).toString());
@ -471,7 +471,6 @@ public final class MetadataImporter { @@ -471,7 +471,6 @@ public final class MetadataImporter {
attribute.setCaption(rs.getString("caption"));
attribute.setDimColumn(rs.getString("dim_column"));
attribute.setDefaultRelease(DbMetaAdapter.getCustomDefaultReleaseForCurrentDate());
attribute.setPosition(Integer.valueOf(numAttributes));
// create a new 'on the fly' identifier for the new metadata
// attribute

10
src/de/superx/bianalysis/metadata/models/json/MetaDimension.java

@ -140,10 +140,11 @@ public class MetaDimension extends MetaObject { @@ -140,10 +140,11 @@ public class MetaDimension extends MetaObject {
builder = builder.withStringCol("is_historical", String.valueOf(conformedDimension.isHistorical));
}
//if(this.isRestricted) {
//} else {
// builder = builder.withStringCol("is_restricted", String.valueOf(conformedDimension.isRestricted));
//}
if(this.isRestricted) {
builder = builder.withStringCol("is_restricted", String.valueOf(isRestricted));
} else {
builder = builder.withStringCol("is_restricted", String.valueOf(conformedDimension.isRestricted));
}
if(this.dimension != null && !this.dimension.isBlank()) {
builder = builder.withStringCol("tablename", this.dimension);
@ -152,7 +153,6 @@ public class MetaDimension extends MetaObject { @@ -152,7 +153,6 @@ public class MetaDimension extends MetaObject {
}
builder = builder.withStringCol("is_restricted", String.valueOf(isRestricted));
builder = builder.withIdCol("conformed", this.conformedDimension.id);
}

2
src/de/superx/bianalysis/metadata/models/json/MetaDimensionAttribute.java

@ -140,7 +140,7 @@ public class MetaDimensionAttribute extends MetaObject { @@ -140,7 +140,7 @@ public class MetaDimensionAttribute extends MetaObject {
String newColumnName = this.caption.toLowerCase()
.replaceAll("\\s+", "_")
.replaceAll("[^a-z0-9-]", "_");
builder = builder.withStringCol("columnname", "custom_" + newColumnName);
builder = builder.withStringCol("columnname", "ind_" + newColumnName);
}
if(confDimAttrRef != null && confDimAttrRef.hierarchicalFilter) {

3
src/de/superx/bianalysis/models/RightParam.java

@ -4,8 +4,7 @@ public enum RightParam { @@ -4,8 +4,7 @@ public enum RightParam {
TOPIC_AREA("bianalysis.topic_area"),
TOPIC("bianalysis.topic"),
ORGUNIT("bianalysis.orgunit"),
PROJECT("bianalysis.project");
ORGUNIT("bianalysis.orgunit");
private String string;

4
src/de/superx/bianalysis/repository/dto/AttributeDto.java

@ -52,7 +52,7 @@ public class AttributeDto extends EntityBase { @@ -52,7 +52,7 @@ public class AttributeDto extends EntityBase {
@Column(value = "sort_order_column")
public String sortOrderColumn;
@DynamicFieldType(label="Filter-Auswahl", readOnly = false, visibleInSimplifiedForm = false)
@DynamicFieldType(label="Filter-Auswahl", readOnly = true, visibleInSimplifiedForm = false)
@Column(value = "filter_selection")
public String filterSelection;
@ -147,7 +147,7 @@ public class AttributeDto extends EntityBase { @@ -147,7 +147,7 @@ public class AttributeDto extends EntityBase {
String newColumnName = caption.toLowerCase()
.replaceAll("\\s+", "_")
.replaceAll("[^a-z0-9]", "_");
return "custom_" + newColumnName;
return "ind_" + newColumnName;
}

21
src/de/superx/bianalysis/rest/BiAnalysisAdminApi.java

@ -16,10 +16,8 @@ import de.superx.bianalysis.models.Dimension; @@ -16,10 +16,8 @@ import de.superx.bianalysis.models.Dimension;
import de.superx.bianalysis.models.FactTable;
import de.superx.bianalysis.models.Measure;
import de.superx.bianalysis.repository.dto.AttributeDto;
import de.superx.bianalysis.repository.dto.DimensionDto;
import de.superx.bianalysis.service.BiAnalysisRightService;
import de.superx.bianalysis.service.DbMetaAdapter;
import de.superx.bianalysis.service.IllegalMetaDataOperation;
import de.superx.common.NotYetImplementedException;
import de.superx.rest.RestControllerBase;
@ -57,7 +55,7 @@ public class BiAnalysisAdminApi extends RestControllerBase { @@ -57,7 +55,7 @@ public class BiAnalysisAdminApi extends RestControllerBase {
}
@RequestMapping(method = RequestMethod.PUT, path = "/attribute-group")
public Identifier saveAttributeGroup(@RequestBody final AttributeDto attributeGroup, @RequestParam(value = "dimension_id") Identifier dimensionId) throws IllegalMetaDataOperation {
public Identifier saveAttributeGroup(@RequestBody final AttributeDto attributeGroup, @RequestParam(value = "dimension_id") Identifier dimensionId) {
if(attributeGroup.id != null && attributeGroup.id.composedId != null && !attributeGroup.id.composedId.isBlank()) {
dbAdapter.deleteColumnForAttributeGroup(attributeGroup.id, dimensionId);
}
@ -77,11 +75,6 @@ public class BiAnalysisAdminApi extends RestControllerBase { @@ -77,11 +75,6 @@ public class BiAnalysisAdminApi extends RestControllerBase {
return savedAttribute.id;
}
@RequestMapping(method = RequestMethod.PUT, path = "/dimension")
public DimensionDto saveDimension(@RequestBody final DimensionDto dimension) {
return dbAdapter.saveExistingDimensionAndOverrideFields(dimension);
}
@RequestMapping(method = RequestMethod.PUT, path = "/attribute")
public AttributeDto saveAttribute(@RequestBody final AttributeDto attribute, @RequestParam(value = "dimension_id") Identifier dimensionId) {
attribute.isCustom = Boolean.TRUE;
@ -94,7 +87,7 @@ public class BiAnalysisAdminApi extends RestControllerBase { @@ -94,7 +87,7 @@ public class BiAnalysisAdminApi extends RestControllerBase {
}
@RequestMapping(method = RequestMethod.DELETE, path = "/attribute-group")
public void deleteAttributeGroup(@RequestParam(value = "attribute_group_id") Identifier attributeGroupId) throws IllegalMetaDataOperation {
public void deleteAttributeGroup(@RequestParam(value = "attribute_group_id") Identifier attributeGroupId) {
dbAdapter.deleteAttributeGroupById(attributeGroupId);
}
@ -114,22 +107,20 @@ public class BiAnalysisAdminApi extends RestControllerBase { @@ -114,22 +107,20 @@ public class BiAnalysisAdminApi extends RestControllerBase {
@RequestMapping(method = RequestMethod.GET, path = "/filterDimensionAttributeValues")
public List<DimensionAttributeLabel> listAttributeValues (@RequestParam(value = "attribute_id") Identifier id,
@RequestParam(value = "q") String q,
// l is not used because its fix to 100 values in sys component
@RequestParam(value = "l") int limit,
@RequestParam(value = "count") int count) {
return dbAdapter.getFilteredValuesForAttribute(id, q, count)
@RequestParam(value = "l") int length) {
return dbAdapter.getFilteredValuesForAttribute(id, q, length)
.stream()
.map(DimensionAttributeLabel::new)
.toList();
}
@RequestMapping(method = RequestMethod.PUT, path = "/hideAttribute")
public void hideAttribute (@RequestBody final Boolean hide, @RequestParam(value = "attribute_id") Identifier id) throws IllegalMetaDataOperation {
public void hideAttribute (@RequestBody final Boolean hide, @RequestParam(value = "attribute_id") Identifier id) {
dbAdapter.hideAttributeById(id, hide);
}
@RequestMapping(method = RequestMethod.PUT, path = "/hideDimension")
public void hideDimension (@RequestBody final Boolean hide, @RequestParam(value = "dimension_id") Identifier id) throws IllegalMetaDataOperation {
public void hideDimension (@RequestBody final Boolean hide, @RequestParam(value = "dimension_id") Identifier id) {
dbAdapter.hideDimensionById(id, hide);
}

38
src/de/superx/bianalysis/rest/BiAnalysisApi.java

@ -40,7 +40,7 @@ import de.superx.rest.model.ResultType; @@ -40,7 +40,7 @@ import de.superx.rest.model.ResultType;
import de.superx.rest.model.Row;
@RestController
@RequestMapping("/api/bianalysis")
@RequestMapping("/api/reportwizard")
public class BiAnalysisApi extends RestControllerBase {
/* Autor: Robin Wübbeling
@ -110,7 +110,7 @@ public class BiAnalysisApi extends RestControllerBase { @@ -110,7 +110,7 @@ public class BiAnalysisApi extends RestControllerBase {
@RequestParam(value = "title") Optional<String> title,
@RequestParam(value = "sach") Optional<Integer> sach,
@RequestParam(value = "facts") Optional<List<String>> facts) {
rightsService.checkCreateOrViewRights();
rightsService.checkCreateRights();
return dbAdapter.findStoredReports(title, sach, facts);
}
@ -119,7 +119,7 @@ public class BiAnalysisApi extends RestControllerBase { @@ -119,7 +119,7 @@ public class BiAnalysisApi extends RestControllerBase {
Optional<StoredReport> storedReportOpt = dbAdapter.findById(id);
if(storedReportOpt.isPresent()) {
StoredReport storedReport = storedReportOpt.get();
checkCreateOrViewRightForLeadingFactTable(storedReport.reportDefinition.factTableId);
checkCreateOrViewRightForFactTables(storedReport.reportDefinition.factTableIds);
try {
storedReport.exportedResult = biAnalysisManager.createResult(storedReport.reportDefinition, dbAdapter);
} catch (Exception e) {
@ -134,22 +134,22 @@ public class BiAnalysisApi extends RestControllerBase { @@ -134,22 +134,22 @@ public class BiAnalysisApi extends RestControllerBase {
@RequestMapping(method = RequestMethod.POST, path = "/report")
public Result getReport(@RequestBody final ReportDefinition reportDefinition) throws Exception {
Identifier factTableId = reportDefinition.factTableId;
checkCreateRightForLeadingFactTable(factTableId);
List<Identifier> factTableIds = reportDefinition.factTableIds;
checkCreateRightForFactTables(factTableIds);
return biAnalysisManager.createResult(reportDefinition, dbAdapter);
}
@RequestMapping(method = RequestMethod.POST, path = "/persistReportDefinition")
public int persistReportDefinition(@RequestBody final StoredReport storedReport) throws Exception {
StoredReport.setReportDefinitionJson(storedReport);
Identifier factTableId = (Identifier) storedReport.reportDefinition.factTableId;
checkCreateRightForLeadingFactTable(factTableId);
List<Identifier> factTableIds = storedReport.reportDefinition.factTableIds;
checkCreateRightForFactTables(factTableIds);
return dbAdapter.saveReportDefinition(storedReport);
}
@RequestMapping(value = "/report/download", method = RequestMethod.POST)
public Download getFile(@RequestBody final StoredReport storedReport) throws Exception {
checkCreateOrViewRightForLeadingFactTable(storedReport.reportDefinition.factTableId);
checkCreateOrViewRightForFactTables(storedReport.reportDefinition.factTableIds);
Date date = new Date();
String fileName = "BI-Analyse_";
if(storedReport.id != 0) {
@ -185,7 +185,7 @@ public class BiAnalysisApi extends RestControllerBase { @@ -185,7 +185,7 @@ public class BiAnalysisApi extends RestControllerBase {
if(reportOpt.isEmpty()) {
throw new Exception("FEHLER: Berichtskonfiguration konnte nicht gefunden werden.");
}
checkCreateRightForLeadingFactTable(reportOpt.get().reportDefinition.factTableId);
checkCreateRightForFactTables(reportOpt.get().reportDefinition.factTableIds);
rightsService.checkDeleteRights();
dbAdapter.deleteById(id);
return true;
@ -211,16 +211,20 @@ public class BiAnalysisApi extends RestControllerBase { @@ -211,16 +211,20 @@ public class BiAnalysisApi extends RestControllerBase {
return storedReports;
}
private void checkCreateOrViewRightForLeadingFactTable(Identifier factTableId) {
int sachgebiet = dbAdapter.getSachgebietByFactTableId(factTableId.composedId);
rightsService.checkSachgebiet(sachgebiet, Right.CREATE_ANALYSIS, Right.VIEW_REPORT);
rightsService.checkFactTable(factTableId, Right.CREATE_ANALYSIS, Right.VIEW_REPORT);
private void checkCreateOrViewRightForFactTables(List<Identifier> factTableIds) {
for (Identifier factId : factTableIds) {
int sachgebiet = dbAdapter.getSachgebietByFactTableId(factId.composedId);
rightsService.checkSachgebiet(sachgebiet, Right.CREATE_ANALYSIS, Right.VIEW_REPORT);
rightsService.checkFactTable(factId, Right.CREATE_ANALYSIS, Right.VIEW_REPORT);
}
}
private void checkCreateRightForLeadingFactTable(Identifier factTableId) {
int sachgebiet = dbAdapter.getSachgebietByFactTableId(factTableId.composedId);
rightsService.checkSachgebiet(sachgebiet, Right.CREATE_ANALYSIS);
rightsService.checkFactTable(factTableId, Right.CREATE_ANALYSIS);
private void checkCreateRightForFactTables(List<Identifier> factTableIds) {
for (Identifier factId : factTableIds) {
int sachgebiet = dbAdapter.getSachgebietByFactTableId(factId.composedId);
rightsService.checkSachgebiet(sachgebiet, Right.CREATE_ANALYSIS);
rightsService.checkFactTable(factId, Right.CREATE_ANALYSIS);
}
}

19
src/de/superx/bianalysis/service/BiAnalysisManager.java

@ -52,13 +52,13 @@ public class BiAnalysisManager { @@ -52,13 +52,13 @@ public class BiAnalysisManager {
public Result createResult(ReportDefinition reportDefinition, DbMetaAdapter dbAdapter) throws Exception {
if(reportDefinition.leftDimensionAttributeIds.isEmpty()) {
throw new IllegalMetaDataOperation("Bei einer BI-Analyse muss mindestens ein Zeilenattribut angegeben werden.");
if(reportDefinition.factTableIds.size() > 1) {
throw new IllegalArgumentException("Only one facttable per report definition allowed.");
}
try {
biAnalysisRightService.checkCreateOrViewRights();
ReportMetadata metadata = new ReportMetadata(reportDefinition, reportDefinition.factTableId, dbAdapter);
ReportMetadata metadata = new ReportMetadata(reportDefinition, reportDefinition.factTableIds.get(0), dbAdapter);
checkColLimit(reportDefinition, dbAdapter, metadata);
Result result = getReportData(metadata, dbAdapter, reportDefinition);
return result;
@ -80,8 +80,8 @@ public class BiAnalysisManager { @@ -80,8 +80,8 @@ public class BiAnalysisManager {
}
}
public Result getReportData(ReportMetadata metadata, DbMetaAdapter dbAdapter, ReportDefinition definition) throws Exception {
String reportSql = getSqlStatement(metadata);
private Result getReportData(ReportMetadata metadata, DbMetaAdapter dbAdapter, ReportDefinition definition) throws Exception {
String reportSql = getSqlStatement(definition, dbAdapter);
String totalsColumnSql = getTotalsColumnSqlStatement(definition, dbAdapter);
List<Item> sqlStatements = new ArrayList<>();
@ -138,11 +138,11 @@ public class BiAnalysisManager { @@ -138,11 +138,11 @@ public class BiAnalysisManager {
return "";
}
String sql = "";
ReportMetadata metadata = new ReportMetadata(definition, definition.factTableId, dbAdapter);
ReportMetadata metadata = new ReportMetadata(definition, definition.factTableIds.get(0), dbAdapter);
if(metadata.hasOuter()) {
ReportDefinition definitionTotals = definition.toTotalsDefinition();
metadata = new ReportMetadata(definitionTotals, definitionTotals.factTableId, dbAdapter);
metadata = new ReportMetadata(definitionTotals, definitionTotals.factTableIds.get(0), dbAdapter);
HashMap<String, ReportMetadata> metadatas = metadata.buildReportMetadatasForDrillAcross();
SQLGeneratorDrillAcross sqlGeneratorDrillAcross
@ -155,14 +155,15 @@ public class BiAnalysisManager { @@ -155,14 +155,15 @@ public class BiAnalysisManager {
return sql;
}
public static String getSqlStatement(ReportMetadata metadata) {
public static String getSqlStatement(ReportDefinition definition, DbMetaAdapter dbAdapter) {
String sql = "";
ReportMetadata metadata = definition.getReportMetadata(dbAdapter, definition.factTableIds.get(0));
if(metadata.hasOuter()) {
HashMap<String, ReportMetadata> metadatas = metadata.buildReportMetadatasForDrillAcross();
SQLGeneratorDrillAcross sqlGeneratorDrillAcross
= new SQLGeneratorDrillAcross(metadata.factTable.getId(), metadatas);
= new SQLGeneratorDrillAcross(definition.factTableIds.get(0), metadatas);
sql = sqlGeneratorDrillAcross.buildFormattedSqlStatement();
} else {

160
src/de/superx/bianalysis/service/DbMetaAdapter.java

@ -146,7 +146,6 @@ public class DbMetaAdapter implements InitializingBean { @@ -146,7 +146,6 @@ public class DbMetaAdapter implements InitializingBean {
fact.setSachgebiet(getSachgebietForFacttable(f.sachgebiettid.intValue()));
return fact;
})
.filter(f -> getSachgebietForFacttable(f.getSachgebiettid()).tid.intValue() != -1)
.collect(Collectors.toList());
return factTables;
} catch (Exception e) {
@ -165,11 +164,10 @@ public class DbMetaAdapter implements InitializingBean { @@ -165,11 +164,10 @@ public class DbMetaAdapter implements InitializingBean {
SxUser user = (SxUser)userService.currentUserDetails();
String userName = user.getName();
String roleName = user.getRole();
int orgunitLid = user.getHisInOneOrgUnitLidOfRole().intValue();
SqlAccessRestrictor accessRestrictor = new SqlAccessRestrictor(this.sqlRestrictionProvider, userName, roleName, orgunitLid);
int orgunitLid = user.getHisInOneOrgUnitLidOfRole().intValue(); SqlAccessRestrictor accessRestrictor = new SqlAccessRestrictor(this.sqlRestrictionProvider, userName, roleName, orgunitLid);
sql = accessRestrictor.restrictSql(sql);
} catch (Exception e) {
throw new RuntimeException(e);
e.printStackTrace();
}
}
return sql;
@ -452,12 +450,9 @@ public class DbMetaAdapter implements InitializingBean { @@ -452,12 +450,9 @@ public class DbMetaAdapter implements InitializingBean {
return attr;
}
public Sachgebiet getSachgebietById(int sachgebietId) {
return sachgebieterepository
.findById(Integer.valueOf(sachgebietId))
.orElseThrow(() ->
new IllegalArgumentException("Sachgebiet with ID " + sachgebietId + " was not found."));
}
public Sachgebiet getSachgebietById(int sachgebietId) {
return this.sachgebieterepository.findById(Integer.valueOf(sachgebietId)).get();
}
public int saveReportDefinition(StoredReport report) {
StoredReport savedStoredReport = this.storedReportRepository.save(report);
@ -486,7 +481,7 @@ public class DbMetaAdapter implements InitializingBean { @@ -486,7 +481,7 @@ public class DbMetaAdapter implements InitializingBean {
Optional<Systeminfo> systeminfoOpt = systeminfoRepository.findById(Integer.valueOf(tid));
if(systeminfoOpt.isPresent()) {
Date lastUpdate = systeminfoOpt.get().datum;
return new SimpleDateFormat("dd.MM.yyyy HH:mm:ss").format(lastUpdate);
return new SimpleDateFormat("dd.MM.yyyy hh:mm").format(lastUpdate);
}
return "Unknown";
}
@ -503,9 +498,12 @@ public class DbMetaAdapter implements InitializingBean { @@ -503,9 +498,12 @@ public class DbMetaAdapter implements InitializingBean {
return -1;
}
public Integer getSachgebietForReport(ReportDefinition reportDefinition) {
int sachgebiet = getSachgebietByFactTableId(reportDefinition.factTableId.composedId);
Integer result = Integer.valueOf(sachgebiet);
public List<Integer> getSachgebieteForReport(ReportDefinition reportDefinition) {
List<Integer> result = new ArrayList<>();
for (Identifier factTableId : reportDefinition.factTableIds) {
int sachgebiet = getSachgebietByFactTableId(factTableId.composedId);
result.add(Integer.valueOf(sachgebiet));
}
return result;
}
@ -725,6 +723,7 @@ public class DbMetaAdapter implements InitializingBean { @@ -725,6 +723,7 @@ public class DbMetaAdapter implements InitializingBean {
dto.defaultRelease = result.getString("default_release");
dto.isCustom = Boolean.valueOf(result.getBoolean("is_custom"));
dto.position = Integer.valueOf(result.getInt("position"));
dto.criteriaGroups = (ArrayNode) result.getObject("criteria_groups");
String jsonString = result.getString("criteria_groups");
ArrayNode arrayNode = null;
if (jsonString != null && !jsonString.trim().isEmpty()) {
@ -825,11 +824,10 @@ public class DbMetaAdapter implements InitializingBean { @@ -825,11 +824,10 @@ public class DbMetaAdapter implements InitializingBean {
for (String table : allTableNames) {
String tableName = "presentation." + table;
String columnType = (isSortOrder) ? "integer" : "text";
String columnSql = String.format("""
ALTER TABLE %s DROP COLUMN IF EXISTS %s;
ALTER TABLE %s ADD COLUMN %s %s;
""", tableName, columnname, tableName, columnname, columnType);
ALTER TABLE %s ADD COLUMN %s text;
""", tableName, columnname, tableName, columnname);
List<CriteriaGroup> groups = attribute.readCriteriaGroups();
SqlQuery query = AttributeGroupQueryBuilder.buildColumnQuery(groups, tableName, isSortOrder, columnname);
@ -1001,19 +999,6 @@ public class DbMetaAdapter implements InitializingBean { @@ -1001,19 +999,6 @@ public class DbMetaAdapter implements InitializingBean {
return updatedOrCreatedAttribute;
}
public DimensionDto saveExistingDimensionAndOverrideFields(DimensionDto dimension) {
DimensionDto dimensionDto = this.dimensionRepository.findByIdOrThrow(dimension.id);
dimensionDto.caption = dimension.caption;
dimensionDto.description = dimension.description;
dimensionDto.defaultRelease = dimension.defaultRelease;
dimensionDto.isCustom = Boolean.valueOf(true);
return this.dimensionRepository.save(dimensionDto);
}
public DimensionDto saveDimension(DimensionDto dimension) {
return this.dimensionRepository.save(dimension);
}
public AttributeDto saveAttribute(AttributeDto attribute) {
return this.dimensionAttrRepo.save(attribute);
}
@ -1036,11 +1021,8 @@ public class DbMetaAdapter implements InitializingBean { @@ -1036,11 +1021,8 @@ public class DbMetaAdapter implements InitializingBean {
return this.dimensionAttrRepo.save(newAttrDto);
}
public void deleteAttributeGroupById(Identifier attributeId) throws IllegalMetaDataOperation {
public void deleteAttributeGroupById(Identifier attributeId) {
AttributeDto attribute = this.dimensionAttrRepo.findByIdOrThrow(attributeId);
failIfIdIsUsedInReport(attributeId, attribute.caption);
deleteColumnForAttributeGroup(attributeId, attribute.dimensionId);
if(attribute.attrConformedId != null) {
@ -1067,12 +1049,12 @@ public class DbMetaAdapter implements InitializingBean { @@ -1067,12 +1049,12 @@ public class DbMetaAdapter implements InitializingBean {
for (String table : allTableNames) {
String sql = """
ALTER TABLE %s.%s
ALTER TABLE presentation.%s
DROP COLUMN IF EXISTS %s;
ALTER TABLE %s.%s
ALTER TABLE presentation.%s
DROP COLUMN IF EXISTS %s_sort_order;
""".formatted("presentation", table, columnname, "presentation", table, columnname);
""".formatted(table, columnname, table, columnname);
jt.update(sql);
}
@ -1096,7 +1078,7 @@ public class DbMetaAdapter implements InitializingBean { @@ -1096,7 +1078,7 @@ public class DbMetaAdapter implements InitializingBean {
"array_position(array[%s], d.%s::text) " +
"FROM presentation.%s d " +
"WHERE d.%s IS NOT NULL " +
"AND d.%s ILIKE ? " +
"AND d.%s LIKE ? " +
// TODO: sometimes we need DESC
"ORDER BY 3, 2 ASC ";
@ -1138,13 +1120,8 @@ public class DbMetaAdapter implements InitializingBean { @@ -1138,13 +1120,8 @@ public class DbMetaAdapter implements InitializingBean {
return result;
}
public void hideAttributeById(Identifier id, Boolean hide) throws IllegalMetaDataOperation {
public void hideAttributeById(Identifier id, Boolean hide) {
AttributeDto attributeDto = this.dimensionAttrRepo.findByIdOrThrow(id);
if(hide.booleanValue()) {
failIfIdIsUsedInReport(id, attributeDto.caption);
}
if(!attributeDto.isCustom.booleanValue()) {
//attributeDto.id = getNewCustomIdentifier(dataSource);
attributeDto.isCustom = Boolean.valueOf(true);
@ -1154,79 +1131,16 @@ public class DbMetaAdapter implements InitializingBean { @@ -1154,79 +1131,16 @@ public class DbMetaAdapter implements InitializingBean {
}
public void hideDimensionById(Identifier id, Boolean hide) {
DimensionDto dimensionDto = this.dimensionRepository.findByIdOrThrow(id);
if(!dimensionDto.isCustom.booleanValue()) {
//dimensionDto.id = getNewCustomIdentifier(dataSource);
dimensionDto.isCustom = Boolean.valueOf(true);
}
dimensionDto.isHidden = hide;
this.dimensionRepository.save(dimensionDto);
public void failIfIdIsUsedInReport(Identifier id, String caption) throws IllegalMetaDataOperation {
String sql = """
WITH report_definitions AS (
SELECT
rd.name AS report_name,
rd.definition::jsonb AS definition
FROM metadata.rw_report_definitions rd
),
direct_ids AS (
SELECT r.report_name, jsonb_array_elements_text(COALESCE(r.definition->'factTableIds', '[]'::jsonb)) AS used_id FROM report_definitions r UNION
SELECT r.report_name, jsonb_array_elements_text(COALESCE(r.definition->'leftDimensionAttributeIds', '[]'::jsonb)) AS used_id FROM report_definitions r UNION
SELECT r.report_name, jsonb_array_elements_text(COALESCE(r.definition->'topDimensionAttributeIds', '[]'::jsonb)) AS used_id FROM report_definitions r UNION
SELECT r.report_name, jsonb_array_elements_text(COALESCE(r.definition->'measureIds', '[]'::jsonb)) AS used_id FROM report_definitions r UNION
SELECT r.report_name, filter_obj->>'dimensionAttributeId' AS used_id
FROM report_definitions r
CROSS JOIN LATERAL jsonb_array_elements(COALESCE(r.definition->'filters', '[]'::jsonb)) AS filter_obj
),
used_attribute_ids AS (
SELECT r.report_name, jsonb_array_elements_text(COALESCE(r.definition->'leftDimensionAttributeIds', '[]'::jsonb)) AS attribute_id FROM report_definitions r UNION
SELECT r.report_name, jsonb_array_elements_text(COALESCE(r.definition->'topDimensionAttributeIds', '[]'::jsonb)) AS attribute_id FROM report_definitions r UNION
SELECT r.report_name, filter_obj->>'dimensionAttributeId' AS attribute_id FROM report_definitions r CROSS JOIN LATERAL jsonb_array_elements(COALESCE(r.definition->'filters', '[]'::jsonb)) AS filter_obj
),
indirect_dimension_ids AS (
SELECT DISTINCT
u.report_name,
da.dimension_id::text AS used_id
FROM used_attribute_ids u
JOIN metadata.dimension_attribute da
ON da.id::text = u.attribute_id
),
all_used_ids AS (
SELECT report_name, used_id
FROM direct_ids
UNION
SELECT report_name, used_id
FROM indirect_dimension_ids
)
SELECT report_name
FROM all_used_ids
WHERE used_id = ?
LIMIT 1
""";
String reportName = new JdbcTemplate(dataSource).query(
sql,
rs -> rs.next() ? rs.getString("report_name") : null,
id.toString()
);
if (reportName != null) {
throw new IllegalMetaDataOperation(
"Das Objekt '" + caption + "' (" + id + ") kann nicht gelöscht werden, "
+ "weil es noch im Bericht '" + reportName + "' verwendet wird."
);
}
}
public void hideDimensionById(Identifier id, Boolean hide) throws IllegalMetaDataOperation {
DimensionDto dimensionDto = this.dimensionRepository.findByIdOrThrow(id);
if(hide.booleanValue()) {
failIfIdIsUsedInReport(id, dimensionDto.caption);
}
if(!dimensionDto.isCustom.booleanValue()) {
//dimensionDto.id = getNewCustomIdentifier(dataSource);
dimensionDto.isCustom = Boolean.valueOf(true);
}
dimensionDto.isHidden = hide;
this.dimensionRepository.save(dimensionDto);
}
}
public List<StoredReport> findStoredReports(Optional<String> title, Optional<Integer> sach, Optional<List<String>> facts) {
@ -1234,9 +1148,10 @@ public class DbMetaAdapter implements InitializingBean { @@ -1234,9 +1148,10 @@ public class DbMetaAdapter implements InitializingBean {
WITH reports_with_facts AS (
SELECT
rd.*
,(rd.definition::jsonb ->> 'factTableId') AS fact_id
,factid #>> '{}' AS fact_id
FROM
metadata.rw_report_definitions rd
metadata.rw_report_definitions rd,
jsonb_array_elements(rd.definition::jsonb -> 'factTableIds') AS factid
)
SELECT rf.*, ft.sachgebiettid FROM reports_with_facts rf
INNER JOIN metadata.facttable ft ON ft.id = fact_id
@ -1551,14 +1466,5 @@ public class DbMetaAdapter implements InitializingBean { @@ -1551,14 +1466,5 @@ public class DbMetaAdapter implements InitializingBean {
return result;
}
public void deleteAttributeDto(AttributeDto attribute) throws IllegalMetaDataOperation {
failIfIdIsUsedInReport(attribute.id, attribute.caption);
this.dimensionAttrRepo.deleteById(attribute.id);
}
public void deleteDimensionDto(DimensionDto dimension) throws IllegalMetaDataOperation {
failIfIdIsUsedInReport(dimension.id, dimension.caption);
this.dimensionAttrRepo.deleteById(dimension.id);
}
}

11
src/de/superx/bianalysis/service/IllegalMetaDataOperation.java

@ -1,11 +0,0 @@ @@ -1,11 +0,0 @@
package de.superx.bianalysis.service;
public class IllegalMetaDataOperation extends Exception {
private static final long serialVersionUID = 1L;
public IllegalMetaDataOperation(String message) {
super(message);
}
}

2
src/de/superx/bianalysis/service/JasperReportExport.java

@ -39,7 +39,7 @@ public class JasperReportExport { @@ -39,7 +39,7 @@ public class JasperReportExport {
@Autowired
SxCaptionsRepository sxCaptionsRepository;
public static String DATASOURCE_URL = "/api/bianalysis/template/datasource/storedReport?";
public static String DATASOURCE_URL = "/api/reportwizard/template/datasource/storedReport?";
private static String DATA_ADAPTER_JSON_FILE = "JsonDataAdapter.jrdax";
public static String DATA_ADAPTER_REST = "JsonDataAdapterRest.jrdax";
public static String MAIN_REPORT = "MainReport.jrxml";

212
src/de/superx/bianalysis/service/UserRestrictionService.java

@ -55,37 +55,6 @@ public class UserRestrictionService { @@ -55,37 +55,6 @@ public class UserRestrictionService {
this.user = user;
this.dataSource = dataSource;
this.jt = new JdbcTemplate(dataSource);
ensureAuthDbStructures();
}
private void ensureAuthDbStructures() {
/* 'view_rechte' wird angepasst:
* Einschränkungen werden als Listen von IDs von Dimensionseinträgen gespeichert.
* Einschränkungen von Faktentabellen ergeben sich daraus anhand der Metadaten. */
jt.execute("DROP TABLE IF EXISTS view_rechte;");
String[] sqlCols = new String[] {
"user_id integer NOT NULL",
"user_name character varying(255)",
"role_name character varying(255)",
"orgunit_lid integer",
"schema_name character varying(255)",
"table_name character varying(255)",
"join_column character varying(255)",
"granted_ids text",
"external_entry smallint DEFAULT 0",
"is_restricted BOOLEAN DEFAULT FALSE",
"is_custom BOOLEAN DEFAULT FALSE"
};
String sqlCreate = "CREATE TABLE IF NOT EXISTS public.user_dimension_grants (\n" + String.join(",\n", sqlCols) + "\n);";
jt.execute(sqlCreate);
for (String colDef : sqlCols) {
String sql = "ALTER TABLE public.user_dimension_grants ADD COLUMN IF NOT EXISTS " + colDef + ";";
jt.execute(sql);
}
}
public void applyMetadataRestrictions() {
@ -97,46 +66,18 @@ public class UserRestrictionService { @@ -97,46 +66,18 @@ public class UserRestrictionService {
for (String tableName : tables) {
String resultSql = buildMetadataTableRestriction(tableName);
if(!resultSql.isBlank()) {
insertRightsViewRestriction(dataSource, "metadata", tableName , "id", resultSql, user.getHisInOneOrgUnitLidOfRole().intValue());
}
insertRightsViewRestriction(dataSource, "metadata", tableName , resultSql,
user.getHisInOneOrgUnitLidOfRole().intValue());
}
}
public String buildOrgunitGrantedIds(Integer userOrgunitLid) {
List<Integer> allowedOrgunitLids = new ArrayList<>();
allowedOrgunitLids.add(userOrgunitLid);
allowedOrgunitLids.add(Integer.valueOf(-1));
List<String> params = getRightParamValues
(RightParam.ORGUNIT, Right.CREATE_ANALYSIS, Right.VIEW_REPORT);
if(!params.isEmpty()) {
for (String lid : params) {
allowedOrgunitLids.add(Integer.valueOf(lid));
}
}
Set<Integer> allowedOrgunitsWithChildren = new HashSet<>();
for (Integer lid : allowedOrgunitLids) {
allowedOrgunitsWithChildren.add(lid);
allowedOrgunitsWithChildren.addAll(fetchOrgunitChildren(lid));
}
String lidsAsString = allowedOrgunitsWithChildren.stream()
.map(String::valueOf)
.collect(Collectors.joining(","));
return lidsAsString;
}
public void applyOrgunitRestrictions() {
public void applyOrgunitRestrictions(Integer userOrgunitLid) {
String tableJoinColSql = """
SELECT DISTINCT ft.tablename, d.joincolumn
FROM metadata.dimension d
LEFT JOIN metadata.facttable ft ON ft.id = d.facttable_id
WHERE (d.tablename = 'dim_orgunit' OR d.tablename = 'dim_orgunit_flat')
WHERE d.tablename = 'dim_orgunit'
OR d.tablename = 'dim_orgunit_flat'
and d.is_restricted = true
""";
@ -153,10 +94,10 @@ public class UserRestrictionService { @@ -153,10 +94,10 @@ public class UserRestrictionService {
continue;
}
String joinColumn = tableJoinColMap.get(tableName);
String restriction = buildOrgunitRestriction(user.getHisInOneOrgUnitLidOfRole(), joinColumn);
//if(!restriction.isEmpty()) {
insertRightsViewRestriction(dataSource, "presentation", tableName, joinColumn, restriction, user.getHisInOneOrgUnitLidOfRole().intValue());
//}
String restriction = buildOrgunitRestriction(userOrgunitLid, joinColumn);
if(!restriction.isEmpty()) {
insertRightsViewRestriction(dataSource, "presentation", tableName, restriction, userOrgunitLid.intValue());
}
}
}
@ -171,16 +112,9 @@ public class UserRestrictionService { @@ -171,16 +112,9 @@ public class UserRestrictionService {
String baseSql = BASE_QUERIES.get(tableName);
String conditions = "";
conditions = buildConditionsForFacttable(topicAreaIds, topicIds);
resultSql = baseSql
resultSql = "WHERE id IN " + baseSql
+ (!conditions.isEmpty() ? " WHERE " + conditions : "") + ")";
List<Map<String, Object>> rows = jt.queryForList(resultSql);
String result = rows.stream()
.map(row -> row.get("id") != null ? row.get("id").toString() : "")
.filter(s -> !s.isEmpty())
.map( id -> "'" + id + "'")
.collect(Collectors.joining(","));
return result;
return resultSql;
}
return "";
}
@ -211,8 +145,7 @@ public class UserRestrictionService { @@ -211,8 +145,7 @@ public class UserRestrictionService {
String restriction = "";
if(!allowedOrgunitsWithChildren.isEmpty()) {
//restriction = "WHERE " + joinColumn + " IN (" + lidsAsString + ")";
restriction = lidsAsString;
restriction = "WHERE " + joinColumn + " IN (" + lidsAsString + ")";
}
return restriction;
}
@ -289,29 +222,28 @@ public class UserRestrictionService { @@ -289,29 +222,28 @@ public class UserRestrictionService {
return whereClause.toString();
}
public void insertRightsViewRestriction(DataSource ds, String schema, String tableName, String joinColumn, String grantedIds, int orgunitLid) {
private void insertRightsViewRestriction(DataSource ds, String schema, String table, String restriction, int orgunitLid) {
String userName = user.getName();
String roleName = user.getRole();
int userId = user.getUserIdAsInt();
try (Connection conn = ds.getConnection();
Statement stmt = conn.createStatement()) {
String sql = "INSERT INTO public.user_dimension_grants(user_id, user_name, "
+ "role_name, orgunit_lid, schema_name, table_name, join_column, granted_ids, external_entry) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
String sql = "INSERT INTO public.view_rechte(user_id, user_name, "
+ "role_name, orgunit_lid, schema_name, table_name, restriction, external_entry) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
try (PreparedStatement pstmt = conn.prepareStatement(sql)) {
pstmt.setInt(1, userId);
pstmt.setString(2, userName);
pstmt.setString(3, roleName);
pstmt.setInt(4, orgunitLid);
pstmt.setString(5, schema);
pstmt.setString(6, tableName);
pstmt.setString(7, joinColumn);
pstmt.setString(8, grantedIds);
pstmt.setInt(9, 1);
pstmt.setString(6, table);
pstmt.setString(7, restriction);
pstmt.setInt(8, 1);
pstmt.execute();
}
} catch (SQLException e) {
throw new RuntimeException(e);
e.printStackTrace();
}
}
@ -325,7 +257,6 @@ public class UserRestrictionService { @@ -325,7 +257,6 @@ public class UserRestrictionService {
public List<String> getRightParamValues(RightParam param, Right... rights) {
if (user == null) return List.of();
// Recht Rechteparameter, Wert des Rechteparameters
Map<String, Map<String, String>> rightsMap = user.getRightsMap();
Map<String, String> rightParams = findFirstValidRight(rightsMap, rights);
@ -338,24 +269,12 @@ public class UserRestrictionService { @@ -338,24 +269,12 @@ public class UserRestrictionService {
}
private static Map<String, String> findFirstValidRight(Map<String, Map<String, String>> rightsMap, Right... rights) {
List<Right> setRights = Arrays.stream(rights)
.filter(right -> rightsMap.get(right.getString()) != null)
.toList();
if (setRights.size() > 1) {
String setRightsString = setRights.stream()
.map(Right::getString)
.collect(Collectors.joining(", "));
String exceptionMessage = "More than one of the supplied rights is set: " + setRightsString;
throw new IllegalStateException(exceptionMessage);
}
if (setRights.isEmpty()) {
return null;
}
return rightsMap.get(setRights.get(0).getString());
return Arrays.stream(rights)
.map(Right::getString)
.map(rightsMap::get)
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}
public static String quoteAndJoin(List<String> strings) {
@ -367,88 +286,11 @@ public class UserRestrictionService { @@ -367,88 +286,11 @@ public class UserRestrictionService {
public void clearRestrictions() throws SQLException {
try(Connection dbConnection = dataSource.getConnection()){
Statement stmt = dbConnection.createStatement();
String sql = "DELETE FROM public.user_dimension_grants WHERE user_name = '"
String sql = "DELETE FROM public.view_rechte WHERE user_name = '"
+ user.getUsername() + "' AND role_name = '" + user.getRole()
+ "' AND external_entry = 1 AND orgunit_lid = " + user.getHisInOneOrgUnitLidOfRole().intValue();
stmt.execute(sql);
}
}
/*
* Die BI-Dimensionstabellen werden Nutzerbezogen zugriffsbeschränkt.
* Die Beschränkungen ergeben sich idR aus der Rollen-/Rechtekonfiguration in HISinOne.
* Diese Methode "übersetzt" dazwischen:
* Die aus HISinOne ausgelesenen Rechte werden in Listen von Dimensions-IDs pro
* Nutzer/Rolle/Tabelle überführt; das passiert idR beim Login des jeweiligen Nutzers
* (resp. Rollenwechsel) - evtl. kann es später noch weitere Auslöser geben.
* Durchgesetzt werden sie aktuell durch einsetzen der Listen in WHERE-Klauseln,
* die ins generierte SQL eingefügt werden umf die referenzierten DimensionIds in der
* Faktentabelle zu filtern.
* siehe Klasse DbSqlRestrictionProvider */
public void calculateDimensionRestrictions(Integer userPersonId) {
if(userPersonId == null) {
// user is not a h1 user, e.g 'gast'
return;
}
// default ist: meine projekte
//
List<String> params = getRightParamValues
(RightParam.PROJECT, Right.CREATE_ANALYSIS, Right.VIEW_REPORT);
if(params.size() > 1) {
// TODO check if that is even possible using HI1 technology
throw new IllegalArgumentException("Es sollte nur einen Wert für den Rechteparameter 'bianalysis.project' geben.");
}
// 1 - für meine Projekte
// 2 - für alle Projekte
if(!params.isEmpty() && params.get(0).equals("2")) {
// keine restriction für Projekte
return;
}
String tableJoinColSql = """
SELECT DISTINCT ft.tablename, d.joincolumn
FROM metadata.dimension d
LEFT JOIN metadata.facttable ft ON ft.id = d.facttable_id
WHERE d.tablename = 'dim_project'
and d.is_restricted = true
""";
List<Map<String, Object>> rows = jt.queryForList(tableJoinColSql);
Map<String, String> tableJoinColMap = rows.stream()
.collect(Collectors.toMap(
row -> (String) row.get("tablename"),
row -> (String) row.get("joincolumn"),
(existing, replacement) -> existing));
for (String tableName : tableJoinColMap.keySet()) {
if(tableName == null) {
continue;
}
String joinColumn = tableJoinColMap.get(tableName);
String sql = """
SELECT pp.project_id
FROM staging.project_person AS pp
JOIN staging.project_person_position AS ppp
ON pp.id = ppp.project_person_id
JOIN staging.k_project_person_position AS kppp
ON ppp.k_project_person_position_id = kppp.id
WHERE kppp.hiskey_id = 1
AND pp.person_id = ?
""";
List<Integer> grantedProjectIds = jt.queryForList(sql, Integer.class, userPersonId);
String restriction = grantedProjectIds.stream()
.map(String::valueOf)
.collect(Collectors.joining(","));
insertRightsViewRestriction(dataSource, "presentation", tableName, joinColumn, restriction, user.getHisInOneOrgUnitLidOfRole().intValue());
}
}
}

21
src/de/superx/bianalysis/sqlgeneration/SQLGenerator.java

@ -394,21 +394,20 @@ public class SQLGenerator { @@ -394,21 +394,20 @@ public class SQLGenerator {
if(reportMetadata.leftDimensionAttributes == null || reportMetadata.leftDimensionAttributes.size() <= 0) {
return "";
}
List<DimensionAttribute> validAttributes = new ArrayList<>();
for (DimensionAttribute attr : reportMetadata.leftDimensionAttributes) {
if (!attr.isNullAttribute()) {
validAttributes.add(attr);
}
}
StringBuilder statement = new StringBuilder("GROUP BY GROUPING SETS (");
StringJoiner groups = new StringJoiner(", ");
for (int i = 0; i < validAttributes.size(); i++) {
for (int i = 0; i < reportMetadata.leftDimensionAttributes.size(); i++) {
DimensionAttribute a = reportMetadata.leftDimensionAttributes.get(i);
if(a.isNullAttribute()) {
continue;
}
StringJoiner group = new StringJoiner(", ");
for (int j = 0; j < validAttributes.size() - i; j++) {
DimensionAttribute attr = validAttributes.get(j);
for (int j = 0; j < reportMetadata.leftDimensionAttributes.size() - i; j++) {
DimensionAttribute attr = reportMetadata.leftDimensionAttributes.get(j);
if(attr.isNullAttribute()) {
continue;
}
group.add(attr.getDimensionTableAlias() + "." + attr.getColumnname());
if(attr.getSortOrderColumn() != null) {
group.add(attr.getDimensionTableAlias() + "." + attr.getSortOrderColumn());

6
src/de/superx/bin/ComponentAdminCLI.java

@ -32,11 +32,11 @@ import de.superx.rest.model.job.Component; @@ -32,11 +32,11 @@ import de.superx.rest.model.job.Component;
import de.superx.rest.model.job.JobExecutionStatus;
import de.superx.servlet.SuperXManager;
import de.superx.servlet.SxPools;
import de.superx.spring.AppConfig;
import de.superx.spring.HisInOneConfiguration;
import de.superx.spring.batch.His1DataSources;
import de.superx.spring.batch.tasklet.ExecuteSqlScriptTasklet;
import de.superx.spring.cli.config.CLIConfig;
import de.superx.spring.config.BatchConfig;
import de.superx.spring.config.DataJdbcConfiguration;
import de.superx.spring.config.ServiceConfig;
import de.superx.spring.service.BatchJobDescriptionAdapter;
@ -56,7 +56,7 @@ import de.superx.util.PathAndFileUtils; @@ -56,7 +56,7 @@ import de.superx.util.PathAndFileUtils;
* The class is just meant to be a frontend, so it uses the same implementation as the web application
* (which happens to be the "EtlJobApi" bean)
* However, there are to be some things to be considered:
* - The "jobLauncher" bean from the "AppConfig" class works asynchronously, which is fine for the
* - The "jobLauncher" bean from the "BatchConfig" class works asynchronously, which is fine for the
* web application context, but in the CLI context, it is overridden by a synchronous implementation
* (in fact the tweaks to get the configurations ready for the CLI context, are placed in the
* "CLIConfig" class).
@ -409,7 +409,7 @@ public class ComponentAdminCLI { @@ -409,7 +409,7 @@ public class ComponentAdminCLI {
* - so it's alright to override some beans via "CLIConfig"
*/
if (APPLICATION_CONTEXT == null) {
APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(AppConfig.class, DataJdbcConfiguration.class, CLIConfig.class, ServiceConfig.class);
APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(BatchConfig.class, DataJdbcConfiguration.class, CLIConfig.class, ServiceConfig.class);
HisInOneConfiguration.configSuperXDbformsXML("pg", SuperXManager.getWEB_INFPfad() + File.separator + "..");
}
// Override the JobDescriptionSource Bean if the -f flag is passed.

4
src/de/superx/bin/DataProfiler.java

@ -45,9 +45,9 @@ import org.springframework.jdbc.core.RowMapper; @@ -45,9 +45,9 @@ import org.springframework.jdbc.core.RowMapper;
import de.superx.servlet.SuperXManager;
import de.superx.servlet.SxPools;
import de.superx.spring.AppConfig;
import de.superx.spring.batch.His1DataSources;
import de.superx.spring.cli.config.CLIConfig;
import de.superx.spring.config.BatchConfig;
import de.superx.spring.config.DataJdbcConfiguration;
import de.superx.spring.config.ServiceConfig;
@ -591,7 +591,7 @@ public class DataProfiler { @@ -591,7 +591,7 @@ public class DataProfiler {
* - so it's alright to override some beans via "CLIConfig"
*/
if (APPLICATION_CONTEXT == null) {
APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(AppConfig.class, DataJdbcConfiguration.class, CLIConfig.class, ServiceConfig.class);
APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(BatchConfig.class, DataJdbcConfiguration.class, CLIConfig.class, ServiceConfig.class);
}
return APPLICATION_CONTEXT;
}

4
src/de/superx/bin/EtlFuzzer.java

@ -43,9 +43,9 @@ import de.superx.rest.model.job.JobExecutionStatus; @@ -43,9 +43,9 @@ import de.superx.rest.model.job.JobExecutionStatus;
import de.superx.rest.model.job.StepExecutionStatus;
import de.superx.servlet.SuperXManager;
import de.superx.servlet.SxPools;
import de.superx.spring.AppConfig;
import de.superx.spring.batch.His1DataSources;
import de.superx.spring.cli.config.CLIConfig;
import de.superx.spring.config.BatchConfig;
import de.superx.spring.config.DataJdbcConfiguration;
@ -270,7 +270,7 @@ public class EtlFuzzer { @@ -270,7 +270,7 @@ public class EtlFuzzer {
* - so it's alright to override some beans via "CLIConfig"
*/
if (APPLICATION_CONTEXT == null) {
APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(AppConfig.class, DataJdbcConfiguration.class, CLIConfig.class);
APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(BatchConfig.class, DataJdbcConfiguration.class, CLIConfig.class);
}
return APPLICATION_CONTEXT;
}

12
src/de/superx/bin/ExecuteMask.java

@ -4,6 +4,8 @@ import static de.superx.servlet.ServletBasics.getParamChecked; @@ -4,6 +4,8 @@ import static de.superx.servlet.ServletBasics.getParamChecked;
import static de.superx.servlet.SxSQL_Server.DEFAULT_MANDANTEN_ID;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URISyntaxException;
@ -17,9 +19,11 @@ import java.util.LinkedList; @@ -17,9 +19,11 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.StringTokenizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.mock.web.MockHttpServletRequest;
@ -45,12 +49,10 @@ import de.superx.servlet.SuperXManager; @@ -45,12 +49,10 @@ import de.superx.servlet.SuperXManager;
import de.superx.servlet.SxPools;
import de.superx.servlet.UserInitializer;
import de.superx.servlet.XmlTransformer;
import de.superx.spring.AppConfig;
import de.superx.spring.cli.config.CLIConfig;
import de.superx.spring.config.BatchConfig;
import de.superx.spring.config.DataJdbcConfiguration;
import de.superx.spring.config.ServiceConfig;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import net.sf.jasperreports.engine.JRException;
public class ExecuteMask {
@ -534,7 +536,7 @@ public class ExecuteMask { @@ -534,7 +536,7 @@ public class ExecuteMask {
* - so it's alright to override some beans via "CLIConfig"
*/
if (APPLICATION_CONTEXT == null) {
APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(AppConfig.class, DataJdbcConfiguration.class, CLIConfig.class, ServiceConfig.class);
APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(BatchConfig.class, DataJdbcConfiguration.class, CLIConfig.class, ServiceConfig.class);
}
return APPLICATION_CONTEXT;
}

11
src/de/superx/bin/FMParser.java

@ -44,7 +44,7 @@ public class FMParser extends TemplateProcessor { @@ -44,7 +44,7 @@ public class FMParser extends TemplateProcessor {
private Statement stm;
private String isSimpleParser;
private String myParams="";
private static String sqlDialect;
public FMParser(String mandantenId, Connection con) throws SQLException {
@ -65,16 +65,7 @@ public class FMParser extends TemplateProcessor { @@ -65,16 +65,7 @@ public class FMParser extends TemplateProcessor {
outfile = GetOpts.getValue(Options.opt_out);
if (GetOpts.isPresent(Options.opt_simpPars))
isSimpleParser = GetOpts.getValue(Options.opt_simpPars);
if (GetOpts.isPresent(Options.opt_params))
myParams = GetOpts.getValue(Options.opt_params);
String input = StringUtils.readFile(new File(infile));
if(myParams !="")
{
String paramName = "<<" +myParams.substring(0, myParams.indexOf("="))+">>";
String paramVal = "'"+myParams.substring(myParams.indexOf("=") + 1)+"'";
input=de.memtext.util.StringUtils.replace(input, paramName, paramVal);
}
String output = "";
if (isSimpleParser != null && isSimpleParser.equalsIgnoreCase("true")) {
output = simpleParser(dbprop, input);

3
src/de/superx/bin/fm/EtlStarter.java

@ -39,7 +39,6 @@ import org.pentaho.metastore.stores.memory.MemoryMetaStore; @@ -39,7 +39,6 @@ import org.pentaho.metastore.stores.memory.MemoryMetaStore;
import de.superx.servlet.SuperXManager;
import de.superx.servlet.SxPools;
import de.superx.spring.batch.His1DataSources;
import de.superx.spring.service.ApplicationContextHolder;
import freemarker.template.SimpleScalar;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateTransformModel;
@ -196,7 +195,7 @@ public class EtlStarter implements TemplateTransformModel { @@ -196,7 +195,7 @@ public class EtlStarter implements TemplateTransformModel {
* get all db connections configured in databases.xml as
* DataSource's by name
*/
His1DataSources dataSources = ApplicationContextHolder.getBean("dataSources", His1DataSources.class);
His1DataSources dataSources = SuperXManager.getBean("dataSources", His1DataSources.class);
// if no dataSources defined, at least try eduetl
Set<String> dbNames = dataSources != null ? dataSources.getKeys() : Set.of("eduetl");
for (String dbName : dbNames) {

4
src/de/superx/bin/fm/SqlVarsDirective.java

@ -18,7 +18,7 @@ import org.springframework.jdbc.core.ResultSetExtractor; @@ -18,7 +18,7 @@ import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.support.rowset.SqlRowSet;
import de.superx.common.TemplateProcessor.SqlVarType;
import de.superx.spring.service.ApplicationContextHolder;
import de.superx.servlet.SuperXManager;
import freemarker.core.Environment;
import freemarker.template.ObjectWrapper;
import freemarker.template.Template;
@ -74,7 +74,7 @@ public class SqlVarsDirective implements TemplateDirectiveModel { @@ -74,7 +74,7 @@ public class SqlVarsDirective implements TemplateDirectiveModel {
}
JdbcTemplate jt = new JdbcTemplate();
DataSource dataSource = ApplicationContextHolder.getBean("dataSource", DataSource.class);
DataSource dataSource = SuperXManager.getBean("dataSource", DataSource.class);
jt.setDataSource(dataSource);
Object result = null;

14
src/de/superx/common/FieldXmlEncoder.java

@ -1,7 +1,6 @@ @@ -1,7 +1,6 @@
package de.superx.common;
import java.util.Enumeration;
import java.util.List;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
@ -17,20 +16,11 @@ public class FieldXmlEncoder { @@ -17,20 +16,11 @@ public class FieldXmlEncoder {
*
* @return
*/
public static String toXml(Field f, boolean isFullXMLRequired, int rownr,
List<String> dependentOnFields) {
public static String toXml(Field f, boolean isFullXMLRequired, int rownr) {
StringBuffer result = new StringBuffer("<feld " + dynamic(f) + ">\n");
result.append(getBasics(f));
appendSichtgruppe(f, result);
appendRelation(f, isFullXMLRequired, result);
if(dependentOnFields!=null && dependentOnFields.size()>0)
{
result.append("<dependentOn>\n");
for (int i = 0; i < dependentOnFields.size(); ++i) {
result.append("<fieldname>"+dependentOnFields.get(i)+"</fieldname>");
}
result.append("</dependentOn>");
}
// result.append(" <maskenname>Nutzungsprotokolle (intern)</maskenname>
result.append("<rownr>" + rownr + "</rownr>\n");
result.append("</feld>\n");
@ -54,7 +44,7 @@ public class FieldXmlEncoder { @@ -54,7 +44,7 @@ public class FieldXmlEncoder {
if (param == null) throw new IllegalArgumentException("Param null");
StringBuffer result = new StringBuffer();
if (param.equalsIgnoreCase("!all"))
result.append(toXml(f, true, 0,null));// bei Ajax-nachladen wird rownr
result.append(toXml(f, true, 0));// bei Ajax-nachladen wird rownr
// nicht benötigt
else {
result.append("<feld " + dynamic(f) + ">\n<relation>");

23
src/de/superx/common/Maske.java

@ -981,13 +981,11 @@ public class Maske extends NamedIdObject { @@ -981,13 +981,11 @@ public class Maske extends NamedIdObject {
}
result.append("<felder>");
for (Iterator it = individualFields.iterator(); it.hasNext();) {
Field feld = (Field) it.next();
List<String> dependentOnFields=getDependentOnFields(feld);
if (feld.getY() == 0) rownr++;
result.append(FieldXmlEncoder.toXml(feld, isFullXMLRequired, rownr,dependentOnFields));
result.append(FieldXmlEncoder.toXml(feld, isFullXMLRequired, rownr));
}
result.append("<rownr>" + rownr + "</rownr></felder>\n");
// derzeit wird nur das erste Stylesheet zur Liste hinzugefügt
@ -1003,25 +1001,6 @@ public class Maske extends NamedIdObject { @@ -1003,25 +1001,6 @@ public class Maske extends NamedIdObject {
return result;
}
/* Feldabhängigkeiten pro Maske abfragen, also ob ein Maskenfeld in der Spalte "relation"
* auf ein anderes Maskenfeld verweist.
* Wird nachher im XML-Strom im Element <dependentOn> abgelegt */
private List<String> getDependentOnFields(Field fld)
{
List<String> dependentOnFields = new ArrayList();
String relation=fld.getRelation();
if(relation !=null) {
for (Iterator it = individualFields.iterator(); it.hasNext();) {
Field feld = (Field) it.next();
if(relation.indexOf("<<"+feld.getName()+">>")>-1)
{
dependentOnFields.add(feld.getName());
}
}
}
return dependentOnFields;
}
/**
* Bereitet aus der Maske in XML-Format einen HTML-String vor, der
* zurückgeliefert werden kann. Oder aktualisiert diesen.

236
src/de/superx/common/SxUser.java

@ -32,20 +32,17 @@ import de.memtext.rights.Rights; @@ -32,20 +32,17 @@ import de.memtext.rights.Rights;
import de.memtext.util.CryptUtils;
import de.memtext.util.DateUtils;
import de.superx.servlet.ServletUtils;
import de.superx.servlet.SuperXManager;
import de.superx.servlet.SxSQL_Server;
import de.superx.servlet.Themenbaum;
import de.superx.util.PathAndFileUtils;
import freemarker.template.TemplateBooleanModel;
public class SxUser extends User implements Serializable, UserDetails, GrantedAuthoritiesContainer {
public static final String USER_ROLE_PLACEHOLDER = "UserRole";
public static final String USER_ROLE_PLACEHOLDER = "UserRole";
public static final String USER_ROLE_ID_PLACEHOLDER = "UserRoleId";
public static final String USER_HISINONE_ORGUNIT_LID_PLACEHOLDER = "UserHISinOneOrgunitLid";
private final UUID uuid = UUID.randomUUID();
private final UUID uuid = UUID.randomUUID();
private Themenbaum themenbaum = new Themenbaum();
private String mandantenID = DEFAULT_MANDANTEN_ID;
@ -55,16 +52,16 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -55,16 +52,16 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
private Collection groupnames = new LinkedList();
private Collection sachgebiete = new LinkedList();
private String[] oldShas = new String[7];
private String currentSha = "";
private String currentSha="";
private SxFinRechte finRechte = new SxFinRechte();
private HashMap basicMap = new HashMap();
private SelectionBuffer sb = new SelectionBuffer();
// private Set<String> maskenEditRights = new HashSet<>();
private boolean hasMaskenEditRights = false;
private Collection hiddenFields = new LinkedList();
private Map<String, Map<String, String>> rightsMap = new HashMap<String, Map<String, String>>();
private String role = "";
private String role="";
private Integer roleId;
private Integer userPersonId;
private Integer hisInOneOrgUnitLidOfRole;
private Set<OrgUnit> allowedOrgUnits = new TreeSet<OrgUnit>();
private Set<OrgUnit> implicitlyAllowedOrgUnits = new TreeSet<OrgUnit>();
@ -88,10 +85,10 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -88,10 +85,10 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
public SxUser(String username, Integer userid) throws SQLException, DBServletException, TransformerException {
super(username, userid);
init();
}
public SxUser(String username, Integer userid, String mandantenId)
throws SQLException, DBServletException, TransformerException {
public SxUser(String username, Integer userid, String mandantenId) throws SQLException, DBServletException, TransformerException {
super(username, userid);
this.mandantenID = mandantenId;
init();
@ -110,6 +107,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -110,6 +107,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
sichten.setUser(this);
basicMap.put("SxFinRechte", finRechte);
basicMap.put("SxKontierungsrechte", sxKontierungsrechte);
basicMap.put("Username", getName());
basicMap.put("today", DateUtils.getTodayString());
basicMap.put("yeartoday", DateUtils.getYear(new java.sql.Date(new java.util.Date().getTime())));
@ -133,6 +131,8 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -133,6 +131,8 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
return ((Integer) getId()).intValue();
}
public SelectionBuffer getSelectionBuffer() {
return sb;
}
@ -154,7 +154,8 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -154,7 +154,8 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
return basicMap;
}
public Themenbaum getThemenbaum() {
public Themenbaum getThemenbaum()
{
return themenbaum;
}
@ -171,14 +172,6 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -171,14 +172,6 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
basicMap.put(USER_ROLE_ID_PLACEHOLDER, roleId);
}
public void setUserPersonId(Integer id) {
this.userPersonId = id;
}
public Integer getUserPersonId() {
return userPersonId;
}
public String getRole() {
return role != null ? role : "";
}
@ -215,24 +208,21 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -215,24 +208,21 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
* geworfen
*/
public void checkAuthentificationForMask(String tidStr) {
if (tidStr == null) {
throw new IllegalStateException("Kann Maske nicht öffnen DETAILS:tid parameter war null");
}
if (!isMaskAllowed(tidStr)) {
throw new IllegalArgumentException("Sie haben keine Berechtigung für Maske mit ID-Nummer " + tidStr);
}
if (tidStr == null) throw new IllegalStateException("Kann Maske nicht öffnen DETAILS:tid parameter war null");
if (!isMaskAllowed(tidStr)) throw new IllegalArgumentException("Sie haben keine Berechtigung für Maske mit ID-Nummer " + tidStr);
}
boolean isMaskAllowed(String tidStr) {
boolean result = false;
if (isAdmin()) {
if (isAdmin())
result = true;
} else {
else
try {
result = erlaubteMaskenTids.contains(Integer.valueOf(tidStr));
} catch (Exception e) {
}
}
return result;
}
@ -245,9 +235,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -245,9 +235,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
String sha1 = CryptUtils.encodeSHA(check);
String sha2 = CryptUtils.encodeSHA(check + CryptUtils.geheimnis1);
for (int i = 0; i < 7; i++) {
if (oldShas[i] != null && (sha1.equals(oldShas[i]) || sha2.equals(oldShas[i]))) {
result = true;
}
if (oldShas[i] != null && (sha1.equals(oldShas[i]) || sha2.equals(oldShas[i]))) result = true;
}
return result;
}
@ -255,9 +243,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -255,9 +243,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
public boolean isPWSHAUsedBefore(String sha) {
boolean result = false;
for (int i = 0; i < 7; i++) {
if (oldShas[i] != null && sha.equals(oldShas[i])) {
result = true;
}
if (oldShas[i] != null && sha.equals(oldShas[i])) result = true;
}
return result;
}
@ -289,20 +275,17 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -289,20 +275,17 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
public String getUser_PW_Command(int gueltigkeit) {
String gueltig = "'1.1.3000'";
if (gueltigkeit > 0) {
gueltig = "'"
+ DateUtils.formatGerman(
DateUtils.makeFutureDate(new java.sql.Date(new java.util.Date().getTime()), gueltigkeit))
+ "'";
gueltig = "'" + DateUtils.formatGerman(DateUtils.makeFutureDate(new java.sql.Date(new java.util.Date().getTime()), gueltigkeit)) + "'";
}
String result = "delete from user_pw where userinfo_id='" + this.getId()
+ "'; insert into user_pw (userinfo_id,pw_gueltig_bis,old_sha1,old_sha2,old_sha3,old_sha4,old_sha5,old_sha6,old_sha7) values ('"
+ getId() + "',date_val(" + gueltig + ")";
+ "'; insert into user_pw (userinfo_id,pw_gueltig_bis,old_sha1,old_sha2,old_sha3,old_sha4,old_sha5,old_sha6,old_sha7) values ('" + getId() + "',date_val("
+ gueltig + ")";
for (int i = 0; i < 7; i++)
if (oldShas[i] == null) {
if (oldShas[i] == null)
result += ",null";
} else {
else
result += ",'" + oldShas[i] + "'";
}
result += ")";
return result;
}
@ -315,24 +298,26 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -315,24 +298,26 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
this.currentSha = sha;
}
public void initFinRechte(SxResultSet rs, int finRightVariant, SxResultSet rsProjToInst, boolean is0FINJoker,
HashMap repository) {
public void initFinRechte(SxResultSet rs, int finRightVariant, SxResultSet rsProjToInst, boolean is0FINJoker, HashMap repository) {
finRechte.setVariant(finRightVariant);
finRechte.setRepository(repository);
finRechte.set0FINJoker(is0FINJoker);
if (isAdmin()) {
finRechte.setHasAllRights(true);
}
if (isAdmin()) finRechte.setHasAllRights(true);
for (Iterator it = rs.iterator(); it.hasNext();) {
SxResultRow row = (SxResultRow) it.next();
finRechte.createAndAddEntry(row);
}
for (Iterator it = rsProjToInst.iterator(); it.hasNext();) {
SxResultRow row = (SxResultRow) it.next();
finRechte.addProj((String) row.get(0));
}
}
public void addGruppe(String groupid, String groupname) {
groups.add(groupid);
groupnames.add(groupname);
@ -347,6 +332,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -347,6 +332,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
basicMap.put("IsUserInGroup", new IsUserInGroup(this));
basicMap.put("IsUserInGroupWithName", new IsUserInGroupWithName(this));
basicMap.put("IsUserInAtLeastOneGroup", new IsUserInAtLeastOneGroup(this));
}
public String getSachgebiete() {
@ -385,6 +371,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -385,6 +371,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
}
public boolean isInGroup(String group) {
return groups.contains(group);
}
@ -405,15 +392,20 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -405,15 +392,20 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
}
public void setMaskenEditRights(SxResultSet rs) {
// for (Iterator it = rs.iterator(); it.hasNext();) {
// SxResultRow row = (SxResultRow) it.next();
// maskenEditRights.add((String)row.get(0));
// }
Iterator it = rs.iterator();
if (it.hasNext()) {
SxResultRow row = (SxResultRow) it.next();
Object o = row.get(0);
this.hasMaskenEditRights = ((Number) o).intValue() > 0;
this.hasMaskenEditRights = ((Number)o).intValue() > 0;
}
}
public boolean isFieldHidingWanted(Object id) {
// System.out.println(id+":"+hiddenFields.contains(id));
return hiddenFields.contains(id);
}
@ -504,6 +496,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -504,6 +496,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
}
});
}
return authorities;
}
@ -517,28 +510,29 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -517,28 +510,29 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
});
}
private String getRoleString() {
String rolle = "standard"; // ohne HisInOne
if (getRoles().size() > 0) {
rolle = StringUtils.join(this.getRoles(), ", ");
}
private String getRoleString()
{
String rolle="standard"; //ohne HisInOne
if (getRoles().size()>0)
rolle=StringUtils.join(this.getRoles(), ", ");
return rolle;
}
public void initKontierungsrechte() throws SQLException, DBServletException {
if (isAdmin()) {
if (isAdmin())
sxKontierungsrechte.setHasAllRights(true);
} else {
else {
sxKontierungsrechte.clearAll();
SxResultSet rs = ServletUtils
.executeALL_el(null, null, "Einlesen von Kontierungsrechten für Benutzer " + getId(),
SxKontierungsrechte.readingSQL + getId(), getMandantenID(), true)
.getResultSet();
.executeALL_el(null, null, "Einlesen von Kontierungsrechten für Benutzer " + getId(), SxKontierungsrechte.readingSQL + getId(), getMandantenID(), true)
.getResultSet();
for (Iterator it = rs.iterator(); it.hasNext();) {
SxResultRow row = (SxResultRow) it.next();
sxKontierungsrechte.createAndAddEntry(row);
}
}
}
public Integer getHisInOneOrgUnitLidOfRole() {
@ -547,8 +541,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -547,8 +541,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
public void setHisInOneOrgUnitLidOfRole(Integer lid) {
this.hisInOneOrgUnitLidOfRole = lid;
// remove if 'null': behaves as if 'unset' in Freemarker an homebrew variable
// replacement
// remove if 'null': behaves as if 'unset' in Freemarker an homebrew variable replacement
basicMap.remove(USER_HISINONE_ORGUNIT_LID_PLACEHOLDER);
if (lid != null) {
basicMap.put(USER_HISINONE_ORGUNIT_LID_PLACEHOLDER, lid.toString());
@ -560,9 +553,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -560,9 +553,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
}
/**
* Saiku braucht alle Schlüssel einzeln sowie explizit Unbekannt und Leer falls
* Berechtigung da
*
* Saiku braucht alle Schlüssel einzeln sowie explizit Unbekannt und Leer falls Berechtigung da
* @return
*/
public Set<String> getAllAllowedOrgUnitsForSaiku() {
@ -573,23 +564,23 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -573,23 +564,23 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
orgUnitRights.addLiveMappingRights(optionalLogMessages, mandantenID);
if (hasAllOrgUnitRights()) { // Berechtigung auf alle orgunits -> Leer/Unbekannt hinzufügen siehe #121106
orgUnitRights.add("Unbekannt");
orgUnitRights.add("-999990"); // "Leer"
orgUnitRights.add("-999990"); //"Leer"
}
return orgUnitRights.getAllRightKeys();
}
public String getUUID() {
return uuid.toString();
public String getUUID()
{
return uuid.toString();
}
@Override
public int hashCode() {
return uuid.hashCode();
}
return uuid.hashCode();
}
@Override
public boolean equals(Object o) {
return hashCode() == o.hashCode();
public boolean equals(Object o)
{
return hashCode()==o.hashCode();
}
@Override
@ -619,108 +610,27 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -619,108 +610,27 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
public boolean hasAllOrgUnitRights() {
boolean result = false;
if (isAdmin()) {
if (isAdmin())
result = true;
} else {
else {
for (OrgUnit ou : allowedOrgUnits) {
if (ou.getApnr().contentEquals("0")) {
result = true;
}
if (ou.getApnr().contentEquals("0")) result = true;
}
}
return result;
}
/*
* Es kann wohl passieren, dass im Betrieb die Komponentenupgrades nicht gemacht werden.
* Diese sind aber notwendig für die korrekte Zuordnung von dbforms zu Sachgebieten und
* somit auch für die korrekte Autorisierung.
* Mit dieser Methode kann geprüft werden, ob die Komponente, zu der ein dbForms gehört,
* aktuell ist.
*/
private boolean isModuleUpdated(String dbFormName) {
String lookUpSql = """
SELECT
dv.his_system,
trim(df.name) AS dbforms_name,
trim(df.form_path) AS dbforms_path,
trim(st.name) AS table_name,
dv.version_hash
FROM
db_forms AS df
JOIN sx_tables st ON df.tablename = st.name
JOIN db_version dv ON st.systeminfo_id = dv.systeminfo_id
WHERE trim(df.name) = '""" + dbFormName + "';";
try {
SxResultSet result = ServletUtils.execute("Berechtigungsprüfung", lookUpSql,
SxSQL_Server.DEFAULT_MANDANTEN_ID);
for (Iterator it = result.iterator(); it.hasNext();) {
SxResultRow row = (SxResultRow) it.next();
String hisSystem = (String)row.get(0);
String dbVersionHash = (String)row.get(4);
String fileVersionHash = PathAndFileUtils.getModuleHash(hisSystem);
if (dbVersionHash.equals(fileVersionHash)) {
return true;
}
}
return false;
} catch (Exception e) {
return false;
}
}
/*
* Prüft die Autorisierung für die jsp-Seiten unter "superx/edit"; diese wird durch die Zuordnung in
* der Datenbank ermittelt; das SQL, welches diese schreibt, wird aus der Modul-XML generiert
* - aus den Einträge unter "/module/dbforms/form".
* Aus denselben Einträgen werden idR auch die zugehörigen dbForms-jep-Seiten generiert,
* sodass es normal eine Zuordnung in der DB für eine jsp-Seite gibt.
* Es gibt aber einen Sonderfall: Es gibt auch jsp-Seiten, die nicht generiert werden (meist welche,
* die aus der Kopie einer generierten erzeugt wurden).
* Für diese gibt es demzufolge auch keine explizite Zuordnung in der DB.
* Hier gilt:
* Sie gehören per 'dbFormName' zu einer _Gruppe_ aus dbForms-jsps, von denen mindestens eine
* generiert sein muss und somit eine Zuordnung in der DB hat.
*/
public boolean hasDbFormRight(String dbFormName) {
if (isAdmin()) {
return true;
}
// ist die Komponente nicht aktuell, wird der Zugriff für nicht-Admins verweigert, weil evtl.
// Zurodnungen veraltet und somit keine korrekte Autorisierung gewährleistet ist
if (!isModuleUpdated(dbFormName)) {
return false;
}
String checkSql = """
SELECT sg.*
FROM
sachgebiete sg
JOIN sachgeb_dbform_bez sdb ON sg.tid = sdb.sachgebiete_id
JOIN db_forms df ON df.tid = dbform_id
JOIN user_sachgeb_bez usb ON sg.tid = usb.sachgebiete_id
WHERE df.name = '""" + dbFormName + "' AND usb.userinfo_id = " + this.getId() + "\n"
+ """
union SELECT sg.*
FROM sachgebiete sg
JOIN sachgeb_dbform_bez sdb ON sg.tid = sdb.sachgebiete_id
JOIN db_forms df ON df.tid = dbform_id
JOIN group_sachgeb_bez gsb ON sg.tid = gsb.sachgebiete_id
JOIN user_group_bez ugb on ugb.groupinfo_id=gsb.groupinfo_id
WHERE df.name = '""" + dbFormName + "' AND ugb.userinfo_id = " + this.getId() + ";";
try {
SxResultSet result = ServletUtils.execute("Berechtigungsprüfung", checkSql,
SxSQL_Server.DEFAULT_MANDANTEN_ID);
return result.iterator().hasNext();
} catch (Exception e) {
return false;
if (!isAdmin()) {
// return maskenEditRights.contains(dbFormName);
return hasMaskenEditRights;
}
return true;
}
public void initThemenbaum()
throws SQLException, DBServletException, TransformerException, NichtAngemeldetException {
public void initThemenbaum() throws SQLException, DBServletException, TransformerException, NichtAngemeldetException {
themenbaum.init((Integer) getId(), getName(), this.isAdmin(), getMandantenID());
}
}
// Created on 06.12.2004 at 14:48:11

4
src/de/superx/db/SpringBeanConnectionProvider.java

@ -7,7 +7,7 @@ import javax.sql.DataSource; @@ -7,7 +7,7 @@ import javax.sql.DataSource;
import org.dbforms.conprovider.ConnectionProvider;
import de.superx.spring.service.ApplicationContextHolder;
import de.superx.servlet.SuperXManager;
public class SpringBeanConnectionProvider extends ConnectionProvider {
@ -24,7 +24,7 @@ public class SpringBeanConnectionProvider extends ConnectionProvider { @@ -24,7 +24,7 @@ public class SpringBeanConnectionProvider extends ConnectionProvider {
@Override
protected void init() throws Exception {
this.dataSource = ApplicationContextHolder.getBean("dataSource", DataSource.class);
this.dataSource = SuperXManager.getBean("dataSource", DataSource.class);
}
}

21
src/de/superx/db/export/ExportConfigReader.java

@ -77,30 +77,13 @@ public class ExportConfigReader { @@ -77,30 +77,13 @@ public class ExportConfigReader {
}
}
// Parse tables with optional per-table column, source, columns, and orderBy overrides
// Parse tables with optional per-table column override
List<ExportTableConfig> tableConfigs = new ArrayList<>();
for (Element tableEl : groupEl.getChildren("table")) {
String tableName = tableEl.getTextTrim();
if (!tableName.isEmpty()) {
String colOverride = tableEl.getAttributeValue("column");
String sourceOverride = tableEl.getAttributeValue("source");
String columnsAttr = tableEl.getAttributeValue("columns");
String orderByAttr = tableEl.getAttributeValue("orderBy");
List<String> columnsList = null;
if (columnsAttr != null && !columnsAttr.isBlank()) {
columnsList = new ArrayList<>();
for (String col : columnsAttr.split(",")) {
String trimmed = col.trim();
if (!trimmed.isEmpty()) {
columnsList.add(trimmed);
}
}
}
tableConfigs.add(new ExportTableConfig(
tableName, colOverride, sourceOverride,
columnsList, orderByAttr));
tableConfigs.add(new ExportTableConfig(tableName, colOverride));
}
}

110
src/de/superx/db/export/ExportConfigWriter.java

@ -1,110 +0,0 @@ @@ -1,110 +0,0 @@
package de.superx.db.export;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.output.Format;
import org.jdom2.output.XMLOutputter;
import org.springframework.stereotype.Component;
/**
* Writes an {@link ExportConfig} to XML, producing the same format
* that {@link ExportConfigReader} can parse.
*
* <p>Example output:
* <pre>{@code
* <export-config>
* <group name="my_group" schema="staging">
* <filter column="tid" source="SELECT tid FROM organigramm"/>
* <table column="id" columns="id, name" orderBy="name ASC">my_table</table>
* </group>
* </export-config>
* }</pre>
*/
@Component
public class ExportConfigWriter {
/**
* Write the config to a file.
*/
public void write(ExportConfig config, File outputFile) throws IOException {
try (FileOutputStream fos = new FileOutputStream(outputFile)) {
write(config, fos);
}
}
/**
* Write the config to an output stream.
*/
public void write(ExportConfig config, OutputStream out) throws IOException {
Document doc = buildDocument(config);
XMLOutputter outputter = new XMLOutputter(
Format.getPrettyFormat().setEncoding("UTF-8"));
outputter.output(doc, out);
}
/**
* Build the JDOM2 Document representing the config.
*/
public Document buildDocument(ExportConfig config) {
Document doc = new Document();
Element root = new Element("export-config");
doc.setRootElement(root);
for (ExportGroup group : config.getGroups()) {
root.addContent(buildGroupElement(group));
}
return doc;
}
private Element buildGroupElement(ExportGroup group) {
Element groupEl = new Element("group");
groupEl.setAttribute("name", group.getName());
if (group.getSchema() != null) {
groupEl.setAttribute("schema", group.getSchema());
}
if (group.hasFilter()) {
Element filterEl = new Element("filter");
filterEl.setAttribute("column", group.getFilterColumn());
filterEl.setAttribute("source", group.getFilterSource());
groupEl.addContent(filterEl);
}
for (ExportTableConfig tableConfig : group.getTableConfigs()) {
groupEl.addContent(buildTableElement(tableConfig));
}
return groupEl;
}
private Element buildTableElement(ExportTableConfig tableConfig) {
Element tableEl = new Element("table");
tableEl.setText(tableConfig.getTableName());
if (tableConfig.getFilterColumn() != null) {
tableEl.setAttribute("column", tableConfig.getFilterColumn());
}
if (tableConfig.hasFilterSource()) {
tableEl.setAttribute("source", tableConfig.getFilterSource());
}
if (tableConfig.hasColumns()) {
tableEl.setAttribute("columns", String.join(", ", tableConfig.getColumns()));
}
if (tableConfig.hasOrderBy()) {
tableEl.setAttribute("orderBy", tableConfig.getOrderBy());
}
return tableEl;
}
}

111
src/de/superx/db/export/ExportTableConfig.java

@ -1,57 +1,26 @@ @@ -1,57 +1,26 @@
package de.superx.db.export;
import java.util.Collections;
import java.util.List;
/**
* Configuration for a single table within an export group.
* Holds the table name and optional overrides for the filter column,
* filter source, column selection, and ordering.
* Holds the table name and an optional override for the filter column.
*/
public class ExportTableConfig {
private final String tableName;
private final String filterColumn;
private final String filterSource;
private final List<String> columns;
private final String orderBy;
/**
* @param tableName the database table name
* @param filterColumn optional override for the group-level filter column;
* null means use the group default
* @param filterSource optional override for the group-level filter source;
* may contain {@code {filter}} as a placeholder that will be
* replaced with the group-level filter expression
* (i.e. "groupColumn IN (groupSource)"). Null means use
* the group-level filter directly.
* @param columns optional list of column names to export; null or empty
* means export all columns (SELECT *)
* @param orderBy optional ORDER BY expression (without the "ORDER BY" keyword);
* null means no ordering
*/
public ExportTableConfig(String tableName, String filterColumn,
String filterSource, List<String> columns,
String orderBy) {
public ExportTableConfig(String tableName, String filterColumn) {
this.tableName = tableName;
this.filterColumn = filterColumn;
this.filterSource = filterSource;
this.columns = columns != null && !columns.isEmpty()
? Collections.unmodifiableList(columns) : null;
this.orderBy = orderBy;
}
public ExportTableConfig(String tableName, String filterColumn,
String filterSource) {
this(tableName, filterColumn, filterSource, null, null);
}
public ExportTableConfig(String tableName, String filterColumn) {
this(tableName, filterColumn, null, null, null);
}
public ExportTableConfig(String tableName) {
this(tableName, null, null, null, null);
this(tableName, null);
}
public String getTableName() {
@ -66,14 +35,6 @@ public class ExportTableConfig { @@ -66,14 +35,6 @@ public class ExportTableConfig {
return filterColumn;
}
/**
* @return the per-table filter source SQL, or null to use the group default.
* May contain {@code {filter}} placeholder.
*/
public String getFilterSource() {
return filterSource;
}
/**
* Returns the effective filter column: this table's override if set,
* otherwise the provided group default.
@ -82,71 +43,11 @@ public class ExportTableConfig { @@ -82,71 +43,11 @@ public class ExportTableConfig {
return filterColumn != null ? filterColumn : groupDefault;
}
/**
* Whether this table has its own filter source override.
*/
public boolean hasFilterSource() {
return filterSource != null;
}
/**
* @return the list of columns to export, or null if all columns
* should be exported
*/
public List<String> getColumns() {
return columns;
}
/**
* Whether this table has a column selection configured.
*/
public boolean hasColumns() {
return columns != null;
}
/**
* @return the ORDER BY expression (without the keyword), or null
* if no ordering is specified
*/
public String getOrderBy() {
return orderBy;
}
/**
* Whether this table has an ORDER BY expression configured.
*/
public boolean hasOrderBy() {
return orderBy != null;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(tableName);
boolean hasExtra = filterColumn != null || filterSource != null
|| columns != null || orderBy != null;
if (hasExtra) {
sb.append(" (");
boolean first = true;
if (filterColumn != null) {
sb.append("column=").append(filterColumn);
first = false;
}
if (filterSource != null) {
if (!first) sb.append(", ");
sb.append("source=").append(filterSource);
first = false;
}
if (columns != null) {
if (!first) sb.append(", ");
sb.append("columns=").append(columns);
first = false;
}
if (orderBy != null) {
if (!first) sb.append(", ");
sb.append("orderBy=").append(orderBy);
}
sb.append(")");
if (filterColumn != null) {
return tableName + " (column=" + filterColumn + ")";
}
return sb.toString();
return tableName;
}
}

8
src/de/superx/db/export/ImportMode.java

@ -15,11 +15,5 @@ public enum ImportMode { @@ -15,11 +15,5 @@ public enum ImportMode {
CREATE_INSERT,
/** Drop and recreate the table from the XML structure, then insert. */
RECREATE,
/** Insert new rows or update existing ones based on primary key. Table must already exist. */
UPSERT,
/** Only update existing rows based on primary key. Rows without a match are skipped. Table must already exist. */
UPDATE
RECREATE
}

11
src/de/superx/dbt/DbtAction.java

@ -1,11 +0,0 @@ @@ -1,11 +0,0 @@
package de.superx.dbt;
public enum DbtAction {
BUILD,
TEST,
GENERATE_DOCS,
STAGING_TABLES,
SQLFLUFF_LINT,
NODE_COUNT_BUILD,
NODE_COUNT_TEST
}

8
src/de/superx/dbt/DbtExecutionState.java

@ -1,8 +0,0 @@ @@ -1,8 +0,0 @@
package de.superx.dbt;
public enum DbtExecutionState {
ACTIVE, // dbt-Lauf ist noch nicht beendet - evtl. braucht es mehr als nur "active"!?
SUCCESS, // dbt-Lauf wurde erfolgreich abgeschlossen
FAILURE, // dbt-Lauf endete in einem Fehler
TIMEOUT
}

59
src/de/superx/dbt/DbtMetrics.java

@ -0,0 +1,59 @@ @@ -0,0 +1,59 @@
package de.superx.dbt;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class DbtMetrics {
private static final String METRICS_LINE = "Found (\\d+) models, (\\d+) snapshots, (\\d+) data tests, (\\d+) seeds,"
+ " (\\d+) sources, (\\d+) macros";
private static final Pattern METRICS_PATTERN = Pattern.compile(METRICS_LINE);
private int models;
private int snapshots;
private int tests;
private int seeds;
private int sources;
private int macros;
public DbtMetrics(String metricLine) {
Matcher metricsMatcher = METRICS_PATTERN.matcher(metricLine);
if (metricsMatcher.find()) {
models = Integer.parseInt(metricsMatcher.group(1));
snapshots = Integer.parseInt(metricsMatcher.group(2));
tests = Integer.parseInt(metricsMatcher.group(3));
seeds = Integer.parseInt(metricsMatcher.group(4));
sources = Integer.parseInt(metricsMatcher.group(5));
macros = Integer.parseInt(metricsMatcher.group(6));
}
}
public int getExecCount(boolean isTests) {
return isTests ? tests : models + snapshots + seeds;
}
public int getModels() {
return models;
}
public int getSnapshots() {
return snapshots;
}
public int getTests() {
return tests;
}
public int getSeeds() {
return seeds;
}
public int getSources() {
return sources;
}
public int getMacros() {
return macros;
}
}

7
src/de/superx/dbt/DbtPermissionException.java

@ -1,7 +0,0 @@ @@ -1,7 +0,0 @@
package de.superx.dbt;
public class DbtPermissionException extends Exception {
DbtPermissionException(String message, Throwable cause) {
super(message, cause);
}
}

443
src/de/superx/dbt/DbtRun.java

@ -1,443 +0,0 @@ @@ -1,443 +0,0 @@
package de.superx.dbt;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import org.apache.commons.lang.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
public final class DbtRun implements Callable<DbtRunResult> {
static Logger logger = LoggerFactory.getLogger(DbtRun.class);
private static final boolean isLinux = SystemUtils.IS_OS_LINUX;
private final DbtRunRequest request;
private final Map<String, String> environment;
private final Path projectPath;
public Process process;
DbtRun(DbtRunRequest request, Map<String, String> environment, Path projectPath) {
this.request = request;
this.environment = environment;
this.projectPath = projectPath;
}
@Override
public DbtRunResult call() throws Exception {
return execute();
}
private DbtRunResult execute() {
return switch (request.action()) {
case BUILD -> runDbtCommand(createBuildCommand());
case TEST -> runDbtCommand(createTestCommand());
case NODE_COUNT_BUILD -> nodeCount(true);
case NODE_COUNT_TEST -> nodeCount(false);
case GENERATE_DOCS -> generateDocs();
case STAGING_TABLES -> findDbtStagingTablesForSelection();
case SQLFLUFF_LINT -> lintDbtModelsUsingSQLFluff();
};
}
private List<String> createTestCommand() {
List<String> baseCommand = new ArrayList<>(DbtUtils.getDbtCommandBase());
baseCommand.addAll(Arrays.asList( "--log-format", "json", "--no-use-colors", "test"));
if(request.select() != null && !request.select().isBlank()) {
baseCommand.add("--select");
baseCommand.add(request.select());
}
return baseCommand;
}
private List<String> createBuildCommand() {
List<String> baseCommand = new ArrayList<>(DbtUtils.getDbtCommandBase());
baseCommand.addAll(Arrays.asList(
"--log-format", "json",
"--no-use-colors",
"build",
"--resource-types",
"snapshot",
"seed",
"model"
));
if(request.select() != null && !request.select().isBlank()) {
baseCommand.add("--select");
baseCommand.add(request.select());
}
return baseCommand;
}
public DbtRunResult runDbtCommand(List<String> pbCommand) {
DbtRunResult result = new DbtRunResult();
logger.info(Arrays.toString(pbCommand.toArray()));
ProcessBuilder p = new ProcessBuilder(pbCommand).directory(projectPath.toFile());
p.environment().putAll(this.environment);
p.redirectErrorStream(false);
List<JsonNode> dbtLog = new ArrayList<>();
JsonNode stats = null;
try {
int progress = 0;
process = p.start();
logger.info("ProcessBuilder start");
logger.info("PYTHONUSERBASE: " + p.environment().get("PYTHONUSERBASE"));
logger.info("PATH: " + p.environment().get((isLinux) ? "PATH" : "Path"));
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
CompletableFuture<String> errFuture = readOutStream(process.getErrorStream());
var line = "";
ObjectMapper objectMapper = new ObjectMapper();
while ((line = reader.readLine()) != null) {
JsonNode node = objectMapper.readTree(line.getBytes());
logger.info(node.get("info").get("msg").asText());
JsonNode data = node.get("data");
if (data.get("stats") != null) {
stats = node;
} else {
if (data.has("index")) {
progress = Math.max(progress, data.get("index").asInt());
if(request.progressCallback() != null) {
request.progressCallback().accept(Integer.valueOf(progress));
}
}
dbtLog.add(node);
}
}
int exitCode = process.waitFor();
String error = errFuture.join();
result.setErrorMessages(getErrorMessages(dbtLog, error));
result.setState(getExecutionState(stats));
result.setExitCode(Integer.valueOf(exitCode));
} catch (Exception e) {
logger.error("Couldn't start dbt run!", e);
result.setErrorMessages(List.of(e.getMessage()));
result.setState(DbtExecutionState.FAILURE);
}
return result;
}
public DbtRunResult generateDocs() {
DbtRunResult result = new DbtRunResult();
try {
List<String> pbCommand = new ArrayList<>(DbtUtils.getDbtCommandBase());
pbCommand.addAll(Arrays.asList("--no-use-colors", "docs", "generate"));
if(request.select() != null && !request.select().isBlank()) {
pbCommand.add("--select");
pbCommand.add(request.select());
}
ProcessBuilder pb = new ProcessBuilder(pbCommand).directory(projectPath.toFile());
pb.environment().putAll(environment);
pb.redirectErrorStream(true);
Process p = pb.start();
final InputStream stdoutInputStream = p.getInputStream();
final BufferedReader stdoutReader =
new BufferedReader(new InputStreamReader(stdoutInputStream));
String line = null;
while ((line = stdoutReader.readLine()) != null) {
logger.info(line);
}
result.setState(DbtExecutionState.SUCCESS);
} catch (Exception iox) {
String message = "Couldn't run dbt generate docs ";
logger.error(message, iox);
result.setState(DbtExecutionState.FAILURE);
result.setErrorMessages(List.of(message));
}
return result;
}
public DbtRunResult nodeCount(boolean test) {
DbtRunResult result = new DbtRunResult();
int lineNr = 0;
try {
List<String> pbCommand = new ArrayList<>(DbtUtils.getDbtCommandBase());
pbCommand.addAll(Arrays.asList("ls", "--resource-types"));
if(test) {
pbCommand.add("test");
} else {
pbCommand.addAll(Arrays.asList(
"snapshot",
"seed",
"model"
));
}
if(request.select() != null && !request.select().isBlank()) {
pbCommand.add("--select");
pbCommand.add(request.select());
}
ProcessBuilder pb = new ProcessBuilder(pbCommand).directory(projectPath.toFile());
pb.environment().putAll(environment);
pb.redirectErrorStream(true);
Process p = pb.start();
final InputStream stdoutInputStream = p.getInputStream();
final BufferedReader stdoutReader =
new BufferedReader(new InputStreamReader(stdoutInputStream));
String line = "";
while ((line = stdoutReader.readLine()) != null) {
if (!line.matches("(\\w+\\.)+\\w+$")) {
continue;
}
lineNr += 1;
logger.info(line);
}
result.setNodeCount(lineNr);
result.setState(DbtExecutionState.SUCCESS);
} catch (Exception iox) {
String message = "Couldn't run dbt ls ";
logger.error(message, iox);
result.setErrorMessages(List.of(message));
result.setState(DbtExecutionState.FAILURE);
}
return result;
}
private static CompletableFuture<String> readOutStream(InputStream is) {
return CompletableFuture.supplyAsync(() -> {
try (
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
) {
StringBuilder res = new StringBuilder();
String inputLine;
while ((inputLine = br.readLine()) != null) {
res.append(inputLine).append(System.lineSeparator());
}
return res.toString();
} catch (Throwable e) {
throw new RuntimeException("problem with executing program", e);
}
});
}
/**
* Finds staging tables for a dbt selection by running {@code dbt ls}.
* <p>
* Matches models from staging schemas such as {@code staging},
* {@code staging_xyz}, or {@code staging_vmpostgres}, and returns only the
* table name without the schema prefix.
*
* @param selectOption the dbt selector to inspect
* @return
* @return staging table names without schema prefix
*/
public DbtRunResult findDbtStagingTablesForSelection() {
DbtRunResult result = new DbtRunResult();
logger.info("Finding dbt staging tables for selection: {}", request.select());
try {
List<String> command = new ArrayList<>(DbtUtils.getDbtCommandBase());
command.addAll(Arrays.asList(
"ls",
"--select", request.select(),
"--output", "name"
));
logger.info("Dbt transform Dir: {}", projectPath);
logger.info("Command: {}", String.join(" ", command));
ProcessBuilder builder = new ProcessBuilder(command)
.directory(projectPath.toFile());
builder.environment().putAll(environment);
this.process = builder.start();
CompletableFuture<String> errFuture = readOutStream(process.getErrorStream());
List<String> results;
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream())
)) {
results = reader.lines().collect(Collectors.toList());
}
int exitCode = process.waitFor();
String error = errFuture.join();
String output = String.join(System.lineSeparator(), results);
if (exitCode != 0) {
String message = "dbt ls failed for selection '" + request.select() + "' with exit code " + exitCode
+ "\n\nstdout:\n" + output
+ "\n\nstderr:\n" + error;
result.setState(DbtExecutionState.FAILURE);
result.setErrorMessages(List.of(message));
return result;
}
if (!error.isBlank()) {
logger.debug("dbt ls stderr for selection '{}': {}", request.select(), error);
}
List<String> tables = new ArrayList<>();
for (String model : results) {
int dotIndex = model.indexOf(".");
if (dotIndex > 0 && model.substring(0, dotIndex).startsWith("staging")) {
tables.add(model.substring(dotIndex + 1));
}
}
logger.info(
"Found {} dbt staging table(s) for selection '{}': {}",
new Object[] { Integer.valueOf(tables.size()), request.select(), tables }
);
result.setOutputMessages(tables);
result.setExitCode(Integer.valueOf(exitCode));
if(!error.isBlank()) {
result.setErrorMessages(List.of(error));
result.setState(DbtExecutionState.FAILURE);
} else {
result.setState(DbtExecutionState.SUCCESS);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
String message = "Interrupted while running dbt ls for selection: " + request.select();
logger.error(message, e);
result.setState(DbtExecutionState.FAILURE);
result.setErrorMessages(List.of(message));
} catch (IOException e) {
String message = "Could not start dbt ls for selection: " + request.select();
logger.error(message , e);
result.setState(DbtExecutionState.FAILURE);
result.setErrorMessages(List.of(message));
}
return result;
}
public DbtRunResult lintDbtModelsUsingSQLFluff() {
DbtRunResult result = new DbtRunResult();
List<String> command = new ArrayList<>(DbtUtils.getSqlFluffCommandBase());
command.addAll(Arrays.asList(
"lint",
"--verbose",
"--nocolor",
"--disable-progress-bar"
));
logger.info("sqlfluff command: " + command);
ProcessBuilder builder = new ProcessBuilder(command)
.directory(projectPath.toFile());
builder.environment().putAll(environment);
try {
this.process = builder.start();
// read stdout
List<String> lintingErrors = new ArrayList<String>();
int progress = 0;
try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))){
String line;
while((line = reader.readLine()) != null) {
logger.info(line);
if(line.contains(".sql")) {
if (line.contains("FAIL")) {
lintingErrors.add(line);
}
if(request.progressCallback() != null) {
request.progressCallback().accept(Integer.valueOf(progress++));
}
}
}
}
List<String> errorsFromErrorStream = new ArrayList<String>();
// read stderr
try (InputStream es = process.getErrorStream();
InputStreamReader esr = new InputStreamReader(es);
BufferedReader errReader = new BufferedReader(esr)) {
String line;
while ((line = errReader.readLine()) != null) {
logger.error(line);
errorsFromErrorStream.add(line);
}
}
// SQLFluff exit codes: 0 = clean, 1 = linting issues found, 2 = execution failed.
int exitCode = process.waitFor();
result.setExitCode(Integer.valueOf(exitCode));
if(exitCode == 0) {
result.setState(DbtExecutionState.SUCCESS);
} else if(exitCode == 1) {
result.setErrorMessages(lintingErrors);
result.setState(DbtExecutionState.FAILURE);
} else {
result.setErrorMessages(errorsFromErrorStream);
result.setState(DbtExecutionState.FAILURE);
}
return result;
} catch (Exception e) {
logger.error("Failed running sqlfluff.", e);
result.setErrorMessages(List.of(e.getMessage()));
result.setState(DbtExecutionState.FAILURE);
return result;
}
}
public DbtExecutionState getExecutionState(JsonNode stats) {
if (stats == null) {
return DbtExecutionState.ACTIVE;
}
if (stats.get("data").get("stats").get("error").asInt() == 0) {
return DbtExecutionState.SUCCESS;
}
return DbtExecutionState.FAILURE;
}
private List<String> getErrorMessages(List<JsonNode> dbtLog, String errors) {
var result = new ArrayList<String>();
if(errors == null) {
return result;
}
if (!errors.isEmpty()) {
// stderr was not empty!
result.add(errors);
} else {
// "regular" errors during dbt execution
for (var node : dbtLog) {
var level = findFirstOccurence("level", node);
if (level.isPresent() && "error".equals(level.get().asText())) {
var msg = findFirstOccurence("msg", node);
if (msg.isPresent()) {
result.add(msg.get().asText());
} else {
logger.warn("No msg property found in dbt log " + node);
}
} else {
logger.warn("No level property found in dbt log " + node);
}
}
}
return result;
}
private static Optional<JsonNode> findFirstOccurence(String name, JsonNode searchNode) {
JsonNode parent = searchNode.findParent(name);
JsonNode result = parent != null ? parent.get(name) : null;
return Optional.of(result);
}
}

24
src/de/superx/dbt/DbtRunRequest.java

@ -1,24 +0,0 @@ @@ -1,24 +0,0 @@
package de.superx.dbt;
import java.util.function.Consumer;
public record DbtRunRequest(
DbtAction action,
String project,
String select,
long timeoutMillis,
Consumer<Integer> progressCallback
) {
public DbtRunRequest(DbtAction action, String project, String select, long timeoutMillis) {
this(action, project, select, timeoutMillis, null);
}
public DbtRunRequest(DbtAction action, String project, Consumer<Integer> progressCallback) {
this(action, project, null, 0, progressCallback);
}
public DbtRunRequest(DbtAction action, String project, String select) {
this(action, project, select, 0, null);
}
}

62
src/de/superx/dbt/DbtRunResult.java

@ -1,62 +0,0 @@ @@ -1,62 +0,0 @@
package de.superx.dbt;
import java.util.List;
public class DbtRunResult {
private Integer exitCode;
private DbtExecutionState state;
private List<String> outputMessages;
private List<String> errorMessages;
private int nodeCount; // used for DbtAction.NODE_COUNT*
// private List<String> stagingTables;// used for DbtAction.STAGING_TABLES
public Integer getExitCode() {
return exitCode;
}
public void setExitCode(Integer exitCode) {
this.exitCode = exitCode;
}
public DbtExecutionState getState() {
return state;
}
public void setState(DbtExecutionState state) {
this.state = state;
}
public List<String> getErrorMessages() {
return errorMessages;
}
public void setErrorMessages(List<String> errorMessages) {
this.errorMessages = errorMessages;
}
public int getNodeCount() {
return nodeCount;
}
public void setNodeCount(int nodeCount) {
this.nodeCount = nodeCount;
}
//public List<String> getStagingTables() {
// return stagingTables;
//}
//public void setStagingTables(List<String> stagingTables) {
// this.stagingTables = stagingTables;
//}
public List<String> getOutputMessages() {
return outputMessages;
}
public void setOutputMessages(List<String> outputMessages) {
this.outputMessages = outputMessages;
}
}

133
src/de/superx/dbt/DbtUtils.java

@ -1,21 +1,15 @@ @@ -1,21 +1,15 @@
package de.superx.dbt;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.StringJoiner;
import java.util.stream.Collectors;
import javax.sql.DataSource;
import org.apache.commons.lang3.SystemUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import de.superx.servlet.SuperXManager;
import de.superx.util.PathAndFileUtils;
@ -24,8 +18,6 @@ public class DbtUtils { @@ -24,8 +18,6 @@ public class DbtUtils {
private final static boolean isLinux = SystemUtils.IS_OS_LINUX;
public static boolean isTestRun = false;
public static String getPythonVersion() {
return getPythonVersionForExecutable(getPython3ExecutableName());
}
@ -61,59 +53,27 @@ public class DbtUtils { @@ -61,59 +53,27 @@ public class DbtUtils {
}
}
private static Path getDbtFolderBinary() {
return Path.of(SuperXManager.getWEB_INFPfad(), "..", "dbt");
}
private static Path getDbtFolder() {
if(isTestRun) {
return Path.of(String.join(File.separator, "test", "resources", "dbt" ));
}
return Path.of(SuperXManager.getWEB_INFPfad(), "..", "dbt");
}
private static Path getDbtInstallLib() {
return Path.of(getDbtFolderBinary().toString(), "dbt_linux", "lib");
}
public static Path getDbtModelsDirectory(String project) {
Path modelsPath = getDbtProjectDirectory(project).resolve("models");
if (!modelsPath.toFile().isDirectory()) {
throw new RuntimeException(modelsPath.toAbsolutePath() + " is no directory!");
}
return modelsPath.toAbsolutePath().normalize();
return Path.of(getDbtFolder().toString(), "dbt_linux", "lib");
}
public static Path getDbtProjectDirectory(String project) {
Path projectPath = Path.of(getDbtFolder().toString(), "projects", project, "transform");
if (!projectPath.toFile().isDirectory()) {
throw new RuntimeException(projectPath.toAbsolutePath() + " is no directory!");
}
File projectFile = Path.of(projectPath.toString(), "dbt_project.yml").toFile();
if (!projectFile.exists()) {
throw new RuntimeException(projectPath + " is no dbt project directory (no dbt_project.yml found)!");
}
File profilesFile = Path.of(projectPath.toString(), "profiles.yml").toFile();
if (!profilesFile.exists()) {
throw new RuntimeException(projectPath + " is no dbt project directory (no profiles.yml found)!");
}
return projectPath.toAbsolutePath().normalize();
public static Path getDbtProjectDirectory() {
return Path.of(getDbtFolder().toString(), "projects", "hisinone", "transform");
}
public static Path getBiAnalysisMetadataFolder(String project) {
return Path.of(getDbtFolder().toString(), "projects", project, "bianalysis_metadata");
public static Path getBiAnalysisMetadataFolder() {
return Path.of(getDbtFolder().toString(), "projects", "hisinone", "bianalysis_metadata");
}
public static Path getVirtualEnvPath() {
if(isLinux) {
return Path.of(getDbtFolderBinary().toString(), "dbt_env_linux");
return Path.of(getDbtFolder().toString(), "dbt_env_linux");
}
return Path.of(getDbtFolderBinary().toString(), "dbt_env_windows");
return Path.of(getDbtFolder().toString(), "dbt_env_windows");
}
public static Path getDbtInstallDir() {
@ -124,7 +84,7 @@ public class DbtUtils { @@ -124,7 +84,7 @@ public class DbtUtils {
if(isLinux) {
folder = "dbt_linux";
}
return Path.of(getDbtFolderBinary().toString(), folder);
return Path.of(getDbtFolder().toString(), folder);
}
public static boolean isVirtualEnvInstalled() {
@ -152,42 +112,11 @@ public class DbtUtils { @@ -152,42 +112,11 @@ public class DbtUtils {
return Path.of(getDbtBinaryFolder().toString(), bin);
}
private static Path getDbtBinaryPath(String bin) {
if(!(bin.equals("dbt") || bin.equals("sqlfluff"))) {
throw new IllegalArgumentException("binary not allowed: " + bin);
}
public static Path getDbtBinaryPath() {
if(isLinux) {
return Path.of(getDbtBinaryFolder().toString(), bin);
}
return Path.of(getDbtBinaryFolder().toString(), bin + ".exe");
}
public static List<String> getDbtCommandBase() {
return getCommandBase("dbt");
}
public static List<String> getSqlFluffCommandBase() {
return getCommandBase("sqlfluff");
}
public static List<String> getCommandBase(String bin) {
List<String> command = new ArrayList<>();
String pythonExecutable = getPython3ExecutableName();
if (!isLinux && DbtUtils.isVirtualEnvInstalled()) {
// On Windows, when using a virtual environment, we must run the dbt
// executable directly without invoking it through the python interpreter
pythonExecutable = "";
}
if (pythonExecutable != null && !pythonExecutable.isBlank()) {
command.add(pythonExecutable);
return Path.of(getDbtBinaryFolder().toString(), "dbt");
}
command.add(getDbtBinaryPath(bin).toString());
return command;
return Path.of(getDbtBinaryFolder().toString(), "dbt.exe");
}
@ -228,46 +157,20 @@ public class DbtUtils { @@ -228,46 +157,20 @@ public class DbtUtils {
}
public static void checkDbtSchemaPermissions(DataSource dataSource) throws DbtPermissionException {
final List<String> DBT_SCHEMAS = List.of(
"staging",
"core",
"presentation",
"metadata"
);
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
for (String schema : DBT_SCHEMAS) {
String fullTableName = schema + ".__dbt_permission_check";
try {
jdbcTemplate.execute("CREATE TABLE " + fullTableName + " (permission_check integer)");
jdbcTemplate.execute("DROP TABLE " + fullTableName);
} catch (DataAccessException e) {
String errorMessage = """
dbt permission check failed for schema '%s'.
Could not create/drop table '%s'.
Grant likely needed:
GRANT USAGE, CREATE ON SCHEMA %s TO <dbt_user>;
""".formatted( schema, fullTableName, schema);
throw new DbtPermissionException(errorMessage, e);
}
}
}
public static void main(String[] args) {
SuperXManager.setWEB_INFPfad(PathAndFileUtils.getWebinfPath());
DbtWrapper.getStagingTables("+fact_application");
System.out.println(getPythonVersion());
System.out.println(getPython3ExecutableName());
System.out.println(getDbtFolder());
System.out.println(getDbtProjectDirectory("hisinone"));
System.out.println(getDbtProjectDirectory());
System.out.println(getDbtBinaryFolder());
System.out.println(getDbtCommandBase());
//BasicConfigurator.configure();
//DbtWrapper dbtWrapper = new DbtWrapper();
//dbtWrapper.init("hisinone", "+fact_application");
//System.out.println(dbtWrapper.findDbtStagingTablesForSelection());
System.out.println(getDbtBinaryPath());
}
}

460
src/de/superx/dbt/DbtWrapper.java

@ -1,20 +1,28 @@ @@ -1,20 +1,28 @@
package de.superx.dbt;
import java.sql.ResultSet;
import static java.util.Map.entry;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.nio.file.Path;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
import javax.sql.DataSource;
import org.apache.commons.lang3.SystemUtils;
import org.jfree.util.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -24,14 +32,29 @@ import org.springframework.jdbc.core.JdbcTemplate; @@ -24,14 +32,29 @@ import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zaxxer.hikari.HikariDataSource;
import de.superx.access.SqlAccessRestrictor;
import de.superx.access.SqlAccessRestrictor.Rechtekonzept;
import de.superx.rest.EtlJobApi;
import de.superx.rest.model.job.Component;
import de.superx.spring.service.ApplicationContextHolder;
import de.superx.servlet.SuperXManager;
@Service
public class DbtWrapper {
public class DbtWrapper implements Runnable {
public enum DbParamsSource {
FROM_PRESETS,
FROM_FILE
}
public enum ExecutionState {
ACTIVE, // dbt-Lauf ist noch nicht beendet - evtl. braucht es mehr als nur "active"!?
SUCCESS, // dbt-Lauf wurde erfolgreich abgeschlossen
FAILURE // dbt-Lauf endete in einem Fehler
}
@Autowired
EtlJobApi etlJobApi;
@ -39,57 +62,142 @@ public class DbtWrapper { @@ -39,57 +62,142 @@ public class DbtWrapper {
@Autowired
DataSource dataSource;
@Autowired
Map<String, String> dbtProfile;
static Logger logger = LoggerFactory.getLogger(DbtWrapper.class);
public static final String DBT_THREAD_NAME_PREFIX = "DBT-";
private final boolean isLinux = SystemUtils.IS_OS_LINUX;
private Path dbtInstallDir;
private String pythonExecutable = "python";
private String user = "postgres";
private String password = "postgres";
private String host = "localhost";
private String port = "5432";
private String db = "eduetl";
private Process process;
private int modelCount = 0;
private String runtimeErrors = "";
private String select;
private boolean test;
public DbtRunResult execute(DbtRunRequest request) {
DbtRun run = new DbtRun(
request,
getEnvironment(), // TODO: costly, reuse for multiple calls ?
DbtUtils.getDbtProjectDirectory(request.project())
);
private Path projectPath;
ExecutorService executor = Executors.newSingleThreadExecutor(r -> {
Thread t = new Thread(r);
// name the thread so that the logging to file and frontend works
// see class: BiJobLoggingListener
t.setName(DBT_THREAD_NAME_PREFIX + System.currentTimeMillis());
return t;
});
Future<DbtRunResult> future = executor.submit(run);
private List<JsonNode> dbtLog = new ArrayList<>();
private JsonNode stats;
try {
return request.timeoutMillis() <= 0
? future.get() : future.get(request.timeoutMillis(), TimeUnit.MILLISECONDS);
private boolean shouldGenerateDoc = true;
} catch (TimeoutException e) {
logger.error("dbt command timed out after {} milliseconds", Long.valueOf(request.timeoutMillis()));
public void init(final Path projectPath, String select) {
init(projectPath, select, DbParamsSource.FROM_FILE, false);
}
if (run.process != null && run.process.isAlive()) {
run.process.destroyForcibly();
public void init(final Path projectPath, String select, boolean test) {
init(projectPath, select, DbParamsSource.FROM_FILE, test);
}
public void init(final Path projectPath, String select, DbParamsSource dbParamsSource, boolean test) {
if (!projectPath.toFile().isDirectory()) {
throw new RuntimeException(projectPath.toAbsolutePath() + " is no directory!");
}
File projectFile = Path.of(projectPath.toString(), "dbt_project.yml").toFile();
if (!projectFile.exists()) {
throw new RuntimeException(projectPath + " is no dbt project directory (no dbt_project.yml found)!");
}
File profilesFile = Path.of(projectPath.toString(), "profiles.yml").toFile();
if (!profilesFile.exists()) {
throw new RuntimeException(projectPath + " is no dbt project directory (no profiles.yml found)!");
}
this.projectPath = projectPath.toAbsolutePath().normalize();
this.dbtInstallDir = DbtUtils.getDbtInstallDir();
this.pythonExecutable = DbtUtils.getPython3ExecutableName();
this.select = select;
this.test = test;
// reset state members
this.process = null;
this.dbtLog = new ArrayList<>();
this.runtimeErrors = "";
this.modelCount = 0;
this.stats = null;
if(this.test) {
this.shouldGenerateDoc = false;
}
// TODO: there has to be a better way
// -> Ja, wie wäre es, wenn die ganzen Parameter für die Datenbankverbindung "injiziert" würden?
if (dbParamsSource == DbParamsSource.FROM_FILE) {
try {
HikariDataSource eduetl = dataSource.unwrap(HikariDataSource.class);
String jdbcUrl = eduetl.getJdbcUrl();
String cleanUrl = jdbcUrl.substring(5); // remove "jdbc:"
URI uri = URI.create(cleanUrl);
String eduetlHost = uri.getHost();
int eduetlPort = uri.getPort(); // returns -1 if no port specified
String eduetlFinalPort = String.valueOf(eduetlPort != -1 ? Integer.valueOf(port) : "5432");
String eduetlUsername = "";
String eduetlDbName = "";
try(Connection con = dataSource.getConnection()){
eduetlUsername = con.getMetaData().getUserName();
eduetlDbName = con.getCatalog();
}
this.user = eduetlUsername;
this.password = eduetl.getPassword();
this.host = eduetlHost;
this.port = eduetlFinalPort;
this.db = eduetlDbName;
} catch (Exception e) {
throw new RuntimeException("Couldn't get connection info for eduetl", e);
}
}
future.cancel(true);
}
DbtRunResult result = new DbtRunResult();
result.setState(DbtExecutionState.TIMEOUT);
result.setErrorMessages(Arrays.asList("DBT run timeout!"));
return result;
} catch (Exception e) {
logger.error("Failed with error: ", e);
DbtRunResult result = new DbtRunResult();
result.setState(DbtExecutionState.FAILURE);
result.setErrorMessages(Arrays.asList(e.getMessage()));
return result;
private List<String> buildDbtCommand() {
String dbt = DbtUtils.getDbtBinaryPath().toString();
logger.info("Project path: "+ projectPath.toString());
logger.info("dbt_bin: "+ dbt.toString());
List<String> baseCommand = new ArrayList<String>(Arrays.asList(
pythonExecutable,
dbt,
"--log-format",
"json",
"--no-use-colors"
));
if(!isLinux && DbtUtils.isVirtualEnvInstalled()) {
// On Windows, when using a virtual environment, we must run the dbt
// executable directly without invoking it through the python interpreter
baseCommand.remove(0);
}
if(this.test) {
baseCommand.add("test");
} else {
baseCommand.addAll(Arrays.asList(
"build",
"--resource-types",
"snapshot",
"seed",
"model"
));
}
if(select != null && !select.isBlank()) {
baseCommand.add("--select");
baseCommand.add(select);
}
return baseCommand;
}
private Map<String, String> getEnvironment(){
@Override
public void run() {
List<String> pbCommand = buildDbtCommand();
logger.info(Arrays.toString(pbCommand.toArray()));
ProcessBuilder p = new ProcessBuilder(pbCommand).directory(projectPath.toFile());
Map<String,String> env = getComponentStatus();
env.putAll(getDbtEnvironment());
env.putAll(getUnloadParams());
@ -97,9 +205,223 @@ public class DbtWrapper { @@ -97,9 +205,223 @@ public class DbtWrapper {
Map<String, String> rls_env_var = Map.of("ROW_LEVEL_SECURITY", "true");
env.putAll(rls_env_var);
}
p.environment().putAll(env);
p.redirectErrorStream(false);
dbtLog = new ArrayList<>();
try {
modelCount = 0;
process = p.start();
logger.info("ProcessBuilder start");
logger.info("PYTHONUSERBASE: " + p.environment().get("PYTHONUSERBASE"));
String pathVar = "PATH";
if(!this.isLinux) {
pathVar = "Path";
}
logger.info("PATH: " + p.environment().get(pathVar));
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
CompletableFuture<String> errFuture = readOutStream(process.getErrorStream());
errFuture.thenAccept( (error) ->{
checkStdError(error);
});
var line = "";
ObjectMapper objectMapper = new ObjectMapper();
while((line = reader.readLine()) != null) {
JsonNode node = objectMapper.readTree(line.getBytes());
logger.info(node.get("info").get("msg").asText());
JsonNode data = node.get("data");
if (data.get("stats") != null) {
stats = node;
} else {
if (data.has("index")) {
modelCount = Math.max(modelCount, data.get("index").asInt());
}
dbtLog.add(node);
}
}
} catch (Exception e) {
logger.error("Couldn't start dbt run!", e);
this.runtimeErrors = ("Couldn't start dbt run!\n" + e.getMessage());
} finally {
if(this.shouldGenerateDoc) {
generateDocs(env);
}
}
}
private Map<String,String> getComponentStatus() {
Map<String,String> result = new HashMap<>();
EtlJobApi api = etlJobApi == null ? SuperXManager.getBean(null, EtlJobApi.class) : etlJobApi;
List<Component> components = api.getInstallJobs();
for (Component comp : components) {
result.put("COMP_" + comp.getAbbreviation().toUpperCase() + "_INSTALLED", comp.isInstalled() ? "TRUE" : "FALSE");
}
return result;
}
public int getModelCount() {
return modelCount;
}
public List<String> getErrorMessages() {
var result = new ArrayList<String>();
if (!this.runtimeErrors.isEmpty()) {
// stderr was not empty!
result.add(runtimeErrors);
} else {
// "regular" errors during dbt execution
for (var node : dbtLog) {
var level = findFirstOccurence("level", node);
if (level.isPresent() && "error".equals(level.get().asText())) {
var msg = findFirstOccurence("msg", node);
if (msg.isPresent()) {
result.add(msg.get().asText());
} else {
logger.warn("No msg property found in dbt log " + node);
}
} else {
logger.warn("No level property found in dbt log " + node);
}
}
}
return result;
}
public Process getProcess() {
return this.process;
}
public ExecutionState getExecutionState() {
if (this.stats == null) {
return ExecutionState.ACTIVE;
}
if (this.stats.get("data").get("stats").get("error").asInt() == 0) {
return ExecutionState.SUCCESS;
}
return ExecutionState.FAILURE;
}
public void generateDocs(Map<String,String> env) {
try {
Path dbt = DbtUtils.getDbtBinaryPath();
List<String> pbCommand = new ArrayList<String>(Arrays.asList(pythonExecutable, dbt.toString(), "--no-use-colors", "docs", "generate"));
if(select != null && !select.isBlank()) {
pbCommand.add("--select");
pbCommand.add(select);
}
ProcessBuilder pb = new ProcessBuilder(pbCommand).directory(projectPath.toFile());
pb.environment().putAll(env);
pb.redirectErrorStream(true);
Process p = pb.start();
final InputStream stdoutInputStream = p.getInputStream();
final BufferedReader stdoutReader =
new BufferedReader(new InputStreamReader(stdoutInputStream));
String line = null;
while ((line = stdoutReader.readLine()) != null) {
logger.info(line);
}
} catch (Exception iox) {
throw new RuntimeException("Couldn't run dbt generate docs ", iox);
}
}
public int getNodeCount() {
int lineNr = 0;
try {
Path dbt = DbtUtils.getDbtBinaryPath();
List<String> pbCommand = new ArrayList<String>(Arrays.asList(
pythonExecutable, dbt.toString(),
"ls",
"--resource-types"
));
if(this.test) {
pbCommand.add("test");
} else {
pbCommand.addAll(Arrays.asList(
"snapshot",
"seed",
"model"
));
}
if(select != null && !select.isBlank()) {
pbCommand.add("--select");
pbCommand.add(select);
}
ProcessBuilder pb = new ProcessBuilder(pbCommand).directory(projectPath.toFile());
Map<String,String> env = getComponentStatus();
env.putAll(getDbtEnvironment());
pb.environment().putAll(env);
pb.redirectErrorStream(true);
Process p = pb.start();
final InputStream stdoutInputStream = p.getInputStream();
final BufferedReader stdoutReader =
new BufferedReader(new InputStreamReader(stdoutInputStream));
String line = "";
while ((line = stdoutReader.readLine()) != null) {
if (!line.matches("(\\w+\\.)+\\w+$")) {
continue;
}
lineNr += 1;
logger.info(line);
}
} catch (Exception iox) {
throw new RuntimeException("Couldn't run dbt ls ", iox);
}
return lineNr;
}
public Map<String,String> getDbtEnvironment() {
Map<String,String> env = Map.ofEntries(
entry("PYTHONUSERBASE", dbtInstallDir.toString()),
// entry("DBT_TARGET_PATH", SuperXManager.getWEB_INFPfad() + File.separator + "dbt_target"),
entry("DBT_HOST", host),
entry("DBT_PORT", port.toString()),
entry("DBT_USER", user),
entry("DBT_ENV_SECRET_PW", password),
entry("DBT_DB", db)
);
return env;
}
public List<JsonNode> getDbtLog() {
return List.copyOf(this.dbtLog);
}
private static Optional<JsonNode> findFirstOccurence(String name, JsonNode searchNode) {
JsonNode parent = searchNode.findParent(name);
JsonNode result = parent != null ? parent.get(name) : null;
return Optional.of(result);
}
private boolean checkStdError(String errorString) {
boolean error = false;
if (errorString.length() > 0) {
this.runtimeErrors = "Error starting dbt: \n" + errorString;
logger.error(runtimeErrors);
error = true;
}
return error;
}
private static CompletableFuture<String> readOutStream(InputStream is) {
return CompletableFuture.supplyAsync(() -> {
try (
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
) {
StringBuilder res = new StringBuilder();
String inputLine;
while ((inputLine = br.readLine()) != null) {
res.append(inputLine).append(System.lineSeparator());
}
return res.toString();
} catch (Throwable e) {
throw new RuntimeException("problem with executing program", e);
}
});
}
private Map<String,String> getUnloadParams() {
Map<String,String> result = new HashMap<>();
if (dataSource != null) {
@ -120,31 +442,45 @@ public class DbtWrapper { @@ -120,31 +442,45 @@ public class DbtWrapper {
}
return params;
}
});
}
return result;
}
private Map<String,String> getComponentStatus() {
Map<String,String> result = new HashMap<>();
EtlJobApi api = etlJobApi == null ? ApplicationContextHolder.getBean(null, EtlJobApi.class) : etlJobApi;
List<Component> components = api.getInstallables();
for (Component comp : components) {
result.put("COMP_" + comp.getAbbreviation().toUpperCase() + "_INSTALLED", comp.isInstalled() ? "TRUE" : "FALSE");
public static List<String> getStagingTables(String select) {
try {
Path dbtProjectDir = Path.of(SuperXManager.getWEB_INFPfad(), "..", "dbt", "projects", "hisinone", "transform");
ProcessBuilder builder = new ProcessBuilder(DbtUtils.getPython3ExecutableName(), DbtUtils.getDbtBinaryPath().toString(), "ls", "--select", select, "--output", "name").directory(dbtProjectDir.toFile());
Map<String, String> env = builder.environment();
env.put("COMP_RES_INSTALLED", "true");
env.put("DBT_DB", "eduetl");
env.put("PYTHONUSERBASE", DbtUtils.getDbtInstallDir().toString());
Process process = builder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
CompletableFuture<String> errFuture = readOutStream(process.getErrorStream());
errFuture.thenAccept( (error) ->{
if (error.length() > 0) {
logger.warn("WARNING: could not get staging tables for select: " + select);
logger.warn(error);
}
});
List<String> results = reader.lines().collect(Collectors.toList());
List<String> tables = new ArrayList<String>();
for (String model : results) {
if(model.startsWith("staging.")) {
tables.add(model.split("staging.")[1]);
}
}
return tables;
} catch (Exception e) {
e.printStackTrace();
}
return result;
return null;
}
private Map<String,String> getDbtEnvironment() {
Map<String,String> env = Map.ofEntries(
entry("PYTHONUSERBASE", DbtUtils.getDbtInstallDir().toString()),
entry("DBT_HOST", dbtProfile.get("DBT_HOST")),
entry("DBT_PORT", dbtProfile.get("DBT_PORT")),
entry("DBT_USER", dbtProfile.get("DBT_USER")),
entry("DBT_ENV_SECRET_PW", dbtProfile.get("DBT_ENV_SECRET_PW")),
entry("DBT_DB", dbtProfile.get("DBT_DB"))
);
return env;
public void setShouldGenerateDoc(boolean shouldGenerateDoc) {
this.shouldGenerateDoc = shouldGenerateDoc;
}

13
src/de/superx/etl/biad/DBSource.java

@ -2,12 +2,15 @@ package de.superx.etl.biad; @@ -2,12 +2,15 @@ package de.superx.etl.biad;
import java.util.List;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import de.superx.job.ExtractAction;
public class DBSource {
private String name;
private String schema;
private String database;
private List<SourceTable> tables;
public DBSource(String name, String schema, String database, List<SourceTable> tables) {
@ -51,14 +54,6 @@ public class DBSource { @@ -51,14 +54,6 @@ public class DBSource {
this.database = database;
}
public String getSrcDatabase() {
int indexUnderscore = schema.indexOf('_');
if (indexUnderscore != -1) {
return schema.substring(indexUnderscore + 1);
}
return "hisinone";
}
public List<SourceTable> getTables() {
return tables;
}

4
src/de/superx/jdbc/entity/Cif.java

@ -33,7 +33,7 @@ public class Cif extends EntityBase { @@ -33,7 +33,7 @@ public class Cif extends EntityBase {
@DynamicFieldType(label = "Gültig bis", editControlType = FieldType.DatePicker, columnType = ColumnType.DateColumn, visibleInSimplifiedForm = false)
public Date d_akt_bis;
@DynamicFieldType(label = "Kurztext")
@DynamicFieldType(label = "Kurzbeschreibung")
public String kurz;
@DynamicFieldType(label = "Drucktext")
@ -48,7 +48,7 @@ public class Cif extends EntityBase { @@ -48,7 +48,7 @@ public class Cif extends EntityBase {
@DynamicFieldType(label = "Langbesch. 3", visibleInSimplifiedForm = false)
public String lang_3;
@DynamicFieldType(label = "Schlüssel (uniquename)")
@DynamicFieldType(label = "Eindeutiger Name")
public String uniquename;
@DynamicFieldType(label = "sort1", visibleInSimplifiedForm = false)

4
src/de/superx/jdbc/entity/Cifx.java

@ -34,7 +34,7 @@ public class Cifx extends EntityBase { @@ -34,7 +34,7 @@ public class Cifx extends EntityBase {
@DynamicFieldType(label = "Gültig bis", editControlType = FieldType.DatePicker, columnType = ColumnType.DateColumn, visibleInSimplifiedForm = false)
public Date d_akt_bis;
@DynamicFieldType(label = "Kurztext")
@DynamicFieldType(label = "Kurzbeschreibung")
public String kurz;
@DynamicFieldType(label = "Drucktext")
@ -73,7 +73,7 @@ public class Cifx extends EntityBase { @@ -73,7 +73,7 @@ public class Cifx extends EntityBase {
@DynamicFieldType(label = "HIS-KEY")
public String hiskey_id;
@DynamicFieldType(label = "Schlüssel (uniquename)")
@DynamicFieldType(label = "Eindeutiger Name")
public String uniquename;
// wird nicht verwendet

43
src/de/superx/jdbc/entity/CifxCustomize.java

@ -1,43 +0,0 @@ @@ -1,43 +0,0 @@
package de.superx.jdbc.entity;
import org.springframework.data.annotation.Id;
import org.springframework.data.relational.core.mapping.Table;
import de.superx.jdbc.model.DynamicFieldType;
import de.superx.jdbc.model.EntityDescriptor;
@Table("cifx_customize")
public class CifxCustomize extends EntityBase {
@Id
@DynamicFieldType(label = "ID", visibleInSimplifiedForm = false, readOnly = true)
public Integer tid;
@DynamicFieldType(label = "CIFX Tabelle")
public String cifx_table;
@DynamicFieldType(label = "Schlüsselgruppe (key)")
public Integer key;
@EntityDescriptor
@DynamicFieldType(label = "Schlüssel (apnr)")
public String apnr;
@DynamicFieldType(label = "Kurztext")
public String kurz;
@DynamicFieldType(label = "Drucktext")
public String druck;
@DynamicFieldType(label = "Langbesch. 1", visibleInSimplifiedForm = false)
public String lang_1;
@DynamicFieldType(label = "Schlüssel (uniquename)")
public String uniquename;
@DynamicFieldType(label = "struktur_c", visibleInSimplifiedForm = true)
public String struktur_c;
}

9
src/de/superx/jdbc/repository/CifxCustomizeRepository.java

@ -1,9 +0,0 @@ @@ -1,9 +0,0 @@
package de.superx.jdbc.repository;
import org.springframework.data.repository.RepositoryDefinition;
import de.superx.jdbc.entity.CifxCustomize;
@RepositoryDefinition(domainClass = CifxCustomize.class, idClass = Integer.class)
public interface CifxCustomizeRepository extends BiaAdminCrudRepository<CifxCustomize> {
}

10
src/de/superx/job/ContainerNode.java

@ -82,12 +82,10 @@ public class ContainerNode extends ActionNode { @@ -82,12 +82,10 @@ public class ContainerNode extends ActionNode {
private Map<String,String> parameters = new HashMap<>();
private boolean ignoreErrors = false;
private JobSource source = JobSource.FILE;
private String description;
public ContainerNode(String name, String id, Integer systemInfoId) {
super(name, id, systemInfoId, true, ActionType.CONTAINER);
this.jobType = JobType.getJobTypeForName(id);
this.description = name;
}
public void add(ActionNode node) {
@ -170,14 +168,6 @@ public class ContainerNode extends ActionNode { @@ -170,14 +168,6 @@ public class ContainerNode extends ActionNode {
return source;
}
public void setDescription(String description) {
this.description = description;
}
public String getDescription() {
return this.description;
}
public void setCustom(int custom) {
this.custom = Integer.valueOf(custom);
List<ActionNode> children = this.getActions();

6
src/de/superx/job/ExecuteSQLFluffAction.java

@ -1,12 +1,8 @@ @@ -1,12 +1,8 @@
package de.superx.job;
public class ExecuteSQLFluffAction extends ActionNode {
public String dbtProject;
public ExecuteSQLFluffAction (String dbtProject, String name, String id, Integer systemInfoId, boolean active, Integer custom) {
public ExecuteSQLFluffAction (String name, String id, Integer systemInfoId, boolean active, Integer custom) {
super(name, id, systemInfoId, active, custom, ActionType.EXECUTE_SQLFLUFF);
this.dbtProject = dbtProject;
}
public ExecuteSQLFluffAction () {}

4
src/de/superx/job/ExtractAction.java

@ -35,10 +35,10 @@ public class ExtractAction extends ActionNode { @@ -35,10 +35,10 @@ public class ExtractAction extends ActionNode {
super();
}
public static ExtractAction createFromSourceTable(SourceTable table, String unlFile, String schema, String sourceSystem) {
public static ExtractAction createFromSourceTable(SourceTable table, String unlFile, String schema) {
return new ExtractAction(table.getName(), table.getName(),
table.getExtractionScript(), table.getExtractionInclude(),
schema, unlFile, sourceSystem, Integer.valueOf(440), null);
schema, unlFile, "hisinone", Integer.valueOf(440), null);
}
private static String makeName(String id, String unlFile, String dataSource) {

4
src/de/superx/rest/Cache.java

@ -42,7 +42,7 @@ public class Cache { @@ -42,7 +42,7 @@ public class Cache {
public static CacheInfo status() {
return new CacheInfo(TransletCache.getLastClear(), TransletCache.getTransletPoolSize(), TransletCache.getMerkmaleSize());
}
@RequestMapping(path = "/clearpool", method = RequestMethod.POST)
@RequestMapping(path = "/clearpool", method = RequestMethod.GET)
public static String clearpool(@Context HttpServletRequest httpRequest) {
String returntext="Problem: Cannot clear server cache.";
try {
@ -58,7 +58,7 @@ public class Cache { @@ -58,7 +58,7 @@ public class Cache {
}
return returntext;
}
@RequestMapping(path = "/cleartranslet", method = RequestMethod.POST)
@RequestMapping(path = "/cleartranslet", method = RequestMethod.GET)
public static String cleartranslet(@Context HttpServletRequest httpRequest) {
String returntext="Problem: Cannot clear translet cache.";
try {

36
src/de/superx/rest/EtlJobApi.java

@ -170,7 +170,7 @@ public class EtlJobApi extends RestControllerBase { @@ -170,7 +170,7 @@ public class EtlJobApi extends RestControllerBase {
* Stop all running JobExecutions.
* @return
*/
@RequestMapping(path="/job/stopAll", method = RequestMethod.POST)
@RequestMapping(path="/job/stopAll", method = RequestMethod.GET)
public boolean stopAllJobs() {
userService.checkAdminRights();
JobUtils.terminateAllJobExecutions(jobExplorer, jobOperator);
@ -189,10 +189,9 @@ public class EtlJobApi extends RestControllerBase { @@ -189,10 +189,9 @@ public class EtlJobApi extends RestControllerBase {
return jobDescriptionSource.getJobsForComponent(abbreviation);
}
@RequestMapping(path="/job/writeJobsToDb", method = RequestMethod.POST)
public boolean writeJobsToDb() {
userService.checkAdminRights();
try {
@RequestMapping(path="/job/writeJobsToDb", method = RequestMethod.GET)
public boolean writeJobsToDb() {
try {
List<ContainerNode> jobs = entityJobDescriptionSource.readJobs();
for (ContainerNode containerNode : jobs) {
try {
@ -217,7 +216,6 @@ public class EtlJobApi extends RestControllerBase { @@ -217,7 +216,6 @@ public class EtlJobApi extends RestControllerBase {
@RequestMapping(path="/job/save", method = RequestMethod.POST)
public Long saveJob(@RequestBody final Map<String, Object> jobJson) throws Exception {
userService.checkAdminRights();
ContainerNode container = (ContainerNode) jsonJobDescriptionAdapter.convert(jobJson);
// TODO beim Speichern in der DB wird custom=true gesetzt, weil sonst (im Moment noch?)
// Beim nächsten Tomcatstart der Job von SuperXManager.initEtl() wieder gelöscht wird!
@ -235,11 +233,10 @@ public class EtlJobApi extends RestControllerBase { @@ -235,11 +233,10 @@ public class EtlJobApi extends RestControllerBase {
return executeJob(sourceDatabase, container);
}
@RequestMapping(path="/job/execute/{sourceDatabase}/{id}", method=RequestMethod.POST)
@RequestMapping(path="/job/execute/{sourceDatabase}/{id}", method=RequestMethod.GET)
public Long executeJob(@PathVariable(value = "sourceDatabase") String sourceDatabase, @PathVariable(value="id") String id)
throws DuplicateJobException, JobExecutionAlreadyRunningException, JobRestartException,
JobInstanceAlreadyCompleteException, JobParametersInvalidException, UnsupportedPythonVersionException {
userService.checkEtlJobRights();
// TODO There is no official convention,
// that job names start with component name
// but for now it holds.
@ -323,7 +320,7 @@ public class EtlJobApi extends RestControllerBase { @@ -323,7 +320,7 @@ public class EtlJobApi extends RestControllerBase {
return startJob(job, stepCounter.getCount(), additionalParameters);
}
@RequestMapping(path="/job/install/{component}", method=RequestMethod.POST)
@RequestMapping(path="/job/install/{component}", method=RequestMethod.GET)
public Long executeInstall(@PathVariable(value="component") String component) throws Exception {
userService.checkAdminRights();
if (isAnyJobInstanceRunning()) {
@ -386,7 +383,7 @@ public class EtlJobApi extends RestControllerBase { @@ -386,7 +383,7 @@ public class EtlJobApi extends RestControllerBase {
return startJob(job, stepCounter.getCount(), parameterBuilder.toJobParameters());
}
@RequestMapping(path="/job/upgrade/{component}", method=RequestMethod.POST)
@RequestMapping(path="/job/upgrade/{component}", method=RequestMethod.GET)
public Long executeUpgrade(@PathVariable(value="component") String component) throws Exception {
userService.checkAdminRights();
if (isAnyJobInstanceRunning()) {
@ -405,7 +402,7 @@ public class EtlJobApi extends RestControllerBase { @@ -405,7 +402,7 @@ public class EtlJobApi extends RestControllerBase {
return startJob(job, stepCounter.getCount(), parameterBuilder.toJobParameters());
}
@RequestMapping(path="/job/upgrade_all", method=RequestMethod.POST)
@RequestMapping(path="/job/upgrade_all", method=RequestMethod.GET)
public Long executeUpgradeAll() throws Exception {
userService.checkAdminRights();
if (isAnyJobInstanceRunning()) {
@ -430,7 +427,7 @@ public class EtlJobApi extends RestControllerBase { @@ -430,7 +427,7 @@ public class EtlJobApi extends RestControllerBase {
return startJob(job, stepCounter.getCount(), null);
}
@RequestMapping(path="/job/uninstall/{component}", method=RequestMethod.POST)
@RequestMapping(path="/job/uninstall/{component}", method=RequestMethod.GET)
public Long executeUninstall(@PathVariable(value="component") String component) throws Exception {
userService.checkAdminRights();
if (isAnyJobInstanceRunning()) {
@ -442,7 +439,7 @@ public class EtlJobApi extends RestControllerBase { @@ -442,7 +439,7 @@ public class EtlJobApi extends RestControllerBase {
return startJob(job, stepCounter.getCount(), null);
}
@RequestMapping(path="/unload/{component}", method = RequestMethod.POST)
@RequestMapping(path="/unload/{component}", method = RequestMethod.GET)
public Long unload(@PathVariable(value = "component") String component) throws Exception {
userService.checkEtlJobRights();
if (isAnyJobInstanceRunning()) {
@ -455,7 +452,7 @@ public class EtlJobApi extends RestControllerBase { @@ -455,7 +452,7 @@ public class EtlJobApi extends RestControllerBase {
return startJob(job, stepCounter.getCount(), null);
}
@RequestMapping(path="/transform/{component}", method = RequestMethod.POST)
@RequestMapping(path="/transform/{component}", method = RequestMethod.GET)
public Long load(@PathVariable(value = "component") String component) throws Exception {
userService.checkEtlJobRights();
if (isAnyJobInstanceRunning()) {
@ -468,7 +465,7 @@ public class EtlJobApi extends RestControllerBase { @@ -468,7 +465,7 @@ public class EtlJobApi extends RestControllerBase {
return startJob(job, stepCounter.getCount(), null);
}
@RequestMapping(path="/complete/{component}", method = RequestMethod.POST)
@RequestMapping(path="/complete/{component}", method = RequestMethod.GET)
public Long complete(@PathVariable(value = "component") String component) throws Exception {
userService.checkEtlJobRights();
if (isAnyJobInstanceRunning()) {
@ -625,13 +622,13 @@ public class EtlJobApi extends RestControllerBase { @@ -625,13 +622,13 @@ public class EtlJobApi extends RestControllerBase {
}
@RequestMapping(path="/stop/{id}", method = RequestMethod.POST)
@RequestMapping(path="/stop/{id}", method = RequestMethod.GET)
public boolean stop(@PathVariable(value = "id") Long id) throws Exception {
userService.checkEtlJobRights();
return JobUtils.terminateJobExecution(jobExplorer, jobOperator, id);
}
@RequestMapping(path="/start/{id}", method = RequestMethod.POST)
@RequestMapping(path="/start/{id}", method = RequestMethod.GET)
public Long start(@PathVariable(value = "id") Long id) throws Exception {
userService.checkEtlJobRights();
Long started = jobOperator.restart(id.longValue());
@ -639,7 +636,7 @@ public class EtlJobApi extends RestControllerBase { @@ -639,7 +636,7 @@ public class EtlJobApi extends RestControllerBase {
}
@RequestMapping(path="/installFunctions/{component}", method = RequestMethod.POST)
@RequestMapping(path="/installFunctions/{component}", method = RequestMethod.GET)
public boolean installModuleFunctions(@PathVariable(value = "component") String component) {
File moduleFile = PathAndFileUtils.getXmlFile(component);
SAXReader sr = new SAXReader();
@ -916,7 +913,6 @@ public class EtlJobApi extends RestControllerBase { @@ -916,7 +913,6 @@ public class EtlJobApi extends RestControllerBase {
job.getJobType(),
job.tid == null ? null : Long.valueOf(job.tid.longValue()),
job.id);
connector.setDescription(job.getDescription());
component.addChild(connector);
}
}
@ -960,7 +956,7 @@ public class EtlJobApi extends RestControllerBase { @@ -960,7 +956,7 @@ public class EtlJobApi extends RestControllerBase {
}
}
public List<Component> getInstallables(){
private List<Component> getInstallables(){
List<Component> components = new ArrayList<>();
try {
//List<ContainerNode> nodes = ((DbJobDescriptionAdapter) jobDescriptionSource).readInstallJobs(null);

2
src/de/superx/rest/LogsProvider.java

@ -120,7 +120,7 @@ public class LogsProvider { @@ -120,7 +120,7 @@ public class LogsProvider {
}
@RequestMapping(path = "/reload_log4j", method = RequestMethod.POST)
@RequestMapping(path = "/reload_log4j", method = RequestMethod.GET)
public static String reloadLog4J(@Context HttpServletRequest httpRequest) {
logger.info("*** log4j.properties neu einlesen ***");
String webInfPath = PathAndFileUtils.getWebinfPath();

2
src/de/superx/rest/Report.java

@ -306,8 +306,6 @@ public class Report extends ReportApiBase { @@ -306,8 +306,6 @@ public class Report extends ReportApiBase {
writeOutputToRespons(params, request_wrapper, response_interceptor, maske, currentXml);
response_interceptor.setContentType(MediaType.APPLICATION_JSON_VALUE);
Download dl = createDownloadObject(response_interceptor, maske, user);
// set content-type again for 'safety' - see https://hiszilla.his.de/hiszilla/show_bug.cgi?id=375530
response_interceptor.setContentType(MediaType.APPLICATION_JSON_VALUE);
return dl;
}
}

8
src/de/superx/rest/RestControllerBase.java

@ -22,7 +22,6 @@ import org.springframework.web.method.annotation.MethodArgumentTypeMismatchExcep @@ -22,7 +22,6 @@ import org.springframework.web.method.annotation.MethodArgumentTypeMismatchExcep
import de.memtext.tree.KeyParentEqualException;
import de.memtext.tree.NoMainEntryException;
import de.superx.bianalysis.FaultyMetadataException;
import de.superx.bianalysis.service.IllegalMetaDataOperation;
import de.superx.common.AccessDeniedException;
import de.superx.common.ConfigurationException;
import de.superx.common.DBServletException;
@ -128,8 +127,6 @@ abstract public class RestControllerBase { @@ -128,8 +127,6 @@ abstract public class RestControllerBase {
return handleFaultyMetadataException((FaultyMetadataException)ex);
} else if (ex instanceof RestMessageWrapperException) {
return handleRestMessageWrapperException((RestMessageWrapperException)ex);
} else if (ex instanceof IllegalMetaDataOperation) {
return handleIllegalMetaDataOperation((IllegalMetaDataOperation)ex);
}
else if (ex instanceof RuntimeException) {
// Log stacktraces of runtime exceptions, otherwise the information is lost!
@ -252,11 +249,6 @@ abstract public class RestControllerBase { @@ -252,11 +249,6 @@ abstract public class RestControllerBase {
return handleServerError(ex.getMessage());
}
public ResponseEntity<ExceptionResponse> handleIllegalMetaDataOperation(IllegalMetaDataOperation ex) {
getLogger().error("IllegalMetaDataOperation occured", ex);
return handleServerError(ex.getMessage());
}
public ResponseEntity<ExceptionResponse> handleFaultyMetadataException(FaultyMetadataException ex) {
getLogger().error("FaultyMetadataException occured", ex);
return handleServerError(ex.getMessage());

5
src/de/superx/rest/model/Column.java

@ -21,11 +21,6 @@ public class Column { @@ -21,11 +21,6 @@ public class Column {
this.type = type;
}
public Column(String field, String header) {
this.field = field;
this.header = header;
}
public Column(String field, String header, ColumnType type, boolean groupable) {
this.field = field;
this.header = header;

10
src/de/superx/rest/model/ColumnType.java

@ -20,7 +20,6 @@ public enum ColumnType { @@ -20,7 +20,6 @@ public enum ColumnType {
TimestampColumn(8, new CustomDateFormat("dd.MM.yyyy HH:mm:ss.SSSSSX")), // 8=timestamp
TimeColumn(9, new CustomDateFormat("HH:mm:ss")), // 9=time
StringColumn(1), // Default ist 1=string^
MinuteAndSecondColumn(16),
// Spezielle, abgeleitete Spaltentypen (links, etc.)
DetailColumn(10, new DetailFormat()), // Information about a Detail-Report for this Master-Report
@ -87,14 +86,7 @@ public enum ColumnType { @@ -87,14 +86,7 @@ public enum ColumnType {
try {
result = format.format(val);
} catch (IllegalArgumentException e) {
/* log-Level runter gesetzt: beim Aufruf von Berichten kommt
* es z.T. zu vielen Parsingexceptions, der Bericht wird dennoch
* angezeigt, aber das Logfile explodiert - das sollte nicht sein,
* daher bekommt die Meldung geringere Relevanz
* (man kann überlegen, evtl. sogar "DEBUG" zu setzen!?)
* https://hiszilla.his.de/hiszilla/show_bug.cgi?id=375194
*/
logger.info("Couldn't format value " + val + " as " + format, e);
logger.error("Couldn't format value " + val + " as " + format, e);
}
}
return result;

1
src/de/superx/rest/model/Result.java

@ -22,7 +22,6 @@ public class Result { @@ -22,7 +22,6 @@ public class Result {
public Legend legend = new Legend();
public List<Column> columns = new ArrayList<Column>();
public List<Row> rows = new ArrayList<Row>();
public List<String> headerOrder = new ArrayList<>();
public List<Result> subResults = new ArrayList<Result>();
public Result() { }

9
src/de/superx/rest/model/job/Connector.java

@ -4,7 +4,6 @@ import de.superx.job.ContainerNode.JobType; @@ -4,7 +4,6 @@ import de.superx.job.ContainerNode.JobType;
public class Connector {
private String name;
private String description;
private String sourceDatabase;
private JobType jobType;
private Long jobId;
@ -23,14 +22,6 @@ public class Connector { @@ -23,14 +22,6 @@ public class Connector {
return name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public JobType getJobType() {
return jobType;
}

4
src/de/superx/saiku/SuperxMondrianDataSourceResolver.java

@ -4,8 +4,8 @@ import javax.sql.DataSource; @@ -4,8 +4,8 @@ import javax.sql.DataSource;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import de.superx.servlet.SuperXManager;
import de.superx.spring.batch.His1DataSources;
import de.superx.spring.service.ApplicationContextHolder;
import mondrian.spi.DataSourceResolver;
public class SuperxMondrianDataSourceResolver implements DataSourceResolver {
@ -14,7 +14,7 @@ public class SuperxMondrianDataSourceResolver implements DataSourceResolver { @@ -14,7 +14,7 @@ public class SuperxMondrianDataSourceResolver implements DataSourceResolver {
public DataSource lookup(String dataSourceName) throws Exception {
DataSource result = null;
try {
His1DataSources h1ds = ApplicationContextHolder.getBean("dataSources", His1DataSources.class);
His1DataSources h1ds = SuperXManager.getBean("dataSources", His1DataSources.class);
result = h1ds.get(dataSourceName);
} catch (NoSuchBeanDefinitionException nsbde) {
return null;

7
src/de/superx/servlet/AbstractSuperXServlet.java

@ -144,8 +144,15 @@ public abstract class AbstractSuperXServlet extends HttpServlet { @@ -144,8 +144,15 @@ public abstract class AbstractSuperXServlet extends HttpServlet {
public static String insertCsrfToken(HttpServletRequest request, String html) throws IOException {
CsrfToken token = SuperXServletHelper.getCsRfToken(request);
// -> introduce more testable signature
if(token==null)
{
return html;
}
else
{
String result = HtmlUtils.insertCsrfToken(token, html);
return result;
}
}
/**

446
src/de/superx/servlet/DbFormsController.java

@ -0,0 +1,446 @@ @@ -0,0 +1,446 @@
package de.superx.servlet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.dbforms.config.DbFormsConfig;
import org.dbforms.config.DbFormsConfigRegistry;
import org.dbforms.config.MultipleValidationException;
import org.dbforms.config.Table;
import org.dbforms.event.DatabaseEvent;
import org.dbforms.event.EventEngine;
import org.dbforms.event.WebEvent;
import org.dbforms.util.MessageResources;
import org.dbforms.util.MultipartRequest;
import org.dbforms.util.ParseUtil;
import org.dbforms.util.SqlUtil;
import org.dbforms.util.Util;
import org.dbforms.validation.ValidatorConstants;
import de.superx.util.DbFormsMultipartRequest;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Vector;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import static de.superx.servlet.SxSQL_Server.DEFAULT_MANDANTEN_ID;
/**
*
* @author Witt
* This class inherits from the DbForms Controller:
* As CSRF protection broke the file upload, we need to adjust some classes...and this is the way we "inject" them to DbForms...
*/
public class DbFormsController extends HttpServlet {
/** logging category for this class */
private static Log logCat = LogFactory.getLog(DbFormsController.class.getName());
/** 100KB default upload size */
private int maxUploadSize = 102400;
/**
* Process an HTTP "GET" request.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet exception occurs
*/
@Override
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
process(request, response);
}
/**
* Process an HTTP "POST" request.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet exception occurs
*/
@Override
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
process(request, response);
}
/**
* Initialize this servlet.
*
* @exception ServletException if the initialization fails
*/
@Override
public void init() throws ServletException {
// if existing and valid, override default maxUploadSize, which determinates how
// big the http/multipart uploads may get
String maxUploadSizeStr = this.getServletConfig().getInitParameter("maxUploadSize");
if (maxUploadSizeStr != null) {
try {
this.maxUploadSize = Integer.parseInt(maxUploadSizeStr);
} catch (NumberFormatException nfe) {
logCat.error("maxUploadSize not a valid number => using default.");
}
}
}
/**
* Gets the connection object. Holds a list of all used connections. So they
* stay open between calls an can be reused
*
* @param request the request object
* @param tableId the table identifier
* @param connectionsTable the connections hash table
*
* @return The connection object
*/
private Connection getConnection(DbFormsConfig config, HttpServletRequest request, int tableId, Hashtable connectionsTable) throws SQLException {
String connectionName = null;
Connection con = null;
// get the connection name from the request;
if (tableId != -1) {
connectionName = ParseUtil.getParameter(request, "invname_" + tableId);
}
connectionName = Util.isNull(connectionName) ? DEFAULT_MANDANTEN_ID : connectionName;
if ((con = (Connection) connectionsTable.get(connectionName)) == null) {
con = config.getConnection(connectionName);
connectionsTable.put(connectionName, con);
}
return con;
}
/**
* In our development, we sometimes set the connection object to autoCommit
* = false in the interceptor (Pre... methods). This allows us to have
* dbForms do part of the required transaction (other parts are done via
* jdbc calls). If the database throws an exception, then we need to make
* sure that the connection is reinitialized (rollbacked) before it is sent
* back into the connection pool. Grunikiewicz.philip&at;hydro.qc.ca
* 2001-10-29
*
* @param con the connection object
*/
private void cleanUpConnectionAfterException(Connection con) {
try {
// Do only if autoCommit is disabled
if ((con != null) && (!con.getAutoCommit())) {
con.rollback();
con.setAutoCommit(true);
}
} catch (java.sql.SQLException e) {
SqlUtil.logSqlException(e);
}
}
/**
* Close all the connections stored into the the connections HashTable.
*
* @param connectionsTable the connections HashTable
*/
private void closeConnections(Hashtable connectionsTable) {
Enumeration cons = connectionsTable.keys();
while (cons.hasMoreElements()) {
String dbConnectionName = (String) cons.nextElement();
Connection con = (Connection) connectionsTable.get(dbConnectionName);
try {
// Do only if autoCommit is disabled
if ((con != null) && (!con.getAutoCommit())) {
con.commit();
con.setAutoCommit(true);
}
} catch (java.sql.SQLException e) {
SqlUtil.logSqlException(e);
}
SqlUtil.closeConnection(con);
}
}
/**
* Process the incoming requests.
*
* @param request the request object
* @param response the response object
*
* @throws IOException
* @throws ServletException
*/
private void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
HttpSession session = request.getSession(true);
logCat.debug("session timeout: " + session.getMaxInactiveInterval());
// take Config-Object from application context - this object should have been
// initalized by Config-Servlet on Webapp/server-startup!
DbFormsConfig config = null;
try {
config = DbFormsConfigRegistry.instance().lookup();
} catch (Exception e) {
logCat.error(e);
throw new ServletException(e);
}
// create RFC-1867 data wrapper for the case the form was sent in multipart mode
// this is needed because common HttpServletRequestImpl - classes do not support it
// the whole application has to access Request via the "ParseUtil" class
// this is also true for jsp files written by the user
// #fixme taglib needed for convenient access to ParseUtil wrapper methods
Hashtable connections = new Hashtable();
String contentType = request.getContentType();
// Verify if Locale have been setted in session with "LOCALE_KEY"
// if not, take the request.getLocale() as default and put it in session
// Verify if Locale have been setted in session with "LOCALE_KEY"
// if not, take the request.getLocale() as default and put it in session
processLocale(request);
if ((contentType != null) && contentType.startsWith("multipart")) {
try {
MultipartRequest multipartRequest = new DbFormsMultipartRequest(request, maxUploadSize);
request.setAttribute("multipartRequest", multipartRequest);
} catch (IOException ioe) {
logCat.error("::process - check if uploaded file(s) exceeded allowed size", ioe);
sendErrorMessage("Check if uploaded file(s) exceeded allowed size.", response);
return;
}
}
Connection con = null;
WebEvent e = null;
Vector errors = new Vector();
try {
request.setAttribute("errors", errors);
EventEngine engine = new EventEngine(request, config);
e = engine.generatePrimaryEvent();
// send as info to dbForms (=> Taglib)
if (e != null) {
request.setAttribute("webEvent", e);
}
try {
con = getConnection(config, request, ((e == null) || (e.getTable() == null)) ? (-1) : e.getTable().getId(), connections);
// primary event can be any kind of event (database, navigation...)
if (e instanceof DatabaseEvent) {
try {
// if hidden formValidatorName exist and it's an Update or Insert event,
// doValidation with Commons-Validator
String formValidatorName = request.getParameter(ValidatorConstants.FORM_VALIDATOR_NAME + "_" + e.getTable().getId());
if (formValidatorName != null) {
((DatabaseEvent) e).doValidation(formValidatorName, getServletContext());
}
((DatabaseEvent) e).processEvent(con);
} catch (SQLException sqle) {
logCat.error("::process - SQLException:", sqle);
errors.addElement(sqle);
cleanUpConnectionAfterException(con);
} catch (MultipleValidationException mve) {
processMultipleValidationException(con, errors, mve);
}
}
//else
//{
// currently, we support db events ONLY
// but in future there may be events with processEvent() method which do not need a jdbc con!
// (you may think: "what about navigation events?" - well they are created by the
// controller but they get executed in the referncing "DbFormTag" at the jsp -- that's why we
// do not any further operations on them right here...we just put them into the request)
//}
// secondary Events are always database events
// (in fact, they all are SQL UPDATEs)
if (engine.getInvolvedTables() != null) {
processInvolvedTables(config, request, connections, e, errors, engine);
}
} catch (SQLException exc) {
throw new ServletException(exc);
}
} finally {
// close all the connections stored into the connections hash table;
closeConnections(connections);
if (e != null) {
// PG - if form contained errors, use followupOnError (if available!)
String followUp = null;
if (errors.size() != 0) {
followUp = e.getFollowUpOnError();
}
if (Util.isNull(followUp)) {
followUp = e.getFollowUp();
}
logCat.info("*** e = " + e + "*** e.getFollowUp() = " + e.getFollowUp());
if (!Util.isNull(followUp)) {
request.getRequestDispatcher(followUp).forward(request, response);
}
}
}
}
/**
* PRIVATE METHODS here
*
* @param request DOCUMENT ME!
* @param connections DOCUMENT ME!
* @param e DOCUMENT ME!
* @param errors DOCUMENT ME!
* @param engine DOCUMENT ME!
*/
/**
* Process tables related to the main event table. <br>
* For every table related to the parent one, generate secundary (update)
* events for that table and execute them.
*
* @param request the request object
* @param connections the connections hashTable
* @param e the main webEvent object
* @param errors the errors vector
* @param engine the eventEngine reference
*/
private void processInvolvedTables(DbFormsConfig config, HttpServletRequest request, Hashtable connections, WebEvent e, Vector errors, EventEngine engine) throws SQLException {
Connection con;
// may be null if empty form!
Enumeration tableEnum = engine.getInvolvedTables().elements();
while (tableEnum.hasMoreElements()) {
Table t = (Table) tableEnum.nextElement();
Enumeration eventEnum = engine.generateSecundaryEvents(t, e);
// scan all the secundary events for the current secundary table;
while (eventEnum.hasMoreElements()) {
DatabaseEvent dbE = (DatabaseEvent) eventEnum.nextElement();
// 2003-02-03 HKK: do not do the work twice - without this every event
// would be generated for each table and event
if (t.getId() == dbE.getTable().getId()) {
con = getConnection(config, request, dbE.getTable().getId(), connections);
// 2003-02-03 HKK: do not do the work twice!!!
String formValidatorName = request.getParameter(ValidatorConstants.FORM_VALIDATOR_NAME + "_" + dbE.getTable().getId());
try {
// if hidden formValidatorName exist and it's an Update or Insert event,
// doValidation with Commons-Validator
if (formValidatorName != null) {
dbE.doValidation(formValidatorName, getServletContext());
}
dbE.processEvent(con);
} catch (SQLException sqle2) {
SqlUtil.logSqlException(sqle2, "::process - exception while process secundary events");
errors.addElement(sqle2);
cleanUpConnectionAfterException(con);
} catch (MultipleValidationException mve) {
processMultipleValidationException(con, errors, mve);
}
}
}
}
}
/**
* Set the default locale
*
* @param request the request object
*/
private void processLocale(HttpServletRequest request) {
String lang = ParseUtil.getParameter(request, "lang");
String country = ParseUtil.getParameter(request, "country", "");
Locale locale = null;
if (!Util.isNull(lang)) {
locale = new Locale(lang, country);
} else if (MessageResources.getLocale(request) == null) {
MessageResources.setLocale(request, request.getLocale());
}
if (locale != null) {
MessageResources.setLocale(request, locale);
}
}
/**
* Process the input MultipleValidationException object.
*
* @param con the connection object to close
* @param errors the errors vector to fill
* @param mve the MultipleValidationException to process
*/
private void processMultipleValidationException(Connection con, Vector errors, MultipleValidationException mve) {
java.util.Vector v = null;
logCat.error("::processMultipleValidationException - exception", mve);
if ((v = mve.getMessages()) != null) {
Enumeration e = v.elements();
while (e.hasMoreElements()) {
errors.addElement(e.nextElement());
}
}
cleanUpConnectionAfterException(con);
}
/**
* Send error messages to the servlet's output stream
*
* @param message the message to display
* @param response the response object
*/
private void sendErrorMessage(String message, HttpServletResponse response) {
try {
PrintWriter out = response.getWriter();
response.setContentType("text/html");
out.println("<html><body><h1>ERROR:</h1><p>");
out.println(message);
out.println("</p></body></html>");
} catch (IOException ioe) {
logCat.error("::sendErrorMessage - senderror message crashed", ioe);
}
}
}

1652
src/de/superx/servlet/SuperXManager.java

File diff suppressed because it is too large Load Diff

36
src/de/superx/servlet/SuperXmlPwChanger.java

@ -39,16 +39,11 @@ import de.superx.util.SqlStringUtils; @@ -39,16 +39,11 @@ import de.superx.util.SqlStringUtils;
public class SuperXmlPwChanger extends HttpServlet {
private static final long serialVersionUID = 1L;
private static String encodeAlgorithm;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
ServletUtils.setPath(getServletConfig().getServletContext().getRealPath("/xml"));
encodeAlgorithm=config
.getInitParameter("encodeAlgorithm");
if(encodeAlgorithm==null || encodeAlgorithm.equals(""))
encodeAlgorithm="SHA512";
}
@ -132,15 +127,8 @@ public class SuperXmlPwChanger extends HttpServlet { @@ -132,15 +127,8 @@ public class SuperXmlPwChanger extends HttpServlet {
sendBackHtml(UserInitializer.getPwChangeHtml(add
+ "<font color=red>Die eingegeben Passw&ouml;rter stimmten nicht &uuml;berein</font>"));
} else {
if (user.isPWUsedBefore(passwort) || CryptUtils.encodeSHA(passwort).equals(
currentSha)
|| CryptUtils.encodeSHA(
passwort + CryptUtils.geheimnis1)
.equals(currentSha)
|| CryptUtils.encodeSHA512(
passwort)
.equals(currentSha)
) {
if (user.isPWUsedBefore(passwort) || CryptUtils.encodeSHA(passwort).equals(currentSha)
|| CryptUtils.encodeSHA(passwort + CryptUtils.geheimnis1).equals(currentSha)) {
sendBackHtml(UserInitializer.getPwChangeHtml(add
+ "<font color=red>Sie m&uuml;ssen ein neues Passwort eingeben (kein bereits fr&uuml;her benutztes)</font>"));
} else {
@ -205,13 +193,8 @@ public class SuperXmlPwChanger extends HttpServlet { @@ -205,13 +193,8 @@ public class SuperXmlPwChanger extends HttpServlet {
if (akt_versuch >= max_versuch)
msg = UserInitializer.MAXIMALE_LOGIN_VERSUCHE_UEBERSCHRITTEN;
else if (CryptUtils.encodeSHA(altesPasswort).equals(oldsha)
||CryptUtils.encodeSHA(
altesPasswort + CryptUtils.geheimnis1).equals(
oldsha)
||CryptUtils.encodeSHA512(
altesPasswort).equals(
oldsha))
isOk = true;
|| CryptUtils.encodeSHA(altesPasswort + CryptUtils.geheimnis1).equals(oldsha))
isOk = true;
else {
try (Statement stm = con.createStatement()) {
stm.executeUpdate("update userinfo set akt_versuch=akt_versuch+1 " + " where tid="
@ -232,14 +215,7 @@ public class SuperXmlPwChanger extends HttpServlet { @@ -232,14 +215,7 @@ public class SuperXmlPwChanger extends HttpServlet {
private void dbUpdate() throws Exception {
Statement stm = con.createStatement();
String newSha = "";
if(encodeAlgorithm.equals("SHA512"))
newSha = CryptUtils.encodeSHA512(passwort);
if(encodeAlgorithm.equals("SHA_SALTED"))
newSha = CryptUtils.encodeSHA512(passwort+ CryptUtils.geheimnis1);
if(encodeAlgorithm.equals("SHA"))
newSha = CryptUtils.encodeSHA(passwort);
String newSha = CryptUtils.encodeSHA(passwort + CryptUtils.geheimnis1);
int rowcount = stm.executeUpdate(
"update userinfo set passwd_sha='" + newSha + "',kennwort='' where tid=" + user.getId() + ";");
if (rowcount != 1)

7
src/de/superx/servlet/SxPool.java

@ -76,9 +76,8 @@ import de.superx.common.TemplateProcessor; @@ -76,9 +76,8 @@ import de.superx.common.TemplateProcessor;
import de.superx.common.TranslationContainer;
import de.superx.conf.InitStatus;
import de.superx.conf.InitStatus.SubSystem;
import de.superx.spring.AppConfig;
import de.superx.spring.batch.His1DataSources;
import de.superx.spring.service.ApplicationContextHolder;
import de.superx.spring.config.BatchConfig;
import de.superx.util.PropsReader;
import de.superx.util.SqlStringUtils;
import freemarker.template.TemplateException;
@ -1683,10 +1682,10 @@ public class SxPool extends GenericObjectPool implements NamedObjectI { @@ -1683,10 +1682,10 @@ public class SxPool extends GenericObjectPool implements NamedObjectI {
}
private void initHikariPool() {
His1DataSources dataSources = ApplicationContextHolder.getBean("dataSources", His1DataSources.class);
His1DataSources dataSources = SuperXManager.getBean("dataSources", His1DataSources.class);
if (dataSources == null) {
logger.warn("No Spring bean dataSources available. Will init Hikari pool standalone!");
AppConfig batchConfig = new AppConfig();
BatchConfig batchConfig = new BatchConfig();
dataSources = batchConfig.dataSources();
}
this.eduetlDataSource = dataSources.get("eduetl");

53
src/de/superx/servlet/UserInitializer.java

@ -57,7 +57,6 @@ import de.superx.common.SxResultRow; @@ -57,7 +57,6 @@ import de.superx.common.SxResultRow;
import de.superx.common.SxResultSet;
import de.superx.common.SxUser;
import de.superx.saiku.SuperxSaikuSessionService;
import de.superx.util.DbUtils;
import de.superx.util.SqlStringUtils;
import freemarker.template.TemplateException;
@ -76,7 +75,6 @@ public class UserInitializer { @@ -76,7 +75,6 @@ public class UserInitializer {
private String mandantenID = DEFAULT_MANDANTEN_ID;
private String oldsha;
private boolean implicit_orgunit_rights_table_exists = false;
private static final String BIAD_MODULE_ABBREVIATION = "biad";
/**
* @param request kann Null sein, wenn von ExecuteMask ausgefuehrt
@ -161,28 +159,15 @@ public class UserInitializer { @@ -161,28 +159,15 @@ public class UserInitializer {
sessionService.authenticate(request, user.getName(), null);
}
}
if(!SuperXManager.isHis1) {
return;
}
SingleConnectionDataSource ds = new SingleConnectionDataSource(con, true);
boolean isBiadInstalled = DbUtils.isModuleInstalled(ds, BIAD_MODULE_ABBREVIATION);
if(!isBiadInstalled) {
return;
}
if(de.superx.servlet.SuperXManager.isHis1)
{
try {
if (SqlAccessRestrictor.rechteModus == SqlAccessRestrictor.Rechtekonzept.INLINE_SQL) {
if ( SqlAccessRestrictor.rechteModus == SqlAccessRestrictor.Rechtekonzept.INLINE_SQL) {
SingleConnectionDataSource ds = new SingleConnectionDataSource(con, true);
UserRestrictionService builder = new UserRestrictionService(user, ds);
builder.clearRestrictions();
// sachgebiete und faktentabellen werden eingeschränkt
builder.applyMetadataRestrictions();
// konkrete dimension werden eingeschränkt bsp: dim_project
builder.calculateDimensionRestrictions(user.getUserPersonId());
builder.applyOrgunitRestrictions();
builder.applyOrgunitRestrictions(user.getHisInOneOrgUnitLidOfRole());
}
if (SqlAccessRestrictor.rechteModus == Rechtekonzept.ROW_LEVEL_SECURITY) {
String dbUserName = String.format("sx_user_%s_%s", user.getUsername(), user.getRole()).toLowerCase();
@ -204,7 +189,7 @@ public class UserInitializer { @@ -204,7 +189,7 @@ public class UserInitializer {
GRANT USAGE ON SCHEMA public TO ${user};
GRANT USAGE ON SCHEMA metadata TO ${user};
GRANT USAGE ON SCHEMA presentation TO ${user};
GRANT SELECT ON TABLE public.user_dimension_grants TO ${user};
GRANT SELECT ON TABLE public.view_rechte TO ${user};
GRANT SELECT ON TABLE public.sachgebiete TO ${user};
GRANT SELECT ON TABLE public.systeminfo TO ${user};
GRANT SELECT ON ALL TABLES IN SCHEMA public TO ${user};
@ -216,6 +201,7 @@ public class UserInitializer { @@ -216,6 +201,7 @@ public class UserInitializer {
// TODO: die Klasse "StrSubstitutor ist deprecated - es gibt als Ersatz
// "StringSubstitutor" in "commons-text" - das müsste man dann bei Zeiten einbinden
String createRoleSql = StrSubstitutor.replace(template, substitutions);
SingleConnectionDataSource ds = new SingleConnectionDataSource(con, true);
JdbcTemplate jt = new JdbcTemplate(ds);
jt.execute(createRoleSql);
}
@ -228,6 +214,7 @@ public class UserInitializer { @@ -228,6 +214,7 @@ public class UserInitializer {
}
throw new NichtAngemeldetException("Probleme bei der Anmeldung. Bitte den Administrator kontaktieren.");
}
}
}
}
@ -410,9 +397,7 @@ public class UserInitializer { @@ -410,9 +397,7 @@ public class UserInitializer {
}
} else {
//Support fuer LDAP-Technik via Tomcat Remote User
if (request.getRemoteUser()!=null ||
(request.getAuthType() !=null && request.getAuthType().equals("shibboleth"))
)
if (request.getRemoteUser()!=null)
{
isPasswordOK=true;
}
@ -931,16 +916,8 @@ public class UserInitializer { @@ -931,16 +916,8 @@ public class UserInitializer {
}
private boolean isPasswordInSuperXDatabaseOK(String enteredPassword) {
boolean isPasswordOK = false;
String encoded_sha = CryptUtils.encodeSHA(enteredPassword);
String encoded_sha2 = CryptUtils.encodeSHA(enteredPassword + CryptUtils.geheimnis1);
String encoded_sha512 = CryptUtils.encodeSHA512(enteredPassword);
if ( user.getCurrentSha().equals(encoded_sha)
|| user.getCurrentSha().equals(encoded_sha2)
|| user.getCurrentSha().equals(encoded_sha512)) {
isPasswordOK = true;
}
return isPasswordOK ;
return EqualsUtil.areEqual(user.getCurrentSha(), CryptUtils.encodeSHA(enteredPassword)) || EqualsUtil
.areEqual(user.getCurrentSha(), CryptUtils.encodeSHA(enteredPassword + CryptUtils.geheimnis1));
}
private void updateLastLogin(Statement stm) throws SQLException {
@ -1076,13 +1053,6 @@ public class UserInitializer { @@ -1076,13 +1053,6 @@ public class UserInitializer {
user.setHisInOneOrgUnitLidOfRole((Integer) attributes.get("orgUnit"));
user.setRoleId((Integer) attributes.get("roleId"));
Object value = attributes.get("userPersonId");
if (value != null) {
user.setUserPersonId(Integer.valueOf(value.toString()));
} else {
user.setUserPersonId(null);
}
// save rights in session
if (request.getSession() != null) {
request.getSession().setAttribute(SessionAttribute.isSuperXStandalone.toString(),
@ -1141,7 +1111,6 @@ public class UserInitializer { @@ -1141,7 +1111,6 @@ public class UserInitializer {
attributes.put("orgUnit", tokenChecker.getOrgunitLid());
attributes.put("rightsString", tokenChecker.getRights());
attributes.put("roleId", tokenChecker.getExternRoleID());
attributes.put("userPersonId", tokenChecker.getExternPersonID());
return attributes;
}
}

281
src/de/superx/spring/AppConfig.java

@ -1,281 +0,0 @@ @@ -1,281 +0,0 @@
package de.superx.spring;
import java.io.File;
import java.nio.file.Path;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.JobLocator;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.JobOperator;
import org.springframework.batch.core.launch.support.SimpleJobOperator;
import org.springframework.batch.core.launch.support.TaskExecutorJobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.init.ScriptUtils;
import org.springframework.transaction.PlatformTransactionManager;
import com.zaxxer.hikari.HikariDataSource;
import de.superx.spring.batch.His1DataSources;
import de.superx.spring.batch.job.JobUtils;
import de.superx.spring.batch.tasklet.RemoveSpringBatchHistoryTasklet;
import de.superx.spring.config.DataSourceDiscardingUserStuff;
import de.superx.util.PathAndFileUtils;
@Configuration
@EnableBatchProcessing(transactionManagerRef = "batchTransactionManager", dataSourceRef = "unrestrictedDataSource")
@PropertySource("classpath:spring-batch.properties")
@PropertySource("classpath:hikari.properties")
@ComponentScan(basePackages = {"de.superx.spring.service", "de.superx.spring.batch.tasklet", "de.superx.rest", "de.superx.dbt",
"de.superx.bianalysis.service", "de.superx.bianalysis.rest", "de.superx.access"})
public class AppConfig {
// time in milliseconds after which an exception is
// thrown if no connection available in pool
@Value("${hikari.connection_timeout:180000}")
protected Long connectionTimeout;
// time in milliseconds a connection can be out of pool
// before an execption gets thrown to prevent connection leaks
// (for debug purposes, 0 means disable leak detection)
@Value("${hikari.leak_detection_threshold:0}")
protected Long leakDetectionThreshold;
public static final int DEFAULT_POOL_SIZE = 5;
public static final int EDUETL_POOL_SIZE = 20; // low pool size led to hanging application on 'muster-head' - former value: 6;
private static Logger logger = Logger.getLogger(AppConfig.class);
@Autowired
private JobRepository jobRepository;
@Autowired
private JobRegistry jobRegistry;
@Autowired
private JobExplorer jobExplorer;
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public PlatformTransactionManager batchTransactionManager() {
return new DataSourceTransactionManager(unrestrictedDataSource());
}
@Bean
public JobLauncher jobLauncher() throws Exception {
TaskExecutorJobLauncher jobLauncher = new TaskExecutorJobLauncher();
jobLauncher.setJobRepository(jobRepository);
jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
@Bean
public JobLocator jobLocator() {
return jobRegistry;
}
@Bean
public JobLauncher syncJobLauncher() throws Exception {
TaskExecutorJobLauncher jobLauncher = new TaskExecutorJobLauncher();
jobLauncher.setJobRepository(jobRepository);
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
@Bean
public JobOperator jobOperator() throws Exception {
SimpleJobOperator jobOperator = new SimpleJobOperator();
jobOperator.setJobLauncher(jobLauncher());
jobOperator.setJobExplorer(jobExplorer);
jobOperator.setJobRepository(jobRepository);
jobOperator.setJobRegistry(jobRegistry);
JobUtils.terminateAllJobExecutions(jobExplorer, jobOperator);
return jobOperator;
}
@Bean
public His1DataSources dataSources() {
//Map<String, DataSourceDiscardingUserStuff> dataSources = new HashMap<>();
Map<String, HikariDataSource> dataSources = new HashMap<>();
try {
for(Entry<String, Map<String,String>> co : DatabaseUtils.getValidConnections().entrySet()) {
HikariDataSource hs = DatabaseUtils.convertConnection(co.getKey().toString(), co.getValue(),
leakDetectionThreshold.longValue(), connectionTimeout.longValue());
//DataSourceDiscardingUserStuff dsdu = new DataSourceDiscardingUserStuff(hs);
dataSources.put(co.getKey().toString(), hs);
logger.info(String.format("Connection to %s added to dataSources bean", co.getKey()));
}
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException
| BadPaddingException e) {
logger.fatal(e);
}
return new His1DataSources(dataSources);
}
@Bean
public Map<String, String> dbtProfile() {
Map<String, String> attributes = new HashMap<>();
Map<String, String> con;
try {
con = DatabaseUtils.getConnection("eduetl");
attributes.put("DBT_HOST", con.get("host"));
attributes.put("DBT_PORT", con.get("dbserverport"));
attributes.put("DBT_USER", con.get("user"));
attributes.put("DBT_ENV_SECRET_PW", con.get("password"));
attributes.put("DBT_DB", con.get("database"));
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException
| BadPaddingException e) {
logger.fatal("dbt profile could not be initalized.", e);
}
return attributes;
}
@Bean
public DataSource unrestrictedDataSource() {
DataSource ds = dataSources().get("eduetl");
if (!batchSchemaExists(ds)) {
dropBatchSchema(ds);
createBatchSchema(ds);
}
upgradeBatchSchema(ds);
if (!quartzSchemaExists(ds)) {
createQuartzSchema(ds);
}
return ds;
}
@Bean
@Primary
public DataSource dataSource() {
DataSource ds = new DataSourceDiscardingUserStuff();
return ds;
}
@Bean
public Step removeStep(@Qualifier("transactionManager" )PlatformTransactionManager transactionManager, @Qualifier("dataSource") DataSource dataSource) {
RemoveSpringBatchHistoryTasklet tasklet = new RemoveSpringBatchHistoryTasklet();
tasklet.setJdbcTemplate(new JdbcTemplate(dataSource));
String name = "removeStep";
return new StepBuilder(name, jobRepository)
.tasklet(tasklet, transactionManager)
.build();
}
@Bean
public Job removeSpringBatchHistoryJob(Step removeStep) {
return new JobBuilder("removeSpringBatchHistoryJob", jobRepository)
.start(removeStep)
.build();
}
//Checks whether the necessary tables for spring batch exist
//TODO: Should implement method of creating a dump before creating new schema in case not all tables exist
private static boolean batchSchemaExists(DataSource dataSource) {
int count = 0;
try(Connection con = dataSource.getConnection()) {
DatabaseMetaData meta = con.getMetaData();
ResultSet resultSet = meta.getTables(null, "public", "batch_%" , new String[] {"TABLE"});
while (resultSet.next()) {
count++;
}
} catch (SQLException e) {
throw new RuntimeException("Couldn't check for spring batch tables", e);
}
return count == 6;
}
private static boolean quartzSchemaExists(DataSource dataSource) {
try(Connection con = dataSource.getConnection()) {
DatabaseMetaData meta = con.getMetaData();
ResultSet resultSet = meta.getTables(null, "public", "qrtz_%" , new String[] {"TABLE"});
if (resultSet.next()) {
return true;
}
} catch (SQLException e) {
throw new RuntimeException("Couldn't check for spring batch tables", e);
}
return false;
}
private static void createBatchSchema(DataSource dataSource) {
try (Connection con = dataSource.getConnection()){
ScriptUtils.executeSqlScript(con, new ClassPathResource("/org/springframework/batch/core/schema-postgresql.sql"));
logger.info("Spring Batch schema created;");
} catch (Exception e) {
logger.error("Couldn't create Spring Batch schema!", e);
}
}
private static void dropBatchSchema(DataSource dataSource) {
try (Connection con = dataSource.getConnection()){
ScriptUtils.executeSqlScript(con, new ClassPathResource("/org/springframework/batch/core/schema-drop-postgresql.sql"));
logger.info("Spring Batch schema dropped;");
} catch (Exception e) {
logger.error("Couldn't drop Spring Batch schema!", e);
}
}
private static void createQuartzSchema(DataSource dataSource) {
try (Connection con = dataSource.getConnection()){
ScriptUtils.executeSqlScript(con, new ClassPathResource("/org/quartz/impl/jdbcjobstore/tables_postgres.sql"));
logger.info("Quartz schema created;");
} catch (Exception e) {
logger.error("Couldn't create Quartz schema!", e);
}
}
public static void upgradeBatchSchema(DataSource dataSource) {
try (Connection con = dataSource.getConnection()){
File file = new File(String.format("%s/conf/edustore/db/install/upgrade/spring_batch_schema_upgrade.sql", PathAndFileUtils.getWebinfPath()));
ScriptUtils.executeSqlScript(con, new FileSystemResource(file));
logger.info("Spring Batch schema upgraded;");
} catch (Exception e) {
logger.error("Couldn't upgrade Spring Batch schema!", e);
}
}
}

130
src/de/superx/spring/CustomDatabases.java

@ -1,130 +0,0 @@ @@ -1,130 +0,0 @@
package de.superx.spring;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
import java.security.InvalidKeyException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.List;
import java.util.Properties;
import org.apache.log4j.Logger;
import de.superx.util.PathAndFileUtils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import javax.crypto.Cipher;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import java.util.Set;
public class CustomDatabases{
private static Logger logger = Logger.getLogger(CustomDatabases.class);
private static SecretKeySpec KEY;
private static Cipher desCipher;
public static Map<String, Map<String,String>> getCustomConnections() throws
InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{
Map<String,Map<String,String>> connections = new HashMap<>();
//loop over all properties and map them to connections
//automatically compute dbconnections from the name property
Properties props = fetchProperties();
List<String> dbconnections = getDBConnectionsFromNameProperty(props);
final Set<String> propertyNames = props.stringPropertyNames();
for (String s : dbconnections) {
Map<String,String> properties = new HashMap<>();
logger.info(String.format("%s connection retrieved from HISinOne", s));
List<String> keys = propertyNames.stream().filter(it -> it.startsWith("db." + s + ".")).collect(Collectors.toList());
for (String key : keys) {
String value = props.getProperty(key);
if (key.endsWith("password")) {
value = decryptDbPassword(value.trim());
}
properties.put(key.substring(key.lastIndexOf('.') + 1), value);
}
connections.put(s, properties);
}
return connections;
}
private static List<String> getPropertyValuesByRegex(Properties properties, String regex) {
List<String> values = new ArrayList<>();
Pattern pattern = Pattern.compile(regex);
for (String key : properties.stringPropertyNames()) {
Matcher matcher = pattern.matcher(key);
if (matcher.matches()) {
values.add(properties.getProperty(key));
}
}
return values;
}
public static List<String> getDBConnectionsFromNameProperty(Properties props){
ArrayList<String> connection_names = new ArrayList<String>();
String regex = "db.*.name";
connection_names = (ArrayList<String>) getPropertyValuesByRegex(props, regex);
//check for duplicate names
Set<String> seen = new HashSet<>();
for (String db_name : connection_names) {
if(!seen.add(db_name)) {
logger.warn("Found atleast two databases with the name" + db_name);
}
}
return connection_names;
}
private static String decryptDbPassword(String password) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
if(password == null || password.equals("")) {
return null;
}else if(!password.startsWith("sx_des")) {
return password;
}
if(desCipher == null) {
desCipher = getDesCipher();
}
String[] stext = password.substring(7).split("#");
byte[] bstext = new byte[stext.length];
for(int i = 0; i<stext.length;i++) {
bstext[i] = Byte.parseByte(stext[i]);
}
desCipher.init(Cipher.DECRYPT_MODE, KEY);
byte[] decrypted = desCipher.doFinal(bstext);
return new String(decrypted, StandardCharsets.UTF_8);
}
public static void copy_encryption_key(SecretKeySpec key){
KEY = key;
}
private static Cipher getDesCipher() throws NoSuchAlgorithmException, NoSuchPaddingException {
return Cipher.getInstance("DES");
}
private static Properties fetchProperties(){
Properties props = new Properties();
String FileName = "extra_connections.properties";
File extra_connections_file_path = new File(PathAndFileUtils.getWebinfPath() + File.separator + "conf" + File.separator + FileName);
try (FileInputStream s = new FileInputStream(extra_connections_file_path)){
props.load(s);
} catch (IOException e1) {
logger.info("Unable to access properties file where extra connections as described: " + e1.getMessage());
}
return props;
}
}

340
src/de/superx/spring/DatabaseUtils.java

@ -1,340 +0,0 @@ @@ -1,340 +0,0 @@
package de.superx.spring;
import static java.util.Map.entry;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import org.apache.log4j.Logger;
import org.springframework.core.io.ClassPathResource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import de.superx.servlet.SuperXManager;
import de.superx.util.PathAndFileUtils;
public class DatabaseUtils {
public static final String HIS1_DATABASES_PROPERTIES = "HIS1_DATABASES_PROPERTIES";
private static final byte[] KEY_DATA = { -108, -50, -5, -75, -98, 28, -116, 107 };
private static final SecretKeySpec KEY = new SecretKeySpec(KEY_DATA, "DES");
private static Cipher desCipher;
private static Logger logger = Logger.getLogger(DatabaseUtils.class);
private DatabaseUtils() { //Mit Ausnahmegenehmigung
//Prevent instantiation
}
private static void check_for_duplicate_names__in_properties_lists(Properties regular_properties, Properties extra_properties) {
List<String> regular_names = CustomDatabases.getDBConnectionsFromNameProperty(regular_properties);
List<String> extra_names = CustomDatabases.getDBConnectionsFromNameProperty(extra_properties);
for (String conn_name : extra_names) {
if(regular_names.contains(conn_name)) {
logger.warn("Found a custom connection with the same name as a regular connection");
}
}
}
public static Map<String,Map<String,String>> getAllConnections() throws
InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{
Map<String, Map<String,String>> his1_connections = fetchConnections();
CustomDatabases.copy_encryption_key(KEY);
Map<String, Map<String,String>> custom_connections = CustomDatabases.getCustomConnections();
//check_for_duplicate_properties(his1_connections, custom_connections);
Map<String, Map<String,String>> all_connections = new HashMap<>(his1_connections);
all_connections.putAll(custom_connections);
return all_connections;
}
public static Map<String,Map<String,String>> getValidConnections() throws
InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{
Map<String,Map<String,String>> conns = getAllConnections();
logger.info("Got connections: " + conns.keySet());
Map<String,Map<String,String>> validConns = new HashMap<>();
for (String key : conns.keySet()) {
Map<String,String> connectionProperties = conns.get(key);
if (
connectionProperties.get(DbPropertyName.database.name()) != null
&&connectionProperties.get(DbPropertyName.user.name()) != null
&& connectionProperties.get(DbPropertyName.url.name()) != null
&& connectionProperties.get(DbPropertyName.protocol.name()) != null
&& connectionProperties.get(DbPropertyName.jdbcDriver.name()) != null
) {
logger.info("Valid connection: " + key);
validConns.put(key, connectionProperties);
} else {
logger.warn("Invalid connection: " + key);
}
}
return validConns;
}
public static Map<String,String> getConnection(String connectionName) throws
InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{
Map<String,Map<String,String>> connections = fetchConnections();
return connections.get(connectionName) != null ? connections.get(connectionName) : new HashMap<>();
}
private static Properties fetchProperties() {
Properties props = new Properties();
String databasesPropertiesFileName = System.getProperty(HIS1_DATABASES_PROPERTIES);
if (databasesPropertiesFileName != null) {
File databasePropertiesFile = new File(databasesPropertiesFileName);
if (databasePropertiesFile.exists()) {
logger.info("Reading database properties from " + databasesPropertiesFileName);
try {
props.load(new FileInputStream(databasePropertiesFile));
} catch (Exception e) {
logger.error("Couldn't load database properties from " + databasesPropertiesFileName,e);
}
} else {
logger.error("HIS1_DATABASES_PROPERTIES file not found: " + databasesPropertiesFileName);
}
}
if (props.size() == 0) {
// no external properties file, loading from classpath
File propFile = new File(PathAndFileUtils.getWebinfPath() + File.separator + "conf" + File.separator + "his1_databases.properties");
if (!propFile.exists()) {
logger.warn("No his1_databasees.properties found. Will try to load from db.properties!");
return readDbProperties();
}
try(FileInputStream s = new FileInputStream(propFile)){
props.load(s);
} catch (IOException e1) {
logger.fatal(String.format("Unable to access his1_databases file: %s",e1.getMessage()));
e1.printStackTrace();
}
}
Properties manualConfig = manualConfiguration();
return mergeProperties(props, manualConfig);
}
@Deprecated
private static void inject_extra_connections_properties(Properties props){
String FileName = "extra_connections.properties";
File extra_connections_file_path = new File(PathAndFileUtils.getWebinfPath() + File.separator + "conf" + File.separator + FileName);
try (FileInputStream s = new FileInputStream(extra_connections_file_path)){
props.load(s);
} catch (IOException e1) {
logger.info("Unable to access properties file where extra connections as described: " + e1.getMessage());
}
}
private static Properties manualConfiguration() {
Properties optProps = new Properties();
File poolConf = new File(PathAndFileUtils.getWebinfPath() + File.separator + "conf" + File.separator + "his1_db_poolsize.properties");
try(FileInputStream s = new FileInputStream(poolConf)){
optProps.load(s);
//inject_extra_connections_properties(optProps);
} catch (IOException e1) {
logger.info("Unable to access manual poolsize configuration\nSkipping manual poolsize configuration - setting defaults: "+e1.getMessage());
}
return optProps;
}
private static Properties mergeProperties(Properties... properties) {
return Stream.of(properties).collect(Properties::new, Map::putAll, Map::putAll);
}
private static Cipher getDesCipher() throws NoSuchAlgorithmException, NoSuchPaddingException {
return Cipher.getInstance("DES");
}
private static String decryptDbPassword(String password) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
if(password == null || password.equals("")) {
return null;
}else if(!password.startsWith("sx_des")) {
return password;
}
if(desCipher == null) {
desCipher = getDesCipher();
}
String[] stext = password.substring(7).split("#");
byte[] bstext = new byte[stext.length];
for(int i = 0; i<stext.length;i++) {
bstext[i] = Byte.parseByte(stext[i]);
}
desCipher.init(Cipher.DECRYPT_MODE, KEY);
byte[] decrypted = desCipher.doFinal(bstext);
return new String(decrypted, StandardCharsets.UTF_8);
}
private static Map<String,Map<String,String>> fetchConnections() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
Map<String,Map<String,String>> connections = new HashMap<>();
Properties props = fetchProperties();
String[] dbconnections = props.get("dbconnections").toString().split(",");
final Set<String> propertyNames = props.stringPropertyNames();
for (String s : dbconnections) {
Map<String,String> properties = new HashMap<>();
logger.info(String.format("%s connection retrieved from HISinOne", s));
List<String> keys = propertyNames.stream().filter(it -> it.startsWith("db." + s + ".")).collect(Collectors.toList());
for (String key : keys) {
String value = props.getProperty(key);
if (key.endsWith("password")) {
value = decryptDbPassword(value.trim());
}
properties.put(key.substring(key.lastIndexOf('.') + 1), value);
}
connections.put(s, properties);
}
return connections;
}
private static Properties readDbProperties() {
Properties properties = new Properties();
String webinfpfad=PathAndFileUtils.getWebinfPath();
File dbPropertiesFile = new File( webinfpfad
+ File.separator
+ "db.properties");
if (dbPropertiesFile.exists()) {
try {
Properties dbProperties = new Properties();
dbProperties.load(new FileReader(dbPropertiesFile));
properties = convertDbProperties(dbProperties);
} catch (Exception e) {
throw new RuntimeException("Couldn't read db.properties!", e);
}
} else {
throw new RuntimeException("Couldn't read " + dbPropertiesFile.getAbsolutePath() + " because file doesn't exist!");
}
return properties;
}
public static Properties convertDbProperties(Properties dbProperties) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
Properties his1Properties = new Properties();
String jdbcUrl = dbProperties.getProperty("connectionURL");
String user = dbProperties.getProperty("connectionName");
String password = decryptDbPassword(dbProperties.getProperty("connectionPassword"));
String sslmode = dbProperties.getProperty("sslmode");
String driver = dbProperties.getProperty("driverName");
String[] urlParts = analyzeJdbcUrl(jdbcUrl);
String prefix = "db.eduetl.";
Map<String,String> config = Map.ofEntries(
entry(prefix + DbPropertyName.user.name(), user),
entry(prefix + DbPropertyName.password.name(), password),
entry(prefix + DbPropertyName.jdbcDriver.name(), driver),
entry(prefix + DbPropertyName.ssl.name(), sslmode != null && sslmode.equalsIgnoreCase("require") ? "true" : "false"),
entry(prefix + DbPropertyName.protocol.name(), urlParts[0]),
entry(prefix + DbPropertyName.host.name(), urlParts[1]),
entry(prefix + DbPropertyName.dbserverport.name(), urlParts[2]),
entry(prefix + DbPropertyName.database.name(), urlParts[3]),
entry(prefix + DbPropertyName.url.name(), jdbcUrl),
entry(DbPropertyName.dbconnections.name(), "eduetl")
);
his1Properties.putAll(config);
return his1Properties;
}
public static String[] analyzeJdbcUrl(String jdbcUrl) {
int protocolEndIndex = jdbcUrl.indexOf("//");
int portBeginIndex = jdbcUrl.indexOf(":", protocolEndIndex);
int dbBeginIndex = jdbcUrl.indexOf("/", portBeginIndex);
int dbEndIndex = jdbcUrl.indexOf("?"); String protocol = jdbcUrl.substring(0, protocolEndIndex);
String host = jdbcUrl.substring(protocolEndIndex + 2, portBeginIndex);
String port = jdbcUrl.substring(portBeginIndex + 1, dbBeginIndex);
String db = jdbcUrl.substring(dbBeginIndex + 1, dbEndIndex == -1 ? jdbcUrl.length() : dbEndIndex);
return new String[] {
protocol, host, port, db
};
}
public static HikariDataSource convertConnection(String connectionName, Map<String,String> co, long leakDetectionThreshold, long connectionTimeout){
HikariConfig conf = new HikariConfig();
conf.setUsername(co.get(DbPropertyName.user.name()));
conf.setPassword(co.get(DbPropertyName.password.name()));
conf.setDriverClassName(co.get(DbPropertyName.jdbcDriver.name()));
conf.setJdbcUrl(co.get(DbPropertyName.url.name()));
if (co.get(DbPropertyName.pgschema.name()) != null) {
conf.setSchema(co.get(DbPropertyName.pgschema.name()));
} else if ("hisinone".equals(connectionName)) {
conf.setSchema("hisinone");
}
if (co.get(DbPropertyName.reconnectAfter.name()) != null) {
// Angabe reconnectAfter in der databases.xml ist in Minuten!
conf.setMaxLifetime(Long.parseLong(co.get(DbPropertyName.reconnectAfter.name())) * 60000);
}
conf.setPoolName(connectionName);
conf.setLeakDetectionThreshold(leakDetectionThreshold);
conf.setConnectionTimeout(connectionTimeout);
// in HISinOne-BI context: Set PostgreSQL variables to be consistent with
// HISinOne itself (which also does some settings - see "DbHandler")
if (co.get(DbPropertyName.jdbcDriver.name()).equals(org.postgresql.Driver.class.getCanonicalName())) {
if (System.getProperty(SuperXManager.SUPER_X_HISINONE_VERSION) != null) {
conf.setConnectionInitSql(
"SET standard_conforming_strings TO 'off'; SET datestyle TO 'ISO, DMY';"
);
} else if ("eduetl".equals(connectionName)) {
conf.setConnectionInitSql(
"SET datestyle TO 'ISO, DMY';"
);
}
}
String minConnectionsStr = co.get("poolsize_min");
String maxConnectionsStr = co.get("poolsize_max");
int defaultPoolSize = "eduetl".equals(connectionName) ? AppConfig.EDUETL_POOL_SIZE : AppConfig.DEFAULT_POOL_SIZE;
int maxConnections = maxConnectionsStr != null ? Integer.parseInt(maxConnectionsStr) : defaultPoolSize;
int minConnections = minConnectionsStr != null ? Integer.parseInt(minConnectionsStr) : maxConnections;
conf.setMinimumIdle(minConnections);
logger.info(String.format("HikariPool for %s initialized with a minimum of %s idle connections.", connectionName, Integer.toString(minConnections)));
conf.setMaximumPoolSize(maxConnections);
logger.info(String.format("HikariPool for %s initialized with a maximum of %s connections", connectionName, Integer.toString(maxConnections)));
addDataSourceProperties(conf, co);
conf.setExceptionOverrideClassName("de.superx.db.HikariSqlExceptionOverride");
HikariDataSource ds = null;
try {
ds = new HikariDataSource(conf);
} catch(Exception ex) {
logger.error("Couldn't create Hikaripool for " + connectionName, ex);
}
return ds;
}
private static void addDataSourceProperties(HikariConfig conf, Map<String, String> co) {
for (Entry<String,String> e : co.entrySet()) {
if (!DbPropertyName.isStandard(e.getKey())) {
conf.addDataSourceProperty(e.getKey(), e.getValue());
logger.debug("Set additional db property " + e.getKey() + " to " + e.getValue());
}
}
conf.addDataSourceProperty("ApplicationName", "HIS-BI");
}
}

301
src/de/superx/spring/HIS1Databases.java

@ -0,0 +1,301 @@ @@ -0,0 +1,301 @@
package de.superx.spring;
import static java.util.Map.entry;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import org.apache.log4j.Logger;
import org.springframework.core.io.ClassPathResource;
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import de.superx.servlet.SuperXManager;
import de.superx.spring.config.BatchConfig;
import de.superx.util.PathAndFileUtils;
public class HIS1Databases {
public static final String HIS1_DATABASES_PROPERTIES = "HIS1_DATABASES_PROPERTIES";
private static final byte[] KEY_DATA = { -108, -50, -5, -75, -98, 28, -116, 107 };
private static final SecretKeySpec KEY = new SecretKeySpec(KEY_DATA, "DES");
private static Cipher desCipher;
private static Logger logger = Logger.getLogger(HIS1Databases.class);
private HIS1Databases() { //Mit Ausnahmegenehmigung
//Prevent instantiation
}
public static Map<String,Map<String,String>> getAllConnections() throws
InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{
return fetchConnections();
}
public static Map<String,Map<String,String>> getValidConnections() throws
InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{
Map<String,Map<String,String>> conns = getAllConnections();
logger.info("Got connections: " + conns.keySet());
Map<String,Map<String,String>> validConns = new HashMap<>();
for (String key : conns.keySet()) {
Map<String,String> connectionProperties = conns.get(key);
if (
connectionProperties.get(DbPropertyName.database.name()) != null
&&connectionProperties.get(DbPropertyName.user.name()) != null
&& connectionProperties.get(DbPropertyName.url.name()) != null
&& connectionProperties.get(DbPropertyName.protocol.name()) != null
&& connectionProperties.get(DbPropertyName.jdbcDriver.name()) != null
) {
logger.info("Valid connection: " + key);
validConns.put(key, connectionProperties);
} else {
logger.warn("Invalid connection: " + key);
}
}
return validConns;
}
public static Map<String,String> getConnection(String connectionName) throws
InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException{
Map<String,Map<String,String>> connections = fetchConnections();
return connections.get(connectionName) != null ? connections.get(connectionName) : new HashMap<>();
}
private static Properties fetchProperties() {
Properties props = new Properties();
String databasesPropertiesFileName = System.getProperty(HIS1_DATABASES_PROPERTIES);
if (databasesPropertiesFileName != null) {
File databasePropertiesFile = new File(databasesPropertiesFileName);
if (databasePropertiesFile.exists()) {
logger.info("Reading database properties from " + databasesPropertiesFileName);
try {
props.load(new FileInputStream(databasePropertiesFile));
} catch (Exception e) {
logger.error("Couldn't load database properties from " + databasesPropertiesFileName,e);
}
} else {
logger.error("HIS1_DATABASES_PROPERTIES file not found: " + databasesPropertiesFileName);
}
}
if (props.size() == 0) {
// no external properties file, loading from classpath
File propFile = new File(PathAndFileUtils.getWebinfPath() + File.separator + "conf" + File.separator + "his1_databases.properties");
if (!propFile.exists()) {
logger.warn("No his1_databasees.properties found. Will try to load from db.properties!");
return readDbProperties();
}
try(FileInputStream s = new FileInputStream(propFile)){
props.load(s);
} catch (IOException e1) {
logger.fatal(String.format("Unable to access his1_databases file: %s",e1.getMessage()));
e1.printStackTrace();
}
}
Properties manualConfig = manualConfiguration();
return mergeProperties(props, manualConfig);
}
private static Properties manualConfiguration() {
Properties optProps = new Properties();
File poolConf = new File(PathAndFileUtils.getWebinfPath() + File.separator + "conf" + File.separator + "his1_db_poolsize.properties");
try(FileInputStream s = new FileInputStream(poolConf)){
optProps.load(s);
} catch (IOException e1) {
logger.info("Unable to access manual poolsize configuration\nSkipping manual poolsize configuration - setting defaults: "+e1.getMessage());
}
return optProps;
}
private static Properties mergeProperties(Properties... properties) {
return Stream.of(properties).collect(Properties::new, Map::putAll, Map::putAll);
}
private static Cipher getDesCipher() throws NoSuchAlgorithmException, NoSuchPaddingException {
return Cipher.getInstance("DES");
}
private static String decryptDbPassword(String password) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
if(password == null || password.equals("")) {
return null;
}else if(!password.startsWith("sx_des")) {
return password;
}
if(desCipher == null) {
desCipher = getDesCipher();
}
String[] stext = password.substring(7).split("#");
byte[] bstext = new byte[stext.length];
for(int i = 0; i<stext.length;i++) {
bstext[i] = Byte.parseByte(stext[i]);
}
desCipher.init(Cipher.DECRYPT_MODE, KEY);
byte[] decrypted = desCipher.doFinal(bstext);
return new String(decrypted, StandardCharsets.UTF_8);
}
private static Map<String,Map<String,String>> fetchConnections() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, IllegalBlockSizeException, BadPaddingException {
Map<String,Map<String,String>> connections = new HashMap<>();
Properties props = fetchProperties();
String[] dbconnections = props.get("dbconnections").toString().split(",");
final Set<String> propertyNames = props.stringPropertyNames();
for (String s : dbconnections) {
Map<String,String> properties = new HashMap<>();
logger.info(String.format("%s connection retrieved from HISinOne", s));
List<String> keys = propertyNames.stream().filter(it -> it.startsWith("db." + s + ".")).collect(Collectors.toList());
for (String key : keys) {
String value = props.getProperty(key);
if (key.endsWith("password")) {
value = decryptDbPassword(value.trim());
}
properties.put(key.substring(key.lastIndexOf('.') + 1), value);
}
connections.put(s, properties);
}
return connections;
}
private static Properties readDbProperties() {
Properties properties = new Properties();
String webinfpfad=PathAndFileUtils.getWebinfPath();
File dbPropertiesFile = new File( webinfpfad
+ File.separator
+ "db.properties");
if (dbPropertiesFile.exists()) {
try {
Properties dbProperties = new Properties();
dbProperties.load(new FileReader(dbPropertiesFile));
properties = convertDbProperties(dbProperties);
} catch (Exception e) {
throw new RuntimeException("Couldn't read db.properties!", e);
}
} else {
throw new RuntimeException("Couldn't read " + dbPropertiesFile.getAbsolutePath() + " because file doesn't exist!");
}
return properties;
}
public static Properties convertDbProperties(Properties dbProperties) throws InvalidKeyException, NoSuchAlgorithmException, NoSuchPaddingException, IllegalBlockSizeException, BadPaddingException {
Properties his1Properties = new Properties();
String jdbcUrl = dbProperties.getProperty("connectionURL");
String user = dbProperties.getProperty("connectionName");
String password = decryptDbPassword(dbProperties.getProperty("connectionPassword"));
String sslmode = dbProperties.getProperty("sslmode");
String driver = dbProperties.getProperty("driverName");
String[] urlParts = analyzeJdbcUrl(jdbcUrl);
String prefix = "db.eduetl.";
Map<String,String> config = Map.ofEntries(
entry(prefix + DbPropertyName.user.name(), user),
entry(prefix + DbPropertyName.password.name(), password),
entry(prefix + DbPropertyName.jdbcDriver.name(), driver),
entry(prefix + DbPropertyName.ssl.name(), sslmode != null && sslmode.equalsIgnoreCase("require") ? "true" : "false"),
entry(prefix + DbPropertyName.protocol.name(), urlParts[0]),
entry(prefix + DbPropertyName.host.name(), urlParts[1]),
entry(prefix + DbPropertyName.dbserverport.name(), urlParts[2]),
entry(prefix + DbPropertyName.database.name(), urlParts[3]),
entry(prefix + DbPropertyName.url.name(), jdbcUrl),
entry(DbPropertyName.dbconnections.name(), "eduetl")
);
his1Properties.putAll(config);
return his1Properties;
}
public static String[] analyzeJdbcUrl(String jdbcUrl) {
int protocolEndIndex = jdbcUrl.indexOf("//");
int portBeginIndex = jdbcUrl.indexOf(":", protocolEndIndex);
int dbBeginIndex = jdbcUrl.indexOf("/", portBeginIndex);
int dbEndIndex = jdbcUrl.indexOf("?"); String protocol = jdbcUrl.substring(0, protocolEndIndex);
String host = jdbcUrl.substring(protocolEndIndex + 2, portBeginIndex);
String port = jdbcUrl.substring(portBeginIndex + 1, dbBeginIndex);
String db = jdbcUrl.substring(dbBeginIndex + 1, dbEndIndex == -1 ? jdbcUrl.length() : dbEndIndex);
return new String[] {
protocol, host, port, db
};
}
public static HikariDataSource convertConnection(String connectionName, Map<String,String> co, long leakDetectionThreshold, long connectionTimeout){
HikariConfig conf = new HikariConfig();
conf.setUsername(co.get(DbPropertyName.user.name()));
conf.setPassword(co.get(DbPropertyName.password.name()));
conf.setDriverClassName(co.get(DbPropertyName.jdbcDriver.name()));
conf.setJdbcUrl(co.get(DbPropertyName.url.name()));
if (co.get(DbPropertyName.pgschema.name()) != null) {
conf.setSchema(co.get(DbPropertyName.pgschema.name()));
} else if ("hisinone".equals(connectionName)) {
conf.setSchema("hisinone");
}
if (co.get(DbPropertyName.reconnectAfter.name()) != null) {
// Angabe reconnectAfter in der databases.xml ist in Minuten!
conf.setMaxLifetime(Long.parseLong(co.get(DbPropertyName.reconnectAfter.name())) * 60000);
}
conf.setPoolName(connectionName);
conf.setLeakDetectionThreshold(leakDetectionThreshold);
conf.setConnectionTimeout(connectionTimeout);
// in HISinOne-BI context: Set PostgreSQL variables to be consistent with
// HISinOne itself (which also does some settings - see "DbHandler")
if (co.get(DbPropertyName.jdbcDriver.name()).equals(org.postgresql.Driver.class.getCanonicalName()) &&
System.getProperty(SuperXManager.SUPER_X_HISINONE_VERSION) != null) {
conf.setConnectionInitSql("SET standard_conforming_strings TO 'off'; SET datestyle TO 'ISO, DMY';");
}
String minConnectionsStr = co.get("poolsize_min");
String maxConnectionsStr = co.get("poolsize_max");
int defaultPoolSize = "eduetl".equals(connectionName) ? BatchConfig.EDUETL_POOL_SIZE : BatchConfig.DEFAULT_POOL_SIZE;
int maxConnections = maxConnectionsStr != null ? Integer.parseInt(maxConnectionsStr) : defaultPoolSize;
int minConnections = minConnectionsStr != null ? Integer.parseInt(minConnectionsStr) : maxConnections;
conf.setMinimumIdle(minConnections);
logger.info(String.format("HikariPool for %s initialized with a minimum of %s idle connections.", connectionName, Integer.toString(minConnections)));
conf.setMaximumPoolSize(maxConnections);
logger.info(String.format("HikariPool for %s initialized with a maximum of %s connections", connectionName, Integer.toString(maxConnections)));
addDataSourceProperties(conf, co);
conf.setExceptionOverrideClassName("de.superx.db.HikariSqlExceptionOverride");
HikariDataSource ds = null;
try {
ds = new HikariDataSource(conf);
} catch(Exception ex) {
logger.error("Couldn't create Hikaripool for " + connectionName, ex);
}
return ds;
}
private static void addDataSourceProperties(HikariConfig conf, Map<String, String> co) {
for (Entry<String,String> e : co.entrySet()) {
if (!DbPropertyName.isStandard(e.getKey())) {
conf.addDataSourceProperty(e.getKey(), e.getValue());
logger.debug("Set additional db property " + e.getKey() + " to " + e.getValue());
}
}
conf.addDataSourceProperty("ApplicationName", "HIS-BI");
}
}

28
src/de/superx/spring/HisInOneConfiguration.java

@ -2,9 +2,6 @@ package de.superx.spring; @@ -2,9 +2,6 @@ package de.superx.spring;
import java.io.File;
import java.io.FilenameFilter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Path;
import java.util.List;
@ -70,19 +67,6 @@ public class HisInOneConfiguration implements ApplicationContextInitializer<Conf @@ -70,19 +67,6 @@ public class HisInOneConfiguration implements ApplicationContextInitializer<Conf
illegalStateExcpetion.setStackTrace(new StackTraceElement[0]);
throw illegalStateExcpetion;
}
// Verzeichnis WEB-INF/conf zum CLASSPATH hinzufügen, damit Conigdateien
// einfach über ClassPathResources geladen werden können
try {
// Pfad zum WEB-INF/conf-Verzeichnis (relativ zur Webapp)
String confDirPath = context.getRealPath("/WEB-INF/conf");
// Erweitere den Classpath
addDirToClasspath(new File(confDirPath));
} catch (Exception e) {
logger.error("Fehler beim Erweitern des Classpaths: ", e);
}
ApplicationConfig.readApplicationProperties();
configSuperXDbformsXML("pg", superxPath.getAbsolutePath());
}
@ -177,16 +161,4 @@ public class HisInOneConfiguration implements ApplicationContextInitializer<Conf @@ -177,16 +161,4 @@ public class HisInOneConfiguration implements ApplicationContextInitializer<Conf
}
}
private static void addDirToClasspath(File dir) throws Exception {
// Hole den ClassLoader der Webapp
URLClassLoader classLoader = (URLClassLoader) Thread.currentThread().getContextClassLoader();
// Methode zum Hinzufügen von URLs zum Classpath
Method addURLMethod = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
addURLMethod.setAccessible(true);
// Füge das Verzeichnis als URL zum Classpath hinzu
addURLMethod.invoke(classLoader, dir.toURI().toURL());
}
}

13
src/de/superx/spring/batch/job/BiJobLoggingListener.java

@ -16,7 +16,6 @@ import org.apache.log4j.spi.LoggingEvent; @@ -16,7 +16,6 @@ import org.apache.log4j.spi.LoggingEvent;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import de.superx.dbt.DbtWrapper;
import de.superx.job.ContainerNode.JobSource;
import de.superx.util.PathAndFileUtils;
@ -30,17 +29,15 @@ public class BiJobLoggingListener implements JobExecutionListener { @@ -30,17 +29,15 @@ public class BiJobLoggingListener implements JobExecutionListener {
private static final List<String> loggerNames = List.of(new String[] {
"de.superx.spring.batch",
"de.superx.dbt.DbtWrapper",
"de.superx.dbt.DbtRun",
"de.superx.bin.fm.EtlStarter",
"de.superx.spring.service.BatchJobDescriptionAdapter",
"de.superx.spring.service.EntityJobDescriptionSource",
"org.springframework.batch.core.job",
"org.springframework.jdbc.datasource.init.ScriptUtils"
});
private static final Level logLevel = Level.DEBUG;
private static final PatternLayout PATTERN_SHORT = new PatternLayout("%d{HH:mm:ss} %p|%c{1}| %m%n");
private static final PatternLayout PATTERN_LONG = new PatternLayout("%d{ISO8601} [%p|%t|%c{1}] %m%n");
private static final PatternLayout PATTERN_SHORT = new PatternLayout("%d{HH:mm:ss,SSS} [%p|%t|%c{1}] %m%n");
private static final PatternLayout PATTERN_LONG = new PatternLayout("%d [%p|%t|%c{1}] %m%n");
private File logFile;
private StringWriter logWriter;
@ -81,10 +78,8 @@ public class BiJobLoggingListener implements JobExecutionListener { @@ -81,10 +78,8 @@ public class BiJobLoggingListener implements JobExecutionListener {
EtlJobLoggingFilter logFilter = new EtlJobLoggingFilter(Thread.currentThread().getName());
String jobFileName = StringUtils.remove(StringUtils.replaceChars(jobName, " /", "_-"), ",");
StringWriter stringWriter = new StringWriter();
WriterAppender writer = new WriterAppender();
writer.setWriter(stringWriter);
WriterAppender writer = new WriterAppender(PATTERN_SHORT, stringWriter);
writer.setName(BATCH_JOB_STRING_APPENDER);
writer.setLayout(PATTERN_SHORT);
writer.setThreshold(logLevel);
writer.clearFilters();
writer.addFilter(logFilter);
@ -144,7 +139,7 @@ class EtlJobLoggingFilter extends Filter { @@ -144,7 +139,7 @@ class EtlJobLoggingFilter extends Filter {
@Override
public int decide(LoggingEvent event) {
String threadName = event.getThreadName();
if (threadName.contains("TaskExecutor-") || threadName.startsWith(DbtWrapper.DBT_THREAD_NAME_PREFIX) || myThread.equals(event.getThreadName())) {
if (threadName.contains("TaskExecutor-") || threadName.startsWith("DBT-") || myThread.equals(event.getThreadName())) {
// wir loggen nur Loggingausgaben von Methoden, die im
// aktuellen Thread des Jobs ausgeführt werden!
return Filter.ACCEPT;

6
src/de/superx/spring/batch/reader/JdbcExtractReader.java

@ -26,8 +26,7 @@ public class JdbcExtractReader<T> extends JdbcCursorItemReader<T> implements Ste @@ -26,8 +26,7 @@ public class JdbcExtractReader<T> extends JdbcCursorItemReader<T> implements Ste
private Logger logger = LoggerFactory.getLogger(this.getClass());
private ExtractAction extractAction;
public String dropTableStmt = "DROP TABLE IF EXISTS %s.%s CASCADE;";
public String createSchemaStmt = "CREATE SCHEMA IF NOT EXISTS %s;";
public String dropTableStmt = "DROP TABLE IF EXISTS %s.%s;";
public String createTableStmt = "CREATE TABLE %s.%s (%s);";
public String selectFromTableStmt = "SELECT %s from %s WHERE %s;";
private List<String> colsToExclude = Arrays.asList("lock_version");
@ -78,9 +77,6 @@ public class JdbcExtractReader<T> extends JdbcCursorItemReader<T> implements Ste @@ -78,9 +77,6 @@ public class JdbcExtractReader<T> extends JdbcCursorItemReader<T> implements Ste
}
createTableStmt = String.format(dropTableStmt, extractAction.stagingSchema, extractAction.stagingTable)
+ String.format(createTableStmt, extractAction.stagingSchema, extractAction.stagingTable, createBody);
if(!extractAction.dataSource.equals("hisinone")) {
createTableStmt = String.format(createSchemaStmt, "staging_" + extractAction.dataSource) + createTableStmt;
}
selectFromTableStmt = String.format(selectFromTableStmt, selectBody, extractAction.sourceTable, extractAction.extractionScript);
this.setSql(selectFromTableStmt);
this.logger.info(this.getSql());

86
src/de/superx/spring/batch/tasklet/ExecuteDbtTasklet.java

@ -1,5 +1,12 @@ @@ -1,5 +1,12 @@
package de.superx.spring.batch.tasklet;
import java.nio.file.Path;
import java.sql.CallableStatement;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Types;
import java.util.List;
import javax.sql.DataSource;
import org.slf4j.Logger;
@ -11,16 +18,19 @@ import org.springframework.batch.core.StepExecutionListener; @@ -11,16 +18,19 @@ import org.springframework.batch.core.StepExecutionListener;
import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.jdbc.core.CallableStatementCallback;
import org.springframework.jdbc.core.CallableStatementCreator;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SqlParameter;
import de.superx.conf.ApplicationConfig;
import de.superx.conf.ApplicationConfig.Key;
import de.superx.dbt.DbtWrapper;
import de.superx.dbt.DbtExecutionState;
import de.superx.dbt.DbtAction;
import de.superx.dbt.DbtPermissionException;
import de.superx.dbt.DbtRunRequest;
import de.superx.dbt.DbtRunResult;
import de.superx.dbt.DbtWrapper.ExecutionState;
import de.superx.dbt.DbtMetrics;
import de.superx.dbt.DbtUtils;
import de.superx.dbt.UnsupportedPythonVersionException;
import de.superx.servlet.SuperXManager;
public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, InternalProgress {
static Logger logger = LoggerFactory.getLogger(ExecuteDbtTasklet.class);
@ -31,17 +41,19 @@ public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, Intern @@ -31,17 +41,19 @@ public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, Intern
private final String dbtSelect;
private final DataSource dataSource;
private final boolean dbtTest;
private final Path dbtProjectDir;
private int totalStepsExecuted = 0;
private int totalSteps = 0;
private DbtWrapper dbtWrapper;
public ExecuteDbtTasklet(DbtWrapper dbtWrapper, DataSource dataSource, String dbtProject, String dbtSelect, boolean dbtTest) throws UnsupportedPythonVersionException {
this.dbtWrapper = dbtWrapper;
this.dbtProject = dbtProject;
this.dataSource = dataSource;
this.dbtSelect = dbtSelect;
this.dbtTest = dbtTest;
dbtProjectDir = Path.of(SuperXManager.getWEB_INFPfad(), "..", "dbt", "projects", dbtProject, "transform");
DbtUtils.checkPythonCompatability();
}
@ -49,8 +61,7 @@ public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, Intern @@ -49,8 +61,7 @@ public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, Intern
public void beforeStep(StepExecution stepExecution) {
try {
DbtUtils.checkPythonCompatability();
DbtUtils.checkDbtSchemaPermissions(dataSource);
} catch (UnsupportedPythonVersionException | DbtPermissionException e) {
} catch (UnsupportedPythonVersionException e) {
throw new RuntimeException(e);
}
}
@ -58,29 +69,34 @@ public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, Intern @@ -58,29 +69,34 @@ public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, Intern
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
try {
// TODO fix, maybe own tasklet for dbt test?
DbtAction action = (this.dbtTest) ? DbtAction.TEST : DbtAction.BUILD;
DbtRunRequest request = new DbtRunRequest(action, dbtProject, dbtSelect,
DBT_TIMEOUT_MILLISECONDS,
progressStep -> totalStepsExecuted = progressStep.intValue());
DbtRunResult result = dbtWrapper.execute(request);
if (result.getState() != DbtExecutionState.SUCCESS) {
String errorMessage = String.join(System.lineSeparator(), result.getErrorMessages());
contribution.setExitStatus(ExitStatus.FAILED.addExitDescription(errorMessage));
} else {
// generate dbt documentation only if dbt run was successful
request = new DbtRunRequest(DbtAction.GENERATE_DOCS, dbtProject, dbtSelect);
result = dbtWrapper.execute(request);
if(result.getState() != DbtExecutionState.SUCCESS) {
String errorMessage = String.join(System.lineSeparator(), result.getErrorMessages());
contribution.setExitStatus(ExitStatus.FAILED.addExitDescription(errorMessage));
//DbtManager manager = new DbtManager(dbtProjectDir, dbtSelect, dbtTest);
dbtWrapper.init(dbtProjectDir, dbtSelect, dbtTest);
Thread t = new Thread(dbtWrapper);
long startTime = System.currentTimeMillis();
t.setName("DBT-" + startTime);
t.start();
while (t.isAlive()) {
Thread.sleep(100);
if (DBT_TIMEOUT_MILLISECONDS > 0 && System.currentTimeMillis() - startTime > DBT_TIMEOUT_MILLISECONDS) {
//TODO In welchem Zustand verbleibt die DB nach der Beendigung von dbt hier?
//Rollback?
//Evt. TIMEOUT zurücksetzen nach jedem DBT Prozessschritt
Process p = dbtWrapper.getProcess();
if (p != null) {
p.destroyForcibly();
}
throw new RuntimeException("DBT run timeout!");
}
}
} catch (Exception e) {
if (dbtWrapper.getExecutionState() != ExecutionState.SUCCESS) {
ExitStatus exitStatus = ExitStatus.FAILED;
List<String> errors = dbtWrapper.getErrorMessages();
for (String error : errors) {
exitStatus = exitStatus.addExitDescription(error);
}
contribution.setExitStatus(exitStatus);
}
} catch(Exception e) {
String errorMsg = e.getMessage();
Throwable cause = e.getCause();
if (cause != null) {
@ -92,8 +108,7 @@ public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, Intern @@ -92,8 +108,7 @@ public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, Intern
exitStatus = exitStatus.addExitDescription(errorMsg);
contribution.setExitStatus(exitStatus);
}
this.totalStepsExecuted = this.totalSteps + 1;
totalStepsExecuted = dbtWrapper.getModelCount();
return RepeatStatus.FINISHED;
}
@ -104,16 +119,13 @@ public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, Intern @@ -104,16 +119,13 @@ public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, Intern
@Override
public int getInternalStepsExecutedCount() {
return totalStepsExecuted;
return totalStepsExecuted > 0 ? totalStepsExecuted : dbtWrapper.getModelCount();
}
@Override
public int getInternalTotalStepsCount() {
DbtAction action = (this.dbtTest) ? DbtAction.NODE_COUNT_TEST : DbtAction.NODE_COUNT_BUILD;
DbtRunRequest request = new DbtRunRequest(action, dbtProject, dbtSelect, DBT_TIMEOUT_MILLISECONDS);
DbtRunResult result = dbtWrapper.execute(request);
this.totalSteps = result.getNodeCount();
return result.getNodeCount();
dbtWrapper.init(dbtProjectDir, dbtSelect, dbtTest);
return dbtWrapper.getNodeCount();
}
}

134
src/de/superx/spring/batch/tasklet/ExecuteSQLFluffTasklet.java

@ -1,8 +1,15 @@ @@ -1,8 +1,15 @@
package de.superx.spring.batch.tasklet;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Map;
import org.apache.commons.lang3.SystemUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.ExitStatus;
@ -12,66 +19,147 @@ import org.springframework.batch.core.step.tasklet.Tasklet; @@ -12,66 +19,147 @@ import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import de.superx.dbt.DbtWrapper;
import de.superx.dbt.DbtAction;
import de.superx.dbt.DbtExecutionState;
import de.superx.dbt.DbtRunRequest;
import de.superx.dbt.DbtRunResult;
import de.superx.dbt.DbtUtils;
import de.superx.util.PathAndFileUtils;
public class ExecuteSQLFluffTasklet implements Tasklet, InternalProgress {
public String filesWithErrors;
public String error;
Logger logger = LoggerFactory.getLogger(this.getClass());
private DbtWrapper dbtWrapper;
private String dbtProject;
private final Path dbtProjectDir = Path.of(
PathAndFileUtils.getWebinfPath(),
"..",
"dbt",
"projects",
"hisinone",
"transform");
private final Path sqlFluffDir = dbtProjectDir.resolve("models");
private int progress = 0;
public ExecuteSQLFluffTasklet(DbtWrapper dbtWrapper, String dbtProject) {
public ExecuteSQLFluffTasklet(DbtWrapper dbtWrapper) {
this.dbtWrapper = dbtWrapper;
this.dbtProject = dbtProject;
}
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
try {
DbtRunRequest request = new DbtRunRequest(DbtAction.SQLFLUFF_LINT, dbtProject,
progressStep -> progress = progressStep.intValue());
DbtRunResult result = dbtWrapper.execute(request);
if(result.getState().equals(DbtExecutionState.SUCCESS)) {
contribution.setExitStatus(ExitStatus.COMPLETED);
} else {
String message = "Linting failed: \n";
message += String.join("\n", result.getErrorMessages());
int sqlFluffStatus = runSQLFluffLint();
if(sqlFluffStatus != 0) {
ExitStatus exitStatus = ExitStatus.FAILED;
String message = "";
if(sqlFluffStatus == 1) {
message += "Linting failed for the following files: \n";
message += filesWithErrors;
} else if(sqlFluffStatus == 2) {
message += error;
} else {
message += "Unknown exit code: " + sqlFluffStatus;
}
exitStatus = exitStatus.addExitDescription(message);
contribution.setExitStatus(exitStatus);
}
} catch (Exception e) {
ExitStatus exitStatus = ExitStatus.FAILED;
String errorMsg = e.getMessage();
exitStatus = exitStatus.addExitDescription(errorMsg);
contribution.setExitStatus(exitStatus);
} finally {
filesWithErrors = "";
error = "";
}
return RepeatStatus.FINISHED;
}
public int runSQLFluffLint() throws IOException, InterruptedException {
// TODO: DbtManager wird hier nur gebraucht um bei Bedarf PYTHONUSERBASE
// in das Environment des Prozesses zu legen.
// Entweder DbtManager erweitern um sqlfluff ausführen zu können,
// oder die Ermittlung PYTHONUSERBASE für Linux herauslösen und generisch
// zur Verfügung zu stellen:
// Unter Linux:
// * Existiert dbt_env_linux, so wird dieses verwendet
// * Ansonsten wird PYTHONUSERBASE auf dbt_linux gesetzt
//DbtManager manager = new DbtManager(dbtProjectDir, "");
this.dbtWrapper.init(dbtProjectDir, "");
Path dbtBinDir = DbtUtils.getDbtBinaryFolder();
String sqlFluffBin = dbtBinDir.toString() + File.separator + "sqlfluff";
if(!SystemUtils.IS_OS_LINUX) {
// we need the exe extension otherwise it does not work on windows
sqlFluffBin += ".exe";
}
logger.info("sqlfluff bin: " + sqlFluffBin);
ProcessBuilder builder = new ProcessBuilder(
DbtUtils.getPython3ExecutableName(),
sqlFluffBin,
"lint",
"--verbose",
"--nocolor",
"--disable-progress-bar"
).directory(dbtProjectDir.toFile());
Map<String, String> env = builder.environment();
env.put("COMP_RES_INSTALLED", "true");
env.put("DBT_DB", "eduetl");
env.putAll(dbtWrapper.getDbtEnvironment());
Process process = builder.start();
// read stdout
filesWithErrors = "";
Process p = builder.start();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()))){
String line;
while((line = reader.readLine()) != null) {
logger.info(line);
if(line.contains(".sql")) {
if (line.contains("FAIL")) {
filesWithErrors += line + "\n";
}
progress += 1;
}
}
}
// read stderr
error = "";
try (InputStream es = process.getErrorStream();
InputStreamReader esr = new InputStreamReader(es);
BufferedReader errReader = new BufferedReader(esr)) {
String line;
while ((line = errReader.readLine()) != null) {
logger.error(line);
error += line + "\n";
}
}
int exitCode = p.waitFor();
return exitCode;
}
private static boolean isBinaryAvailable(String binaryName) {
ProcessBuilder pb = new ProcessBuilder(binaryName, "--version");
try {
Process process = pb.start();
return true;
} catch (IOException e) {
return false;
}
}
@Override
public int getInternalTotalStepsCount() {
Path modelsDir = DbtUtils.getDbtModelsDirectory(dbtProject);
int count = 0;
try {
count = (int) Files.walk(modelsDir)
count = (int) Files.walk(sqlFluffDir)
.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".sql"))
.count();
}
catch (IOException e) {
logger.error("Couldn't count sql files in " + modelsDir, e);
logger.error("Couldn't count sql files in " + sqlFluffDir, e);
}
return count;
}

18
src/de/superx/spring/batch/tasklet/ExecuteSqlTasklet.java

@ -82,7 +82,7 @@ public class ExecuteSqlTasklet extends CommonSqlTasklet implements Tasklet, Step @@ -82,7 +82,7 @@ public class ExecuteSqlTasklet extends CommonSqlTasklet implements Tasklet, Step
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
String sqlWithSubstitutions = SqlStringUtils.replaceAllProperties(sql, params);
sqlWithSubstitutions = checkFreemarker(sqlWithSubstitutions);
sql_execution: try {
try {
jdbcTemplate.execute(sqlWithSubstitutions);
} catch (DataAccessException e) {
Throwable rootCause = e.getRootCause();
@ -93,21 +93,21 @@ public class ExecuteSqlTasklet extends CommonSqlTasklet implements Tasklet, Step @@ -93,21 +93,21 @@ public class ExecuteSqlTasklet extends CommonSqlTasklet implements Tasklet, Step
// diese kann aber ignoriert werden!
if (sx.getErrorCode() == -111 || "IX000".equals(sx.getSQLState())) {
logger.info("INSERT … SELECT returned no rows – nothing to do. ");
break sql_execution;
}
} else {
HikariDataSource hikariDataSource = (HikariDataSource) jdbcTemplate.getDataSource();
Connection con = hikariDataSource.getConnection();
con.close();
hikariDataSource.evictConnection(con);
logger.error("Error Executing SQL:", rootCause);
if (!continueOnError) {
throw e;
}
ExitStatus exitStatus = ExitStatus.FAILED;
String errorMsg = rootCause.getMessage();
exitStatus = exitStatus.addExitDescription(errorMsg.substring(0, errorMsg.length()));
contribution.setExitStatus(exitStatus);
}
if (!continueOnError) {
throw e;
}
ExitStatus exitStatus = ExitStatus.FAILED;
String errorMsg = rootCause.getMessage();
exitStatus = exitStatus.addExitDescription(errorMsg.substring(0, errorMsg.length()));
contribution.setExitStatus(exitStatus);
}
logger.info(sqlWithSubstitutions);
return RepeatStatus.FINISHED;

9
src/de/superx/spring/batch/tasklet/PgCopyLoadTasklet.java

@ -22,7 +22,6 @@ import org.springframework.batch.core.scope.context.ChunkContext; @@ -22,7 +22,6 @@ import org.springframework.batch.core.scope.context.ChunkContext;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.core.JdbcTemplate;
import de.superx.util.TrailingDelimiterRemovingReader;
@ -62,10 +61,6 @@ public class PgCopyLoadTasklet implements Tasklet, StepListener { @@ -62,10 +61,6 @@ public class PgCopyLoadTasklet implements Tasklet, StepListener {
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
logger.info("Load from " + unlFile.getFile().getAbsolutePath());
if (!unlFile.exists()) {
logger.warn("Not loading because unl file does not exist: " + unlFile.getFilename());
return RepeatStatus.FINISHED;
}
logger.info("Into table " + table);
String copySql = "COPY " + table + " FROM STDIN WITH ("+ (isCsv ? "FORMAT csv, " : "") + "DELIMITER '" + delimiter
+ "', NULL '', ENCODING '" + encoding + "'" + (header ? " HEADER" : "") + ")";
@ -84,9 +79,7 @@ public class PgCopyLoadTasklet implements Tasklet, StepListener { @@ -84,9 +79,7 @@ public class PgCopyLoadTasklet implements Tasklet, StepListener {
CopyManager copyManager = getPgConnection(con).getCopyAPI();
count = copyManager.copyIn(copySql, reader);
}
JdbcTemplate jt = new JdbcTemplate(dataSource);
jt.execute("ANALYZE " + table);
logger.info(count + " records loaded with analyze.");
logger.info(count + " records loaded.");
} catch (final Exception e) {
String errorMsg = e.getMessage();
logger.error("Error loading table: " + table);

2
src/de/superx/spring/cli/config/CLIConfig.java

@ -8,7 +8,7 @@ import org.springframework.context.annotation.Configuration; @@ -8,7 +8,7 @@ import org.springframework.context.annotation.Configuration;
/**
* This configuration serves a relatively simple purpose:
* It is meant to be used in combination with the "AppConfig" configuration,
* It is meant to be used in combination with the "BatchConfig" configuration,
* but overwrites the "JobLauncher"-Bean with a synchronized version.
* This way, all (spring) batch jobs should always gets executed synchronously,
* which ist (e.g.) suitable for the context of a command line call (thus the class name).

257
src/de/superx/spring/config/BatchConfig.java

@ -0,0 +1,257 @@ @@ -0,0 +1,257 @@
package de.superx.spring.config;
import java.io.File;
import java.nio.file.Path;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.sql.DataSource;
import org.apache.log4j.Logger;
import org.springframework.batch.core.Job;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.configuration.JobLocator;
import org.springframework.batch.core.configuration.JobRegistry;
import org.springframework.batch.core.configuration.annotation.EnableBatchProcessing;
import org.springframework.batch.core.explore.JobExplorer;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.launch.JobLauncher;
import org.springframework.batch.core.launch.JobOperator;
import org.springframework.batch.core.launch.support.SimpleJobOperator;
import org.springframework.batch.core.launch.support.TaskExecutorJobLauncher;
import org.springframework.batch.core.repository.JobRepository;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.init.ScriptUtils;
import org.springframework.transaction.PlatformTransactionManager;
import com.zaxxer.hikari.HikariDataSource;
import de.superx.spring.HIS1Databases;
import de.superx.spring.batch.His1DataSources;
import de.superx.spring.batch.job.JobUtils;
import de.superx.spring.batch.tasklet.RemoveSpringBatchHistoryTasklet;
import de.superx.util.PathAndFileUtils;
@Configuration
@EnableBatchProcessing
@PropertySource("classpath:spring-batch.properties")
@PropertySource("classpath:hikari.properties")
@ComponentScan(basePackages = {"de.superx.spring.service", "de.superx.spring.batch.tasklet", "de.superx.rest", "de.superx.dbt",
"de.superx.bianalysis.service", "de.superx.bianalysis.rest", "de.superx.access"})
public class BatchConfig {
// time in milliseconds after which an exception is
// thrown if no connection available in pool
@Value("${hikari.connection_timeout:180000}")
protected Long connectionTimeout;
// time in milliseconds a connection can be out of pool
// before an execption gets thrown to prevent connection leaks
// (for debug purposes, 0 means disable leak detection)
@Value("${hikari.leak_detection_threshold:0}")
protected Long leakDetectionThreshold;
public static final int DEFAULT_POOL_SIZE = 5;
public static final int EDUETL_POOL_SIZE = 20; // low pool size led to hanging application on 'muster-head' - former value: 6;
private static Logger logger = Logger.getLogger(BatchConfig.class);
@Autowired
private JobRepository jobRepository;
@Autowired
private JobRegistry jobRegistry;
@Autowired
private JobExplorer jobExplorer;
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public JobLauncher jobLauncher() throws Exception {
TaskExecutorJobLauncher jobLauncher = new TaskExecutorJobLauncher();
jobLauncher.setJobRepository(jobRepository);
jobLauncher.setTaskExecutor(new SimpleAsyncTaskExecutor());
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
@Bean
public JobLocator jobLocator() {
return jobRegistry;
}
@Bean
public JobLauncher syncJobLauncher() throws Exception {
TaskExecutorJobLauncher jobLauncher = new TaskExecutorJobLauncher();
jobLauncher.setJobRepository(jobRepository);
jobLauncher.afterPropertiesSet();
return jobLauncher;
}
@Bean
public JobOperator jobOperator() throws Exception {
SimpleJobOperator jobOperator = new SimpleJobOperator();
jobOperator.setJobLauncher(jobLauncher());
jobOperator.setJobExplorer(jobExplorer);
jobOperator.setJobRepository(jobRepository);
jobOperator.setJobRegistry(jobRegistry);
JobUtils.terminateAllJobExecutions(jobExplorer, jobOperator);
return jobOperator;
}
@Bean
public His1DataSources dataSources() {
//Map<String, DataSourceDiscardingUserStuff> dataSources = new HashMap<>();
Map<String, HikariDataSource> dataSources = new HashMap<>();
try {
for(Entry<String, Map<String,String>> co : HIS1Databases.getValidConnections().entrySet()) {
HikariDataSource hs = HIS1Databases.convertConnection(co.getKey().toString(), co.getValue(),
leakDetectionThreshold.longValue(), connectionTimeout.longValue());
//DataSourceDiscardingUserStuff dsdu = new DataSourceDiscardingUserStuff(hs);
dataSources.put(co.getKey().toString(), hs);
logger.info(String.format("Connection to %s added to dataSources bean", co.getKey()));
}
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException
| BadPaddingException e) {
logger.fatal(e);
e.printStackTrace();
}
return new His1DataSources(dataSources);
}
@Bean
public DataSource unrestrictedDataSource() {
DataSource ds = dataSources().get("eduetl");
if (!batchSchemaExists(ds)) {
dropBatchSchema(ds);
createBatchSchema(ds);
}
upgradeBatchSchema(ds);
if (!quartzSchemaExists(ds)) {
createQuartzSchema(ds);
}
return ds;
}
@Bean
@Primary
public DataSource dataSource() {
DataSource ds = new DataSourceDiscardingUserStuff();
return ds;
}
@Bean
public Step removeStep(PlatformTransactionManager transactionManager, DataSource dataSource) {
RemoveSpringBatchHistoryTasklet tasklet = new RemoveSpringBatchHistoryTasklet();
tasklet.setJdbcTemplate(new JdbcTemplate(dataSource));
String name = "removeStep";
return new StepBuilder(name, jobRepository)
.tasklet(tasklet, transactionManager)
.build();
}
@Bean
public Job removeSpringBatchHistoryJob(Step removeStep) {
return new JobBuilder("removeSpringBatchHistoryJob", jobRepository)
.start(removeStep)
.build();
}
//Checks whether the necessary tables for spring batch exist
//TODO: Should implement method of creating a dump before creating new schema in case not all tables exist
private static boolean batchSchemaExists(DataSource dataSource) {
int count = 0;
try(Connection con = dataSource.getConnection()) {
DatabaseMetaData meta = con.getMetaData();
ResultSet resultSet = meta.getTables(null, "public", "batch_%" , new String[] {"TABLE"});
while (resultSet.next()) {
count++;
}
} catch (SQLException e) {
throw new RuntimeException("Couldn't check for spring batch tables", e);
}
return count == 6;
}
private static boolean quartzSchemaExists(DataSource dataSource) {
try(Connection con = dataSource.getConnection()) {
DatabaseMetaData meta = con.getMetaData();
ResultSet resultSet = meta.getTables(null, "public", "qrtz_%" , new String[] {"TABLE"});
if (resultSet.next()) {
return true;
}
} catch (SQLException e) {
throw new RuntimeException("Couldn't check for spring batch tables", e);
}
return false;
}
private static void createBatchSchema(DataSource dataSource) {
try (Connection con = dataSource.getConnection()){
ScriptUtils.executeSqlScript(con, new ClassPathResource("/org/springframework/batch/core/schema-postgresql.sql"));
logger.info("Spring Batch schema created;");
} catch (Exception e) {
logger.error("Couldn't create Spring Batch schema!", e);
}
}
private static void dropBatchSchema(DataSource dataSource) {
try (Connection con = dataSource.getConnection()){
ScriptUtils.executeSqlScript(con, new ClassPathResource("/org/springframework/batch/core/schema-drop-postgresql.sql"));
logger.info("Spring Batch schema dropped;");
} catch (Exception e) {
logger.error("Couldn't drop Spring Batch schema!", e);
}
}
private static void createQuartzSchema(DataSource dataSource) {
try (Connection con = dataSource.getConnection()){
ScriptUtils.executeSqlScript(con, new ClassPathResource("/org/quartz/impl/jdbcjobstore/tables_postgres.sql"));
logger.info("Quartz schema created;");
} catch (Exception e) {
logger.error("Couldn't create Quartz schema!", e);
}
}
public static void upgradeBatchSchema(DataSource dataSource) {
try (Connection con = dataSource.getConnection()){
File file = new File(String.format("%s/conf/edustore/db/install/upgrade/spring_batch_schema_upgrade.sql", PathAndFileUtils.getWebinfPath()));
ScriptUtils.executeSqlScript(con, new FileSystemResource(file));
logger.info("Spring Batch schema upgraded;");
} catch (Exception e) {
logger.error("Couldn't upgrade Spring Batch schema!", e);
}
}
}

4
src/de/superx/spring/config/BiApplicationContextInit.java

@ -41,9 +41,7 @@ public class BiApplicationContextInit { @@ -41,9 +41,7 @@ public class BiApplicationContextInit {
logger.warn("eduetl db empty. Not installing module functions.");
return;
}
if(SuperXManager.isDBPropsSet())
return; //only install functions and etl-Jobs in H1, not standalone
logger.info("Installing module functions:");
logger.info("Installing module funtions:");
if (!dbFunctionsLoaded) {
EtlJobApi jobApi = ctx.getBean(EtlJobApi.class);
List<Component> components = jobApi.getAllComponentAndConnectorDetails();

48
src/de/superx/spring/service/ApplicationContextHolder.java

@ -1,48 +0,0 @@ @@ -1,48 +0,0 @@
package de.superx.spring.service;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
/**
* Holds the Spring ApplicationContext in a static field.
* Implemented as a Spring bean so Spring itself injects the context.
*/
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static volatile ApplicationContext context;
@Override
public void setApplicationContext(ApplicationContext ctx) {
context = ctx; // called once during startup
}
public static <T> T getBean(String beanName, Class<T> requiredType) {
if (getContext() != null) {
if (beanName != null && !beanName.isEmpty()) {
return getContext().getBean(beanName, requiredType);
}
return getContext().getBean(requiredType);
}
return null;
}
public static Object getBean(String name) {
return getContext().getBean(name);
}
public static <T> T getBean(Class<T> requiredType) {
return getContext().getBean(requiredType);
}
/** Public static accessor */
public static ApplicationContext getContext() {
return context;
}
/** Optional: clear the reference when the webapp stops (helps GC) */
public static void clear() {
context = null;
}
}

54
src/de/superx/spring/service/BatchJobDescriptionAdapter.java

@ -5,6 +5,7 @@ import static de.superx.util.PathAndFileUtils.substituteParamStrings; @@ -5,6 +5,7 @@ import static de.superx.util.PathAndFileUtils.substituteParamStrings;
import java.io.File;
import java.io.IOException;
import java.io.Writer;
import java.nio.file.Files;
import java.sql.Connection;
import java.sql.ResultSet;
@ -12,11 +13,11 @@ import java.sql.SQLException; @@ -12,11 +13,11 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import javax.sql.DataSource;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.QuoteMode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.batch.core.BatchStatus;
@ -44,8 +45,11 @@ import org.springframework.batch.core.step.job.JobParametersExtractor; @@ -44,8 +45,11 @@ import org.springframework.batch.core.step.job.JobParametersExtractor;
import org.springframework.batch.core.step.tasklet.MethodInvokingTaskletAdapter;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.core.step.tasklet.TaskletStep;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.file.FlatFileHeaderCallback;
import org.springframework.batch.item.file.FlatFileItemReader;
import org.springframework.batch.item.file.FlatFileItemWriter;
import org.springframework.batch.item.file.LineMapper;
import org.springframework.batch.item.file.transform.LineAggregator;
import org.springframework.batch.item.file.transform.PassThroughLineAggregator;
import org.springframework.beans.factory.annotation.Autowired;
@ -348,8 +352,7 @@ public class BatchJobDescriptionAdapter { @@ -348,8 +352,7 @@ public class BatchJobDescriptionAdapter {
UpdateMetadataAction umd = (UpdateMetadataAction) node;
result = createUpdateMetadataStep(stepBuilder, umd.dbtProject);
} else if(node instanceof ExecuteSQLFluffAction) {
ExecuteSQLFluffAction eda = (ExecuteSQLFluffAction) node;
result = new StepData(stepBuilder.tasklet(new ExecuteSQLFluffTasklet(dbtWrapper, eda.dbtProject),transactionManager).build());
result = new StepData(stepBuilder.tasklet(new ExecuteSQLFluffTasklet(dbtWrapper),transactionManager).build());
} else if(node instanceof RemoveSpringBatchHistoryAction) {
RemoveSpringBatchHistoryTasklet rsbht = new RemoveSpringBatchHistoryTasklet();
rsbht.setJdbcTemplate(new JdbcTemplate(dataSources.get("eduetl")));
@ -368,8 +371,9 @@ public class BatchJobDescriptionAdapter { @@ -368,8 +371,9 @@ public class BatchJobDescriptionAdapter {
}
private StepData createSqlActionNoSxPool(StepBuilder stepBuilder, SqlActionNoSxPool sqlScriptAction) {
DataSource ds = getDataSource(sqlScriptAction.database, "SqlActionNoSxPool " + sqlScriptAction.name).orElse(null);
DataSource ds = dataSources.get(sqlScriptAction.database);
if (ds == null) {
logger.error("DataSource not found: " + sqlScriptAction.database);
return null;
}
SqlScriptActionNoSxPoolTasklet tasklet = new SqlScriptActionNoSxPoolTasklet(ds);
@ -400,8 +404,9 @@ public class BatchJobDescriptionAdapter { @@ -400,8 +404,9 @@ public class BatchJobDescriptionAdapter {
}
private StepData createUnloadParamsStep(StepBuilder stepBuilder, Integer systemInfoId) {
DataSource ds = getDataSource("eduetl", "Create UnloadParams").orElse(null);
DataSource ds = dataSources.get("eduetl");
if (ds == null) {
logger.error("DataSource not found: eduetl");
return null;
}
Tasklet unloadReader = new UnloadParamReaderTasklet(systemInfoId.intValue(), ds);
@ -409,8 +414,9 @@ public class BatchJobDescriptionAdapter { @@ -409,8 +414,9 @@ public class BatchJobDescriptionAdapter {
}
private StepData createTimestampStep(StepBuilder stepBuilder, Integer systemInfoId) {
DataSource ds = getDataSource("eduetl", "Create Timestamp").orElse(null);
DataSource ds = dataSources.get("eduetl");
if (ds == null) {
logger.error("DataSource not found: eduetl");
return null;
}
String sql = String.format("update systeminfo s set datum = now() where "
@ -420,8 +426,9 @@ public class BatchJobDescriptionAdapter { @@ -420,8 +426,9 @@ public class BatchJobDescriptionAdapter {
}
private StepData createUnloadStep(StepBuilder stepBuilder, UnloadAction unloadAction, String paramCompVersion) {
DataSource ds = getDataSource(unloadAction.dataSource, "UnloadAction " + unloadAction.name).orElse(null);
DataSource ds = dataSources.get(unloadAction.dataSource);
if (ds == null) {
logger.error("DataSource not found: " + unloadAction.dataSource);
return null;
}
// TODO: We really should use a header line in csv
@ -450,8 +457,9 @@ public class BatchJobDescriptionAdapter { @@ -450,8 +457,9 @@ public class BatchJobDescriptionAdapter {
}
private StepData createExtractStep(StepBuilder stepBuilder, ExtractAction extractAction) {
DataSource ds = getDataSource(extractAction.dataSource, "ExtractAction " + extractAction.name).orElse(null);
DataSource ds = dataSources.get(extractAction.dataSource);
if (ds == null) {
logger.error("DataSource not found: " + extractAction.dataSource);
return null;
}
JdbcExtractReader<String> reader = new JdbcExtractReader<String>(extractAction, dataSources.get(extractAction.dataSource));
@ -480,8 +488,9 @@ public class BatchJobDescriptionAdapter { @@ -480,8 +488,9 @@ public class BatchJobDescriptionAdapter {
}
private StepData createTransferStep(StepBuilder stepBuilder, TransferAction transferAction) {
DataSource ds = getDataSource("eduetl", "TransferAction " + transferAction.name).orElse(null);
DataSource ds = dataSources.get("eduetl");
if (ds == null) {
logger.error("DataSource not found: eduetl");
return null;
}
ExecuteSqlTasklet transfer = new ExecuteSqlTasklet("truncate table " +
@ -505,14 +514,15 @@ public class BatchJobDescriptionAdapter { @@ -505,14 +514,15 @@ public class BatchJobDescriptionAdapter {
}
private StepData createSqlScriptStep(StepBuilder stepBuilder, SqlScriptAction sqlScriptAction) {
DataSource ds = getDataSource(sqlScriptAction.dataSource, "SqlScriptAction " + sqlScriptAction.name).orElse(null);
DataSource ds = dataSources.get(sqlScriptAction.dataSource);
if (ds == null) {
logger.error("DataSource not found: " + sqlScriptAction.dataSource);
return null;
}
DataSource varDs = sqlScriptAction.sqlVarDataSource != null && !sqlScriptAction.sqlVarDataSource.isEmpty() ?
dataSources.get(sqlScriptAction.sqlVarDataSource) : ds;
if (varDs == null) {
logger.warn("VarDataSource not found: " + sqlScriptAction.sqlVarDataSource);
logger.error("VarDataSource not found: " + sqlScriptAction.sqlVarDataSource);
return null;
}
File scriptFile = new File(substituteParamStrings(sqlScriptAction.sqlScriptPath));
@ -531,8 +541,9 @@ public class BatchJobDescriptionAdapter { @@ -531,8 +541,9 @@ public class BatchJobDescriptionAdapter {
}
private StepData createSqlStep(StepBuilder stepBuilder, SqlAction sqlAction) {
DataSource ds = getDataSource(sqlAction.dataSource, "SqlAction " + sqlAction.name).orElse(null);
DataSource ds = dataSources.get(sqlAction.dataSource);
if (ds == null) {
logger.error("DataSource not found: " + sqlAction.dataSource);
return null;
}
DataSource varDs = sqlAction.sqlVarDataSource != null && !sqlAction.sqlVarDataSource.isEmpty() ?
@ -557,21 +568,21 @@ public class BatchJobDescriptionAdapter { @@ -557,21 +568,21 @@ public class BatchJobDescriptionAdapter {
}
private StepData createLoadStep(StepBuilder stepBuilder, LoadAction loadAction) {
DataSource ds = getDataSource(loadAction.database, "LoadAction " + loadAction.name).orElse(null);
DataSource ds = dataSources.get(loadAction.database);
if (ds == null) {
logger.error("DataSource not found: " + loadAction.database);
return null;
}
if (loadAction.checkEmpty && !tableEmpty(loadAction.tableName, ds)) {
logger.warn("Not executing " + loadAction.name + " because table not empty and checkEmpty=true!");
return null;
}
File unlFile = new File(substituteParamStrings(loadAction.unlFilePath));
if (loadAction.usePostgresCopyApi) {
PgCopyLoadTasklet upload = new PgCopyLoadTasklet(new FileSystemResource(unlFile),
PgCopyLoadTasklet upload = new PgCopyLoadTasklet(new FileSystemResource(substituteParamStrings(loadAction.unlFilePath)),
loadAction.delimiter, loadAction.tableName, ds, loadAction.refresh, loadAction.isCsv);
return new StepData(stepBuilder.tasklet(upload, transactionManager).build());
}
FlatFileItemReader<String[]> csvReader = createCsvReaderIfx(unlFile, loadAction.delimiter);
FlatFileItemReader<String[]> csvReader = createCsvReaderIfx(new File(substituteParamStrings(loadAction.unlFilePath)), loadAction.delimiter);
TypeinferringJdbcBatchItemWriter dbTableWriter =
new TypeinferringJdbcBatchItemWriter(ds, loadAction.tableName,
loadAction.refresh);
@ -646,17 +657,6 @@ public class BatchJobDescriptionAdapter { @@ -646,17 +657,6 @@ public class BatchJobDescriptionAdapter {
return job;
}
private Optional<DataSource> getDataSource(String dataSourceName, String forWhat) {
Optional<DataSource> result = Optional.empty();
DataSource ds = this.dataSources.get(dataSourceName);
if (ds != null) {
result = Optional.of(ds);
} else {
logger.warn("DataSource " + dataSourceName + " not found for " + forWhat);
}
return result;
}
}
class StepData {

108
src/de/superx/spring/service/EntityJobDescriptionSource.java

@ -17,7 +17,6 @@ import java.util.ArrayList; @@ -17,7 +17,6 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
@ -30,7 +29,6 @@ import javax.xml.transform.stream.StreamSource; @@ -30,7 +29,6 @@ import javax.xml.transform.stream.StreamSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.w3c.dom.CharacterData;
import org.w3c.dom.Document;
@ -43,10 +41,6 @@ import org.xml.sax.SAXException; @@ -43,10 +41,6 @@ import org.xml.sax.SAXException;
import de.memtext.util.StringUtils;
import de.memtext.util.XMLUtils;
import de.superx.dbt.DbtAction;
import de.superx.dbt.DbtExecutionState;
import de.superx.dbt.DbtRunRequest;
import de.superx.dbt.DbtRunResult;
import de.superx.dbt.DbtWrapper;
import de.superx.etl.biad.DBSource;
import de.superx.etl.biad.SourceTable;
@ -85,9 +79,6 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -85,9 +79,6 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
public static final int SYSTEMINFO_ID_SOS = 7;
static Logger logger = LoggerFactory.getLogger(EntityJobDescriptionSource.class);
@Autowired
DbtWrapper dbtWrapper;
@Override
public List<ContainerNode> readJobs() {
List<ContainerNode> jobs = new ArrayList<>();
@ -142,12 +133,12 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -142,12 +133,12 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
} else {
SourcesYML sourcesYML = SourcesYML.fromJson(file);
for (DBSource source : sourcesYML.getSources()) {
if(!source.getName().startsWith("staging")) {
if(!source.getName().equals("staging")) {
continue;
}
for (SourceTable table : source.getTables()) {
String unlFile = PathAndFileUtils.buildUnlFile(EntityJobDescriptionSource.BIAD_COMPONENT_ABBREVIATION, table.getName());
ExtractAction action = ExtractAction.createFromSourceTable(table, unlFile, source.getName(), source.getSrcDatabase());
ExtractAction action = ExtractAction.createFromSourceTable(table, unlFile, source.getSchema());
root.add(action);
}
}
@ -250,7 +241,7 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -250,7 +241,7 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
}
logger.info("etl-step: " + name);
ContainerNode container = createContainerNode(name, id, systemInfoId);
List<ActionNode> stepList = createActions(filterElements(etlStep.getChildNodes()), systemInfoId, componentAbbreviation);
List<ActionNode> stepList = createActions(etlStep.getChildNodes(), systemInfoId, componentAbbreviation);
if (stepList.size() > 0) {
container.addAll(stepList);
}
@ -387,13 +378,8 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -387,13 +378,8 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
&& "true".equalsIgnoreCase(active)
&& "true".equalsIgnoreCase(visible)) {
if (generated) {
List<Node> childNodes = filterElements(etlStep.getChildNodes());
List<Node> desc = childNodes.stream().filter(el -> el.getNodeName().equals("description")).collect(Collectors.toList());
ContainerNode root = createContainerNode(componentAbbreviation.toUpperCase() + " " + name, JobType.special.createName(componentAbbreviation, id), systemInfoId);
if (desc.size() > 0) {
root.setDescription(desc.get(0).getTextContent().trim());
}
List<ActionNode> stepList = createActions(childNodes, systemInfoId, componentAbbreviation);
List<ActionNode> stepList = createActions(etlStep.getChildNodes(), systemInfoId, componentAbbreviation);
if (stepList.size() > 0) {
root.addAll(stepList);
}
@ -490,7 +476,7 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -490,7 +476,7 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
}
continue;
}
List<ActionNode> stepList = createActions(filterElements(installStep.getChildNodes()), systemInfoId, componentAbbreviation);
List<ActionNode> stepList = createActions(installStep.getChildNodes(), systemInfoId, componentAbbreviation);
for (ActionNode node : stepList) {
if (node.id.endsWith("upgrade_tables_pg.sql") || node.id.endsWith("upgrade_views_pg.sql")
|| node.id.endsWith("upgrade_fields_pg.sql")
@ -512,7 +498,7 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -512,7 +498,7 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
scriptStepsContainer.addAll(scriptSteps);
installSteps = document.getElementsByTagName("upgrade-step");
if (installSteps.getLength() == 1) {
List<ActionNode> stepList = createActions(filterElements(installSteps.item(0).getChildNodes()), systemInfoId, componentAbbreviation);
List<ActionNode> stepList = createActions(installSteps.item(0).getChildNodes(), systemInfoId, componentAbbreviation);
if (stepList.size() > 0) {
scriptStepsContainer.addAll(stepList);
}
@ -572,14 +558,14 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -572,14 +558,14 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
container.addAll(actionNodes);
} else {
// generic container
container.addAll(createActions(filterElements(installStep.getChildNodes()), systemInfoId, componentAbbreviation));
container.addAll(createActions(installStep.getChildNodes(), systemInfoId, componentAbbreviation));
}
return container;
}
private List<ActionNode> metadaten(String componentAbbreviation, Integer systemInfoId, Node installStep) {
return createActions(filterElements(installStep.getChildNodes()), systemInfoId, componentAbbreviation);
return createActions(installStep.getChildNodes(), systemInfoId, componentAbbreviation);
}
private List<ActionNode> installMasks(String componentAbbreviation, Integer systemInfoId) throws IOException {
@ -719,8 +705,9 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -719,8 +705,9 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
return actions;
}
private List<ActionNode> createActions(List<Node> actionNodes, Integer systemInfoId, String componentAbbreviation) {
private List<ActionNode> createActions(NodeList steps, Integer systemInfoId, String componentAbbreviation) {
List<ActionNode> result = new ArrayList<>();
List<Node> actionNodes = filterElements(steps);
actionNodes.forEach(node -> {
logger.info("action :");
List<Node> actions = filterElements(node.getChildNodes());
@ -761,16 +748,11 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -761,16 +748,11 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
result.addAll(createLoadAfterExtract(componentAbbreviation, systemInfoId));
} else if("biad_unload_and_load".equals(name)) {
result.add(getUnloadJob(EntityJobDescriptionSource.BIAD_COMPONENT_ABBREVIATION));
List<ActionNode> nodes = createLoadAfterExtract(componentAbbreviation, Integer.valueOf(SYSTEMINFO_ID_BIAD));
result.addAll(nodes);
result.addAll(createLoadAfterExtract(componentAbbreviation, Integer.valueOf(SYSTEMINFO_ID_BIAD)));
} else if("evaluate-dbt-json".equals(name)) {
String noGroupActionAttr = getAttribute(attributes, "noGroupAction");
boolean groupAction = !Boolean.parseBoolean(noGroupActionAttr);
result.addAll(createDbtExecutionStep(attributes, systemInfoId));
result.add(createMetaimportJsonStep(attributes, systemInfoId));
if (groupAction) {
result.add(new ExecuteActionGroupAction(systemInfoId));
}
result.add(new ExecuteActionGroupAction(systemInfoId));
} else if("evaluate-metadata".equals(name)) {
result.add(createMetaimportJsonStep(attributes, systemInfoId));
} else if("evaluate-dbt-test".equals(name)) {
@ -794,7 +776,7 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -794,7 +776,7 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
String dbtProject = getAttribute(attributes, "project");
String dbtSelect = getAttribute(attributes, "select");
nodes.add(new ExecuteDbtAction(dbtProject, dbtSelect, systemInfoId, true));
nodes.add(new ExecuteSQLFluffAction(dbtProject, "Lint DBT Models using SQLFluff", "sqlfluff_action", Integer.valueOf(SYSTEMINFO_ID_BIAD), true, Integer.valueOf(0)));
nodes.add(new ExecuteSQLFluffAction("Lint DBT Models using SQLFluff", "sqlfluff_action", Integer.valueOf(SYSTEMINFO_ID_BIAD), true, Integer.valueOf(0)));
return nodes;
}
@ -802,24 +784,13 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -802,24 +784,13 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
List<ActionNode> nodes = new ArrayList<>();
String dbtProject = getAttribute(attributes, "project");
String dbtSelect = getAttribute(attributes, "select");
String sourceSystem = getAttribute(attributes, "sourcesystem");
if(sourceSystem == null || sourceSystem.isBlank()) {
sourceSystem = "hisinone";
}
if(dbtSelect != null && !dbtSelect.isBlank()) {
String id = getAttribute(attributes, "id");
ContainerNode extractLoad = createContainerNode("Extract and Load", "extract_load_" + id, systemInfoId);
DbtRunRequest request = new DbtRunRequest(DbtAction.STAGING_TABLES, dbtProject, dbtSelect);
DbtRunResult result = dbtWrapper.execute(request);
if(!result.getState().equals(DbtExecutionState.SUCCESS)) {
logger.error("Could not get specific Staging Tables.", String.join("\n", result.getErrorMessages()));
}
List<String> stagingTables = result.getOutputMessages();
List<String> stagingTables = DbtWrapper.getStagingTables(dbtSelect);
if(stagingTables != null && !stagingTables.isEmpty()) {
extractLoad.addAll(createExtractAndLoadStep(stagingTables, systemInfoId, sourceSystem));
extractLoad.addAll(createExtractAndLoadStep(stagingTables, systemInfoId));
nodes.add(extractLoad);
}
}
@ -833,31 +804,26 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -833,31 +804,26 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
return new UpdateMetadataAction(dbtProject, systemInfoId);
}
private static List<ActionNode> createExtractAndLoadStep(List<String> tables, Integer systemInfoId, String sourceSystem) {
private static List<ActionNode> createExtractAndLoadStep(List<String> tables, Integer systemInfoId) {
List<ActionNode> result = new ArrayList<>();
SourcesYML sourcesYML = SourcesYML.fromJson(new File(PathAndFileUtils.getSourcesYML("hisinone")));
List<DBSource> sources = sourcesYML.getSources();
for (DBSource source : sources) {
if(!source.getName().startsWith("staging")) {
for (DBSource source : sourcesYML.getSources()) {
if(!source.getName().equals("staging")) {
continue;
}
for (SourceTable table : source.getTables()) {
if(!tables.contains(table.getName())) {
continue;
}
String unlFile = PathAndFileUtils.buildUnlFile(EntityJobDescriptionSource.BIAD_COMPONENT_ABBREVIATION, table.getName());
ExtractAction action = ExtractAction.createFromSourceTable(table, unlFile, source.getSchema(), sourceSystem);
ExtractAction action = ExtractAction.createFromSourceTable(table, unlFile, source.getSchema());
result.add(action);
String unloadPath = PathAndFileUtils.getUnloadPath("biad")
+ File.separator + "tables" + File.separator + table.getName() + ".sql";
SqlScriptAction createTableAction = new SqlScriptAction("postgres", systemInfoId, unloadPath);
result.add(createTableAction);
LoadAction loadAction = new LoadAction(systemInfoId, false, ",", false,
"eduetl", source.getSchema() + "." + table.getName(), unlFile, true);
"eduetl", table.getName(), unlFile, true);
result.add(loadAction);
logger.info("\tloadtable: tabname=" + action.sourceTable + " path=" + action.unlFile);
}
@ -869,19 +835,19 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -869,19 +835,19 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
List<ActionNode> result = new ArrayList<>();
SourcesYML sourcesYML = SourcesYML.fromJson(new File(PathAndFileUtils.getSourcesYML("hisinone")));
for (DBSource source : sourcesYML.getSources()) {
if(!source.getName().startsWith("staging")) {
if(!source.getName().equals("staging")) {
continue;
}
for (SourceTable table : source.getTables()) {
String unlFile = PathAndFileUtils.buildUnlFile(EntityJobDescriptionSource.BIAD_COMPONENT_ABBREVIATION, table.getName());
ExtractAction action = ExtractAction.createFromSourceTable(table, unlFile, source.getSchema(), source.getSrcDatabase());
ExtractAction action = ExtractAction.createFromSourceTable(table, unlFile, source.getSchema());
String unloadPath = PathAndFileUtils.getUnloadPath("biad")
+ File.separator + "tables" + File.separator + table.getName() + ".sql";
SqlScriptAction createTableAction = new SqlScriptAction("postgres", systemInfoId, unloadPath);
LoadAction loadAction = new LoadAction(systemInfoId, false, ",", false,
"eduetl", source.getSchema() + "." + table.getName(), unlFile, true);
"eduetl", "staging." + table.getName(), unlFile, true);
result.add(createTableAction);
result.add(loadAction);
@ -922,7 +888,7 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -922,7 +888,7 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
Boolean.parseBoolean(refresh),
delimiter,
Boolean.parseBoolean(header),
"eduetl",
database.isEmpty() ? "eduetl" : database,
tabName,
unloadFileAttribute
);
@ -1005,18 +971,15 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -1005,18 +971,15 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
}
@Override
public List<ContainerNode> readInstallJobs(List<String> components) {
logger.debug("Reading install jobs from file system");
List<ContainerNode> jobs = new ArrayList<>();
for (String componentAbbreviation : PathAndFileUtils.getModules()) {
ContainerNode job = getInstallUpgradeJob(componentAbbreviation);
if (job != null) {
jobs.add(job);
}
}
logger.debug("Count of jobs: " + jobs.size());
return jobs;
}
public List<ContainerNode> readInstallJobs(List<String> components) {
logger.debug("Reading install jobs from file system");
List<ContainerNode> jobs = new ArrayList<>();
for (String componentAbbreviation : PathAndFileUtils.getModules()) {
jobs.add(getInstallUpgradeJob(componentAbbreviation));
}
logger.debug("Count of jobs: " + jobs.size());
return jobs;
}
public static ContainerNode getPreKernInstallJob() {
ContainerNode root = createContainerNode("kern_pre_install", "kern_pre_install", Integer.valueOf(9));
@ -1127,8 +1090,6 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -1127,8 +1090,6 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
int count = etlSteps.getLength();
for (int i = 0; i < count; i++) {
Node etlStep = etlSteps.item(i);
List<Node> childNodes = filterElements(etlStep.getChildNodes());
List<Node> desc = childNodes.stream().filter(el -> el.getNodeName().equals("description")).collect(Collectors.toList());
String id = getAttribute(etlStep.getAttributes(), "id");
String name = getAttribute(etlStep.getAttributes(), "name");
String type = getAttribute(etlStep.getAttributes(), "type");
@ -1138,9 +1099,6 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -1138,9 +1099,6 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
&& "true".equalsIgnoreCase(active)
&& "true".equalsIgnoreCase(visible)) {
ContainerNode root = createContainerNode(name, JobType.special.createName(abbreviation, id), systemInfoId);
if (desc.size() > 0) {
root.setDescription(desc.get(0).getTextContent().trim());
}
result.add(root);
}
}

48
src/de/superx/spring/service/JobAdapter.java

@ -150,31 +150,25 @@ public class JobAdapter implements JobDescriptionSource { @@ -150,31 +150,25 @@ public class JobAdapter implements JobDescriptionSource {
}
}
public static List<ContainerNode> mergeJobsPreferringDatabase(
List<ContainerNode> jobsDatabase,
List<ContainerNode> jobsFilesystem) {
List<ContainerNode> result = new ArrayList<>(jobsDatabase);
for (ContainerNode jobFs : jobsFilesystem) {
if (jobFs == null) {
logger.warn("Skipping null job from filesystem");
continue;
}
boolean isJobInDatabase = false;
for (ContainerNode jobDb : jobsDatabase) {
if (jobDb != null && jobDb.name.equals(jobFs.name)) {
isJobInDatabase = true;
break;
}
}
if (!isJobInDatabase) {
result.add(jobFs);
}
}
return result;
}
public static List<ContainerNode> mergeJobsPreferringDatabase(
List<ContainerNode> jobsDatabase,
List<ContainerNode> jobsFilesystem){
List<ContainerNode> result = new ArrayList<>(jobsDatabase);
for (ContainerNode jobFs : jobsFilesystem) {
boolean isJobInDatabase = false;
for (ContainerNode jobDb : jobsDatabase) {
if(jobDb.name.equals(jobFs.name)) {
isJobInDatabase = true;
break;
}
}
if(!isJobInDatabase) {
result.add(jobFs);
}
}
return result;
}
}

67
src/de/superx/spring/service/export/TableExportService.java

@ -17,7 +17,6 @@ import java.util.Set; @@ -17,7 +17,6 @@ import java.util.Set;
import javax.sql.DataSource;
import org.jdom2.CDATA;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.output.Format;
@ -90,25 +89,7 @@ public class TableExportService { @@ -90,25 +89,7 @@ public class TableExportService {
log.info("Exporting table '{}'", tableName);
String whereClause = null;
if (tableConfig.hasFilterSource()) {
// Per-table filter source overrides group filter.
// Supported placeholders:
// {filter} → full group filter expression: "col" IN (source)
// {filterValues} → just the group filter source: VALUES(...) or subquery
String tableFilterSource = tableConfig.getFilterSource();
if (group.hasFilter()) {
String groupFilterExpr = quoteIdentifier(group.getFilterColumn())
+ " IN (" + group.getFilterSource() + ")";
tableFilterSource = tableFilterSource.replace(
"{filter}", groupFilterExpr);
tableFilterSource = tableFilterSource.replace(
"{filterValues}", group.getFilterSource());
}
String col = tableConfig.getEffectiveFilterColumn(
group.getFilterColumn());
whereClause = quoteIdentifier(col) + " IN ("
+ tableFilterSource + ")";
} else if (group.hasFilter()) {
if (group.hasFilter()) {
String col = tableConfig.getEffectiveFilterColumn(
group.getFilterColumn());
whereClause = quoteIdentifier(col) + " IN ("
@ -116,7 +97,7 @@ public class TableExportService { @@ -116,7 +97,7 @@ public class TableExportService {
}
Element tableEl = exportTable(conn, meta, group.getSchema(),
tableName, tableConfig, whereClause);
tableName, whereClause);
root.addContent(tableEl);
}
}
@ -126,7 +107,6 @@ public class TableExportService { @@ -126,7 +107,6 @@ public class TableExportService {
private Element exportTable(Connection conn, DatabaseMetaData meta,
String schema, String tableName,
ExportTableConfig tableConfig,
String whereClause) throws SQLException {
Element tableEl = new Element("table");
tableEl.setAttribute("name", tableName);
@ -137,7 +117,7 @@ public class TableExportService { @@ -137,7 +117,7 @@ public class TableExportService {
Element structureEl = exportStructure(meta, schema, tableName);
tableEl.addContent(structureEl);
Element dataEl = exportData(conn, schema, tableName, tableConfig, whereClause);
Element dataEl = exportData(conn, schema, tableName, whereClause);
tableEl.addContent(dataEl);
return tableEl;
@ -195,10 +175,9 @@ public class TableExportService { @@ -195,10 +175,9 @@ public class TableExportService {
private Element exportData(Connection conn, String schema,
String tableName,
ExportTableConfig tableConfig,
String whereClause) throws SQLException {
Element dataEl = new Element("data");
String sql = buildSelectSql(schema, tableName, tableConfig, whereClause);
String sql = buildSelectSql(schema, tableName, whereClause);
int rowCount = 0;
try (Statement stmt = conn.createStatement()) {
@ -217,16 +196,11 @@ public class TableExportService { @@ -217,16 +196,11 @@ public class TableExportService {
if (value == null) {
colEl.setAttribute("nil", "true");
} else if (value instanceof byte[]) {
String encoded = Base64.getEncoder().encodeToString((byte[]) value);
colEl.setText(encoded);
colEl.setText(
Base64.getEncoder().encodeToString((byte[]) value));
colEl.setAttribute("encoding", "base64");
} else {
String text = value.toString();
if (needsCdata(text)) {
colEl.addContent(new CDATA(text));
} else {
colEl.setText(text);
}
colEl.setText(value.toString());
}
rowEl.addContent(colEl);
@ -242,20 +216,8 @@ public class TableExportService { @@ -242,20 +216,8 @@ public class TableExportService {
}
private String buildSelectSql(String schema, String tableName,
ExportTableConfig tableConfig,
String whereClause) {
StringBuilder sql = new StringBuilder("SELECT ");
if (tableConfig.hasColumns()) {
boolean first = true;
for (String col : tableConfig.getColumns()) {
if (!first) sql.append(", ");
sql.append(quoteIdentifier(col));
first = false;
}
} else {
sql.append("*");
}
sql.append(" FROM ");
StringBuilder sql = new StringBuilder("SELECT * FROM ");
if (schema != null) {
sql.append(quoteIdentifier(schema)).append(".");
}
@ -263,22 +225,9 @@ public class TableExportService { @@ -263,22 +225,9 @@ public class TableExportService {
if (whereClause != null) {
sql.append(" WHERE ").append(whereClause);
}
if (tableConfig.hasOrderBy()) {
sql.append(" ORDER BY ").append(tableConfig.getOrderBy());
}
return sql.toString();
}
private boolean needsCdata(String text) {
for (int i = 0; i < text.length(); i++) {
char c = text.charAt(i);
if (c == '<' || c == '>' || c == '&' || c == '"' || c == '\'') {
return true;
}
}
return false;
}
private String quoteIdentifier(String identifier) {
return "\"" + identifier.replace("\"", "\"\"") + "\"";
}

202
src/de/superx/spring/service/export/TableImportService.java

@ -78,8 +78,7 @@ public class TableImportService { @@ -78,8 +78,7 @@ public class TableImportService {
try (Connection conn = dataSource.getConnection()) {
// Validate schema for modes that operate on existing tables
if (mode == ImportMode.INSERT || mode == ImportMode.TRUNCATE_INSERT
|| mode == ImportMode.UPSERT || mode == ImportMode.UPDATE) {
if (mode == ImportMode.INSERT || mode == ImportMode.TRUNCATE_INSERT) {
List<SchemaValidationResult> failures = new ArrayList<>();
for (Element tableEl : root.getChildren("table")) {
SchemaValidationResult result = validateTableSchema(conn, tableEl);
@ -329,19 +328,10 @@ public class TableImportService { @@ -329,19 +328,10 @@ public class TableImportService {
break;
case INSERT:
break;
case UPSERT:
case UPDATE:
break;
}
if (dataEl != null) {
if (mode == ImportMode.UPSERT) {
upsertData(conn, schema, tableName, structureEl, dataEl);
} else if (mode == ImportMode.UPDATE) {
updateData(conn, schema, tableName, structureEl, dataEl);
} else {
insertData(conn, schema, tableName, structureEl, dataEl);
}
insertData(conn, schema, tableName, structureEl, dataEl);
}
}
@ -518,194 +508,6 @@ public class TableImportService { @@ -518,194 +508,6 @@ public class TableImportService {
}
}
private void upsertData(Connection conn, String schema, String tableName,
Element structureEl, Element dataEl) throws SQLException {
List<Element> columns = structureEl.getChildren("column");
if (columns.isEmpty()) {
return;
}
List<Element> rows = dataEl.getChildren("row");
if (rows.isEmpty()) {
return;
}
List<Element> pkColumns = new ArrayList<>();
List<Element> nonPkColumns = new ArrayList<>();
for (Element col : columns) {
if ("true".equals(col.getAttributeValue("primaryKey"))) {
pkColumns.add(col);
} else {
nonPkColumns.add(col);
}
}
if (pkColumns.isEmpty()) {
throw new SQLException("UPSERT requires primary key columns defined in "
+ "structure for table " + buildQualifiedName(schema, tableName));
}
// Build MERGE statement (SQL standard, supported by H2 and PostgreSQL 15+)
// Using: INSERT ... ON CONFLICT (pk) DO UPDATE SET ...
StringBuilder sql = new StringBuilder("MERGE INTO ");
sql.append(buildQualifiedName(schema, tableName));
sql.append(" (");
for (int i = 0; i < columns.size(); i++) {
if (i > 0) sql.append(", ");
sql.append(quoteIdentifier(columns.get(i).getAttributeValue("name")));
}
sql.append(") KEY (");
for (int i = 0; i < pkColumns.size(); i++) {
if (i > 0) sql.append(", ");
sql.append(quoteIdentifier(pkColumns.get(i).getAttributeValue("name")));
}
sql.append(") VALUES (");
for (int i = 0; i < columns.size(); i++) {
if (i > 0) sql.append(", ");
sql.append("?");
}
sql.append(")");
log.debug("Upsert SQL: {}", sql);
try (PreparedStatement ps = conn.prepareStatement(sql.toString())) {
int batchCount = 0;
for (Element rowEl : rows) {
List<Element> colValues = rowEl.getChildren("col");
setRowParameters(ps, columns, colValues);
ps.addBatch();
batchCount++;
if (batchCount % BATCH_SIZE == 0) {
ps.executeBatch();
}
}
if (batchCount % BATCH_SIZE != 0) {
ps.executeBatch();
}
log.info("Upserted {} rows into {}",
Integer.valueOf(batchCount), buildQualifiedName(schema, tableName));
}
}
private void updateData(Connection conn, String schema, String tableName,
Element structureEl, Element dataEl) throws SQLException {
List<Element> columns = structureEl.getChildren("column");
if (columns.isEmpty()) {
return;
}
List<Element> rows = dataEl.getChildren("row");
if (rows.isEmpty()) {
return;
}
List<Element> pkColumns = new ArrayList<>();
List<Element> nonPkColumns = new ArrayList<>();
for (Element col : columns) {
if ("true".equals(col.getAttributeValue("primaryKey"))) {
pkColumns.add(col);
} else {
nonPkColumns.add(col);
}
}
if (pkColumns.isEmpty()) {
throw new SQLException("UPDATE requires primary key columns defined in "
+ "structure for table " + buildQualifiedName(schema, tableName));
}
if (nonPkColumns.isEmpty()) {
log.warn("Table {} has only primary key columns, nothing to update",
buildQualifiedName(schema, tableName));
return;
}
// Build UPDATE ... SET ... WHERE pk = ?
StringBuilder sql = new StringBuilder("UPDATE ");
sql.append(buildQualifiedName(schema, tableName));
sql.append(" SET ");
for (int i = 0; i < nonPkColumns.size(); i++) {
if (i > 0) sql.append(", ");
sql.append(quoteIdentifier(nonPkColumns.get(i).getAttributeValue("name")));
sql.append(" = ?");
}
sql.append(" WHERE ");
for (int i = 0; i < pkColumns.size(); i++) {
if (i > 0) sql.append(" AND ");
sql.append(quoteIdentifier(pkColumns.get(i).getAttributeValue("name")));
sql.append(" = ?");
}
log.debug("Update SQL: {}", sql);
try (PreparedStatement ps = conn.prepareStatement(sql.toString())) {
int batchCount = 0;
int paramIndex;
for (Element rowEl : rows) {
List<Element> colValues = rowEl.getChildren("col");
paramIndex = 1;
// SET clause parameters (non-PK columns)
for (Element colDef : nonPkColumns) {
String colName = colDef.getAttributeValue("name");
int jdbcType = Integer.parseInt(colDef.getAttributeValue("jdbcType"));
Element valEl = findColElement(colValues, colName);
setParameter(ps, paramIndex++, jdbcType, valEl);
}
// WHERE clause parameters (PK columns)
for (Element colDef : pkColumns) {
String colName = colDef.getAttributeValue("name");
int jdbcType = Integer.parseInt(colDef.getAttributeValue("jdbcType"));
Element valEl = findColElement(colValues, colName);
setParameter(ps, paramIndex++, jdbcType, valEl);
}
ps.addBatch();
batchCount++;
if (batchCount % BATCH_SIZE == 0) {
ps.executeBatch();
}
}
if (batchCount % BATCH_SIZE != 0) {
ps.executeBatch();
}
log.info("Updated {} rows in {}",
Integer.valueOf(batchCount), buildQualifiedName(schema, tableName));
}
}
private void setRowParameters(PreparedStatement ps, List<Element> columns,
List<Element> colValues) throws SQLException {
for (int i = 0; i < columns.size(); i++) {
Element colDef = columns.get(i);
String colName = colDef.getAttributeValue("name");
int jdbcType = Integer.parseInt(colDef.getAttributeValue("jdbcType"));
Element valEl = findColElement(colValues, colName);
setParameter(ps, i + 1, jdbcType, valEl);
}
}
private void setParameter(PreparedStatement ps, int paramIndex,
int jdbcType, Element valEl) throws SQLException {
if (valEl == null || "true".equals(valEl.getAttributeValue("nil"))) {
ps.setNull(paramIndex, jdbcType);
} else if ("base64".equals(valEl.getAttributeValue("encoding"))) {
ps.setBytes(paramIndex,
Base64.getDecoder().decode(valEl.getTextTrim()));
} else {
setTypedValue(ps, paramIndex, jdbcType, valEl.getTextTrim());
}
}
private Element findColElement(List<Element> colValues, String name) {
for (Element el : colValues) {
if (name.equals(el.getAttributeValue("name"))) {

4
src/de/superx/stat/StatisticExport.java

@ -157,9 +157,9 @@ public class StatisticExport implements DataProvider, SurveyResourceProvider { @@ -157,9 +157,9 @@ public class StatisticExport implements DataProvider, SurveyResourceProvider {
String configDir;
short sdfValidationMode = SurveyDataDocumentGenerator.SDF_VALIDATION_MODE_FREE;
short sdfValidationMode = SurveyDataDocumentGenerator.SDF_VALIDATION_MODE_STRICT;
short sdfMetaValidationMode = SurveyDataDocumentGenerator.SDF_META_VALIDATION_MODE_FREE;
short sdfMetaValidationMode = SurveyDataDocumentGenerator.SDF_META_VALIDATION_MODE_STRICT;
MetaErhebungsbeschreibung meta;

375
src/de/superx/util/DbFormsMultipartRequest.java

@ -0,0 +1,375 @@ @@ -0,0 +1,375 @@
/*
* $Header: /cvsroot/jdbforms/dbforms/src/org/dbforms/util/MultipartRequest.java,v 1.19 2005/02/19 21:26:32 hkollmann Exp $
* $Revision: 1.19 $
* $Date: 2005/02/19 21:26:32 $
*
* DbForms - a Rapid Application Development Framework
* Copyright (C) 2001 Joachim Peer <joepeer@excite.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package de.superx.util;
import org.apache.commons.fileupload.DiskFileUpload;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletRequestWrapper;
import jakarta.servlet.http.HttpServletRequest;
import org.dbforms.util.FileHolder;
import org.dbforms.util.MultipartRequest;
import org.dbforms.util.Util;
import org.dbforms.util.external.FileUtil;
import de.superx.util.multipart.support.DefaultMultipartHttpServletRequest;
/**
* A utility class to handle <code>multipart/form-data</code> requests, the
* kind of requests that support file uploads. This class emulates the interface
* of <code>HttpServletRequest</code>, making it familiar to use. It uses a
* "push" model where any incoming files are read and saved directly to disk in
* the constructor. If you wish to have more flexibility, e.g. write the files
* to a database, use the "pull" model <code>MultipartParser</code> instead.
* <p>
* This class can receive arbitrarily large files (up to an artificial limit you
* can set), and fairly efficiently too. It cannot handle nested data (multipart
* content within multipart content) or internationalized content (such as non
* Latin-1 filenames).
* </p>
* <p>
* See the included <a href="upload.war">upload.war </a> for an example of how
* to use this class.
* <p>
* The full file upload specification is contained in experimental RFC 1867,
* available at <a href="http://www.ietf.org/rfc/rfc1867.txt"
* http://www.ietf.org/rfc/rfc1867.txt </a>.
* </p>
* <p>
* PLEASE NOTE: this class uses Multipart Support Classes by Jason Hunter.
* Copyright (C) 1998 by Jason Hunter. All rights reserved.
* Use of this class is limited. Please see the LICENSE for more information.
* Make sure that you project meets the requierements defined by Jason Hunter
* respective O'reilly!
* </p>
*
* @see MultipartParser
*
* @author Jason Hunter
* @author Geoff Soutter
* @author Joe Peer - _changed_ it for use in DbForms (i apologize)
*/
public class DbFormsMultipartRequest extends MultipartRequest {
private static Log logCat = LogFactory.getLog(DbFormsMultipartRequest.class); // logging category
private Hashtable files = new Hashtable(); // name - UploadedFile
// for this class
//private File dir;
private Hashtable parameters = new Hashtable(); // name - Vector of values
/**
* Constructs a new MultipartRequest to handle the specified request,
* {saving any uploaded files to the given directory}, and limiting the
* upload size to the specified length. If the content is too large, an
* IOException is thrown. This constructor actually parses the
* <tt>multipart/form-data</tt> and throws an IOException if there's any
* problem reading or parsing the request.
*
* @param request
* the servlet request.
* @param maxPostSize
* the maximum size of the POST content.
* @exception IOException
* if the uploaded content is larger than
* <tt>maxPostSize</tt> or there's a problem reading or
* parsing the request.
*/
public DbFormsMultipartRequest(HttpServletRequest request, int maxPostSize) throws IOException {
super(request, maxPostSize);
// Sanity check values
if (request == null) {
throw new IllegalArgumentException("request cannot be null");
}
// Create a new file upload handler
DiskFileUpload upload = new DiskFileUpload();
// Set upload parameters
upload.setSizeMax(maxPostSize);
String tmpDir = System.getProperty("java.io.tmpdir");
upload.setRepositoryPath(tmpDir);
// Parse the request
try {
// we initilize this list with 'null', as the following unwrapping code should do the work - contrary to the 'org.dbforms.util.MultipartRequest code, from which this is copied
List<FileItem> items = null; //upload.parseRequest(request);
if (items == null || items.size() == 0) {
ServletRequest originalRequest = request;
DefaultMultipartHttpServletRequest dmhsr = null;
while (originalRequest instanceof ServletRequestWrapper) {
if (originalRequest instanceof DefaultMultipartHttpServletRequest) {
dmhsr = (DefaultMultipartHttpServletRequest) originalRequest;
items = dmhsr.getFileItems();
break;
}
originalRequest = ((ServletRequestWrapper) originalRequest).getRequest();
}
}
Iterator iter = items.iterator();
while (iter.hasNext()) {
FileItem item = (FileItem) iter.next();
String name = item.getFieldName();
if (item.isFormField()) {
String value = item.getString();
// plain parameters get allways into parameters structure
Vector existingValues = (Vector) parameters.get(name);
if (existingValues == null) {
existingValues = new Vector();
parameters.put(name, existingValues);
}
existingValues.addElement(value);
} else {
String fileName = item.getName();
if (!Util.isNull(fileName)) {
File f = new File(fileName);
// 2004-08-05-HKK/Dziugas Baltrunas:
// FileItem always returns the full pathname!
fileName = FileUtil.filename(f.getName());
// The part actually contained a file
// #changes by joe peer:
// we must delay storing the file-inputstream (into
// database, filesystem or whatever)
// until we know exactly what should happen with it
FileHolder fileHolder = new FileHolder(fileName, item);
files.put(name, fileHolder);
logCat.info("buffered and now added as " + name + " the following fileHolder:" + fileHolder.getFileName());
// #changes by joe peer:
// if a file parameter is not null it gets into the
// parameters structures as well
// this is important to provide a simple access to
// request (no distinction between files and plain
// params)
Vector existingValues = (Vector) parameters.get(name);
if (existingValues == null) {
existingValues = new Vector();
parameters.put(name, existingValues);
}
existingValues.addElement(fileName);
}
}
}
} catch (Exception e) {
logCat.error("MultipartRequest", e);
}
}
/**
* Returns the content type of the specified file (as supplied by the client
* browser), or null if the file was not included in the upload.
*
* @param name
* the file name.
* @return the content type of the file.
*/
@Override
public String getContentType(String name) {
try {
//#changed by joe peer
//UploadedFile file = (UploadedFile)files.get(name);
FileHolder filePart = (FileHolder) files.get(name);
return filePart.getContentType(); // may be null
} catch (Exception e) {
return null;
}
}
/**
* Returns a FilePart object for the specified file this method was added by
* joe peer
*
* @param name
* the file name.
* @return a FilePart object for the named file.
*/
@Override
public FileHolder getFileHolder(String name) {
try {
return (FileHolder) files.get(name);
} catch (Exception e) {
return null;
}
}
/**
* Returns a InputStream object for the specified file saved on the server's
* filesystem, or null if the file was not included in the upload.
*
* @param name
* the file name.
* @return a InputStream object for the named file.
*/
@Override
public InputStream getFileInputStream(String name) {
try {
//#changed by joe peer
//UploadedFile file = (UploadedFile)files.get(name);
FileHolder filePart = (FileHolder) files.get(name);
return filePart.getInputStreamFromBuffer(); // may be null
} catch (Exception e) {
return null;
}
}
/**
* Returns the names of all the uploaded files as an Enumeration of Strings.
* It returns an empty Enumeration if there are no uploaded files. Each file
* name is the name specified by the form, not by the user.
*
* @return the names of all the uploaded files as an Enumeration of Strings.
*/
@Override
public Enumeration getFileNames() {
return files.keys();
}
/**
* Returns the filesystem name of the specified file, or null if the file
* was not included in the upload. A filesystem name is the name specified
* by the user. It is also the name under which the file is actually saved.
*
* @param name
* the file name.
* @return the filesystem name of the file.
*/
@Override
public String getFilesystemName(String name) {
try {
//#changed by joe peer
//UploadedFile file = (UploadedFile)files.get(name);
FileHolder filePart = (FileHolder) files.get(name);
return filePart.getFileName(); // may be null
} catch (Exception e) {
return null;
}
}
/**
* Returns the value of the named parameter as a String, or null if the
* parameter was not sent or was sent without a value. The value is
* guaranteed to be in its normal, decoded form. If the parameter has
* multiple values, only the last one is returned (for backward
* compatibility). For parameters with multiple values, it's possible the
* last "value" may be null.
*
* @param name
* the parameter name.
* @return the parameter value.
*/
@Override
public String getParameter(String name) {
try {
Vector values = (Vector) parameters.get(name);
if ((values == null) || (values.size() == 0)) {
return null;
}
String value = (String) values.elementAt(0);
return value;
} catch (Exception e) {
return null;
}
}
/**
* Returns the names of all the parameters as an Enumeration of Strings. It
* returns an empty Enumeration if there are no parameters.
*
* @return the names of all the parameters as an Enumeration of Strings.
*/
@Override
public Enumeration getParameterNames() {
return parameters.keys();
}
/**
* Returns the values of the named parameter as a String array, or null if
* the parameter was not sent. The array has one entry for each parameter
* field sent. If any field was sent without a value that entry is stored in
* the array as a null. The values are guaranteed to be in their normal,
* decoded form. A single value is returned as a one-element array.
*
* @param name
* the parameter name.
* @return the parameter values.
*/
@Override
public String[] getParameterValues(String name) {
try {
Vector values = (Vector) parameters.get(name);
if ((values == null) || (values.size() == 0)) {
return null;
}
String[] valuesArray = new String[values.size()];
values.copyInto(valuesArray);
return valuesArray;
} catch (Exception e) {
return null;
}
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save