diff --git a/.classpath b/.classpath
index 41aa8b0..35b0b7d 100644
--- a/.classpath
+++ b/.classpath
@@ -1,75 +1,93 @@
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
-
-
+
+
-
-
+
+
-
+
-
+
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
-
-
+
+
-
+
-
-
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -78,171 +96,270 @@
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
+
+
+
-
-
-
-
+
+
+
+
+
+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+
+
diff --git a/.externalToolBuilders/Ant Superx Version.launch b/.externalToolBuilders/Ant Superx Version.launch
new file mode 100644
index 0000000..a5803e9
--- /dev/null
+++ b/.externalToolBuilders/Ant Superx Version.launch
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.gitignore b/.gitignore
index 4ae600a..993ef11 100644
--- a/.gitignore
+++ b/.gitignore
@@ -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/
diff --git a/.project b/.project
index c89aa55..ee681a0 100644
--- a/.project
+++ b/.project
@@ -1,36 +1,58 @@
-
-
- eduStore_superX
-
-
-
-
-
- org.eclipse.jdt.core.javabuilder
-
-
-
-
- org.eclipse.babel.editor.rbeBuilder
-
-
-
-
-
- org.eclipse.jdt.groovy.core.groovyNature
- org.eclipse.jdt.core.javanature
- com.sysdeo.eclipse.tomcat.tomcatnature
- com.jaspersoft.studio.babel.editor.rbeNature
-
-
-
- 1459344926910
-
- 10
-
- org.eclipse.ui.ide.multiFilter
- 1.0-name-matches-false-false-dist
-
-
-
-
+
+
+ eduStore_superX
+
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.ui.externaltools.ExternalToolBuilder
+ full,incremental,
+
+
+ LaunchConfigHandle
+ <project>/.externalToolBuilders/Ant Superx Version.launch
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+ com.sysdeo.eclipse.tomcat.tomcatnature
+ com.jaspersoft.studio.babel.editor.rbeNature
+
+
+
+ 1617792163171
+
+ 10
+
+ org.eclipse.ui.ide.multiFilter
+ 1.0-name-matches-false-false-dist
+
+
+
+ 1617792163186
+
+ 6
+
+ org.eclipse.ui.ide.multiFilter
+ 1.0-name-matches-false-true-.+\.class
+
+
+
+ 1687335947806
+
+ 30
+
+ org.eclipse.core.resources.regexFilterMatcher
+ node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__
+
+
+
+
diff --git a/src-patches/com/sun/jersey/spi/container/servlet/WebComponent.java b/src-patches/com/sun/jersey/spi/container/servlet/WebComponent.java
new file mode 100644
index 0000000..1678fc2
--- /dev/null
+++ b/src-patches/com/sun/jersey/spi/container/servlet/WebComponent.java
@@ -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.
+ *
+ * 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 requestInvoker =
+ new ThreadLocalInvoker();
+
+ private final ThreadLocalInvoker responseInvoker =
+ new ThreadLocalInvoker();
+
+
+ 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.
+ *
+ * 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 headers = cResponse.getHttpHeaders();
+ for (Map.Entry> 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 the type of the constant value.
+ */
+ protected static class ContextInjectableProvider extends
+ SingletonTypeInjectableProvider {
+
+ /**
+ * 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}.
+ *
+ * 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}.
+ *
+ * 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.
+ *
+ * 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
+ * super.configure(servletConfig, rc, wa) as the first statement
+ * of that method.
+ *
+ * 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.class,
+ (HttpServletRequest) Proxy.newProxyInstance(
+ this.getClass().getClassLoader(),
+ new Class[]{HttpServletRequest.class},
+ requestInvoker)));
+
+ rc.getSingletons().add(new ContextInjectableProvider(
+ HttpServletResponse.class,
+ (HttpServletResponse) Proxy.newProxyInstance(
+ this.getClass().getClassLoader(),
+ new Class[]{HttpServletResponse.class},
+ responseInvoker)));
+
+ GenericEntity> requestThreadLocal =
+ new GenericEntity>(requestInvoker.getImmutableThreadLocal()) {
+ };
+ rc.getSingletons().add(new ContextInjectableProvider>(
+ requestThreadLocal.getType(), requestThreadLocal.getEntity()));
+
+ GenericEntity> responseThreadLocal =
+ new GenericEntity>(responseInvoker.getImmutableThreadLocal()) {
+ };
+ rc.getSingletons().add(new ContextInjectableProvider>(
+ responseThreadLocal.getType(), responseThreadLocal.getEntity()));
+
+ rc.getSingletons().add(new ContextInjectableProvider(
+ ServletContext.class,
+ wc.getServletContext()));
+
+ rc.getSingletons().add(new ContextInjectableProvider(
+ 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}.
+ *
+ * This method will be called once at initiation and for
+ * each reload of the Web application.
+ *
+ * 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.
+ *
+ * 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.
+ *
+ * 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 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.
+ *
+ * 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.
+ *
+ * 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 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 props = getInitParams(webConfig);
+ final ResourceConfig rc = createResourceConfig(webConfig, props);
+ rc.setPropertiesAndFeatures(props);
+ return rc;
+ }
+
+ private ResourceConfig createResourceConfig(WebConfig webConfig, Map 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 getInitParams(WebConfig webConfig) {
+ Map props = new HashMap();
+ 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 resourcePaths = new ArrayList();
+ 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> 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 names = request.getHeaderNames(); names.hasMoreElements();) {
+ String name = names.nextElement();
+ List valueList = new LinkedList();
+ for (Enumeration values = request.getHeaders(name); values.hasMoreElements();) {
+ valueList.add(values.nextElement());
+ }
+ rh.put(name, valueList);
+ }
+
+ return rh;
+ }
+}
diff --git a/src-patches/org/dbforms/util/MultipartRequest.java b/src-patches/org/dbforms/util/MultipartRequest.java
new file mode 100644
index 0000000..3776261
--- /dev/null
+++ b/src-patches/org/dbforms/util/MultipartRequest.java
@@ -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
+ *
+ * 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 multipart/form-data requests, the
+ * kind of requests that support file uploads. This class emulates the interface
+ * of HttpServletRequest, 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 MultipartParser instead.
+ *
+ * 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).
+ *
+ *
+ * See the included upload.war for an example of how
+ * to use this class.
+ *
+ * The full file upload specification is contained in experimental RFC 1867,
+ * available at .
+ *
+ *
+ * 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!
+ *
+ *
+ * @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
+ * multipart/form-data 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
+ * maxPostSize 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 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;
+ }
+ }
+}
diff --git a/src-patches/org/springframework/security/web/util/OnCommittedResponseWrapper.java b/src-patches/org/springframework/security/web/util/OnCommittedResponseWrapper.java
new file mode 100644
index 0000000..2418994
--- /dev/null
+++ b/src-patches/org/springframework/security/web/util/OnCommittedResponseWrapper.java
@@ -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 sendError()
+ */
+ @Override
+ public final void sendError(int sc) throws IOException {
+ doOnResponseCommitted();
+ super.sendError(sc);
+ }
+
+ /**
+ * Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked
+ * before calling the superclass sendError()
+ */
+ @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 sendRedirect()
+ */
+ @Override
+ public final void sendRedirect(String location) throws IOException {
+ doOnResponseCommitted();
+ super.sendRedirect(location);
+ }
+
+ /**
+ * Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked
+ * before calling the calling getOutputStream().close() or
+ * getOutputStream().flush()
+ */
+ @Override
+ public ServletOutputStream getOutputStream() throws IOException {
+ return new SaveContextServletOutputStream(super.getOutputStream());
+ }
+
+ /**
+ * Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked
+ * before calling the getWriter().close() or
+ * getWriter().flush()
+ */
+ @Override
+ public PrintWriter getWriter() throws IOException {
+ return new SaveContextPrintWriter(super.getWriter());
+ }
+
+ /**
+ * Makes sure {@link OnCommittedResponseWrapper#onResponseCommitted()} is invoked
+ * before calling the superclass flushBuffer()
+ */
+ @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 onResponseCommmitted() 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() + "]";
+ }
+
+ }
+
+}
diff --git a/src/de/statspez/plausi/astat/csv/Studienfach_Land.csv b/src/de/statspez/plausi/astat/csv/Studienfach_Land.csv
index de2fe7a..4ca38d4 100644
--- a/src/de/statspez/plausi/astat/csv/Studienfach_Land.csv
+++ b/src/de/statspez/plausi/astat/csv/Studienfach_Land.csv
@@ -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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
diff --git a/src/de/superx/access/DbSqlRestrictionProvider.java b/src/de/superx/access/DbSqlRestrictionProvider.java
index 912b7fe..eadc3a8 100644
--- a/src/de/superx/access/DbSqlRestrictionProvider.java
+++ b/src/de/superx/access/DbSqlRestrictionProvider.java
@@ -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
@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 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) {
diff --git a/src/de/superx/access/InlineSelectVisitor.java b/src/de/superx/access/InlineSelectVisitor.java
index bccaf70..db0a958 100644
--- a/src/de/superx/access/InlineSelectVisitor.java
+++ b/src/de/superx/access/InlineSelectVisitor.java
@@ -47,6 +47,11 @@ public class InlineSelectVisitor implements net.sf.jsqlparser.statement.select.S
Set 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());
diff --git a/src/de/superx/bianalysis/ExcelSheetBuilder.java b/src/de/superx/bianalysis/ExcelSheetBuilder.java
index 63a4882..b1a4804 100644
--- a/src/de/superx/bianalysis/ExcelSheetBuilder.java
+++ b/src/de/superx/bianalysis/ExcelSheetBuilder.java
@@ -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];
}
diff --git a/src/de/superx/bianalysis/ReportDefinition.java b/src/de/superx/bianalysis/ReportDefinition.java
index 6bc76a2..35c43f1 100644
--- a/src/de/superx/bianalysis/ReportDefinition.java
+++ b/src/de/superx/bianalysis/ReportDefinition.java
@@ -9,7 +9,7 @@ import de.superx.bianalysis.service.DbMetaAdapter;
public class ReportDefinition {
- public List factTableIds;
+ public Identifier factTableId;
public List leftDimensionAttributeIds;
public List topDimensionAttributeIds;
public List measureIds;
@@ -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 {
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;
diff --git a/src/de/superx/bianalysis/ReportMetadata.java b/src/de/superx/bianalysis/ReportMetadata.java
index 26a2e7e..131cc2d 100644
--- a/src/de/superx/bianalysis/ReportMetadata.java
+++ b/src/de/superx/bianalysis/ReportMetadata.java
@@ -135,9 +135,9 @@ public class ReportMetadata {
List leftAttributes = new ArrayList<>();
List rightAttributes = new ArrayList<>();
List filterAttributes = new ArrayList<>();
- List measures2 = dbMetaAdapter.getMeasureMetadata(allMeasures);
+ List 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 {
}
}
- 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 {
}
}
}
+
+ // 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 secondary, List measureIds) {
+ this.hideEmptyColumns = metadata.hideEmptyColumns;
this.dbMetaAdapter = metadata.dbMetaAdapter;
this.factTable = metadata.factTable;
this.sachgebiet = metadata.sachgebiet;
@@ -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 {
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;
diff --git a/src/de/superx/bianalysis/bin/BiAnalysisCLI.java b/src/de/superx/bianalysis/bin/BiAnalysisCLI.java
index a85a423..1de4bc6 100644
--- a/src/de/superx/bianalysis/bin/BiAnalysisCLI.java
+++ b/src/de/superx/bianalysis/bin/BiAnalysisCLI.java
@@ -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 {
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 {
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 {
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 {
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 {
}
+ 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 {
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;
+ }
+
}
diff --git a/src/de/superx/bianalysis/jasper/JasperReportJson.java b/src/de/superx/bianalysis/jasper/JasperReportJson.java
index 3115add..0273ed4 100644
--- a/src/de/superx/bianalysis/jasper/JasperReportJson.java
+++ b/src/de/superx/bianalysis/jasper/JasperReportJson.java
@@ -105,6 +105,9 @@ public class JasperReportJson {
for (Map cell : cells) {
Map newRow = new HashMap<>();
for (Map.Entry entry : cell.entrySet()) {
+ if(entry.getKey().endsWith("_sorting")) {
+ break;
+ }
String fieldCaptionWithoutBrackets = fieldCaptionMap.get(entry.getKey())
.replace("[", "_")
.replace("]", "_")
diff --git a/src/de/superx/bianalysis/metadata/MetadataImporter.java b/src/de/superx/bianalysis/metadata/MetadataImporter.java
index 117363b..0432f6f 100644
--- a/src/de/superx/bianalysis/metadata/MetadataImporter.java
+++ b/src/de/superx/bianalysis/metadata/MetadataImporter.java
@@ -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 {
}
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 {
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
diff --git a/src/de/superx/bianalysis/metadata/models/json/MetaDimension.java b/src/de/superx/bianalysis/metadata/models/json/MetaDimension.java
index 80a652f..1e9b498 100644
--- a/src/de/superx/bianalysis/metadata/models/json/MetaDimension.java
+++ b/src/de/superx/bianalysis/metadata/models/json/MetaDimension.java
@@ -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 {
}
+ builder = builder.withStringCol("is_restricted", String.valueOf(isRestricted));
builder = builder.withIdCol("conformed", this.conformedDimension.id);
}
diff --git a/src/de/superx/bianalysis/metadata/models/json/MetaDimensionAttribute.java b/src/de/superx/bianalysis/metadata/models/json/MetaDimensionAttribute.java
index 109aebe..0d6c48e 100644
--- a/src/de/superx/bianalysis/metadata/models/json/MetaDimensionAttribute.java
+++ b/src/de/superx/bianalysis/metadata/models/json/MetaDimensionAttribute.java
@@ -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) {
diff --git a/src/de/superx/bianalysis/models/RightParam.java b/src/de/superx/bianalysis/models/RightParam.java
index 8922f35..e161c85 100644
--- a/src/de/superx/bianalysis/models/RightParam.java
+++ b/src/de/superx/bianalysis/models/RightParam.java
@@ -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;
diff --git a/src/de/superx/bianalysis/repository/dto/AttributeDto.java b/src/de/superx/bianalysis/repository/dto/AttributeDto.java
index 49ecb8c..9426a7b 100644
--- a/src/de/superx/bianalysis/repository/dto/AttributeDto.java
+++ b/src/de/superx/bianalysis/repository/dto/AttributeDto.java
@@ -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 {
String newColumnName = caption.toLowerCase()
.replaceAll("\\s+", "_")
.replaceAll("[^a-z0-9]", "_");
- return "ind_" + newColumnName;
+ return "custom_" + newColumnName;
}
diff --git a/src/de/superx/bianalysis/rest/BiAnalysisAdminApi.java b/src/de/superx/bianalysis/rest/BiAnalysisAdminApi.java
index cd66222..d490cbb 100644
--- a/src/de/superx/bianalysis/rest/BiAnalysisAdminApi.java
+++ b/src/de/superx/bianalysis/rest/BiAnalysisAdminApi.java
@@ -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 {
}
@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 {
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 {
}
@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 {
@RequestMapping(method = RequestMethod.GET, path = "/filterDimensionAttributeValues")
public List 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);
}
diff --git a/src/de/superx/bianalysis/rest/BiAnalysisApi.java b/src/de/superx/bianalysis/rest/BiAnalysisApi.java
index 8a78eac..bd13046 100644
--- a/src/de/superx/bianalysis/rest/BiAnalysisApi.java
+++ b/src/de/superx/bianalysis/rest/BiAnalysisApi.java
@@ -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 {
@RequestParam(value = "title") Optional title,
@RequestParam(value = "sach") Optional sach,
@RequestParam(value = "facts") Optional> facts) {
- rightsService.checkCreateRights();
+ rightsService.checkCreateOrViewRights();
return dbAdapter.findStoredReports(title, sach, facts);
}
@@ -119,7 +119,7 @@ public class BiAnalysisApi extends RestControllerBase {
Optional 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 {
@RequestMapping(method = RequestMethod.POST, path = "/report")
public Result getReport(@RequestBody final ReportDefinition reportDefinition) throws Exception {
- List 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 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 {
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 {
return storedReports;
}
- private void checkCreateOrViewRightForFactTables(List 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 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);
}
diff --git a/src/de/superx/bianalysis/service/BiAnalysisManager.java b/src/de/superx/bianalysis/service/BiAnalysisManager.java
index 4a42661..2bcbac7 100644
--- a/src/de/superx/bianalysis/service/BiAnalysisManager.java
+++ b/src/de/superx/bianalysis/service/BiAnalysisManager.java
@@ -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 {
}
}
- 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 sqlStatements = new ArrayList<>();
@@ -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 metadatas = metadata.buildReportMetadatasForDrillAcross();
SQLGeneratorDrillAcross sqlGeneratorDrillAcross
@@ -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 metadatas = metadata.buildReportMetadatasForDrillAcross();
SQLGeneratorDrillAcross sqlGeneratorDrillAcross
- = new SQLGeneratorDrillAcross(definition.factTableIds.get(0), metadatas);
+ = new SQLGeneratorDrillAcross(metadata.factTable.getId(), metadatas);
sql = sqlGeneratorDrillAcross.buildFormattedSqlStatement();
} else {
diff --git a/src/de/superx/bianalysis/service/DbMetaAdapter.java b/src/de/superx/bianalysis/service/DbMetaAdapter.java
index 27ae963..c874b2c 100644
--- a/src/de/superx/bianalysis/service/DbMetaAdapter.java
+++ b/src/de/superx/bianalysis/service/DbMetaAdapter.java
@@ -1,1470 +1,1564 @@
-package de.superx.bianalysis.service;
-
-import java.sql.Array;
-import java.sql.Connection;
-import java.sql.PreparedStatement;
-import java.sql.SQLException;
-import java.text.SimpleDateFormat;
-import java.time.LocalDate;
-import java.time.format.DateTimeFormatter;
-import java.util.ArrayList;
-import java.util.Comparator;
-import java.util.Date;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Optional;
-import java.util.Set;
-import java.util.StringJoiner;
-import java.util.stream.Collectors;
-
-import javax.sql.DataSource;
-
-import org.springframework.beans.factory.InitializingBean;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.dao.DataAccessException;
-import org.springframework.dao.EmptyResultDataAccessException;
-import org.springframework.dao.IncorrectResultSizeDataAccessException;
-import org.springframework.jdbc.core.JdbcTemplate;
-import org.springframework.jdbc.core.PreparedStatementCreator;
-import org.springframework.jdbc.core.PreparedStatementSetter;
-import org.springframework.jdbc.support.rowset.SqlRowSet;
-import org.springframework.stereotype.Service;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.core.type.TypeReference;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.fasterxml.jackson.databind.node.ArrayNode;
-
-import de.superx.access.DbSqlRestrictionProvider;
-import de.superx.access.SqlAccessRestrictor;
-import de.superx.bianalysis.FaultyMetadataException;
-import de.superx.bianalysis.JoinCriteria;
-import de.superx.bianalysis.ReportDefinition;
-import de.superx.bianalysis.SecondaryFacttable;
-import de.superx.bianalysis.StoredReport;
-import de.superx.bianalysis.metadata.Identifier;
-import de.superx.bianalysis.models.Condition;
-import de.superx.bianalysis.models.CriteriaGroup;
-import de.superx.bianalysis.models.Dimension;
-import de.superx.bianalysis.models.DimensionAttribute;
-import de.superx.bianalysis.models.FactTable;
-import de.superx.bianalysis.models.Filter;
-import de.superx.bianalysis.models.Measure;
-import de.superx.bianalysis.repository.DimensionAttributeRepository;
-import de.superx.bianalysis.repository.DimensionRepository;
-import de.superx.bianalysis.repository.MeasureFilterRepository;
-import de.superx.bianalysis.repository.MeasureRepository;
-import de.superx.bianalysis.repository.StoredReportRepository;
-import de.superx.bianalysis.repository.dto.AttributeDto;
-import de.superx.bianalysis.repository.dto.DimensionDto;
-import de.superx.bianalysis.repository.dto.FactDto;
-import de.superx.bianalysis.repository.dto.MeasureDto;
-import de.superx.bianalysis.repository.dto.MeasureFilterDto;
-import de.superx.bianalysis.sqlgeneration.AttributeGroupQueryBuilder;
-import de.superx.bianalysis.sqlgeneration.SqlQuery;
-import de.superx.common.NotYetImplementedException;
-import de.superx.common.SxUser;
-import de.superx.jdbc.entity.Sachgebiet;
-import de.superx.jdbc.entity.Systeminfo;
-import de.superx.jdbc.repository.SachgebieteRepository;
-import de.superx.jdbc.repository.SysteminfoRepository;
-import de.superx.rest.model.Item;
-import de.superx.spring.service.UserService;
-
-@Service()
-public class DbMetaAdapter implements InitializingBean {
-
-// @Autowired
-// FactRepository factRepository;
-
- @Autowired
- MeasureRepository measureRepository;
-
- @Autowired
- DimensionRepository dimensionRepository;
-
- @Autowired
- DimensionAttributeRepository dimensionAttrRepo;
-
- @Autowired
- MeasureFilterRepository measureFilterRepo;
-
- @Autowired
- StoredReportRepository storedReportRepository;
-
- @Autowired
- SachgebieteRepository sachgebieterepository;
-
- @Autowired
- SysteminfoRepository systeminfoRepository;
-
- @Autowired
- UserService userService;
-
- @Autowired
- DataSource dataSource;
-
- @Autowired
- DbSqlRestrictionProvider sqlRestrictionProvider;
-
- private static final ObjectMapper MAPPER = new ObjectMapper();
-
- private JdbcTemplate jt;
-
- @Override
- public void afterPropertiesSet() throws Exception {
- this.jt = new JdbcTemplate(dataSource);
- }
-
- private List getFactDtos(String sql) {
- List factDtos = new ArrayList();
- SqlRowSet result = jt.queryForRowSet(sql);
- result.beforeFirst();
- while (result.next()) {
- FactDto fd = new FactDto();
- fd.id = new Identifier(result.getString("id"));
- fd.caption = result.getString("caption");
- fd.tablename = result.getString("tablename");
- fd.defaultRelease = result.getString("default_release");
- fd.isCustom = Boolean.valueOf(result.getBoolean("is_custom"));
- fd.sachgebiettid = Integer.valueOf(result.getInt("sachgebiettid"));
- fd.description = result.getString("description");
- factDtos.add(fd);
- }
- return factDtos;
- }
-
- public List getFactTables() throws NotYetImplementedException {
- try {
- String sql = "SELECT * FROM metadata.facttable;";
- sql = applyAccessRestrictions(sql);
- List fdtos = getFactDtos(sql);
- List factTables = fdtos
- .stream()
- .map(f -> {
- FactTable fact = new FactTable(f);
- fact.setSachgebiet(getSachgebietForFacttable(f.sachgebiettid.intValue()));
- return fact;
- })
- .collect(Collectors.toList());
- return factTables;
- } catch (Exception e) {
-
- e.printStackTrace();
- if (e.getCause().getMessage().contains("FEHLER: Relation »metadata.facttable« existiert nicht")) {
- throw new NotYetImplementedException("Bitte installieren Sie zuerst die Komponente 'BI-Analyse-Daten' und führen Sie anschließend den Konnektor aus.");
- }
- throw e;
- }
- }
-
- public String applyAccessRestrictions(String sql) {
- if ( SqlAccessRestrictor.rechteModus == SqlAccessRestrictor.Rechtekonzept.INLINE_SQL) {
- try {
- SxUser user = (SxUser)userService.currentUserDetails();
- String userName = user.getName();
- String roleName = user.getRole();
- int orgunitLid = user.getHisInOneOrgUnitLidOfRole().intValue();
SqlAccessRestrictor accessRestrictor = new SqlAccessRestrictor(this.sqlRestrictionProvider, userName, roleName, orgunitLid);
- sql = accessRestrictor.restrictSql(sql);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- return sql;
- }
-
- private boolean hasSachgebietTopicRestrictions(List facts, int tid) {
- for (Identifier factId : facts) {
- if(getFactTable(factId).getSachgebiettid() == tid) {
- return true;
- }
- }
- return false;
- }
-
-
-
- public DimensionAttribute getDimensionAttributeMetadata(Identifier id) {
- Optional optAttribute = dimensionAttrRepo.findById(id);
- if(optAttribute.isEmpty()) {
- throw new FaultyMetadataException(id, "Attribute");
- }
-
- DimensionAttribute attr = new DimensionAttribute(optAttribute.get());
- Dimension dim = getDimension(attr.getDimensionId());
-
- attr.setDimension(dim);
-
- if(attr.getAttrConformedId() != null) {
- Optional optAttributeConf = dimensionAttrRepo.findById(new Identifier(attr.getAttrConformedId()));
- if(optAttributeConf.isPresent()) {
- Dimension dimConf = getDimension(optAttributeConf.get().dimensionId);
- attr.setDimConformedId(dimConf.getId().composedId);
- }
- }
- return attr;
- }
-
- public List getDimensionAttributeMetadata(List attributeIds, Identifier factId) {
-
- if (attributeIds == null || attributeIds.size() <= 0 ) {
- return null;
- }
-
- List result = new ArrayList();
- for (Identifier id : attributeIds) {
- Optional optAttribute = dimensionAttrRepo.findById(id);
- if(optAttribute.isEmpty()) {
- throw new FaultyMetadataException(id, "Attribute");
- }
-
- DimensionAttribute attr = new DimensionAttribute(optAttribute.get());
- Dimension dim = getDimension(attr.getDimensionId());
-
- attr.setDimension(dim);
-
- if(attr.getAttrConformedId() != null) {
- Optional optAttributeConf = dimensionAttrRepo.findById(new Identifier(attr.getAttrConformedId()));
- if(optAttributeConf.isPresent()) {
- Dimension dimConf = getDimension(optAttributeConf.get().dimensionId);
- attr.setDimConformedId(dimConf.getId().composedId);
- }
- }
- result.add(attr);
- }
- return result;
- }
-
- public List getMeasureMetadata(List measureIds) {
- if (measureIds != null && measureIds.size() > 0 ) {
- List result = new ArrayList();
- for (Identifier id : measureIds) {
- Measure measure = getMeasure(id);
- FactTable factTable = getFactTable(measure.getFacttableId());
- measure.factTableName = factTable.getTablename();
- if(measure.getMeasureFilterId() != null) {
- MeasureFilterDto filter = measureFilterRepo.findById(measure.getMeasureFilterId()).get();
- if(filter.dimensionAttributeId != null) {
- DimensionAttribute attribute = getDimensionAttributeById(filter.dimensionAttributeId);
- Dimension dimension = getDimension(attribute.getDimensionId());
- measure.setMeasureFilterAttributes(filter, attribute, dimension);
- } else if(filter.factColumnFilter != null) {
- measure.setFactColumnFilter(filter);
- }
- }
- result.add(measure);
- }
- return result;
- }
- return null;
- }
-
-
- public List getDimensionsWithoutHidden(Identifier factTableId){
-
- List dimensions = new ArrayList<>();
- for (Dimension dimension : getDimensions(factTableId)) {
- if(dimension.isHidden()) {
- continue;
- }
- dimensions.add(dimension);
-
- List attributes = new ArrayList<>();
- for (DimensionAttribute attribute : dimension.getDimensionAttributes()) {
- if(!attribute.isHidden()) {
- attributes.add(attribute);
- }
- }
- dimension.setDimensionAttributes(attributes);
- }
-
- return dimensions;
- }
-
-
-
- public List getDimensions(Identifier factTableId) {
- List dimensions = new ArrayList<>();
-
- for (Dimension dimension : findByFactTableId(factTableId)) {
- dimensions.add(dimension);
-
- List attr = dimensionAttrRepo.findByDimensionId(dimension.getId());
- List attributes = attr.stream()
- .map(a -> new DimensionAttribute(a))
- .collect(Collectors.toList());
-
- attributes.sort(Comparator.comparing(
- DimensionAttribute::getPosition,
- Comparator.nullsLast(Integer::compareTo)
- ));
-
- dimension.setDimensionAttributes(attributes);
- for(DimensionAttribute a : attributes) {
- a.setDimension(dimension);
- }
- }
-
- return dimensions;
- }
-
- private Optional getFactDtoById(Identifier id) {
- String sql = String.format("SELECT * FROM metadata.facttable WHERE id = '%s';",
- id.toString());
- sql = applyAccessRestrictions(sql);
- List fdtos = getFactDtos(sql);
- Optional factOpt = Optional.ofNullable(null);
- if (fdtos.size() > 0) {
- factOpt = Optional.ofNullable(fdtos.get(0));
- }
- return factOpt;
- }
-
- public List getDimensionAttributeValues(List attributes, List factTables) {
- List result = new ArrayList();
- List tables = new ArrayList();
- for (DimensionAttribute attr : attributes) {
- if(tables.contains(attr.getTablename())) {
- continue;
- }
- tables.add(attr.getTablename());
- for(Identifier factId : factTables) {
- result.addAll(getValuesOfAttribute(attr, factId, null, null));
- }
- }
- return result;
- }
-
- public List getValuesOfAttribute(DimensionAttribute attr, Identifier factId, List filtersInDimension, Boolean hideEmptyColumns) {
- FactTable fact = getFactTable(factId);
- Dimension dim = getDimension(attr.getDimensionId());
- List values = getDimensionAttributeValues(attr, dim, fact, filtersInDimension, hideEmptyColumns);
- return values;
- }
-
- public List getFilterMetadata(List filters) {
- for (Filter filter : filters) {
- DimensionAttribute attr = getDimensionAttributeById(filter.dimensionAttributeId);
- Dimension dim = getDimension(attr.getDimensionId());
- attr.setDimension(dim);
- filter.setDimension(dim);
- filter.setDimensionAttribute(attr);
- }
- return filters;
- }
-
- public Filter setFilterMetadata(Filter filter) {
- DimensionAttribute attr = getDimensionAttributeById(filter.dimensionAttributeId);
- Dimension dim = getDimension(attr.getDimensionId());
- attr.setDimension(dim);
- filter.setDimension(dim);
- filter.setDimensionAttribute(attr);
- return filter;
- }
-
- public List getDimensionAttributeValues(
- DimensionAttribute attr, Dimension dim, FactTable factTable,
- List filtersInDimension, Boolean hideEmptyColumns) {
- String sortOrderColumnName = attr.getSortOrderColumn() != null ? attr.getSortOrderColumn(): attr.getColumnname();
-
- String templateSql =
- "SELECT DISTINCT d.%s AS value, d.%s, " +
- "array_position(array[%s], d.%s::text) " +
- "FROM presentation.%s d";
-
- /// default
- if((factTable != null && attr.getFilterSelection() == null
- || (dim != null && attr.getFilterSelection().equals("show_existing_only"))) && hideEmptyColumns == null
- || hideEmptyColumns != null && hideEmptyColumns.booleanValue() == true) {
-
- String idColumn = "id";
- if(attr.getDimIdJoinColumn() != null && !attr.getDimIdJoinColumn().isBlank()) {
- idColumn = attr.getDimIdJoinColumn();
- }
-
- templateSql += " INNER JOIN presentation." + factTable.getTablename()
- + " f ON d." + idColumn + " = f." + dim.getJoincolumn();
- }
-
- templateSql += " WHERE d.%s IS NOT NULL ";
-
- if(dim != null
- && attr.getFilterSelection() != null
- && attr.getFilterSelection().equals("show_range")) {
-
- String idColumn = "id";
- if(attr.getDimIdJoinColumn() != null && !attr.getDimIdJoinColumn().isBlank()) {
- idColumn = attr.getDimIdJoinColumn();
- }
-
- templateSql += " AND d." + idColumn + " BETWEEN ("
- + "SELECT MIN(" + dim.getJoincolumn() + ")"
- + " FROM presentation." + factTable.getTablename()
- + ") AND ("
- + "SELECT MAX(" + dim.getJoincolumn()
- + " FROM presentation." + factTable.getTablename() + ")";
- }
-
- if(filtersInDimension != null) {
- boolean first = true;
- for (Filter filter : filtersInDimension) {
- templateSql += " AND ";
- first = true;
- for( String filterValue : filter.filterValues) {
- if(first) {
- first = false;
- } else {
- templateSql += " OR ";
- }
- templateSql += " d." + filter.columnname + " = " + "'" + filterValue + "'";
- }
- }
- }
-
- // TODO: sometimes we need DESC
- templateSql += " ORDER BY 3, 2 ASC;";
- String query = String.format(
- templateSql,
- attr.getColumnname(),
- sortOrderColumnName,
- DimensionAttribute.specialValueListForSql(),
- attr.getColumnname(),
- attr.getTablename(),
- attr.getColumnname()
- );
-
- List values = jt.query(query, (rs, rowNum) -> rs.getString("value"))
- .stream().distinct().collect(Collectors.toList());
-
- return values;
- }
-
- public DimensionAttribute getDimensionAttributeMetadataById(Identifier id) {
- DimensionAttribute attr = getDimensionAttributeById(id);
- Dimension dim = getDimension(attr.getDimensionId());
- attr.setDimension(dim);
- if(dim.getConformed() != null && !dim.getConformed().isBlank()) {
- Dimension confDim = getDimension(new Identifier(dim.getConformed()));
- attr.setDimConformedId(confDim.getId().composedId);
- }
- return attr;
- }
-
- public Sachgebiet getSachgebietById(int sachgebietId) {
- return this.sachgebieterepository.findById(Integer.valueOf(sachgebietId)).get();
- }
-
- public int saveReportDefinition(StoredReport report) {
- StoredReport savedStoredReport = this.storedReportRepository.save(report);
- return savedStoredReport.id;
- }
-
- public int getColNumbers(List topDimensionAttributes, List filters) {
- JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
- int num = 1;
- for (DimensionAttribute dimensionAttribute : topDimensionAttributes) {
- if(filters != null && filters.size() > 0) {
- Filter filter = Filter.findFilterById(filters, dimensionAttribute.getId());
- if(filter != null && filter.filterValues.size() > 0) {
- num *= filter.filterValues.size();
- continue;
- }
- }
- String sql = String.format("SELECT count(DISTINCT %s) FROM presentation.%s WHERE %s IS NOT NULL", dimensionAttribute.getColumnname(), dimensionAttribute.getTablename(), dimensionAttribute.getColumnname());
- int value = jdbcTemplate.queryForObject(sql, new Object[] {}, Integer.class).intValue();
- num *= value;
- }
- return num;
- }
-
- public String getLastUpdate(int tid) {
- Optional systeminfoOpt = systeminfoRepository.findById(Integer.valueOf(tid));
- if(systeminfoOpt.isPresent()) {
- Date lastUpdate = systeminfoOpt.get().datum;
- return new SimpleDateFormat("dd.MM.yyyy hh:mm").format(lastUpdate);
- }
- return "Unknown";
- }
-
- public DataSource getDataSource() {
- return this.dataSource;
- }
-
- public int getSachgebietByFactTableId(String factTableId) {
- Optional fact = getFactDtoById(new Identifier(factTableId));
- if(fact.isPresent()) {
- return fact.get().sachgebiettid.intValue();
- }
- return -1;
- }
-
- public List getSachgebieteForReport(ReportDefinition reportDefinition) {
- List result = new ArrayList<>();
- for (Identifier factTableId : reportDefinition.factTableIds) {
- int sachgebiet = getSachgebietByFactTableId(factTableId.composedId);
- result.add(Integer.valueOf(sachgebiet));
- }
- return result;
- }
-
- public List findAllStoredReports(){
- List reports = new ArrayList<>();
- for (StoredReport report : this.storedReportRepository.findAll()) {
- StoredReport.setReportDefinitionFromJson(report);
- reports.add(report);
- }
- return reports;
- }
-
- public Optional findById(int id) {
- Optional report = storedReportRepository.findById(id);
- if(report.isPresent()) {
- StoredReport.setReportDefinitionFromJson(report.get());
- }
- return report;
- }
-
- public void deleteById(int id) {
- storedReportRepository.deleteById(id);
- }
-
- private Sachgebiet getSachgebietForFacttable(int sachgebiettid) {
- Optional sachgebiet = sachgebieterepository
- .findById(Integer.valueOf(sachgebiettid));
- if(sachgebiet.isPresent()) {
- return sachgebiet.get();
- }
- return new Sachgebiet(Integer.valueOf(-1), "Unknown", null);
- }
-
- public List getSachgebieteForFactTables(List rightParamValues) {
- List result = new ArrayList<>();
- for (String id : rightParamValues) {
- int sachgebiet = getSachgebietByFactTableId(id);
- if(sachgebiet != -1) {
- result.add(String.valueOf(sachgebiet));
- }
- }
- return result;
- }
-
- public List getAttributesOfDimension(Identifier dimId){
- List attr = dimensionAttrRepo.findByDimensionId(dimId);
- List attributes = attr.stream()
- .map(a -> new DimensionAttribute(a))
- .collect(Collectors.toList());
-
- attributes.sort(Comparator.comparing(
- DimensionAttribute::getPosition,
- Comparator.nullsLast(Integer::compareTo)
- ));
-
- return attributes;
- }
-
- public Dimension getDimension(Identifier id) {
- Optional dimOpt = dimensionRepository.findById(id);
- if(dimOpt.isEmpty()) {
- throw new FaultyMetadataException(id, "Dimension");
- }
- Dimension dimension = new Dimension(dimOpt.get());
- return dimension;
- }
-
- public Measure getMeasure(Identifier id) {
- Optional measureOpt = measureRepository.findById(id);
- if(measureOpt.isEmpty()) {
- throw new FaultyMetadataException(id, "Measure");
- }
- Measure measure = new Measure(measureOpt.get());
- return measure;
- }
-
-
- public List findByFactTableId(Identifier factTableId) {
- ObjectMapper mapper = new ObjectMapper();
- String sql = """
- WITH join_partners AS (
- SELECT
- conformed AS conf,
- json_agg(json_build_object('id', id, 'caption', caption, 'factTableId', facttable_id)) AS role_playing_dimension
- FROM metadata.dimension d WHERE conformed IS NOT NULL
- AND facttable_id != ?
- GROUP BY conformed
- )
- SELECT d.id, d.caption, d.description, d.facttable_id, d.tablename, d.joincolumn, d.alias,
- d.is_historical, d.conformed, d.id_column, d.default_release,
- d.is_hidden, d.position, d.is_custom, join_partners.role_playing_dimension
- FROM metadata.dimension d
- LEFT JOIN join_partners ON join_partners.conf = d.conformed
- WHERE d.facttable_id = ?
- ORDER BY position
- """;
-
- sql = applyAccessRestrictions(sql);
-
- List dimensions = new ArrayList<>();
-
- SqlRowSet result = jt.queryForRowSet(sql, factTableId.composedId, factTableId.composedId);
- result.beforeFirst();
- while (result.next()) {
- Dimension dimension = new Dimension();
- DimensionDto dto = new DimensionDto();
- dto.id = new Identifier(result.getString("id"));
- dto.caption = result.getString("caption");
- dto.description = result.getString("description");
- dto.factTableId = new Identifier(result.getString("facttable_id"));
- dto.tablename = result.getString("tablename");
- dto.joincolumn = result.getString("joincolumn");
- dto.alias = result.getString("alias");
- dto.isHistorical = Boolean.valueOf(result.getBoolean("is_historical"));
- dto.conformed = result.getString("conformed");
- dto.idColumn = result.getString("id_column");
- dto.defaultRelease = result.getString("default_release");
- dto.isCustom = Boolean.valueOf(result.getBoolean("is_custom"));
- dto.isHidden = Boolean.valueOf(result.getBoolean("is_hidden"));
- dto.position = Integer.valueOf(result.getInt("position"));
-
- // Handle JSON array for rolePlayingDimensions
- String jsonArray = result.getString("role_playing_dimension");
- if (jsonArray != null && !jsonArray.isEmpty() && !"null".equals(jsonArray)) {
- try {
- List rolePlayingDimensions =
- mapper.readValue(jsonArray, new TypeReference>() {});
- dimension.setRolePlayingDimensions(rolePlayingDimensions);
- } catch (Exception e) {
- // Log error or handle gracefully
- System.err.println("Failed to parse role_playing_dimension JSON: " + e.getMessage());
- dimension.setRolePlayingDimensions(new ArrayList<>());
- }
- } else {
- dimension.setRolePlayingDimensions(new ArrayList<>());
- }
-
- dimension.setDimensionDto(dto);
- dimensions.add(dimension);
- }
- return dimensions;
- }
-
-
- public List findMeasuresByFactTableId(Identifier factTableId) {
- String sql = """
- SELECT id, caption, description, facttable_id, columnname, measure_filter_id,
- aggregation_type, measure_type, default_release, is_hidden, is_custom
- FROM metadata.measure
- WHERE facttable_id = ?
- """;
-
- sql = applyAccessRestrictions(sql);
-
-
- List measureDtos = new ArrayList<>();
- SqlRowSet result = jt.queryForRowSet(sql, factTableId.composedId);
- result.beforeFirst();
- while (result.next()) {
- MeasureDto dto = new MeasureDto();
- dto.id = new Identifier(result.getString("id"));
- dto.caption = result.getString("caption");
- dto.description = result.getString("description");
- dto.factTableId = new Identifier(result.getString("facttable_id"));
- dto.columnname = result.getString("columnname");
- dto.measureFilterId = new Identifier(result.getString("measure_filter_id"));
- dto.aggregationType = result.getString("aggregation_type");
- dto.measureType = result.getString("measure_type");
- dto.defaultRelease = result.getString("default_release");
- dto.isCustom = Boolean.valueOf(result.getBoolean("is_custom"));
- dto.isHidden = Boolean.valueOf(result.getBoolean("is_hidden"));
- measureDtos.add(dto);
- }
-
- List measures = new ArrayList<>();
- for (MeasureDto measureDto : measureDtos) {
- if(measureDto.isHidden == null || !(measureDto.isHidden.booleanValue())) {
- measures.add(measureDto);
- }
- }
- return measures;
- }
-
-
- public List getDimensionAttributeByIds(List ids, List factIds) {
- String idsQuoted = ids.stream()
- .map(s -> "'" + s.composedId + "'")
- .collect(Collectors.joining(","));
- String sql =
- String.format(
- """
- SELECT id, caption, description, dimension_id, columnname, sort_order_column,
- filter_selection, hierarchical_filter, is_hidden, conformed,
- default_release, position, criteria_groups, is_custom
- FROM metadata.dimension_attribute
- WHERE id IN (%s)
- """, idsQuoted);
-
- sql = applyAccessRestrictions(sql);
-
- List attributes = new ArrayList<>();
-
- SqlRowSet result = jt.queryForRowSet(sql);
- result.beforeFirst();
- while (result.next()) {
- AttributeDto dto = new AttributeDto();
- dto.id = new Identifier(result.getString("id"));
- dto.caption = result.getString("caption");
- dto.description = result.getString("description");
- dto.dimensionId = new Identifier(result.getString("dimension_id"));
- dto.columnname = result.getString("columnname");
- dto.sortOrderColumn = result.getString("sort_order_column");
- dto.filterSelection = result.getString("filter_selection");
- dto.hierarchicalFilter = Boolean.valueOf(result.getBoolean("hierarchical_filter"));
- dto.isHidden = Boolean.valueOf(result.getBoolean("is_hidden"));
- dto.attrConformedId = result.getString("conformed");
- dto.defaultRelease = result.getString("default_release");
- dto.isCustom = Boolean.valueOf(result.getBoolean("is_custom"));
- dto.position = Integer.valueOf(result.getInt("position"));
- dto.criteriaGroups = (ArrayNode) result.getObject("criteria_groups");
- String jsonString = result.getString("criteria_groups");
- ArrayNode arrayNode = null;
- if (jsonString != null && !jsonString.trim().isEmpty()) {
- try {
- arrayNode = (ArrayNode) MAPPER.readTree(jsonString);
- } catch (JsonProcessingException e) {
- e.printStackTrace();
- }
- }
- dto.criteriaGroups = arrayNode;
- attributes.add(dto);
- }
-
-
- List finalAttributes = new ArrayList<>();
- for (AttributeDto attribute : attributes) {
- DimensionAttribute attr = new DimensionAttribute(attribute);
- Dimension dim = getDimension(attr.getDimensionId());
- attr.setDimension(dim);
- if(factIds.contains(dim.getFactTableId())) {
- finalAttributes.add(attr);
- }
- }
- return finalAttributes;
- }
-
- public DimensionAttribute getDimensionAttributeById(Identifier dimAttrId) {
- Optional optAttr = this.dimensionAttrRepo.findById(dimAttrId);
- if(optAttr.isEmpty()) {
- throw new FaultyMetadataException(dimAttrId, "Attribute");
- }
- return new DimensionAttribute(optAttr.get());
- }
-
- public List getMeasures(Identifier factTableId) {
- List measures = new ArrayList<>();
- for (MeasureDto measureDto : measureRepository.findByFactTableId(factTableId)) {
- if(!(measureDto.isHidden.booleanValue())) {
- measures.add(new Measure(measureDto));
- }
- }
- return measures;
- }
-
- public FactTable getFactTable(Identifier factTableId) {
- Optional optFact = getFactDtoById(factTableId);
- if(optFact.isEmpty()) {
- throw new FaultyMetadataException(factTableId, "Fact");
- }
- return new FactTable(optFact.get());
- }
-
- public static List getDefaultReleaseValues(DataSource dataSource) {
- JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
- String sql =
- "SELECT default_release FROM metadata.facttable UNION " +
- "SELECT default_release FROM metadata.dimension UNION " +
- "SELECT default_release FROM metadata.dimension_attribute UNION " +
- "SELECT default_release FROM metadata.measure UNION " +
- "SELECT default_release FROM metadata.measure_filter";
-
- List results = jdbcTemplate.queryForList(sql, String.class);
- List items = new ArrayList();
- for (String value : results) {
- if (value != null && !value.isEmpty()) {
- items.add(new Item(value, value));
- }
- }
- return items;
- }
-
-
- public void saveAllAttributeGroups() {
- List groups = this.dimensionAttrRepo.findByNonEmptyCriteriaGroup();
- for (AttributeDto attribute : groups) {
- saveAttributeGroupColumn(attribute, false);
- saveAttributeGroupColumn(attribute, true);
- }
- }
-
- public void saveAttributeGroupColumn(AttributeDto attribute, boolean isSortOrder) {
-
- // Attribute and Dimension for which the attribute group was created
- DimensionDto dimension = this.dimensionRepository.findByIdOrThrow(attribute.dimensionId);
- String columnname = attribute.columnname;
- if(isSortOrder) {
- if(attribute.sortOrderColumn != null && !attribute.sortOrderColumn.isBlank()) {
- columnname = attribute.sortOrderColumn;
- } else {
- columnname += "_sort_order";
- }
- }
-
- // Dimensions can define a custom 'view' in JSON metadata (*_metadata.json),
- // overriding the conformed dimension table used e.g. in fact table joins.
- // Collect all such tables here to ensure the new column is added universally.
- List allTableNames = this.dimensionRepository.getAllTableNamesById(attribute.dimensionId.composedId);
-
- for (String table : allTableNames) {
- String tableName = "presentation." + table;
- String columnSql = String.format("""
- ALTER TABLE %s DROP COLUMN IF EXISTS %s;
- ALTER TABLE %s ADD COLUMN %s text;
- """, tableName, columnname, tableName, columnname);
-
- List groups = attribute.readCriteriaGroups();
- SqlQuery query = AttributeGroupQueryBuilder.buildColumnQuery(groups, tableName, isSortOrder, columnname);
-
- jt.execute(columnSql);
- jt.update(query.getSql(), query.getParams(), query.getTypes());
- }
-
- }
-
- private static List getAllColumns(List groups) {
- Set columns = new LinkedHashSet<>();
- if (groups != null) {
- for (CriteriaGroup group : groups) {
- if (!group.isElse) {
- collectColumns(group.conditions, columns);
- }
- }
- }
- return new ArrayList<>(columns);
- }
-
- private static void collectColumns(List