Browse Source

Angleichung Code an H1 2026.12 #10

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

495
.classpath

@ -1,75 +1,93 @@ @@ -1,75 +1,93 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="lib" path="superx/WEB-INF/lib/xml-apis-1.4.01.jar"/>
<classpathentry kind="src" path="test-src"/>
<classpathentry kind="src" path="src-patches"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/activation-1.1.1.jar"/>
<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/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"/>
<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/antlr-2.7.7.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/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/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"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/aspectjweaver-1.9.20.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/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"/>
<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/cewolf-0.10.1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/cglib-nodep-3.2.4.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/commons-beanutils-1.9.4.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-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-el-1.0.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-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-javaflow-20060411.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-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-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.1-20150824.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/concurrent-1.3.4.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/connect.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/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/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"/>
<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/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"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/enunciate-core-rt-1.28.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-jaxws-ri-rt-1.28.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/enunciate-jersey-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-rt-1.28.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/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/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"/>
@ -78,171 +96,270 @@ @@ -78,171 +96,270 @@
<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"/>
<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/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/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.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/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/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"/>
<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/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/javacup-10k.jar"/>
<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/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/javax.mail.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/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/jaxws-spring-1.8.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/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/jfor-0.7.2rc1.jar"/>
<classpathentry kind="lib" path="superx/WEB-INF/lib/jfreechart-1.0.19.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/jh.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/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/js-1.7R3.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/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/liberation-fonts-ttf.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/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/metastore-6.0.0.0-353.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/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/miredot-annotations-1.3.1.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/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/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/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/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/pl_generator.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-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-ooxml-schemas-4.1.2.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/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/saiku-query-0.4-SNAPSHOT-HIS.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/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/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"/>
<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/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/xml-apis-ext-1.4.01.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="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="output" path="superx/WEB-INF/classes"/>
</classpath>

16
.externalToolBuilders/Ant Superx Version.launch

@ -0,0 +1,16 @@ @@ -0,0 +1,16 @@
<?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,46 +1,270 @@ @@ -1,46 +1,270 @@
# ignore all log files
*.log
*.err
*.log.*
# agent and copilot context - local/temporary files
.github/
# Gradle build output and wrapper cache
build/
.gradle/
# ignore all CVS + sub dirs
*CVS/
# ignore /dist/*
*dist/
# ignore java classes
*classes/
# ignore swap files
superx/WEB-INF/reports/swap_*
# 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
# ignore eclipse settings
.settings/org.eclipse.ltk.core.refactoring.prefs
# ignore temporary files
*.tmp
*.tmp.sql
temp.xml
tmp.fo
# vim .swp files
*.swp
# ignore auto generated files
db.properties
dbforms-config.xml
superx/WEB-INF/dbforms-config.old
superx.datum
# ignore gradle bin
scripts/gradle/bin/
scripts/gradle/.gradle/
# ignore iReport Designer
*.jasper
# cobertura
superx-build/cobertura*
# ignore /dist/*
*dist/
# classes
superx/WEB-INF/classes/
# ignore swap files
superx/WEB-INF/reports/swap_*
# tag files
**/tags
src/edustore/edustore_*.xml
# .orig files
**/*.orig
# 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
# 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
# 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
# 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
# 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
# vscode personal settings
.vscode/
.metadata/
# edustore.xml soll nicht mehr ausgeliefert werden siehe #313658
src/edustore/edustore.xml
# package folder und config file für statisch generierte JSPs
src/de/superx/jsp/
superx/WEB-INF/jsp_servlet_config.xml
# jaspers werden nicht mehr commitet, sondern im build gebaut
*.jasper
# updateUnmodifiedJars backup
superx/WEB-INF/lib/backup/

94
.project

@ -1,36 +1,58 @@ @@ -1,36 +1,58 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>eduStore_superX</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.babel.editor.rbeBuilder</name>
<arguments>
</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>1459344926910</id>
<name></name>
<type>10</type>
<matcher>
<id>org.eclipse.ui.ide.multiFilter</id>
<arguments>1.0-name-matches-false-false-dist</arguments>
</matcher>
</filter>
</filteredResources>
</projectDescription>
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>eduStore_superX</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.ui.externaltools.ExternalToolBuilder</name>
<triggers>full,incremental,</triggers>
<arguments>
<dictionary>
<key>LaunchConfigHandle</key>
<value>&lt;project&gt;/.externalToolBuilders/Ant Superx Version.launch</value>
</dictionary>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<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>
<name></name>
<type>10</type>
<matcher>
<id>org.eclipse.ui.ide.multiFilter</id>
<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>

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

@ -0,0 +1,901 @@ @@ -0,0 +1,901 @@
/*
* 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

@ -0,0 +1,366 @@ @@ -0,0 +1,366 @@
/*
* $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

@ -0,0 +1,699 @@ @@ -0,0 +1,699 @@
/*
* 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() + "]";
}
}
}

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;20251;Rechtspflege;380;-1
256;20252;04;1;0329650;99999;Rechtspflege;380;-1
258;20072;04;1;0329150;20251;Arbeitsverwaltung;345;258
258;20252;04;0;0329150;20251;Arbeitsverwaltung;345;
258;20252;04;0;0329150;99999;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;20251;Bankwesen;343;
265;20252;04;0;0329300;99999;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;20251;Justizvollzug;861;168
168;20252;06;1;1129550;99999;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;20251;Rechtspflege;380;256
256;20252;06;1;1129650;99999;Rechtspflege;380;256
257;20072;06;1;0329100;20251;Arbeits- und Berufsberatung;762;257
257;20252;06;1;1129100;20251;Arbeits- und Berufsberatung;762;257
257;20252;06;1;1129100;99999;Arbeits- und Berufsberatung;762;257
258;20072;06;1;0329150;20251;Arbeitsverwaltung;345;258
258;20252;06;1;1129150;20251;Arbeitsverwaltung;345;258
258;20252;06;1;1129150;99999;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;20251;Bundeswehrverwaltung;345;260
260;20252;06;1;1129400;99999;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;20251;Bibliothekswesen;322;262
262;20252;06;1;1129350;99999;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;20251;Bankwesen;343;265
265;20252;06;1;1129300;99999;Bankwesen;343;265
266;20072;06;1;0329450;20251;Finanzverwaltung;344;266
266;20252;06;1;1129450;20251;Finanzverwaltung;344;266
266;20252;06;1;1129450;99999;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;20122;Quantitative Economics;340;021
a53;20252;06;1;1230187;99999;Quantitative Economics;340;021
a54;20112;06;1;0330188;20122;Quantitative Finance;340;021
a54;20252;06;1;1230188;20122;Quantitative Finance;340;021
a54;20252;06;1;1230188;99999;Quantitative Finance;340;021
a55;20112;06;1;0330189;20122;Quantitative Management;340;021
a55;20252;06;1;1230189;20122;Quantitative Management;340;021
a55;20252;06;1;1230189;99999;Quantitative Management;340;021
a56;20112;06;1;0330190;20122;Quantitative Marketing;340;021
a56;20252;06;1;1230190;20122;Quantitative Marketing;340;021
a56;20252;06;1;1230190;99999;Quantitative Marketing;340;021
a57;20112;06;1;0330191;20122;Law and Quantitative Economics;340;021
a57;20252;06;1;1230191;20122;Law and Quantitative Economics;340;021
a57;20252;06;1;1230191;99999;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;20122;Ph.D. in Economics;340;021
a61;20252;06;1;1230192;99999;Ph.D. in Economics;340;021
a62;20112;06;1;0330193;20122;Ph.D. in Finance;340;021
a62;20252;06;1;1230193;20122;Ph.D. in Finance;340;021
a62;20252;06;1;1230193;99999;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;20122;Ph.D. in Marketing;340;021
a64;20252;06;1;1230195;99999;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;20122;Ph.D. in Law and Economics;340;021
a65;20252;06;1;1230196;99999;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;99999;LifeCycle Catering;621;125
c12;20252;06;1;1231210;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
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;99999;Islamwissenschaft;222;083
083;20062;03;0;0113450;20251;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;99999;Optoelektronik;524;088
088;20062;03;0;0864600;20251;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;99999;Ausländerpädagogik;142;117
117;20152;03;0;0333100;20251;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;99999;Rechtswissenschaft;380;135
135;20062;03;0;0328100;20251;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;99999;Verwaltungswissenschaft/-wesen;345;172
172;20062;03;0;0329800;20251;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;99999;Volkswirtschaftslehre;314;175
175;20062;03;0;0330450;20251;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;99999;Wirtschaftspädagogik;142;181
181;20062;03;0;0330500;20251;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;99999;Wirtschaftswissenschaften;340;184
184;20062;03;0;0330550;20251;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;99999;Auswärtige Angelegenheiten;345;259
259;20062;03;0;0329250;20251;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;99999;Tourismuswirtschaft;812;274
274;20062;03;0;0330350;20251;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;99999;Pädagogik der frühen Kindheit;142;365
365;20152;03;1;0333450;20251;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;99999;Umweltrecht/-ökonomie(WB);142;030
683;20062;03;0;0323101;20251;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;99999;Sozial-und Kulturgeschichte;312;148
685;20062;03;0;0326201;20251;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;99999;Sprache u. Kommunikation;220;284
687;20062;03;0;0107301;20251;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;99999;Tax Law - Steuerrecht;380;042
688;20232;03;1;0328201;20251;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;99999;Gesundheit;345;232
693;20062;03;0;0548203;20251;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;99999;Krankenversicherungsmanagement;340;021
702;20062;03;0;0330154;20251;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;99999;Betr.Wirtsch.-Automobilwirtschaft;340;021
723;20072;03;0;0330155;20251;Betr.Wirtsch.-Automobilwirtschaft;340;021
723;20252;03;0;1230155;99999;Automobilwirtschaft;;
724;20062;03;0;0330552;99999;Organization Studies;340;184
724;20062;03;0;0330552;20251;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;99999;Reedereilogistik;340;021
726;20062;03;0;0330156;20251;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;99999;Umwelt-u.Qualitätsmanagement;340;184
727;20112;03;0;0330557;20251;Umwelt-u.Qualitätsmanagement;340;184
727;20252;03;0;1230557;99999;Umwelt-u.Qualitätsmanagement;340;184
728;20062;03;0;0863823;99999;Environmental Engineering (Internat.Umwelt-Verfahrenstechnik),Bio-u.Enviromental Engineering;850;457
728;20062;03;0;0863823;20251;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;99999;Frauen-u.Geschlechterstudien,Geschlechterforschung;312;148
732;20062;03;0;0326203;20251;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;99999;Kreislaufwirtschaft;340;179
737;20072;03;0;0331203;20251;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;99999;Bildungsmanagement und Schulentwicklung;340;021
801;20062;03;0;0330158;20251;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.

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

@ -1,5 +1,8 @@ @@ -1,5 +1,8 @@
package de.superx.access;
import java.util.ArrayList;
import java.util.List;
import javax.sql.DataSource;
import org.springframework.beans.factory.InitializingBean;
@ -24,27 +27,37 @@ public class DbSqlRestrictionProvider implements SqlRestrictionProvider, Initial @@ -24,27 +27,37 @@ public class DbSqlRestrictionProvider implements SqlRestrictionProvider, Initial
@Override
public Select getRestriction(String username, String userrole, String schemaName, String tableName, int orgunitLid) {
try {
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));
SqlRowSet result = jt.queryForRowSet(lookupSql);
result.beforeFirst();
String restriction = "";
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));
List<String> restrictions = new ArrayList<>();
while (result.next()) {
restriction = result.getString("restriction");
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);
}
String resultSql = String.format(
"(SELECT * FROM %s.%s %s) AS %s",
schemaName, tableName, restriction, tableName);
"(SELECT * FROM %s.%s%s) AS %s",
schemaName, tableName, whereClause, tableName);
Select select = (Select)CCJSqlParserUtil.parse(resultSql);
return select;
return (Select) CCJSqlParserUtil.parse(resultSql);
} catch (DataAccessException e) {
throw new IllegalStateException("Restriction lookup failed", e);
} catch (JSQLParserException e) {

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

@ -47,6 +47,11 @@ public class InlineSelectVisitor implements net.sf.jsqlparser.statement.select.S @@ -47,6 +47,11 @@ 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,11 +369,22 @@ public class ExcelSheetBuilder { @@ -369,11 +369,22 @@ 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 = columnHeader[j];
String header = columnHeaderSorted[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 List<Identifier> factTableIds;
public Identifier factTableId;
public List<Identifier> leftDimensionAttributeIds;
public List<Identifier> topDimensionAttributeIds;
public List<Identifier> measureIds;
@ -18,7 +18,6 @@ public class ReportDefinition { @@ -18,7 +18,6 @@ public class ReportDefinition {
public boolean hideEmptyColumns;
public ReportDefinition() {
this.factTableIds = new ArrayList<>();
this.leftDimensionAttributeIds = new ArrayList<>();
this.topDimensionAttributeIds = new ArrayList<>();
this.measureIds = new ArrayList<>();
@ -28,7 +27,7 @@ public class ReportDefinition { @@ -28,7 +27,7 @@ public class ReportDefinition {
public ReportDefinition(ReportDefinition definition) {
super();
this.factTableIds = definition.factTableIds;
this.factTableId = definition.factTableId;
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> measures2 = dbMetaAdapter.getMeasureMetadata(allMeasures);
List<Measure> reportMeasures = dbMetaAdapter.getMeasureMetadata(allMeasures);
for (Measure measure : measures2) {
for (Measure measure : reportMeasures) {
if(!measure.getFacttableId().equals(secondaryFacttable.factId)) {
measure.isNullMeasure = true;
}
@ -190,16 +190,29 @@ public class ReportMetadata { @@ -190,16 +190,29 @@ public class ReportMetadata {
}
}
ReportMetadata secondaryFacttableMetadata = new ReportMetadata(secondaryFacttable.factTable, leftAttributes, rightAttributes, measures2, filterAttributes, dbMetaAdapter);
ReportMetadata secondaryFacttableMetadata = new ReportMetadata(secondaryFacttable.factTable, leftAttributes, rightAttributes, reportMeasures, filterAttributes, dbMetaAdapter);
secondaryFacttableMetadata.hideEmptyColumns = primaryReportMetadata.hideEmptyColumns;
// 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 =
@ -217,11 +230,22 @@ public class ReportMetadata { @@ -217,11 +230,22 @@ 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;
@ -255,7 +279,6 @@ public class ReportMetadata { @@ -255,7 +279,6 @@ public class ReportMetadata {
for (DimensionAttribute attribute : this.leftDimensionAttributes) {
attribute.setGrouping(true);
}
this.hideEmptyColumns = metadata.hideEmptyColumns;
this.secondaryFacttables = secondaryFacttables;
}
@ -474,21 +497,25 @@ public class ReportMetadata { @@ -474,21 +497,25 @@ 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;

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

@ -7,6 +7,7 @@ import java.io.IOException; @@ -7,6 +7,7 @@ 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;
@ -267,7 +268,7 @@ public class BiAnalysisCLI { @@ -267,7 +268,7 @@ public class BiAnalysisCLI {
private static void validateMetadataAgainstSchema() {
SuperXManager.setWEB_INFPfad(PathAndFileUtils.getWebinfPath());
Path metadataFolder = DbtUtils.getBiAnalysisMetadataFolder();
Path metadataFolder = DbtUtils.getBiAnalysisMetadataFolder("hisinone");
File directory = metadataFolder.toFile();
File[] files = directory.listFiles();
@ -335,7 +336,7 @@ public class BiAnalysisCLI { @@ -335,7 +336,7 @@ public class BiAnalysisCLI {
throw new RuntimeException("Error processing validation output", e);
}
}
private static void generateWikiMarkdown(CommandLine parsedArgs) throws IOException {
SuperXManager.setWEB_INFPfad(PathAndFileUtils.getWebinfPath());
String facttable = parsedArgs.getOptionValue("d");
@ -348,7 +349,42 @@ public class BiAnalysisCLI { @@ -348,7 +349,42 @@ 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)) {
@ -408,7 +444,8 @@ public class BiAnalysisCLI { @@ -408,7 +444,8 @@ public class BiAnalysisCLI {
for(MetaDimensionAttribute a : attributes) {
String caption = a.getCaption() == null ? a.getConfDimAttrRef().getCaption() : a.getCaption();
writer.println("*"+caption);
String isHidden = a.isHidden() ? " (Standardmäßig ausgeblendet)" : "";
writer.println("*"+caption + isHidden);
try {
String desc = a.getDescription() == null ? a.getConfDimAttrRef().getDescription() : a.getDescription();
if(!desc.equals("null")) {
@ -427,6 +464,10 @@ public class BiAnalysisCLI { @@ -427,6 +464,10 @@ 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");
@ -764,5 +805,14 @@ public class BiAnalysisCLI { @@ -764,5 +805,14 @@ public class BiAnalysisCLI {
public static boolean isNullOrEmpty(List<?> list) {
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,6 +105,9 @@ public class JasperReportJson { @@ -105,6 +105,9 @@ 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\report_wizard\metadaten.adoc
* doc\bi_analysis\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", "reportwizard", "metadata"});
String metaFilesDir = String.join(File.separator, new String[] {"test", "resources", "db", "fixtures", "bianalysis", "metadata"});
deserializeMetadataFromJsonFiles(metaFilesDir);
JdbcTemplate jt = new JdbcTemplate(dataSource);
String upserts = String.join("\n", getAllUpsertStrings(false).toString());
@ -471,6 +471,7 @@ public final class MetadataImporter { @@ -471,6 +471,7 @@ 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,11 +140,10 @@ public class MetaDimension extends MetaObject { @@ -140,11 +140,10 @@ public class MetaDimension extends MetaObject {
builder = builder.withStringCol("is_historical", String.valueOf(conformedDimension.isHistorical));
}
if(this.isRestricted) {
builder = builder.withStringCol("is_restricted", String.valueOf(isRestricted));
} else {
builder = builder.withStringCol("is_restricted", String.valueOf(conformedDimension.isRestricted));
}
//if(this.isRestricted) {
//} else {
// builder = builder.withStringCol("is_restricted", String.valueOf(conformedDimension.isRestricted));
//}
if(this.dimension != null && !this.dimension.isBlank()) {
builder = builder.withStringCol("tablename", this.dimension);
@ -153,6 +152,7 @@ public class MetaDimension extends MetaObject { @@ -153,6 +152,7 @@ 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", "ind_" + newColumnName);
builder = builder.withStringCol("columnname", "custom_" + newColumnName);
}
if(confDimAttrRef != null && confDimAttrRef.hierarchicalFilter) {

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

@ -4,7 +4,8 @@ public enum RightParam { @@ -4,7 +4,8 @@ public enum RightParam {
TOPIC_AREA("bianalysis.topic_area"),
TOPIC("bianalysis.topic"),
ORGUNIT("bianalysis.orgunit");
ORGUNIT("bianalysis.orgunit"),
PROJECT("bianalysis.project");
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 = true, visibleInSimplifiedForm = false)
@DynamicFieldType(label="Filter-Auswahl", readOnly = false, 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 "ind_" + newColumnName;
return "custom_" + newColumnName;
}

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

@ -16,8 +16,10 @@ import de.superx.bianalysis.models.Dimension; @@ -16,8 +16,10 @@ 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;
@ -55,7 +57,7 @@ public class BiAnalysisAdminApi extends RestControllerBase { @@ -55,7 +57,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) {
public Identifier saveAttributeGroup(@RequestBody final AttributeDto attributeGroup, @RequestParam(value = "dimension_id") Identifier dimensionId) throws IllegalMetaDataOperation {
if(attributeGroup.id != null && attributeGroup.id.composedId != null && !attributeGroup.id.composedId.isBlank()) {
dbAdapter.deleteColumnForAttributeGroup(attributeGroup.id, dimensionId);
}
@ -75,6 +77,11 @@ public class BiAnalysisAdminApi extends RestControllerBase { @@ -75,6 +77,11 @@ 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;
@ -87,7 +94,7 @@ public class BiAnalysisAdminApi extends RestControllerBase { @@ -87,7 +94,7 @@ public class BiAnalysisAdminApi extends RestControllerBase {
}
@RequestMapping(method = RequestMethod.DELETE, path = "/attribute-group")
public void deleteAttributeGroup(@RequestParam(value = "attribute_group_id") Identifier attributeGroupId) {
public void deleteAttributeGroup(@RequestParam(value = "attribute_group_id") Identifier attributeGroupId) throws IllegalMetaDataOperation {
dbAdapter.deleteAttributeGroupById(attributeGroupId);
}
@ -107,20 +114,22 @@ public class BiAnalysisAdminApi extends RestControllerBase { @@ -107,20 +114,22 @@ 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,
@RequestParam(value = "l") int length) {
return dbAdapter.getFilteredValuesForAttribute(id, q, length)
// 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)
.stream()
.map(DimensionAttributeLabel::new)
.toList();
}
@RequestMapping(method = RequestMethod.PUT, path = "/hideAttribute")
public void hideAttribute (@RequestBody final Boolean hide, @RequestParam(value = "attribute_id") Identifier id) {
public void hideAttribute (@RequestBody final Boolean hide, @RequestParam(value = "attribute_id") Identifier id) throws IllegalMetaDataOperation {
dbAdapter.hideAttributeById(id, hide);
}
@RequestMapping(method = RequestMethod.PUT, path = "/hideDimension")
public void hideDimension (@RequestBody final Boolean hide, @RequestParam(value = "dimension_id") Identifier id) {
public void hideDimension (@RequestBody final Boolean hide, @RequestParam(value = "dimension_id") Identifier id) throws IllegalMetaDataOperation {
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/reportwizard")
@RequestMapping("/api/bianalysis")
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.checkCreateRights();
rightsService.checkCreateOrViewRights();
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();
checkCreateOrViewRightForFactTables(storedReport.reportDefinition.factTableIds);
checkCreateOrViewRightForLeadingFactTable(storedReport.reportDefinition.factTableId);
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 {
List<Identifier> factTableIds = reportDefinition.factTableIds;
checkCreateRightForFactTables(factTableIds);
Identifier factTableId = reportDefinition.factTableId;
checkCreateRightForLeadingFactTable(factTableId);
return biAnalysisManager.createResult(reportDefinition, dbAdapter);
}
@RequestMapping(method = RequestMethod.POST, path = "/persistReportDefinition")
public int persistReportDefinition(@RequestBody final StoredReport storedReport) throws Exception {
StoredReport.setReportDefinitionJson(storedReport);
List<Identifier> factTableIds = storedReport.reportDefinition.factTableIds;
checkCreateRightForFactTables(factTableIds);
Identifier factTableId = (Identifier) storedReport.reportDefinition.factTableId;
checkCreateRightForLeadingFactTable(factTableId);
return dbAdapter.saveReportDefinition(storedReport);
}
@RequestMapping(value = "/report/download", method = RequestMethod.POST)
public Download getFile(@RequestBody final StoredReport storedReport) throws Exception {
checkCreateOrViewRightForFactTables(storedReport.reportDefinition.factTableIds);
checkCreateOrViewRightForLeadingFactTable(storedReport.reportDefinition.factTableId);
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.");
}
checkCreateRightForFactTables(reportOpt.get().reportDefinition.factTableIds);
checkCreateRightForLeadingFactTable(reportOpt.get().reportDefinition.factTableId);
rightsService.checkDeleteRights();
dbAdapter.deleteById(id);
return true;
@ -211,20 +211,16 @@ public class BiAnalysisApi extends RestControllerBase { @@ -211,20 +211,16 @@ public class BiAnalysisApi extends RestControllerBase {
return storedReports;
}
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 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 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);
}
private void checkCreateRightForLeadingFactTable(Identifier factTableId) {
int sachgebiet = dbAdapter.getSachgebietByFactTableId(factTableId.composedId);
rightsService.checkSachgebiet(sachgebiet, Right.CREATE_ANALYSIS);
rightsService.checkFactTable(factTableId, 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.factTableIds.size() > 1) {
throw new IllegalArgumentException("Only one facttable per report definition allowed.");
if(reportDefinition.leftDimensionAttributeIds.isEmpty()) {
throw new IllegalMetaDataOperation("Bei einer BI-Analyse muss mindestens ein Zeilenattribut angegeben werden.");
}
try {
biAnalysisRightService.checkCreateOrViewRights();
ReportMetadata metadata = new ReportMetadata(reportDefinition, reportDefinition.factTableIds.get(0), dbAdapter);
ReportMetadata metadata = new ReportMetadata(reportDefinition, reportDefinition.factTableId, 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 {
}
}
private Result getReportData(ReportMetadata metadata, DbMetaAdapter dbAdapter, ReportDefinition definition) throws Exception {
String reportSql = getSqlStatement(definition, dbAdapter);
public Result getReportData(ReportMetadata metadata, DbMetaAdapter dbAdapter, ReportDefinition definition) throws Exception {
String reportSql = getSqlStatement(metadata);
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.factTableIds.get(0), dbAdapter);
ReportMetadata metadata = new ReportMetadata(definition, definition.factTableId, dbAdapter);
if(metadata.hasOuter()) {
ReportDefinition definitionTotals = definition.toTotalsDefinition();
metadata = new ReportMetadata(definitionTotals, definitionTotals.factTableIds.get(0), dbAdapter);
metadata = new ReportMetadata(definitionTotals, definitionTotals.factTableId, dbAdapter);
HashMap<String, ReportMetadata> metadatas = metadata.buildReportMetadatasForDrillAcross();
SQLGeneratorDrillAcross sqlGeneratorDrillAcross
@ -155,15 +155,14 @@ public class BiAnalysisManager { @@ -155,15 +155,14 @@ public class BiAnalysisManager {
return sql;
}
public static String getSqlStatement(ReportDefinition definition, DbMetaAdapter dbAdapter) {
public static String getSqlStatement(ReportMetadata metadata) {
String sql = "";
ReportMetadata metadata = definition.getReportMetadata(dbAdapter, definition.factTableIds.get(0));
if(metadata.hasOuter()) {
HashMap<String, ReportMetadata> metadatas = metadata.buildReportMetadatasForDrillAcross();
SQLGeneratorDrillAcross sqlGeneratorDrillAcross
= new SQLGeneratorDrillAcross(definition.factTableIds.get(0), metadatas);
= new SQLGeneratorDrillAcross(metadata.factTable.getId(), metadatas);
sql = sqlGeneratorDrillAcross.buildFormattedSqlStatement();
} else {

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

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
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/reportwizard/template/datasource/storedReport?";
public static String DATASOURCE_URL = "/api/bianalysis/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,6 +55,37 @@ public class UserRestrictionService { @@ -55,6 +55,37 @@ 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() {
@ -66,18 +97,46 @@ public class UserRestrictionService { @@ -66,18 +97,46 @@ public class UserRestrictionService {
for (String tableName : tables) {
String resultSql = buildMetadataTableRestriction(tableName);
insertRightsViewRestriction(dataSource, "metadata", tableName , resultSql,
user.getHisInOneOrgUnitLidOfRole().intValue());
if(!resultSql.isBlank()) {
insertRightsViewRestriction(dataSource, "metadata", tableName , "id", 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(Integer userOrgunitLid) {
public void applyOrgunitRestrictions() {
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
""";
@ -94,10 +153,10 @@ public class UserRestrictionService { @@ -94,10 +153,10 @@ public class UserRestrictionService {
continue;
}
String joinColumn = tableJoinColMap.get(tableName);
String restriction = buildOrgunitRestriction(userOrgunitLid, joinColumn);
if(!restriction.isEmpty()) {
insertRightsViewRestriction(dataSource, "presentation", tableName, restriction, userOrgunitLid.intValue());
}
String restriction = buildOrgunitRestriction(user.getHisInOneOrgUnitLidOfRole(), joinColumn);
//if(!restriction.isEmpty()) {
insertRightsViewRestriction(dataSource, "presentation", tableName, joinColumn, restriction, user.getHisInOneOrgUnitLidOfRole().intValue());
//}
}
}
@ -112,9 +171,16 @@ public class UserRestrictionService { @@ -112,9 +171,16 @@ public class UserRestrictionService {
String baseSql = BASE_QUERIES.get(tableName);
String conditions = "";
conditions = buildConditionsForFacttable(topicAreaIds, topicIds);
resultSql = "WHERE id IN " + baseSql
resultSql = baseSql
+ (!conditions.isEmpty() ? " WHERE " + conditions : "") + ")";
return resultSql;
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 "";
}
@ -145,7 +211,8 @@ public class UserRestrictionService { @@ -145,7 +211,8 @@ public class UserRestrictionService {
String restriction = "";
if(!allowedOrgunitsWithChildren.isEmpty()) {
restriction = "WHERE " + joinColumn + " IN (" + lidsAsString + ")";
//restriction = "WHERE " + joinColumn + " IN (" + lidsAsString + ")";
restriction = lidsAsString;
}
return restriction;
}
@ -222,28 +289,29 @@ public class UserRestrictionService { @@ -222,28 +289,29 @@ public class UserRestrictionService {
return whereClause.toString();
}
private void insertRightsViewRestriction(DataSource ds, String schema, String table, String restriction, int orgunitLid) {
public void insertRightsViewRestriction(DataSource ds, String schema, String tableName, String joinColumn, String grantedIds, 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.view_rechte(user_id, user_name, "
+ "role_name, orgunit_lid, schema_name, table_name, restriction, external_entry) " +
"VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
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 (?, ?, ?, ?, ?, ?, ?, ?, ?)";
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, table);
pstmt.setString(7, restriction);
pstmt.setInt(8, 1);
pstmt.setString(6, tableName);
pstmt.setString(7, joinColumn);
pstmt.setString(8, grantedIds);
pstmt.setInt(9, 1);
pstmt.execute();
}
} catch (SQLException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
@ -257,6 +325,7 @@ public class UserRestrictionService { @@ -257,6 +325,7 @@ 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);
@ -269,12 +338,24 @@ public class UserRestrictionService { @@ -269,12 +338,24 @@ public class UserRestrictionService {
}
private static Map<String, String> findFirstValidRight(Map<String, Map<String, String>> rightsMap, Right... rights) {
return Arrays.stream(rights)
.map(Right::getString)
.map(rightsMap::get)
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
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());
}
public static String quoteAndJoin(List<String> strings) {
@ -286,11 +367,88 @@ public class UserRestrictionService { @@ -286,11 +367,88 @@ public class UserRestrictionService {
public void clearRestrictions() throws SQLException {
try(Connection dbConnection = dataSource.getConnection()){
Statement stmt = dbConnection.createStatement();
String sql = "DELETE FROM public.view_rechte WHERE user_name = '"
String sql = "DELETE FROM public.user_dimension_grants 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,20 +394,21 @@ public class SQLGenerator { @@ -394,20 +394,21 @@ 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 < reportMetadata.leftDimensionAttributes.size(); i++) {
DimensionAttribute a = reportMetadata.leftDimensionAttributes.get(i);
if(a.isNullAttribute()) {
continue;
}
for (int i = 0; i < validAttributes.size(); i++) {
StringJoiner group = new StringJoiner(", ");
for (int j = 0; j < reportMetadata.leftDimensionAttributes.size() - i; j++) {
DimensionAttribute attr = reportMetadata.leftDimensionAttributes.get(j);
if(attr.isNullAttribute()) {
continue;
}
for (int j = 0; j < validAttributes.size() - i; j++) {
DimensionAttribute attr = validAttributes.get(j);
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 "BatchConfig" class works asynchronously, which is fine for the
* - The "jobLauncher" bean from the "AppConfig" 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(BatchConfig.class, DataJdbcConfiguration.class, CLIConfig.class, ServiceConfig.class);
APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(AppConfig.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(BatchConfig.class, DataJdbcConfiguration.class, CLIConfig.class, ServiceConfig.class);
APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(AppConfig.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(BatchConfig.class, DataJdbcConfiguration.class, CLIConfig.class);
APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(AppConfig.class, DataJdbcConfiguration.class, CLIConfig.class);
}
return APPLICATION_CONTEXT;
}

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

@ -4,8 +4,6 @@ import static de.superx.servlet.ServletBasics.getParamChecked; @@ -4,8 +4,6 @@ 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;
@ -19,11 +17,9 @@ import java.util.LinkedList; @@ -19,11 +17,9 @@ 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;
@ -49,10 +45,12 @@ import de.superx.servlet.SuperXManager; @@ -49,10 +45,12 @@ 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 {
@ -536,7 +534,7 @@ public class ExecuteMask { @@ -536,7 +534,7 @@ public class ExecuteMask {
* - so it's alright to override some beans via "CLIConfig"
*/
if (APPLICATION_CONTEXT == null) {
APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(BatchConfig.class, DataJdbcConfiguration.class, CLIConfig.class, ServiceConfig.class);
APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(AppConfig.class, DataJdbcConfiguration.class, CLIConfig.class, ServiceConfig.class);
}
return APPLICATION_CONTEXT;
}

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

@ -1,233 +1,225 @@ @@ -1,233 +1,225 @@
package de.superx.bin;
import static de.superx.servlet.SxSQL_Server.DEFAULT_MANDANTEN_ID;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Properties;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Level;
import de.memtext.db.ConnectionCreator;
import de.memtext.util.DateUtils;
import de.memtext.util.GetOpts;
import de.memtext.util.GetOpts.Options;
import de.memtext.util.StringUtils;
import de.superx.common.FieldContainer;
import de.superx.common.Sichten;
import de.superx.common.StandaloneSicht;
import de.superx.common.SuperX_el;
import de.superx.common.SxResultRow;
import de.superx.common.SxResultSet;
import de.superx.common.SxSqlHelper;
import de.superx.common.SxUser;
import de.superx.common.TableFieldExists;
import de.superx.common.TemplateProcessor;
import de.superx.servlet.SxPools;
import de.superx.util.PropsReader;
import de.superx.util.SqlStringUtils;
public class FMParser extends TemplateProcessor {
private String dbprop, infile, outfile;
private Properties props;
private Statement stm;
private String isSimpleParser;
private String myParams="";
private static String sqlDialect;
public FMParser(String mandantenId, Connection con) throws SQLException {
super(mandantenId, con);
if (con.getMetaData().getDatabaseProductName().toLowerCase().contains("postgres")) {
sqlDialect = "Postgres";
}
}
public FMParser(String mandantenId) {
super(mandantenId);
}
private void run() throws FileNotFoundException, IOException, Exception {
dbprop = GetOpts.getValue(Options.opt_dbprops);
props = PropsReader.prepareProps(new File(dbprop));
infile = GetOpts.getValue(Options.opt_in);
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);
} else {
SxPools.initTesting(props);
initConAndParser(dbprop);
sqlDialect = de.superx.util.SqlStringUtils.getSqlDialect(props.getProperty("driverName"));
HashMap map = new HashMap();
map.put("TableFieldExists", new TableFieldExists(con));
output = "--automatically created by SuperX/Freemarker for " + sqlDialect + " ("
+ DateUtils.getTodayString() + " " + DateUtils.getNowString() + ")\n"
+ process("FM-Parsing " + infile, input, map, sqlDialect);
stm.close();
con.close();
}
FileUtils.writeStringToFile(new File(outfile), output);
}
private void initConAndParser(String dbprop) throws IOException, SQLException, ClassNotFoundException {
super.con = ConnectionCreator.getConnectionCryptPassword(dbprop, "driverName", "connectionURL",
"connectionName", "connectionPassword");
stm = con.createStatement();
if (SqlStringUtils.tableExists(con, "fm_templates", mandantenID)) {
setTemplates(readFromDb("select trim(both from id),content from fm_templates"));
repositoryToMap(readFromDb(REPOSITORY_SELECT), repositoryMap);
}
if (SqlStringUtils.tableExists(con, "konstanten", mandantenID)) {
SxResultSet rs = readFromDb("select trim(both from beschreibung),apnr from konstanten");
for (Iterator it = rs.iterator(); it.hasNext();) {
SxResultRow row = (SxResultRow) it.next();
String beschreibung = (String) row.get(0);
repositoryMap.put("K_" + beschreibung.trim(), row.get(1));
}
}
}
@Override
protected SxResultSet readFromDb(String sql) throws SQLException {
SuperX_el el = new SuperX_el();
SxSqlHelper sh = new SxSqlHelper();
sh.execute(sql, this.con, el);
if (el.getError_String() != null && !el.getError_String().trim().equals("")) {
throw new SQLException("\nProblem bei:" + "\n\n Meldung:" + el.getError_String() + "\n sql:" + sql);
}
return el.getResultSet();
}
/**
* @deprecated
* @param sql
* @return
* @throws SQLException
*/
@Deprecated
protected SxResultSet readFromDbAlt(String sql) throws SQLException {
SxResultSet result = new SxResultSet();
ResultSet rs = stm.executeQuery(sql);
result.setColumnNames(rs.getMetaData());
int naturalOrder = 0;
while (rs.next()) {
SxResultRow row = new SxResultRow(rs.getMetaData().getColumnCount(), naturalOrder++);
for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
if (rs.getMetaData().getColumnType(i) == java.sql.Types.LONGVARCHAR)
row.add(rs.getString(i));
else
row.add(rs.getObject(i));
}
result.add(row);
}
rs.close();
return result;
}
public static void main(String[] args) {
GetOpts.setOpts(args);
String isdrin = GetOpts.isAllRequiredOptionsPresent(new Options[]{Options.opt_dbprops, Options.opt_in, Options.opt_out});
if (isdrin != null) {
System.err.println("-dbproperties: -in: -out:");
System.exit(1);
}
try {
new FMParser(DEFAULT_MANDANTEN_ID).run();
} catch (Exception e) {
System.err.println("Fehler beim FM-Parsen ");
e.printStackTrace();
System.exit(1);
}
}
/*
* Einfacher Parser für Fremddatenbanken, nur sqlDialect wird übergeben wird von
* DOSQL direkt aufgerufen
*/
public static String simpleParser(String dbprop, String input)
throws FileNotFoundException, IOException, Exception {
Properties props = PropsReader.prepareProps(new File(dbprop));
FMParser myParser=new FMParser(DEFAULT_MANDANTEN_ID);
myParser.initConAndParser(dbprop);
String output = "";
sqlDialect = de.superx.util.SqlStringUtils.getSqlDialect(props.getProperty("driverName"));
HashMap map = new HashMap();
try {
output = "--automatically created by SuperX/Freemarker for " + sqlDialect + " ("
+ DateUtils.getTodayString() + " " + DateUtils.getNowString() + ")\n"
+ myParser.process("FM-Parsing ", input, map, sqlDialect);
} catch (Exception e) {
System.err.println("Fehler beim FM-Parsen ");
e.printStackTrace();
System.exit(1);
}
return output;
}
/**
* Methode war ursprünglich für direktes Einspielen einer einzelnen konkret mit
* name_intern benannten Sicht. Jetzt erweitert, dass aufgebaute Sicht auch
* zurückgeliefert wird, damit eine sichtsequence aufgebaut werden kann
*/
@Override
protected StandaloneSicht addSqlVarSicht(HashMap hmap, String varname, String name_intern, String gewuenschterStand)
throws SQLException {
String sql = "select tid,name,label,'',beschreibung,quelle,standbutton,art \n"
+ " ,type,alt_hier_id,treecfgtable,treecfgid,gueltig_seit,gueltig_bis,xmlmaxentries,name_intern,attribut1,attribut2,attribut3,cacheapplet,cachexml \n"
+ "from sichten where name_intern='"
+ name_intern
+ "' and aktiv=1 order by type";
SxResultSet roh = new SxResultSet();
StandaloneSicht sicht = null;
roh = readFromDb(sql);
Sichten sichten = new Sichten();
SxUser user = new SxUser();
user.setAdmin(true); // Adminuser ok weil nur von DOSQL gebraucht
sichten.setUser(user);
for (Iterator iter = roh.iterator(); iter.hasNext();) {
SxResultRow row = (SxResultRow) iter.next();
Integer id = Integer.valueOf(1);
try {
sichten.initNonInternalSicht(DEFAULT_MANDANTEN_ID, StandaloneSicht.class, row, id);
sicht = (StandaloneSicht) sichten.getById(id);
sicht.setConnection(con);
sicht.setSqlDialect(sqlDialect);
if (gewuenschterStand.trim().startsWith("today")) {
gewuenschterStand = DateUtils.getTodayString();
}
sicht.setStand(new Hashtable(), hmap, new FieldContainer(), gewuenschterStand);
hmap.put(varname, sicht);
} catch (Exception e) {
e.printStackTrace();
throw new SQLException("Fehler beim Aufbau von Sicht " + name_intern + " " + e);
}
}
return sicht;
}
}
// Created on 08.12.2006 at 18:03:46
package de.superx.bin;
import static de.superx.servlet.SxSQL_Server.DEFAULT_MANDANTEN_ID;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Properties;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Level;
import de.memtext.db.ConnectionCreator;
import de.memtext.util.DateUtils;
import de.memtext.util.GetOpts;
import de.memtext.util.GetOpts.Options;
import de.memtext.util.StringUtils;
import de.superx.common.FieldContainer;
import de.superx.common.Sichten;
import de.superx.common.StandaloneSicht;
import de.superx.common.SuperX_el;
import de.superx.common.SxResultRow;
import de.superx.common.SxResultSet;
import de.superx.common.SxSqlHelper;
import de.superx.common.SxUser;
import de.superx.common.TableFieldExists;
import de.superx.common.TemplateProcessor;
import de.superx.servlet.SxPools;
import de.superx.util.PropsReader;
import de.superx.util.SqlStringUtils;
public class FMParser extends TemplateProcessor {
private String dbprop, infile, outfile;
private Properties props;
private Statement stm;
private String isSimpleParser;
private static String sqlDialect;
public FMParser(String mandantenId, Connection con) throws SQLException {
super(mandantenId, con);
if (con.getMetaData().getDatabaseProductName().toLowerCase().contains("postgres")) {
sqlDialect = "Postgres";
}
}
public FMParser(String mandantenId) {
super(mandantenId);
}
private void run() throws FileNotFoundException, IOException, Exception {
dbprop = GetOpts.getValue(Options.opt_dbprops);
props = PropsReader.prepareProps(new File(dbprop));
infile = GetOpts.getValue(Options.opt_in);
outfile = GetOpts.getValue(Options.opt_out);
if (GetOpts.isPresent(Options.opt_simpPars))
isSimpleParser = GetOpts.getValue(Options.opt_simpPars);
String input = StringUtils.readFile(new File(infile));
String output = "";
if (isSimpleParser != null && isSimpleParser.equalsIgnoreCase("true")) {
output = simpleParser(dbprop, input);
} else {
SxPools.initTesting(props);
initConAndParser(dbprop);
sqlDialect = de.superx.util.SqlStringUtils.getSqlDialect(props.getProperty("driverName"));
HashMap map = new HashMap();
map.put("TableFieldExists", new TableFieldExists(con));
output = "--automatically created by SuperX/Freemarker for " + sqlDialect + " ("
+ DateUtils.getTodayString() + " " + DateUtils.getNowString() + ")\n"
+ process("FM-Parsing " + infile, input, map, sqlDialect);
stm.close();
con.close();
}
FileUtils.writeStringToFile(new File(outfile), output);
}
private void initConAndParser(String dbprop) throws IOException, SQLException, ClassNotFoundException {
super.con = ConnectionCreator.getConnectionCryptPassword(dbprop, "driverName", "connectionURL",
"connectionName", "connectionPassword");
stm = con.createStatement();
if (SqlStringUtils.tableExists(con, "fm_templates", mandantenID)) {
setTemplates(readFromDb("select trim(both from id),content from fm_templates"));
repositoryToMap(readFromDb(REPOSITORY_SELECT), repositoryMap);
}
if (SqlStringUtils.tableExists(con, "konstanten", mandantenID)) {
SxResultSet rs = readFromDb("select trim(both from beschreibung),apnr from konstanten");
for (Iterator it = rs.iterator(); it.hasNext();) {
SxResultRow row = (SxResultRow) it.next();
String beschreibung = (String) row.get(0);
repositoryMap.put("K_" + beschreibung.trim(), row.get(1));
}
}
}
@Override
protected SxResultSet readFromDb(String sql) throws SQLException {
SuperX_el el = new SuperX_el();
SxSqlHelper sh = new SxSqlHelper();
sh.execute(sql, this.con, el);
if (el.getError_String() != null && !el.getError_String().trim().equals("")) {
throw new SQLException("\nProblem bei:" + "\n\n Meldung:" + el.getError_String() + "\n sql:" + sql);
}
return el.getResultSet();
}
/**
* @deprecated
* @param sql
* @return
* @throws SQLException
*/
@Deprecated
protected SxResultSet readFromDbAlt(String sql) throws SQLException {
SxResultSet result = new SxResultSet();
ResultSet rs = stm.executeQuery(sql);
result.setColumnNames(rs.getMetaData());
int naturalOrder = 0;
while (rs.next()) {
SxResultRow row = new SxResultRow(rs.getMetaData().getColumnCount(), naturalOrder++);
for (int i = 1; i <= rs.getMetaData().getColumnCount(); i++) {
if (rs.getMetaData().getColumnType(i) == java.sql.Types.LONGVARCHAR)
row.add(rs.getString(i));
else
row.add(rs.getObject(i));
}
result.add(row);
}
rs.close();
return result;
}
public static void main(String[] args) {
GetOpts.setOpts(args);
String isdrin = GetOpts.isAllRequiredOptionsPresent(new Options[]{Options.opt_dbprops, Options.opt_in, Options.opt_out});
if (isdrin != null) {
System.err.println("-dbproperties: -in: -out:");
System.exit(1);
}
try {
new FMParser(DEFAULT_MANDANTEN_ID).run();
} catch (Exception e) {
System.err.println("Fehler beim FM-Parsen ");
e.printStackTrace();
System.exit(1);
}
}
/*
* Einfacher Parser für Fremddatenbanken, nur sqlDialect wird übergeben wird von
* DOSQL direkt aufgerufen
*/
public static String simpleParser(String dbprop, String input)
throws FileNotFoundException, IOException, Exception {
Properties props = PropsReader.prepareProps(new File(dbprop));
FMParser myParser=new FMParser(DEFAULT_MANDANTEN_ID);
myParser.initConAndParser(dbprop);
String output = "";
sqlDialect = de.superx.util.SqlStringUtils.getSqlDialect(props.getProperty("driverName"));
HashMap map = new HashMap();
try {
output = "--automatically created by SuperX/Freemarker for " + sqlDialect + " ("
+ DateUtils.getTodayString() + " " + DateUtils.getNowString() + ")\n"
+ myParser.process("FM-Parsing ", input, map, sqlDialect);
} catch (Exception e) {
System.err.println("Fehler beim FM-Parsen ");
e.printStackTrace();
System.exit(1);
}
return output;
}
/**
* Methode war ursprünglich für direktes Einspielen einer einzelnen konkret mit
* name_intern benannten Sicht. Jetzt erweitert, dass aufgebaute Sicht auch
* zurückgeliefert wird, damit eine sichtsequence aufgebaut werden kann
*/
@Override
protected StandaloneSicht addSqlVarSicht(HashMap hmap, String varname, String name_intern, String gewuenschterStand)
throws SQLException {
String sql = "select tid,name,label,'',beschreibung,quelle,standbutton,art \n"
+ " ,type,alt_hier_id,treecfgtable,treecfgid,gueltig_seit,gueltig_bis,xmlmaxentries,name_intern,attribut1,attribut2,attribut3,cacheapplet,cachexml \n"
+ "from sichten where name_intern='"
+ name_intern
+ "' and aktiv=1 order by type";
SxResultSet roh = new SxResultSet();
StandaloneSicht sicht = null;
roh = readFromDb(sql);
Sichten sichten = new Sichten();
SxUser user = new SxUser();
user.setAdmin(true); // Adminuser ok weil nur von DOSQL gebraucht
sichten.setUser(user);
for (Iterator iter = roh.iterator(); iter.hasNext();) {
SxResultRow row = (SxResultRow) iter.next();
Integer id = Integer.valueOf(1);
try {
sichten.initNonInternalSicht(DEFAULT_MANDANTEN_ID, StandaloneSicht.class, row, id);
sicht = (StandaloneSicht) sichten.getById(id);
sicht.setConnection(con);
sicht.setSqlDialect(sqlDialect);
if (gewuenschterStand.trim().startsWith("today")) {
gewuenschterStand = DateUtils.getTodayString();
}
sicht.setStand(new Hashtable(), hmap, new FieldContainer(), gewuenschterStand);
hmap.put(varname, sicht);
} catch (Exception e) {
e.printStackTrace();
throw new SQLException("Fehler beim Aufbau von Sicht " + name_intern + " " + e);
}
}
return sicht;
}
}
// Created on 08.12.2006 at 18:03:46

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

@ -39,6 +39,7 @@ import org.pentaho.metastore.stores.memory.MemoryMetaStore; @@ -39,6 +39,7 @@ 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;
@ -195,7 +196,7 @@ public class EtlStarter implements TemplateTransformModel { @@ -195,7 +196,7 @@ public class EtlStarter implements TemplateTransformModel {
* get all db connections configured in databases.xml as
* DataSource's by name
*/
His1DataSources dataSources = SuperXManager.getBean("dataSources", His1DataSources.class);
His1DataSources dataSources = ApplicationContextHolder.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.servlet.SuperXManager;
import de.superx.spring.service.ApplicationContextHolder;
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 = SuperXManager.getBean("dataSource", DataSource.class);
DataSource dataSource = ApplicationContextHolder.getBean("dataSource", DataSource.class);
jt.setDataSource(dataSource);
Object result = null;

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

@ -1,6 +1,7 @@ @@ -1,6 +1,7 @@
package de.superx.common;
import java.util.Enumeration;
import java.util.List;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
@ -16,11 +17,20 @@ public class FieldXmlEncoder { @@ -16,11 +17,20 @@ public class FieldXmlEncoder {
*
* @return
*/
public static String toXml(Field f, boolean isFullXMLRequired, int rownr) {
public static String toXml(Field f, boolean isFullXMLRequired, int rownr,
List<String> dependentOnFields) {
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");
@ -44,7 +54,7 @@ public class FieldXmlEncoder { @@ -44,7 +54,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));// bei Ajax-nachladen wird rownr
result.append(toXml(f, true, 0,null));// bei Ajax-nachladen wird rownr
// nicht benötigt
else {
result.append("<feld " + dynamic(f) + ">\n<relation>");

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

@ -981,11 +981,13 @@ public class Maske extends NamedIdObject { @@ -981,11 +981,13 @@ 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));
result.append(FieldXmlEncoder.toXml(feld, isFullXMLRequired, rownr,dependentOnFields));
}
result.append("<rownr>" + rownr + "</rownr></felder>\n");
// derzeit wird nur das erste Stylesheet zur Liste hinzugefügt
@ -1001,6 +1003,25 @@ public class Maske extends NamedIdObject { @@ -1001,6 +1003,25 @@ 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.

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

@ -32,18 +32,21 @@ import de.memtext.rights.Rights; @@ -32,18 +32,21 @@ 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;
private Sichten sichten = new Sichten();
@ -52,16 +55,16 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -52,16 +55,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>();
@ -85,15 +88,15 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -85,15 +88,15 @@ 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();
}
/**
* Nur für Testfälle - bei Echtbetrieb wird ID benötigt!!
*
@ -107,7 +110,6 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -107,7 +110,6 @@ 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())));
@ -131,8 +133,6 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -131,8 +133,6 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
return ((Integer) getId()).intValue();
}
public SelectionBuffer getSelectionBuffer() {
return sb;
}
@ -154,8 +154,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -154,8 +154,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
return basicMap;
}
public Themenbaum getThemenbaum()
{
public Themenbaum getThemenbaum() {
return themenbaum;
}
@ -166,12 +165,20 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -166,12 +165,20 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
public void setUserid(Integer id) {
super.setId(id);
}
public void setRoleId(Integer id) {
this.roleId = id;
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 : "";
}
@ -187,7 +194,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -187,7 +194,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
public void clearCache() {
sichten.clear();
}
public void initErlaubteMasken(List<Integer> maskenTids) {
erlaubteMaskenTids.addAll(maskenTids);
basicMap.put("UserMaskRights", getMaskRights());
@ -208,21 +215,24 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -208,21 +215,24 @@ 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;
}
@ -235,7 +245,9 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -235,7 +245,9 @@ 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;
}
@ -243,7 +255,9 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -243,7 +255,9 @@ 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;
}
@ -275,17 +289,20 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -275,17 +289,20 @@ 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;
}
@ -298,26 +315,24 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -298,26 +315,24 @@ 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);
@ -332,7 +347,6 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -332,7 +347,6 @@ 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() {
@ -342,7 +356,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -342,7 +356,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
}
return result.toString();
}
public String getSachgebieteIn() {
StringBuffer result = new StringBuffer();
for (Iterator it = sachgebiete.iterator(); it.hasNext();) {
@ -371,7 +385,6 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -371,7 +385,6 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
}
public boolean isInGroup(String group) {
return groups.contains(group);
}
@ -390,22 +403,17 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -390,22 +403,17 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
hiddenFields.add(row.get(0));
}
}
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);
}
@ -486,7 +494,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -486,7 +494,7 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
return SxUser.this.role;
}
});
// add rights
for (String right : rightsMap.keySet()) {
authorities.add(new GrantedAuthority() {
@ -496,7 +504,6 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -496,7 +504,6 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu
}
});
}
return authorities;
}
@ -510,29 +517,28 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -510,29 +517,28 @@ 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())
public void initKontierungsrechte() throws SQLException, DBServletException {
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() {
@ -541,7 +547,8 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -541,7 +547,8 @@ 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());
@ -553,7 +560,9 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -553,7 +560,9 @@ 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() {
@ -564,25 +573,25 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -564,25 +573,25 @@ 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
public String toString() {
return super.toString() + " (" + getRoleString() + ")";
@ -610,27 +619,108 @@ public class SxUser extends User implements Serializable, UserDetails, GrantedAu @@ -610,27 +619,108 @@ 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 maskenEditRights.contains(dbFormName);
return hasMaskenEditRights;
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;
}
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.servlet.SuperXManager;
import de.superx.spring.service.ApplicationContextHolder;
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 = SuperXManager.getBean("dataSource", DataSource.class);
this.dataSource = ApplicationContextHolder.getBean("dataSource", DataSource.class);
}
}

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

@ -77,13 +77,30 @@ public class ExportConfigReader { @@ -77,13 +77,30 @@ public class ExportConfigReader {
}
}
// Parse tables with optional per-table column override
// Parse tables with optional per-table column, source, columns, and orderBy overrides
List<ExportTableConfig> tableConfigs = new ArrayList<>();
for (Element tableEl : groupEl.getChildren("table")) {
String tableName = tableEl.getTextTrim();
if (!tableName.isEmpty()) {
String colOverride = tableEl.getAttributeValue("column");
tableConfigs.add(new ExportTableConfig(tableName, colOverride));
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));
}
}

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

@ -0,0 +1,110 @@ @@ -0,0 +1,110 @@
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,26 +1,57 @@ @@ -1,26 +1,57 @@
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 an optional override for the filter column.
* Holds the table name and optional overrides for the filter column,
* filter source, column selection, and ordering.
*/
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) {
public ExportTableConfig(String tableName, String filterColumn,
String filterSource, List<String> columns,
String orderBy) {
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);
this(tableName, null, null, null, null);
}
public String getTableName() {
@ -35,6 +66,14 @@ public class ExportTableConfig { @@ -35,6 +66,14 @@ 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.
@ -43,11 +82,71 @@ public class ExportTableConfig { @@ -43,11 +82,71 @@ 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() {
if (filterColumn != null) {
return tableName + " (column=" + filterColumn + ")";
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(")");
}
return tableName;
return sb.toString();
}
}

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

@ -15,5 +15,11 @@ public enum ImportMode { @@ -15,5 +15,11 @@ public enum ImportMode {
CREATE_INSERT,
/** Drop and recreate the table from the XML structure, then insert. */
RECREATE
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
}

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

@ -0,0 +1,11 @@ @@ -0,0 +1,11 @@
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

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
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

@ -1,59 +0,0 @@ @@ -1,59 +0,0 @@
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

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

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

@ -0,0 +1,443 @@ @@ -0,0 +1,443 @@
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

@ -0,0 +1,24 @@ @@ -0,0 +1,24 @@
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

@ -0,0 +1,62 @@ @@ -0,0 +1,62 @@
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;
}
}

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

@ -1,15 +1,21 @@ @@ -1,15 +1,21 @@
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;
@ -17,6 +23,8 @@ import de.superx.util.PathAndFileUtils; @@ -17,6 +23,8 @@ import de.superx.util.PathAndFileUtils;
public class DbtUtils {
private final static boolean isLinux = SystemUtils.IS_OS_LINUX;
public static boolean isTestRun = false;
public static String getPythonVersion() {
return getPythonVersionForExecutable(getPython3ExecutableName());
@ -36,7 +44,7 @@ public class DbtUtils { @@ -36,7 +44,7 @@ public class DbtUtils {
}
throw new RuntimeException("No Python 3 installation found in PATH!");
}
public static String getPythonVersionForExecutable(String executable) {
try {
ProcessBuilder p = new ProcessBuilder(executable, "--version");
@ -53,27 +61,59 @@ public class DbtUtils { @@ -53,27 +61,59 @@ 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(getDbtFolder().toString(), "dbt_linux", "lib");
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();
}
public static Path getDbtProjectDirectory() {
return Path.of(getDbtFolder().toString(), "projects", "hisinone", "transform");
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 getBiAnalysisMetadataFolder() {
return Path.of(getDbtFolder().toString(), "projects", "hisinone", "bianalysis_metadata");
public static Path getBiAnalysisMetadataFolder(String project) {
return Path.of(getDbtFolder().toString(), "projects", project, "bianalysis_metadata");
}
public static Path getVirtualEnvPath() {
if(isLinux) {
return Path.of(getDbtFolder().toString(), "dbt_env_linux");
return Path.of(getDbtFolderBinary().toString(), "dbt_env_linux");
}
return Path.of(getDbtFolder().toString(), "dbt_env_windows");
return Path.of(getDbtFolderBinary().toString(), "dbt_env_windows");
}
public static Path getDbtInstallDir() {
@ -84,7 +124,7 @@ public class DbtUtils { @@ -84,7 +124,7 @@ public class DbtUtils {
if(isLinux) {
folder = "dbt_linux";
}
return Path.of(getDbtFolder().toString(), folder);
return Path.of(getDbtFolderBinary().toString(), folder);
}
public static boolean isVirtualEnvInstalled() {
@ -112,11 +152,42 @@ public class DbtUtils { @@ -112,11 +152,42 @@ public class DbtUtils {
return Path.of(getDbtBinaryFolder().toString(), bin);
}
public static Path getDbtBinaryPath() {
private static Path getDbtBinaryPath(String bin) {
if(!(bin.equals("dbt") || bin.equals("sqlfluff"))) {
throw new IllegalArgumentException("binary not allowed: " + bin);
}
if(isLinux) {
return Path.of(getDbtBinaryFolder().toString(), "dbt");
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 = "";
}
return Path.of(getDbtBinaryFolder().toString(), "dbt.exe");
if (pythonExecutable != null && !pythonExecutable.isBlank()) {
command.add(pythonExecutable);
}
command.add(getDbtBinaryPath(bin).toString());
return command;
}
@ -157,20 +228,46 @@ public class DbtUtils { @@ -157,20 +228,46 @@ 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());
System.out.println(getDbtProjectDirectory("hisinone"));
System.out.println(getDbtBinaryFolder());
System.out.println(getDbtBinaryPath());
System.out.println(getDbtCommandBase());
//BasicConfigurator.configure();
//DbtWrapper dbtWrapper = new DbtWrapper();
//dbtWrapper.init("hisinone", "+fact_application");
//System.out.println(dbtWrapper.findDbtStagingTablesForSelection());
}
}

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

@ -1,28 +1,20 @@ @@ -1,28 +1,20 @@
package de.superx.dbt;
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 static java.util.Map.entry;
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.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
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 javax.sql.DataSource;
import org.apache.commons.lang3.SystemUtils;
import org.jfree.util.Log;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -32,172 +24,72 @@ import org.springframework.jdbc.core.JdbcTemplate; @@ -32,172 +24,72 @@ 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.servlet.SuperXManager;
import de.superx.spring.service.ApplicationContextHolder;
@Service
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
}
public class DbtWrapper {
@Autowired
EtlJobApi etlJobApi;
@Autowired
DataSource dataSource;
static Logger logger = LoggerFactory.getLogger(DbtWrapper.class);
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;
private Path projectPath;
private List<JsonNode> dbtLog = new ArrayList<>();
private JsonNode stats;
private boolean shouldGenerateDoc = true;
@Autowired
Map<String, String> dbtProfile;
public void init(final Path projectPath, String select) {
init(projectPath, select, DbParamsSource.FROM_FILE, false);
}
static Logger logger = LoggerFactory.getLogger(DbtWrapper.class);
public void init(final Path projectPath, String select, boolean test) {
init(projectPath, select, DbParamsSource.FROM_FILE, test);
}
public static final String DBT_THREAD_NAME_PREFIX = "DBT-";
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;
}
public DbtRunResult execute(DbtRunRequest request) {
DbtRun run = new DbtRun(
request,
getEnvironment(), // TODO: costly, reuse for multiple calls ?
DbtUtils.getDbtProjectDirectory(request.project())
);
// 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 {
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);
HikariDataSource eduetl = dataSource.unwrap(HikariDataSource.class);
String jdbcUrl = eduetl.getJdbcUrl();
try {
return request.timeoutMillis() <= 0
? future.get() : future.get(request.timeoutMillis(), TimeUnit.MILLISECONDS);
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");
} catch (TimeoutException e) {
logger.error("dbt command timed out after {} milliseconds", Long.valueOf(request.timeoutMillis()));
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);
if (run.process != null && run.process.isAlive()) {
run.process.destroyForcibly();
}
}
}
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"
));
future.cancel(true);
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);
}
DbtRunResult result = new DbtRunResult();
result.setState(DbtExecutionState.TIMEOUT);
result.setErrorMessages(Arrays.asList("DBT run timeout!"));
return result;
if(this.test) {
baseCommand.add("test");
} else {
baseCommand.addAll(Arrays.asList(
"build",
"--resource-types",
"snapshot",
"seed",
"model"
));
} 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;
}
}
if(select != null && !select.isBlank()) {
baseCommand.add("--select");
baseCommand.add(select);
}
return baseCommand;
}
@Override
public void run() {
List<String> pbCommand = buildDbtCommand();
logger.info(Arrays.toString(pbCommand.toArray()));
ProcessBuilder p = new ProcessBuilder(pbCommand).directory(projectPath.toFile());
private Map<String, String> getEnvironment(){
Map<String,String> env = getComponentStatus();
env.putAll(getDbtEnvironment());
env.putAll(getUnloadParams());
@ -205,223 +97,9 @@ public class DbtWrapper implements Runnable { @@ -205,223 +97,9 @@ public class DbtWrapper implements Runnable {
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) {
@ -442,46 +120,32 @@ public class DbtWrapper implements Runnable { @@ -442,46 +120,32 @@ public class DbtWrapper implements Runnable {
}
return params;
}
});
}
return result;
}
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();
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");
}
return null;
return result;
}
public void setShouldGenerateDoc(boolean shouldGenerateDoc) {
this.shouldGenerateDoc = shouldGenerateDoc;
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;
}
}

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

@ -2,15 +2,12 @@ package de.superx.etl.biad; @@ -2,15 +2,12 @@ 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) {
@ -49,10 +46,18 @@ public class DBSource { @@ -49,10 +46,18 @@ public class DBSource {
public String getDatabase() {
return database;
}
public void setDatabase(String database) {
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 = "Kurzbeschreibung")
@DynamicFieldType(label = "Kurztext")
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 = "Eindeutiger Name")
@DynamicFieldType(label = "Schlüssel (uniquename)")
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 = "Kurzbeschreibung")
@DynamicFieldType(label = "Kurztext")
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 = "Eindeutiger Name")
@DynamicFieldType(label = "Schlüssel (uniquename)")
public String uniquename;
// wird nicht verwendet

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

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
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

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
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,10 +82,12 @@ public class ContainerNode extends ActionNode { @@ -82,10 +82,12 @@ 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) {
@ -168,6 +170,14 @@ public class ContainerNode extends ActionNode { @@ -168,6 +170,14 @@ 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,8 +1,12 @@ @@ -1,8 +1,12 @@
package de.superx.job;
public class ExecuteSQLFluffAction extends ActionNode {
public ExecuteSQLFluffAction (String name, String id, Integer systemInfoId, boolean active, Integer custom) {
public String dbtProject;
public ExecuteSQLFluffAction (String dbtProject, 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) {
public static ExtractAction createFromSourceTable(SourceTable table, String unlFile, String schema, String sourceSystem) {
return new ExtractAction(table.getName(), table.getName(),
table.getExtractionScript(), table.getExtractionInclude(),
schema, unlFile, "hisinone", Integer.valueOf(440), null);
schema, unlFile, sourceSystem, 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.GET)
@RequestMapping(path = "/clearpool", method = RequestMethod.POST)
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.GET)
@RequestMapping(path = "/cleartranslet", method = RequestMethod.POST)
public static String cleartranslet(@Context HttpServletRequest httpRequest) {
String returntext="Problem: Cannot clear translet cache.";
try {

31
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.GET)
@RequestMapping(path="/job/stopAll", method = RequestMethod.POST)
public boolean stopAllJobs() {
userService.checkAdminRights();
JobUtils.terminateAllJobExecutions(jobExplorer, jobOperator);
@ -189,7 +189,7 @@ public class EtlJobApi extends RestControllerBase { @@ -189,7 +189,7 @@ public class EtlJobApi extends RestControllerBase {
return jobDescriptionSource.getJobsForComponent(abbreviation);
}
@RequestMapping(path="/job/writeJobsToDb", method = RequestMethod.GET)
@RequestMapping(path="/job/writeJobsToDb", method = RequestMethod.POST)
public boolean writeJobsToDb() {
try {
List<ContainerNode> jobs = entityJobDescriptionSource.readJobs();
@ -216,6 +216,7 @@ public class EtlJobApi extends RestControllerBase { @@ -216,6 +216,7 @@ 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!
@ -233,10 +234,11 @@ public class EtlJobApi extends RestControllerBase { @@ -233,10 +234,11 @@ public class EtlJobApi extends RestControllerBase {
return executeJob(sourceDatabase, container);
}
@RequestMapping(path="/job/execute/{sourceDatabase}/{id}", method=RequestMethod.GET)
@RequestMapping(path="/job/execute/{sourceDatabase}/{id}", method=RequestMethod.POST)
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.
@ -320,7 +322,7 @@ public class EtlJobApi extends RestControllerBase { @@ -320,7 +322,7 @@ public class EtlJobApi extends RestControllerBase {
return startJob(job, stepCounter.getCount(), additionalParameters);
}
@RequestMapping(path="/job/install/{component}", method=RequestMethod.GET)
@RequestMapping(path="/job/install/{component}", method=RequestMethod.POST)
public Long executeInstall(@PathVariable(value="component") String component) throws Exception {
userService.checkAdminRights();
if (isAnyJobInstanceRunning()) {
@ -383,7 +385,7 @@ public class EtlJobApi extends RestControllerBase { @@ -383,7 +385,7 @@ public class EtlJobApi extends RestControllerBase {
return startJob(job, stepCounter.getCount(), parameterBuilder.toJobParameters());
}
@RequestMapping(path="/job/upgrade/{component}", method=RequestMethod.GET)
@RequestMapping(path="/job/upgrade/{component}", method=RequestMethod.POST)
public Long executeUpgrade(@PathVariable(value="component") String component) throws Exception {
userService.checkAdminRights();
if (isAnyJobInstanceRunning()) {
@ -402,7 +404,7 @@ public class EtlJobApi extends RestControllerBase { @@ -402,7 +404,7 @@ public class EtlJobApi extends RestControllerBase {
return startJob(job, stepCounter.getCount(), parameterBuilder.toJobParameters());
}
@RequestMapping(path="/job/upgrade_all", method=RequestMethod.GET)
@RequestMapping(path="/job/upgrade_all", method=RequestMethod.POST)
public Long executeUpgradeAll() throws Exception {
userService.checkAdminRights();
if (isAnyJobInstanceRunning()) {
@ -427,7 +429,7 @@ public class EtlJobApi extends RestControllerBase { @@ -427,7 +429,7 @@ public class EtlJobApi extends RestControllerBase {
return startJob(job, stepCounter.getCount(), null);
}
@RequestMapping(path="/job/uninstall/{component}", method=RequestMethod.GET)
@RequestMapping(path="/job/uninstall/{component}", method=RequestMethod.POST)
public Long executeUninstall(@PathVariable(value="component") String component) throws Exception {
userService.checkAdminRights();
if (isAnyJobInstanceRunning()) {
@ -439,7 +441,7 @@ public class EtlJobApi extends RestControllerBase { @@ -439,7 +441,7 @@ public class EtlJobApi extends RestControllerBase {
return startJob(job, stepCounter.getCount(), null);
}
@RequestMapping(path="/unload/{component}", method = RequestMethod.GET)
@RequestMapping(path="/unload/{component}", method = RequestMethod.POST)
public Long unload(@PathVariable(value = "component") String component) throws Exception {
userService.checkEtlJobRights();
if (isAnyJobInstanceRunning()) {
@ -452,7 +454,7 @@ public class EtlJobApi extends RestControllerBase { @@ -452,7 +454,7 @@ public class EtlJobApi extends RestControllerBase {
return startJob(job, stepCounter.getCount(), null);
}
@RequestMapping(path="/transform/{component}", method = RequestMethod.GET)
@RequestMapping(path="/transform/{component}", method = RequestMethod.POST)
public Long load(@PathVariable(value = "component") String component) throws Exception {
userService.checkEtlJobRights();
if (isAnyJobInstanceRunning()) {
@ -465,7 +467,7 @@ public class EtlJobApi extends RestControllerBase { @@ -465,7 +467,7 @@ public class EtlJobApi extends RestControllerBase {
return startJob(job, stepCounter.getCount(), null);
}
@RequestMapping(path="/complete/{component}", method = RequestMethod.GET)
@RequestMapping(path="/complete/{component}", method = RequestMethod.POST)
public Long complete(@PathVariable(value = "component") String component) throws Exception {
userService.checkEtlJobRights();
if (isAnyJobInstanceRunning()) {
@ -622,13 +624,13 @@ public class EtlJobApi extends RestControllerBase { @@ -622,13 +624,13 @@ public class EtlJobApi extends RestControllerBase {
}
@RequestMapping(path="/stop/{id}", method = RequestMethod.GET)
@RequestMapping(path="/stop/{id}", method = RequestMethod.POST)
public boolean stop(@PathVariable(value = "id") Long id) throws Exception {
userService.checkEtlJobRights();
return JobUtils.terminateJobExecution(jobExplorer, jobOperator, id);
}
@RequestMapping(path="/start/{id}", method = RequestMethod.GET)
@RequestMapping(path="/start/{id}", method = RequestMethod.POST)
public Long start(@PathVariable(value = "id") Long id) throws Exception {
userService.checkEtlJobRights();
Long started = jobOperator.restart(id.longValue());
@ -636,7 +638,7 @@ public class EtlJobApi extends RestControllerBase { @@ -636,7 +638,7 @@ public class EtlJobApi extends RestControllerBase {
}
@RequestMapping(path="/installFunctions/{component}", method = RequestMethod.GET)
@RequestMapping(path="/installFunctions/{component}", method = RequestMethod.POST)
public boolean installModuleFunctions(@PathVariable(value = "component") String component) {
File moduleFile = PathAndFileUtils.getXmlFile(component);
SAXReader sr = new SAXReader();
@ -913,6 +915,7 @@ public class EtlJobApi extends RestControllerBase { @@ -913,6 +915,7 @@ 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);
}
}
@ -956,7 +959,7 @@ public class EtlJobApi extends RestControllerBase { @@ -956,7 +959,7 @@ public class EtlJobApi extends RestControllerBase {
}
}
private List<Component> getInstallables(){
public 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.GET)
@RequestMapping(path = "/reload_log4j", method = RequestMethod.POST)
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,6 +306,8 @@ public class Report extends ReportApiBase { @@ -306,6 +306,8 @@ 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,6 +22,7 @@ import org.springframework.web.method.annotation.MethodArgumentTypeMismatchExcep @@ -22,6 +22,7 @@ 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;
@ -127,6 +128,8 @@ abstract public class RestControllerBase { @@ -127,6 +128,8 @@ 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!
@ -249,6 +252,11 @@ abstract public class RestControllerBase { @@ -249,6 +252,11 @@ 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,6 +21,11 @@ public class Column { @@ -21,6 +21,11 @@ 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,6 +20,7 @@ public enum ColumnType { @@ -20,6 +20,7 @@ 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
@ -86,7 +87,14 @@ public enum ColumnType { @@ -86,7 +87,14 @@ public enum ColumnType {
try {
result = format.format(val);
} catch (IllegalArgumentException e) {
logger.error("Couldn't format value " + val + " as " + format, 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);
}
}
return result;

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

@ -22,6 +22,7 @@ public class Result { @@ -22,6 +22,7 @@ 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,6 +4,7 @@ import de.superx.job.ContainerNode.JobType; @@ -4,6 +4,7 @@ import de.superx.job.ContainerNode.JobType;
public class Connector {
private String name;
private String description;
private String sourceDatabase;
private JobType jobType;
private Long jobId;
@ -22,6 +23,14 @@ public class Connector { @@ -22,6 +23,14 @@ 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 = SuperXManager.getBean("dataSources", His1DataSources.class);
His1DataSources h1ds = ApplicationContextHolder.getBean("dataSources", His1DataSources.class);
result = h1ds.get(dataSourceName);
} catch (NoSuchBeanDefinitionException nsbde) {
return null;

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

@ -144,15 +144,8 @@ public abstract class AbstractSuperXServlet extends HttpServlet { @@ -144,15 +144,8 @@ 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

@ -1,446 +0,0 @@ @@ -1,446 +0,0 @@
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);
}
}
}

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

File diff suppressed because it is too large Load Diff

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

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

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

@ -57,6 +57,7 @@ import de.superx.common.SxResultRow; @@ -57,6 +57,7 @@ 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;
@ -75,6 +76,7 @@ public class UserInitializer { @@ -75,6 +76,7 @@ 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
@ -159,15 +161,28 @@ public class UserInitializer { @@ -159,15 +161,28 @@ public class UserInitializer {
sessionService.authenticate(request, user.getName(), null);
}
}
if(de.superx.servlet.SuperXManager.isHis1)
{
if(!SuperXManager.isHis1) {
return;
}
SingleConnectionDataSource ds = new SingleConnectionDataSource(con, true);
boolean isBiadInstalled = DbUtils.isModuleInstalled(ds, BIAD_MODULE_ABBREVIATION);
if(!isBiadInstalled) {
return;
}
try {
if ( SqlAccessRestrictor.rechteModus == SqlAccessRestrictor.Rechtekonzept.INLINE_SQL) {
SingleConnectionDataSource ds = new SingleConnectionDataSource(con, true);
if (SqlAccessRestrictor.rechteModus == SqlAccessRestrictor.Rechtekonzept.INLINE_SQL) {
UserRestrictionService builder = new UserRestrictionService(user, ds);
builder.clearRestrictions();
// sachgebiete und faktentabellen werden eingeschränkt
builder.applyMetadataRestrictions();
builder.applyOrgunitRestrictions(user.getHisInOneOrgUnitLidOfRole());
// konkrete dimension werden eingeschränkt bsp: dim_project
builder.calculateDimensionRestrictions(user.getUserPersonId());
builder.applyOrgunitRestrictions();
}
if (SqlAccessRestrictor.rechteModus == Rechtekonzept.ROW_LEVEL_SECURITY) {
String dbUserName = String.format("sx_user_%s_%s", user.getUsername(), user.getRole()).toLowerCase();
@ -189,7 +204,7 @@ public class UserInitializer { @@ -189,7 +204,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.view_rechte TO ${user};
GRANT SELECT ON TABLE public.user_dimension_grants 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};
@ -201,7 +216,6 @@ public class UserInitializer { @@ -201,7 +216,6 @@ 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);
}
@ -214,7 +228,6 @@ public class UserInitializer { @@ -214,7 +228,6 @@ public class UserInitializer {
}
throw new NichtAngemeldetException("Probleme bei der Anmeldung. Bitte den Administrator kontaktieren.");
}
}
}
}
@ -1052,6 +1065,13 @@ public class UserInitializer { @@ -1052,6 +1065,13 @@ public class UserInitializer {
user.setRights((Map<String, Map<String, String>>) attributes.get("rightsMap"), (String) attributes.get("role"));
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) {
@ -1111,6 +1131,7 @@ public class UserInitializer { @@ -1111,6 +1131,7 @@ 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;
}
}

44
src/de/superx/spring/config/BatchConfig.java → src/de/superx/spring/AppConfig.java

@ -1,4 +1,4 @@ @@ -1,4 +1,4 @@
package de.superx.spring.config;
package de.superx.spring;
import java.io.File;
@ -33,6 +33,7 @@ import org.springframework.batch.core.launch.support.TaskExecutorJobLauncher; @@ -33,6 +33,7 @@ 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;
@ -49,21 +50,21 @@ import org.springframework.transaction.PlatformTransactionManager; @@ -49,21 +50,21 @@ 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.spring.config.DataSourceDiscardingUserStuff;
import de.superx.util.PathAndFileUtils;
@Configuration
@EnableBatchProcessing
@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 BatchConfig {
public class AppConfig {
// time in milliseconds after which an exception is
// thrown if no connection available in pool
@Value("${hikari.connection_timeout:180000}")
@ -78,7 +79,7 @@ public class BatchConfig { @@ -78,7 +79,7 @@ public class BatchConfig {
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);
private static Logger logger = Logger.getLogger(AppConfig.class);
@Autowired
private JobRepository jobRepository;
@ -93,6 +94,11 @@ public class BatchConfig { @@ -93,6 +94,11 @@ public class BatchConfig {
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
public PlatformTransactionManager batchTransactionManager() {
return new DataSourceTransactionManager(unrestrictedDataSource());
}
@Bean
@ -133,8 +139,8 @@ public class BatchConfig { @@ -133,8 +139,8 @@ public class BatchConfig {
//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(),
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);
@ -143,10 +149,28 @@ public class BatchConfig { @@ -143,10 +149,28 @@ public class BatchConfig {
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException
| BadPaddingException e) {
logger.fatal(e);
e.printStackTrace();
}
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() {
@ -169,7 +193,7 @@ public class BatchConfig { @@ -169,7 +193,7 @@ public class BatchConfig {
return ds;
}
@Bean
public Step removeStep(PlatformTransactionManager transactionManager, DataSource dataSource) {
public Step removeStep(@Qualifier("transactionManager" )PlatformTransactionManager transactionManager, @Qualifier("dataSource") DataSource dataSource) {
RemoveSpringBatchHistoryTasklet tasklet = new RemoveSpringBatchHistoryTasklet();
tasklet.setJdbcTemplate(new JdbcTemplate(dataSource));

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

@ -0,0 +1,130 @@ @@ -0,0 +1,130 @@
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;
}
}

45
src/de/superx/spring/HIS1Databases.java → src/de/superx/spring/DatabaseUtils.java

@ -10,6 +10,7 @@ import java.nio.charset.StandardCharsets; @@ -10,6 +10,7 @@ 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;
@ -31,28 +32,47 @@ import com.zaxxer.hikari.HikariConfig; @@ -31,28 +32,47 @@ 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 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(HIS1Databases.class);
private static Logger logger = Logger.getLogger(DatabaseUtils.class);
private HIS1Databases() { //Mit Ausnahmegenehmigung
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{
return fetchConnections();
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
@ -118,12 +138,25 @@ public class HIS1Databases { @@ -118,12 +138,25 @@ public class HIS1Databases {
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());
}
@ -268,7 +301,7 @@ public class HIS1Databases { @@ -268,7 +301,7 @@ public class HIS1Databases {
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 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);

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

@ -2,6 +2,9 @@ package de.superx.spring; @@ -2,6 +2,9 @@ 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;
@ -67,6 +70,19 @@ public class HisInOneConfiguration implements ApplicationContextInitializer<Conf @@ -67,6 +70,19 @@ 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());
}
@ -161,4 +177,16 @@ public class HisInOneConfiguration implements ApplicationContextInitializer<Conf @@ -161,4 +177,16 @@ 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,6 +16,7 @@ import org.apache.log4j.spi.LoggingEvent; @@ -16,6 +16,7 @@ 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;
@ -29,15 +30,17 @@ public class BiJobLoggingListener implements JobExecutionListener { @@ -29,15 +30,17 @@ 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,SSS} [%p|%t|%c{1}] %m%n");
private static final PatternLayout PATTERN_LONG = new PatternLayout("%d [%p|%t|%c{1}] %m%n");
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 File logFile;
private StringWriter logWriter;
@ -78,8 +81,10 @@ public class BiJobLoggingListener implements JobExecutionListener { @@ -78,8 +81,10 @@ 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(PATTERN_SHORT, stringWriter);
WriterAppender writer = new WriterAppender();
writer.setWriter(stringWriter);
writer.setName(BATCH_JOB_STRING_APPENDER);
writer.setLayout(PATTERN_SHORT);
writer.setThreshold(logLevel);
writer.clearFilters();
writer.addFilter(logFilter);
@ -139,7 +144,7 @@ class EtlJobLoggingFilter extends Filter { @@ -139,7 +144,7 @@ class EtlJobLoggingFilter extends Filter {
@Override
public int decide(LoggingEvent event) {
String threadName = event.getThreadName();
if (threadName.contains("TaskExecutor-") || threadName.startsWith("DBT-") || myThread.equals(event.getThreadName())) {
if (threadName.contains("TaskExecutor-") || threadName.startsWith(DbtWrapper.DBT_THREAD_NAME_PREFIX) || 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,7 +26,8 @@ public class JdbcExtractReader<T> extends JdbcCursorItemReader<T> implements Ste @@ -26,7 +26,8 @@ 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;";
public String dropTableStmt = "DROP TABLE IF EXISTS %s.%s CASCADE;";
public String createSchemaStmt = "CREATE SCHEMA IF NOT EXISTS %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");
@ -77,6 +78,9 @@ public class JdbcExtractReader<T> extends JdbcCursorItemReader<T> implements Ste @@ -77,6 +78,9 @@ 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());

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

@ -1,12 +1,5 @@ @@ -1,12 +1,5 @@
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;
@ -18,19 +11,16 @@ import org.springframework.batch.core.StepExecutionListener; @@ -18,19 +11,16 @@ 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.DbtWrapper.ExecutionState;
import de.superx.dbt.DbtMetrics;
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.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);
@ -41,19 +31,17 @@ public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, Intern @@ -41,19 +31,17 @@ 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();
}
@ -61,7 +49,8 @@ public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, Intern @@ -61,7 +49,8 @@ public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, Intern
public void beforeStep(StepExecution stepExecution) {
try {
DbtUtils.checkPythonCompatability();
} catch (UnsupportedPythonVersionException e) {
DbtUtils.checkDbtSchemaPermissions(dataSource);
} catch (UnsupportedPythonVersionException | DbtPermissionException e) {
throw new RuntimeException(e);
}
}
@ -69,34 +58,29 @@ public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, Intern @@ -69,34 +58,29 @@ public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, Intern
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
try {
//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!");
}
}
if (dbtWrapper.getExecutionState() != ExecutionState.SUCCESS) {
ExitStatus exitStatus = ExitStatus.FAILED;
List<String> errors = dbtWrapper.getErrorMessages();
for (String error : errors) {
exitStatus = exitStatus.addExitDescription(error);
// 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));
}
contribution.setExitStatus(exitStatus);
}
} catch(Exception e) {
} catch (Exception e) {
String errorMsg = e.getMessage();
Throwable cause = e.getCause();
if (cause != null) {
@ -108,7 +92,8 @@ public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, Intern @@ -108,7 +92,8 @@ public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, Intern
exitStatus = exitStatus.addExitDescription(errorMsg);
contribution.setExitStatus(exitStatus);
}
totalStepsExecuted = dbtWrapper.getModelCount();
this.totalStepsExecuted = this.totalSteps + 1;
return RepeatStatus.FINISHED;
}
@ -119,13 +104,16 @@ public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, Intern @@ -119,13 +104,16 @@ public class ExecuteDbtTasklet implements Tasklet, StepExecutionListener, Intern
@Override
public int getInternalStepsExecutedCount() {
return totalStepsExecuted > 0 ? totalStepsExecuted : dbtWrapper.getModelCount();
return totalStepsExecuted;
}
@Override
public int getInternalTotalStepsCount() {
dbtWrapper.init(dbtProjectDir, dbtSelect, dbtTest);
return dbtWrapper.getNodeCount();
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();
}
}

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

@ -1,15 +1,8 @@ @@ -1,15 +1,8 @@
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;
@ -19,147 +12,66 @@ import org.springframework.batch.core.step.tasklet.Tasklet; @@ -19,147 +12,66 @@ 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 final Path dbtProjectDir = Path.of(
PathAndFileUtils.getWebinfPath(),
"..",
"dbt",
"projects",
"hisinone",
"transform");
private final Path sqlFluffDir = dbtProjectDir.resolve("models");
private String dbtProject;
private int progress = 0;
public ExecuteSQLFluffTasklet(DbtWrapper dbtWrapper) {
public ExecuteSQLFluffTasklet(DbtWrapper dbtWrapper, String dbtProject) {
this.dbtWrapper = dbtWrapper;
this.dbtProject = dbtProject;
}
@Override
public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception {
try {
int sqlFluffStatus = runSQLFluffLint();
if(sqlFluffStatus != 0) {
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());
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;
}
return RepeatStatus.FINISHED;
}
@Override
public int getInternalTotalStepsCount() {
Path modelsDir = DbtUtils.getDbtModelsDirectory(dbtProject);
int count = 0;
try {
count = (int) Files.walk(sqlFluffDir)
count = (int) Files.walk(modelsDir)
.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".sql"))
.count();
}
catch (IOException e) {
logger.error("Couldn't count sql files in " + sqlFluffDir, e);
logger.error("Couldn't count sql files in " + modelsDir, 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);
try {
sql_execution: 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,6 +22,7 @@ import org.springframework.batch.core.scope.context.ChunkContext; @@ -22,6 +22,7 @@ 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;
@ -61,6 +62,10 @@ public class PgCopyLoadTasklet implements Tasklet, StepListener { @@ -61,6 +62,10 @@ 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" : "") + ")";
@ -79,7 +84,9 @@ public class PgCopyLoadTasklet implements Tasklet, StepListener { @@ -79,7 +84,9 @@ public class PgCopyLoadTasklet implements Tasklet, StepListener {
CopyManager copyManager = getPgConnection(con).getCopyAPI();
count = copyManager.copyIn(copySql, reader);
}
logger.info(count + " records loaded.");
JdbcTemplate jt = new JdbcTemplate(dataSource);
jt.execute("ANALYZE " + table);
logger.info(count + " records loaded with analyze.");
} 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 "BatchConfig" configuration,
* It is meant to be used in combination with the "AppConfig" 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).

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

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

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

@ -0,0 +1,48 @@ @@ -0,0 +1,48 @@
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,7 +5,6 @@ import static de.superx.util.PathAndFileUtils.substituteParamStrings; @@ -5,7 +5,6 @@ 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;
@ -13,11 +12,11 @@ import java.sql.SQLException; @@ -13,11 +12,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;
@ -45,11 +44,8 @@ import org.springframework.batch.core.step.job.JobParametersExtractor; @@ -45,11 +44,8 @@ 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;
@ -352,7 +348,8 @@ public class BatchJobDescriptionAdapter { @@ -352,7 +348,8 @@ public class BatchJobDescriptionAdapter {
UpdateMetadataAction umd = (UpdateMetadataAction) node;
result = createUpdateMetadataStep(stepBuilder, umd.dbtProject);
} else if(node instanceof ExecuteSQLFluffAction) {
result = new StepData(stepBuilder.tasklet(new ExecuteSQLFluffTasklet(dbtWrapper),transactionManager).build());
ExecuteSQLFluffAction eda = (ExecuteSQLFluffAction) node;
result = new StepData(stepBuilder.tasklet(new ExecuteSQLFluffTasklet(dbtWrapper, eda.dbtProject),transactionManager).build());
} else if(node instanceof RemoveSpringBatchHistoryAction) {
RemoveSpringBatchHistoryTasklet rsbht = new RemoveSpringBatchHistoryTasklet();
rsbht.setJdbcTemplate(new JdbcTemplate(dataSources.get("eduetl")));
@ -371,9 +368,8 @@ public class BatchJobDescriptionAdapter { @@ -371,9 +368,8 @@ public class BatchJobDescriptionAdapter {
}
private StepData createSqlActionNoSxPool(StepBuilder stepBuilder, SqlActionNoSxPool sqlScriptAction) {
DataSource ds = dataSources.get(sqlScriptAction.database);
DataSource ds = getDataSource(sqlScriptAction.database, "SqlActionNoSxPool " + sqlScriptAction.name).orElse(null);
if (ds == null) {
logger.error("DataSource not found: " + sqlScriptAction.database);
return null;
}
SqlScriptActionNoSxPoolTasklet tasklet = new SqlScriptActionNoSxPoolTasklet(ds);
@ -404,9 +400,8 @@ public class BatchJobDescriptionAdapter { @@ -404,9 +400,8 @@ public class BatchJobDescriptionAdapter {
}
private StepData createUnloadParamsStep(StepBuilder stepBuilder, Integer systemInfoId) {
DataSource ds = dataSources.get("eduetl");
DataSource ds = getDataSource("eduetl", "Create UnloadParams").orElse(null);
if (ds == null) {
logger.error("DataSource not found: eduetl");
return null;
}
Tasklet unloadReader = new UnloadParamReaderTasklet(systemInfoId.intValue(), ds);
@ -414,9 +409,8 @@ public class BatchJobDescriptionAdapter { @@ -414,9 +409,8 @@ public class BatchJobDescriptionAdapter {
}
private StepData createTimestampStep(StepBuilder stepBuilder, Integer systemInfoId) {
DataSource ds = dataSources.get("eduetl");
DataSource ds = getDataSource("eduetl", "Create Timestamp").orElse(null);
if (ds == null) {
logger.error("DataSource not found: eduetl");
return null;
}
String sql = String.format("update systeminfo s set datum = now() where "
@ -426,9 +420,8 @@ public class BatchJobDescriptionAdapter { @@ -426,9 +420,8 @@ public class BatchJobDescriptionAdapter {
}
private StepData createUnloadStep(StepBuilder stepBuilder, UnloadAction unloadAction, String paramCompVersion) {
DataSource ds = dataSources.get(unloadAction.dataSource);
DataSource ds = getDataSource(unloadAction.dataSource, "UnloadAction " + unloadAction.name).orElse(null);
if (ds == null) {
logger.error("DataSource not found: " + unloadAction.dataSource);
return null;
}
// TODO: We really should use a header line in csv
@ -457,9 +450,8 @@ public class BatchJobDescriptionAdapter { @@ -457,9 +450,8 @@ public class BatchJobDescriptionAdapter {
}
private StepData createExtractStep(StepBuilder stepBuilder, ExtractAction extractAction) {
DataSource ds = dataSources.get(extractAction.dataSource);
DataSource ds = getDataSource(extractAction.dataSource, "ExtractAction " + extractAction.name).orElse(null);
if (ds == null) {
logger.error("DataSource not found: " + extractAction.dataSource);
return null;
}
JdbcExtractReader<String> reader = new JdbcExtractReader<String>(extractAction, dataSources.get(extractAction.dataSource));
@ -488,9 +480,8 @@ public class BatchJobDescriptionAdapter { @@ -488,9 +480,8 @@ public class BatchJobDescriptionAdapter {
}
private StepData createTransferStep(StepBuilder stepBuilder, TransferAction transferAction) {
DataSource ds = dataSources.get("eduetl");
DataSource ds = getDataSource("eduetl", "TransferAction " + transferAction.name).orElse(null);
if (ds == null) {
logger.error("DataSource not found: eduetl");
return null;
}
ExecuteSqlTasklet transfer = new ExecuteSqlTasklet("truncate table " +
@ -514,15 +505,14 @@ public class BatchJobDescriptionAdapter { @@ -514,15 +505,14 @@ public class BatchJobDescriptionAdapter {
}
private StepData createSqlScriptStep(StepBuilder stepBuilder, SqlScriptAction sqlScriptAction) {
DataSource ds = dataSources.get(sqlScriptAction.dataSource);
DataSource ds = getDataSource(sqlScriptAction.dataSource, "SqlScriptAction " + sqlScriptAction.name).orElse(null);
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.error("VarDataSource not found: " + sqlScriptAction.sqlVarDataSource);
logger.warn("VarDataSource not found: " + sqlScriptAction.sqlVarDataSource);
return null;
}
File scriptFile = new File(substituteParamStrings(sqlScriptAction.sqlScriptPath));
@ -541,9 +531,8 @@ public class BatchJobDescriptionAdapter { @@ -541,9 +531,8 @@ public class BatchJobDescriptionAdapter {
}
private StepData createSqlStep(StepBuilder stepBuilder, SqlAction sqlAction) {
DataSource ds = dataSources.get(sqlAction.dataSource);
DataSource ds = getDataSource(sqlAction.dataSource, "SqlAction " + sqlAction.name).orElse(null);
if (ds == null) {
logger.error("DataSource not found: " + sqlAction.dataSource);
return null;
}
DataSource varDs = sqlAction.sqlVarDataSource != null && !sqlAction.sqlVarDataSource.isEmpty() ?
@ -568,21 +557,21 @@ public class BatchJobDescriptionAdapter { @@ -568,21 +557,21 @@ public class BatchJobDescriptionAdapter {
}
private StepData createLoadStep(StepBuilder stepBuilder, LoadAction loadAction) {
DataSource ds = dataSources.get(loadAction.database);
DataSource ds = getDataSource(loadAction.database, "LoadAction " + loadAction.name).orElse(null);
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(substituteParamStrings(loadAction.unlFilePath)),
PgCopyLoadTasklet upload = new PgCopyLoadTasklet(new FileSystemResource(unlFile),
loadAction.delimiter, loadAction.tableName, ds, loadAction.refresh, loadAction.isCsv);
return new StepData(stepBuilder.tasklet(upload, transactionManager).build());
}
FlatFileItemReader<String[]> csvReader = createCsvReaderIfx(new File(substituteParamStrings(loadAction.unlFilePath)), loadAction.delimiter);
FlatFileItemReader<String[]> csvReader = createCsvReaderIfx(unlFile, loadAction.delimiter);
TypeinferringJdbcBatchItemWriter dbTableWriter =
new TypeinferringJdbcBatchItemWriter(ds, loadAction.tableName,
loadAction.refresh);
@ -656,6 +645,17 @@ public class BatchJobDescriptionAdapter { @@ -656,6 +645,17 @@ 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;
}
}

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

@ -17,6 +17,7 @@ import java.util.ArrayList; @@ -17,6 +17,7 @@ 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;
@ -29,6 +30,7 @@ import javax.xml.transform.stream.StreamSource; @@ -29,6 +30,7 @@ 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;
@ -41,6 +43,10 @@ import org.xml.sax.SAXException; @@ -41,6 +43,10 @@ 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;
@ -78,6 +84,9 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -78,6 +84,9 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
public static final int SYSTEMINFO_ID_ASTAT = 280;
public static final int SYSTEMINFO_ID_SOS = 7;
static Logger logger = LoggerFactory.getLogger(EntityJobDescriptionSource.class);
@Autowired
DbtWrapper dbtWrapper;
@Override
public List<ContainerNode> readJobs() {
@ -133,12 +142,12 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -133,12 +142,12 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
} else {
SourcesYML sourcesYML = SourcesYML.fromJson(file);
for (DBSource source : sourcesYML.getSources()) {
if(!source.getName().equals("staging")) {
if(!source.getName().startsWith("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());
ExtractAction action = ExtractAction.createFromSourceTable(table, unlFile, source.getName(), source.getSrcDatabase());
root.add(action);
}
}
@ -241,7 +250,7 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -241,7 +250,7 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
}
logger.info("etl-step: " + name);
ContainerNode container = createContainerNode(name, id, systemInfoId);
List<ActionNode> stepList = createActions(etlStep.getChildNodes(), systemInfoId, componentAbbreviation);
List<ActionNode> stepList = createActions(filterElements(etlStep.getChildNodes()), systemInfoId, componentAbbreviation);
if (stepList.size() > 0) {
container.addAll(stepList);
}
@ -378,8 +387,13 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -378,8 +387,13 @@ 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);
List<ActionNode> stepList = createActions(etlStep.getChildNodes(), systemInfoId, componentAbbreviation);
if (desc.size() > 0) {
root.setDescription(desc.get(0).getTextContent().trim());
}
List<ActionNode> stepList = createActions(childNodes, systemInfoId, componentAbbreviation);
if (stepList.size() > 0) {
root.addAll(stepList);
}
@ -476,7 +490,7 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -476,7 +490,7 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
}
continue;
}
List<ActionNode> stepList = createActions(installStep.getChildNodes(), systemInfoId, componentAbbreviation);
List<ActionNode> stepList = createActions(filterElements(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")
@ -498,7 +512,7 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -498,7 +512,7 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
scriptStepsContainer.addAll(scriptSteps);
installSteps = document.getElementsByTagName("upgrade-step");
if (installSteps.getLength() == 1) {
List<ActionNode> stepList = createActions(installSteps.item(0).getChildNodes(), systemInfoId, componentAbbreviation);
List<ActionNode> stepList = createActions(filterElements(installSteps.item(0).getChildNodes()), systemInfoId, componentAbbreviation);
if (stepList.size() > 0) {
scriptStepsContainer.addAll(stepList);
}
@ -558,14 +572,14 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -558,14 +572,14 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
container.addAll(actionNodes);
} else {
// generic container
container.addAll(createActions(installStep.getChildNodes(), systemInfoId, componentAbbreviation));
container.addAll(createActions(filterElements(installStep.getChildNodes()), systemInfoId, componentAbbreviation));
}
return container;
}
private List<ActionNode> metadaten(String componentAbbreviation, Integer systemInfoId, Node installStep) {
return createActions(installStep.getChildNodes(), systemInfoId, componentAbbreviation);
return createActions(filterElements(installStep.getChildNodes()), systemInfoId, componentAbbreviation);
}
private List<ActionNode> installMasks(String componentAbbreviation, Integer systemInfoId) throws IOException {
@ -705,9 +719,8 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -705,9 +719,8 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
return actions;
}
private List<ActionNode> createActions(NodeList steps, Integer systemInfoId, String componentAbbreviation) {
private List<ActionNode> createActions(List<Node> actionNodes, 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());
@ -748,11 +761,16 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -748,11 +761,16 @@ 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));
result.addAll(createLoadAfterExtract(componentAbbreviation, Integer.valueOf(SYSTEMINFO_ID_BIAD)));
List<ActionNode> nodes = createLoadAfterExtract(componentAbbreviation, Integer.valueOf(SYSTEMINFO_ID_BIAD));
result.addAll(nodes);
} 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));
result.add(new ExecuteActionGroupAction(systemInfoId));
if (groupAction) {
result.add(new ExecuteActionGroupAction(systemInfoId));
}
} else if("evaluate-metadata".equals(name)) {
result.add(createMetaimportJsonStep(attributes, systemInfoId));
} else if("evaluate-dbt-test".equals(name)) {
@ -776,7 +794,7 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -776,7 +794,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("Lint DBT Models using SQLFluff", "sqlfluff_action", Integer.valueOf(SYSTEMINFO_ID_BIAD), true, Integer.valueOf(0)));
nodes.add(new ExecuteSQLFluffAction(dbtProject, "Lint DBT Models using SQLFluff", "sqlfluff_action", Integer.valueOf(SYSTEMINFO_ID_BIAD), true, Integer.valueOf(0)));
return nodes;
}
@ -784,13 +802,24 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -784,13 +802,24 @@ 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);
List<String> stagingTables = DbtWrapper.getStagingTables(dbtSelect);
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();
if(stagingTables != null && !stagingTables.isEmpty()) {
extractLoad.addAll(createExtractAndLoadStep(stagingTables, systemInfoId));
extractLoad.addAll(createExtractAndLoadStep(stagingTables, systemInfoId, sourceSystem));
nodes.add(extractLoad);
}
}
@ -804,26 +833,31 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -804,26 +833,31 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
return new UpdateMetadataAction(dbtProject, systemInfoId);
}
private static List<ActionNode> createExtractAndLoadStep(List<String> tables, Integer systemInfoId) {
private static List<ActionNode> createExtractAndLoadStep(List<String> tables, Integer systemInfoId, String sourceSystem) {
List<ActionNode> result = new ArrayList<>();
SourcesYML sourcesYML = SourcesYML.fromJson(new File(PathAndFileUtils.getSourcesYML("hisinone")));
for (DBSource source : sourcesYML.getSources()) {
if(!source.getName().equals("staging")) {
List<DBSource> sources = sourcesYML.getSources();
for (DBSource source : sources) {
if(!source.getName().startsWith("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());
ExtractAction action = ExtractAction.createFromSourceTable(table, unlFile, source.getSchema(), sourceSystem);
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", table.getName(), unlFile, true);
"eduetl", source.getSchema() + "." + table.getName(), unlFile, true);
result.add(loadAction);
logger.info("\tloadtable: tabname=" + action.sourceTable + " path=" + action.unlFile);
}
@ -835,19 +869,19 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -835,19 +869,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().equals("staging")) {
if(!source.getName().startsWith("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());
ExtractAction action = ExtractAction.createFromSourceTable(table, unlFile, source.getSchema(), source.getSrcDatabase());
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", "staging." + table.getName(), unlFile, true);
"eduetl", source.getSchema() + "." + table.getName(), unlFile, true);
result.add(createTableAction);
result.add(loadAction);
@ -888,7 +922,7 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -888,7 +922,7 @@ public class EntityJobDescriptionSource implements JobDescriptionSource {
Boolean.parseBoolean(refresh),
delimiter,
Boolean.parseBoolean(header),
database.isEmpty() ? "eduetl" : database,
"eduetl",
tabName,
unloadFileAttribute
);
@ -1090,6 +1124,8 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -1090,6 +1124,8 @@ 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");
@ -1099,6 +1135,9 @@ public class EntityJobDescriptionSource implements JobDescriptionSource { @@ -1099,6 +1135,9 @@ 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);
}
}

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

@ -17,6 +17,7 @@ import java.util.Set; @@ -17,6 +17,7 @@ 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;
@ -89,7 +90,25 @@ public class TableExportService { @@ -89,7 +90,25 @@ public class TableExportService {
log.info("Exporting table '{}'", tableName);
String whereClause = null;
if (group.hasFilter()) {
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()) {
String col = tableConfig.getEffectiveFilterColumn(
group.getFilterColumn());
whereClause = quoteIdentifier(col) + " IN ("
@ -97,7 +116,7 @@ public class TableExportService { @@ -97,7 +116,7 @@ public class TableExportService {
}
Element tableEl = exportTable(conn, meta, group.getSchema(),
tableName, whereClause);
tableName, tableConfig, whereClause);
root.addContent(tableEl);
}
}
@ -107,6 +126,7 @@ public class TableExportService { @@ -107,6 +126,7 @@ 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);
@ -117,7 +137,7 @@ public class TableExportService { @@ -117,7 +137,7 @@ public class TableExportService {
Element structureEl = exportStructure(meta, schema, tableName);
tableEl.addContent(structureEl);
Element dataEl = exportData(conn, schema, tableName, whereClause);
Element dataEl = exportData(conn, schema, tableName, tableConfig, whereClause);
tableEl.addContent(dataEl);
return tableEl;
@ -175,9 +195,10 @@ public class TableExportService { @@ -175,9 +195,10 @@ 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, whereClause);
String sql = buildSelectSql(schema, tableName, tableConfig, whereClause);
int rowCount = 0;
try (Statement stmt = conn.createStatement()) {
@ -196,11 +217,16 @@ public class TableExportService { @@ -196,11 +217,16 @@ public class TableExportService {
if (value == null) {
colEl.setAttribute("nil", "true");
} else if (value instanceof byte[]) {
colEl.setText(
Base64.getEncoder().encodeToString((byte[]) value));
String encoded = Base64.getEncoder().encodeToString((byte[]) value);
colEl.setText(encoded);
colEl.setAttribute("encoding", "base64");
} else {
colEl.setText(value.toString());
String text = value.toString();
if (needsCdata(text)) {
colEl.addContent(new CDATA(text));
} else {
colEl.setText(text);
}
}
rowEl.addContent(colEl);
@ -216,8 +242,20 @@ public class TableExportService { @@ -216,8 +242,20 @@ public class TableExportService {
}
private String buildSelectSql(String schema, String tableName,
ExportTableConfig tableConfig,
String whereClause) {
StringBuilder sql = new StringBuilder("SELECT * FROM ");
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 ");
if (schema != null) {
sql.append(quoteIdentifier(schema)).append(".");
}
@ -225,9 +263,22 @@ public class TableExportService { @@ -225,9 +263,22 @@ 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,7 +78,8 @@ public class TableImportService { @@ -78,7 +78,8 @@ 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) {
if (mode == ImportMode.INSERT || mode == ImportMode.TRUNCATE_INSERT
|| mode == ImportMode.UPSERT || mode == ImportMode.UPDATE) {
List<SchemaValidationResult> failures = new ArrayList<>();
for (Element tableEl : root.getChildren("table")) {
SchemaValidationResult result = validateTableSchema(conn, tableEl);
@ -328,10 +329,19 @@ public class TableImportService { @@ -328,10 +329,19 @@ public class TableImportService {
break;
case INSERT:
break;
case UPSERT:
case UPDATE:
break;
}
if (dataEl != null) {
insertData(conn, schema, tableName, structureEl, dataEl);
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);
}
}
}
@ -508,6 +518,194 @@ public class TableImportService { @@ -508,6 +518,194 @@ 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_STRICT;
short sdfValidationMode = SurveyDataDocumentGenerator.SDF_VALIDATION_MODE_FREE;
short sdfMetaValidationMode = SurveyDataDocumentGenerator.SDF_META_VALIDATION_MODE_STRICT;
short sdfMetaValidationMode = SurveyDataDocumentGenerator.SDF_META_VALIDATION_MODE_FREE;
MetaErhebungsbeschreibung meta;

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

@ -1,375 +0,0 @@ @@ -1,375 +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 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;
}
}
}

18
src/de/superx/util/DbUtils.java

@ -0,0 +1,18 @@ @@ -0,0 +1,18 @@
package de.superx.util;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
public final class DbUtils {
private DbUtils() { }
public static boolean isModuleInstalled(DataSource dataSource, String moduleAbbreviation) {
String sql = "SELECT COUNT(*) FROM db_version WHERE his_system = ?";
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
Integer count = jdbcTemplate.queryForObject(sql, Integer.class, moduleAbbreviation);
return count != null && count.intValue() > 0;
}
}

4
src/edustore/edustore_kern.xml

@ -0,0 +1,4 @@ @@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--Automatisch generiert von Edustore durch mondrian_schema_3.xsl 01.07.2023 10:52:04-->
<Schema name="HISinOne">
</Schema>

24
superx-build/.ant-targets-build.xml

@ -1,24 +0,0 @@ @@ -1,24 +0,0 @@
clean
cleanBuildPath
cleanTestDirs
compile
compile-testclasses
compileJasperFiles
compileSuperx
create_all_modules
create_test_db_eduetl
cvs_checkout_eduetl_dbconv_sch
delete_test_db_eduetl
distApplet
distApplet2
distServer
dist_his1
his_icons_for_saiku
initTestDirs
initTimestamp
module_checksum
standalone
test
testcase
war
war_his1

3
superx-build/applet-manifest.mf

@ -1,3 +0,0 @@ @@ -1,3 +0,0 @@
Application-Name: SuperX
Main-Class: de.superx.applet.SuperX
Permissions: all-permissions

4
superx-build/applet_includes.txt

@ -1,4 +0,0 @@ @@ -1,4 +0,0 @@
de/memtext/**/*.*
de/superx/applet/*.class
de/superx/common/*.class
*.properties

86
superx-build/build.xml

@ -10,6 +10,7 @@ @@ -10,6 +10,7 @@
<property name="SUPERX_DIR" value="${BASE_DIR}/superx"/>
<property name="BUILD_PATH" value="${SUPERX_DIR}/WEB-INF/classes"/>
<property name="SRC_DIR" value="${BASE_DIR}/src" />
<property name="SRC_DIR_PATCHES" value="${BASE_DIR}/src-patches" />
<property name="SRC_DIR_TEST" value="${BASE_DIR}/test-src"/>
<property name="LIB_SUPERX_DIR" value="${SUPERX_DIR}/WEB-INF/lib"/>
<property name="LIB_WEBAPP_DIR" value="${LIB_SUPERX_DIR}"/>
@ -46,7 +47,7 @@ @@ -46,7 +47,7 @@
classname="org.codehaus.groovy.ant.Groovy"
classpathref="classpath"/>
<property name="version" value="6.0" />
<property name="version" value="5.3" />
<target name="cvs_checkout_eduetl_dbconv_sch">
@ -98,8 +99,11 @@ @@ -98,8 +99,11 @@
<exclude name="**/*.java" />
</jar>
</target>
<target name="build_dist" depends="get_git_info,create_all_modules,his_icons_for_saiku,compileSuperx">
</target>
<target name="dist_his1" depends="create_all_modules,his_icons_for_saiku,compileSuperx,test" >
<target name="dist_his1" depends="build_dist" >
<property name="dist_dir" location="../dist" />
<delete dir="${distDir}" />
<mkdir dir="${distDir}" />
@ -128,11 +132,20 @@ @@ -128,11 +132,20 @@
<fileset dir="${superxBuildBaseDir}/../superx/WEB-INF/reports"
includesfile="${superxBuildBaseDir}/included_reports_list.txt" />
</copy>
<!-- Copy SBOM -->
<!-- <copy todir="${distDir}/sbom"> -->
<!-- <fileset dir="${superxBuildBaseDir}/results/sbom" /> -->
<!-- </copy> -->
<!-- Include Java Sources -->
<copy todir="${distDir}/WEB-INF/src">
<fileset dir="${superxBuildBaseDir}/../src" />
</copy>
<!-- In der Auslieferung sollen bei leerer eduetl DB alle Komponenten installiert werden -->
<copy file="${distDir}/WEB-INF/conf/edustore/db/module/module_filesystem_initial.properties.dist" tofile="${distDir}/WEB-INF/conf/edustore/db/module/module_filesystem_initial.properties" overwrite="true" />
<defaultexcludes default="true" />
<!-- Musterdatendump integrieren -->
<get src="http://qisdev.his.de/Distributionen/BI-Musterdaten/${git.branch}/eduetl_muster_update.sql.zip" dest="${distDir}/scripts/database/eduetl_muster_update.sql.zip" />
</target>
<target name="clean">
@ -178,6 +191,13 @@ @@ -178,6 +191,13 @@
<compilerarg compiler="org.eclipse.jdt.core.JDTCompilerAdapter" />
</javac>
<javac source="17" target="17"
srcdir="${SRC_DIR_PATCHES}" destdir="${BUILD_PATH}"
listfiles="false" encoding="UTF-8" nowarn="on" debug="true" debuglevel="lines,vars,source">
<classpath refid="classpath" />
<compilerarg compiler="org.eclipse.jdt.core.JDTCompilerAdapter" />
</javac>
<antcall target="compileJasperFiles" />
</target>
@ -382,6 +402,8 @@ @@ -382,6 +402,8 @@
<copy todir="${test-target}" filtering="true">
<fileset dir="${SRC_DIR_TEST}">
<include name="**/javax.xml.xpath.XPathFactory" />
<include name="**/*.properties" />
<include name="**/*.xml" />
</fileset>
</copy>
@ -390,10 +412,9 @@ @@ -390,10 +412,9 @@
<target name="test" depends="compile-testclasses">
<junit printsummary="yes" haltonerror="false" fork="yes" forkmode="once" dir="${BASE_DIR}" showoutput="true" threads="1">
<jvmarg line="--add-opens java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/sun.net.www.protocol.jar=ALL-UNNAMED --add-opens java.base/javax.crypto=ALL-UNNAMED --add-opens java.base/java.util.stream=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.math=ALL-UNNAMED" />
<classpath location="${BUILD_PATH}" />
<!-- classpath location="${superx-classes}"/ -->
<classpath location="${test-target}"/>
<classpath>
<pathelement location="${test-target}"/>
<pathelement location="${BUILD_PATH}" />
<fileset dir="${LIB_SUPERX_DIR}">
<include name="*.jar"/>
<exclude name="superx*.jar" />
@ -413,15 +434,17 @@ @@ -413,15 +434,17 @@
</batchtest>
</junit>
</target>
<target name="compile_and_test" depends="create_all_modules,compileSuperx,test">
</target>
<!-- invokation for example: "ant testcase -DCLASS=de.superx.util.CsrfTokenInsertionTest -->
<target name="testcase" depends="compile-testclasses">
<junit printsummary="yes" haltonerror="false" fork="yes" forkmode="once" dir="${BASE_DIR}" showoutput="yes">
<jvmarg line="--add-opens java.naming/com.sun.jndi.ldap=ALL-UNNAMED --add-opens java.base/sun.nio.ch=ALL-UNNAMED --add-opens java.base/sun.net.www.protocol.jar=ALL-UNNAMED --add-opens java.base/javax.crypto=ALL-UNNAMED --add-opens java.base/java.util.stream=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.net=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.math=ALL-UNNAMED" />
<classpath location="${BUILD_PATH}" />
<!-- classpath location="${superx-classes}"/ -->
<classpath location="${test-target}" />
<classpath>
<pathelement location="${test-target}"/>
<pathelement location="${BUILD_PATH}" />
<fileset dir="${LIB_SUPERX_DIR}">
<include name="*.jar" />
<exclude name="superx*.jar" />
@ -438,7 +461,7 @@ @@ -438,7 +461,7 @@
</target>
<target name="create_all_modules">
<subant target="all">
<subant target="all_parallel">
<property name="basedir" value="${BASE_DIR}/src-modules/module/" />
<fileset dir="${BASE_DIR}/src-modules/module/" includes="create_modules_ant.xml" />
</subant>
@ -450,4 +473,49 @@ @@ -450,4 +473,49 @@
<fileset dir="${BASE_DIR}/superx/saiku/css/saiku/" includes="his_icons_for_saiku_ant.xml" />
</subant>
</target>
<target name="get_git_info">
<!-- Get the current Git branch -->
<exec executable="git" output="${basedir}/git_branch.txt">
<arg value="rev-parse" />
<arg value="--abbrev-ref" />
<arg value="HEAD" />
</exec>
<!-- Load the branch name into an Ant property -->
<loadfile property="_git.branch" srcfile="${basedir}/git_branch.txt">
<filterchain>
<striplinebreaks />
</filterchain>
</loadfile>
<if>
<equals arg1="${_git.branch}" arg2="master" />
<then>
<property name="git.branch" value="HEAD" />
</then>
<else>
<property name="git.branch" value="${_git.branch}" />
</else>
</if>
<delete file="${basedir}/git_branch.txt"/>
<!-- Get the commit hash of the current HEAD -->
<exec executable="git" output="${basedir}/git_commit.txt">
<arg value="rev-parse"/>
<arg value="HEAD"/>
</exec>
<!-- Load the commit hash into an Ant property -->
<loadfile property="git.commit.hash" srcfile="${basedir}/git_commit.txt"/>
<delete file="${basedir}/git_commit.txt"/>
<!-- Create version.properties file -->
<echo file="${SUPERX_DIR}/WEB-INF/conf/version.properties" message="git.branch=${git.branch}${line.separator}" append="false"/>
<echo file="${SUPERX_DIR}/WEB-INF/conf/version.properties" message="git.commit.hash=${git.commit.hash}" append="true"/>
<echo message="Created version.properties file with contents:"/>
<echo message="git.branch=${git.branch}"/>
<echo message="git.commit.hash=${git.commit.hash}"/>
</target>
</project>

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

Loading…
Cancel
Save