package de.superx.bin; import static de.superx.servlet.ServletBasics.getParamChecked; import static de.superx.servlet.SxSQL_Server.DEFAULT_MANDANTEN_ID; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.net.URISyntaxException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; import java.sql.Connection; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.StringTokenizer; import java.util.logging.ConsoleHandler; import java.util.logging.LogManager; import java.util.logging.Logger; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.support.GenericApplicationContext; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; import org.springframework.mock.web.MockServletConfig; import de.memtext.baseobjects.coll.NamedIdObjectList; import de.memtext.util.GetOpts; import de.memtext.util.GetOpts.Options; import de.memtext.util.ServletHelper; import de.memtext.util.StringUtils; import de.memtext.util.TimeUtils; import de.memtext.util.TransletUtils; import de.superx.common.Maske; import de.superx.common.SxResultRow; import de.superx.common.SxResultSet; import de.superx.common.SxUser; import de.superx.sec.InputCheckRegistry; import de.superx.servlet.RequestParameter; import de.superx.servlet.ServletUtils; import de.superx.servlet.StatisticExporter; import de.superx.servlet.SuperXManager; import de.superx.servlet.SxPools; import de.superx.servlet.UserInitializer; import de.superx.servlet.XmlTransformer; import de.superx.spring.cli.config.CLIConfig; import de.superx.spring.config.BatchConfig; import de.superx.spring.config.DataJdbcConfiguration; import net.sf.jasperreports.engine.JRException; public class ExecuteMask { //Ausgabe als PDF nicht möglich? -> Korrupte Datei 30/10/2020 Jan Malte Hientzsch /** * Kommandozeilen-Interface für Ausführung von Masken und Export des Ergebnis * als Datei * * @param args * @param -logger:Pfad zu logging.properties, normalerweise * $SUPERX_DIR/db/conf * @param -tid:Nummer der Maske * @param -out:Ausgabedatei * @param -user:Benutzerkennung, unter der Maske laufen soll * @param -locale:Locale (optional) * @param -params:Parameter für die Maske, jeweils mit "&" getrennt in * einem String * * * Beispiele: HTML Druckversion (Default): java * de.superx.bin.ExecuteMask -tid:16000 -out:test.htm -user:admin * "-params:Köpfe oder Fälle ?=1=1&Stichtag=1" * -logger:/home/superx/git/superx/superx/WEB-INF/conf/edustore/db/conf/logging.properties * PDF Datei: java de.superx.bin.ExecuteMask -tid:16000 * -out:test.pdf -user:admin "-params:Köpfe oder Fälle * ?=1=1&Stichtag=1&stylesheet=tabelle_fo_pdf.xsl&contenttype=application/pdf" * -logger:/home/superx/git/superx/superx/WEB-INF/conf/edustore/db/conf/logging.properties * Excel-Datei java de.superx.bin.ExecuteMask -tid:16000 * -out:test.xls -user:admin "-params:Köpfe oder Fälle * ?=1=1&Stichtag=1&stylesheet=tabelle_xls.xsl&contenttype=application/vnd.ms-excel" * -logger:/home/superx/git/superx/superx/WEB-INF/conf/edustore/db/conf/logging.properties */ private static GenericApplicationContext APPLICATION_CONTEXT = null; private static String mandantenID = "default"; private static TimeUtils tutil = new TimeUtils(); private static String myLocale; private static String myParams = ""; private static Object adminUser; private static Integer userid; private static String myWEBINFFilePath = System.getProperty("user.dir"); private static Logger log; private static String usage = " Kommandozeilen-Interface für Ausführung von Masken und Export des Ergebnis als Datei\n" + " Parameter:\n" + " -logger:Pfad zu logging.properties, normalerweise $SUPERX_DIR/db/conf\n" + " -tid:Nummer der Maske\n" + " -out:Ausgabedatei\n" + " -user:Benutzerkennung, unter der Maske laufen soll\n" + " -locale:Locale (optional)\n" + " -params:Parameter für die Maske, jeweils mit \"&\" getrennt in einem String\n" + " \n" + " Sollte im Arbeitsverzeichnis webapps/superx/WEB-INF laufen\n" + " \n" + " Beispiele:\n" + " HTML Druckversion (Default): \n" + " java de.superx.bin.ExecuteMask -tid:16000 -out:test.htm -user:admin \"-params:Köpfe oder Fälle ?=1=1&Stichtag=1\" -logger:/home/superx/git/superx/superx/WEB-INF/conf/edustore/db/conf/logging.properties\n" + " PDF Datei:\n" + " java de.superx.bin.ExecuteMask -tid:16000 -out:test.pdf -user:admin \"-params:Köpfe oder Fälle ?=1=1&Stichtag=1&stylesheet=tabelle_fo_pdf.xsl&contenttype=application/pdf\" -logger:/home/superx/git/superx/superx/WEB-INF/conf/edustore/db/conf/logging.properties\n" + " Excel-Datei\n" + " java de.superx.bin.ExecuteMask -tid:16000 -out:test.xls -user:admin \"-params:Köpfe oder Fälle ?=1=1&Stichtag=1&stylesheet=tabelle_xls.xsl&contenttype=application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\" -logger:/home/superx/git/superx/superx/WEB-INF/conf/edustore/db/conf/logging.properties\n" + " CSV-Datei\n" + " java de.superx.bin.ExecuteMask -tid:16000 -out:test.csv -user:admin \"-params:Köpfe oder Fälle ?=1=1&Stichtag=1&contenttype=text/csv\" -logger:/home/superx/git/superx/superx/WEB-INF/conf/edustore/db/conf/logging.properties\n" + " -fopxconf (Pfad dazu - optional)"; static MockHttpServletRequest mock ; static MockHttpServletResponse mockResponse = new MockHttpServletResponse(); static MockServletConfig mockServletConfig = new MockServletConfig(); static SxUser user; private static File fopxconfFile = null; public static void main(String[] args) { try { GenericApplicationContext context = createContext(); SxPools.closeAll(); SxPools.init(); SxPools.get(mandantenID).init(); SxPools.get(mandantenID).initLogging(true, org.apache.log4j.Level.DEBUG); execute(args); } catch (Exception e) { System.err.println("Could not init SxPools!"); e.printStackTrace(System.err); System.exit(1); } System.exit(0); } public static void execute(String[] args) throws Exception { // mock muss neu intialisiert werden, sonst Fehlschläge bei mehreren Aufrufen // von ExecuteMaske in JUnitTest Batterie mittels ant mock = new MockHttpServletRequest(); GetOpts.setOpts(args); String isdrin = GetOpts.isAllRequiredOptionsPresent(new Options[]{Options.opt_logger, Options.opt_tid, Options.opt_out, Options.opt_user}); if (isdrin != null) { System.err.println(usage); return; } String userName = GetOpts.getValue(Options.opt_user); String loggingProperties = GetOpts.getValue(Options.opt_logger); try { myWEBINFFilePath= de.superx.util.PathAndFileUtils.getWebinfDirectory(); } catch (URISyntaxException e1) { // Default ist user.dir: myWEBINFFilePath=getMyWEBINFFilePath(); } // if (GetOpts.isPresent(Options.opt_fopxconf)) { fopxconfFile = new File(GetOpts.getOpt(Options.opt_fopxconf).replaceAll(Options.opt_fopxconf.stringValue()+":", "")); } else { fopxconfFile = new File(myWEBINFFilePath+ File.separator + "conf"+ File.separator +"fop.xconf"); } List mandantenNamen = new LinkedList(); if (GetOpts.isPresent(Options.opt_mandID)) { String mid = GetOpts.getValue(Options.opt_mandID); System.out.println("Opt -mandantenID present: " + mid); mandantenID = mid; } else if (mandantenID == null){ System.out.println("No opt -mandantenID present. Set to " + DEFAULT_MANDANTEN_ID); mandantenID = DEFAULT_MANDANTEN_ID; } else { System.out.println("No opt -mandantenID present. mandantenID was already set to " + mandantenID); } mandantenNamen.add(mandantenID); try ( FileInputStream ins = new FileInputStream(loggingProperties) ) { LogManager MyLogManager = java.util.logging.LogManager.getLogManager(); MyLogManager.readConfiguration(ins); log = Logger.getLogger(ExecuteMask.class.getName()); log.addHandler(new ConsoleHandler()); System.out.println("Using Loggging-Level " + log.getLevel()); } catch (FileNotFoundException e2) { System.err.println("Datei " + loggingProperties + " nicht gefunden:" + e2.toString()); return; } catch (IOException e) { System.err.println("Datei " + loggingProperties + " kann nicht gelesen werden:" + e.toString()); return; } tutil.start(); try { // SxPools.closeAll(); // SxPools.init(mandantenNamen); // SxPools.get(mandantenID).init(); // SxPools.get(mandantenID).initLogging(true); SuperXManager.initKettleEnv(APPLICATION_CONTEXT); userid = getUserID(userName, mandantenID); adminUser = getUserAdmin(userName, mandantenID); UserInitializer ui=new UserInitializer(mandantenID, userName, userid, adminUser); Connection con=SxPools.get(mandantenID).getConnection(); ui.initUser(con,null); user=ui.getUser(); user.setMandantenID(mandantenID); con.close(); } catch (Exception e) { log.severe("Fehler beim Aufbau der Connection: " + e.toString()); e.printStackTrace(); return; } String tidString = GetOpts.getValue(Options.opt_tid); String outfile = GetOpts.getValue(Options.opt_out); if (GetOpts.isPresent(Options.opt_locale)) myLocale = GetOpts.getValue(Options.opt_locale); else myLocale = de.superx.util.SqlStringUtils.getEncoding(); // TODO Do we really want to leave static field myParams // undefined if opt_params is not set?! if (GetOpts.isPresent(Options.opt_params)) myParams = GetOpts.getValue(Options.opt_params); Locale desiredLocale = new Locale(myLocale); SuperXManager.maxRows = 1000000; Maske maske = null; InputCheckRegistry.registerDefaultChecks(); try { maske = new Maske(mandantenID, user, Integer.valueOf(tidString), desiredLocale); NamedIdObjectList fields = maske.readFelderFromDb(user); setParams(mock, myParams); maske.setFieldDefaults(user, mock, true); exportTable(mock, mockResponse, maske, user, outfile, desiredLocale); System.out.println("Datei " + outfile + " für Maske " + tidString + " erzeugt in " + tutil.getSinceStart()); } catch (Exception e) { e.printStackTrace(); log.severe("Fehler beim Ausführen der Maske " + tidString + ": " + e.toString()); throw e; } } public static HttpServletResponse getHttpResponse() { return mockResponse; } /** * Ermittelt die user-id aus dem Login-Namen * * * @param userName Login Kennung * @param mandantenID_par Mandanten-ID * @return userID */ private static Integer getUserID(String userName, String mandantenID_par) { String query = "select tid from userinfo where benutzer='" + userName + "';"; Integer userID = null; SxResultSet rs = null; try { rs = ServletUtils.execute("Ermittle ID für " + userName, query, mandantenID_par); } catch (Exception e) { log.severe("Fehler beim Ermitteln der ID für " + userName + ": " + e.toString()); } for (Iterator it = rs.iterator(); it.hasNext();) { SxResultRow row = it.next(); userID = (Integer) row.get(0); } if (userID == null) { System.err.println("User " + userName + "unbekannt"); return Integer.valueOf(-1); } return userID; } /** * Ermittelt, ob ein user Admin-Rechte hat * * * @param userName Login Kennung * @param mandantenID_par Mandanten-ID * @return admin ("1" oder "0") */ private static String getUserAdmin(String userName, String mandantenID_par) { String query = "select administration from userinfo where benutzer='" + userName + "';"; String admin = null; SxResultSet rs = null; try { rs = ServletUtils.execute("Ermittle Admin-Flag für " + userName, query, mandantenID_par); } catch (Exception e) { log.severe("Fehler beim Ermitteln der Admin-Rechte für " + userName + ": " + e.toString()); } if (rs == null) { return admin; } for (Iterator it = rs.iterator(); it.hasNext();) { SxResultRow row = it.next(); admin = row.get(0).toString(); } return admin; } /** * Überführt einen String mit Parametern ("&"-getrennt) ins request Objekt) * * * @param mock Request Objekt * @param paramsString String mit Parametern (Achtung: Umlaute und Sonderzeichen * nicht encodiert) * */ private static void setParams(MockHttpServletRequest mock, String paramsString) { StringTokenizer st = new StringTokenizer(paramsString, "&"); for (; st.hasMoreTokens();) { String paramString = st.nextToken(); String paramName = paramString.substring(0, paramString.indexOf("=")); String paramVal = paramString.substring(paramString.indexOf("=") + 1); mock.addParameter(paramName, paramVal); } } /** * Führt eine Maske aus und speichert das Ergebnis in einer Datei * * * @param mock_par Request Objekt * @param maske Masken-Objekt * @param user_par User-Objekt * @param outfile Name Ausgabedatei * @param desiredLocale Locale für Ausgabedatei * */ private static void exportTable(HttpServletRequest mock_par, HttpServletResponse mockResponse_par, Maske maske, SxUser user_par, String outfile, Locale desiredLocale) { String currentXml_local = null; String method = "html"; String stylesheet = ServletHelper.getParameter(mock_par, RequestParameter.stylesheet); // Default ist html Druckversion: if (stylesheet == null || stylesheet.equals("")) stylesheet = "tabelle_html_p.xsl"; log.info("Stylesheet " + stylesheet + " wird genutzt"); String contenttype = ServletHelper.getParameter(mock_par, RequestParameter.contenttype); // Default ist html: if (contenttype == null || contenttype.equals("")) { contenttype = "text/html"; } if (contenttype.indexOf("pdf") > -1) { method = "pdf"; } if (contenttype.indexOf("xml") > -1) { method = "xml"; } if (contenttype.indexOf("spreadsheetml") > -1) { method = "xls"; } log.info("Contenttype " + contenttype + " wird erzeugt"); maske.setMaxOffset(10000000); maske.setReuseResult(false); maske.resetTableStylesheet(); maske.setSelectedTableStylesheetFileAndContenttype(stylesheet); maske.setDesiredContenttype(contenttype); log.info("Start Maskengenerierung"); try { String statistikExport = getParamChecked(mock_par, maske, RequestParameter.Statistikexport.toString()); if (!statistikExport.isEmpty()) { String ausgabeFormat = ServletHelper.getParameter(mock_par, RequestParameter.Exportformat); if (ausgabeFormat == null) { ausgabeFormat = "text/html"; } log.info("Amtliche Statistik. Ausgabeformat = " + ausgabeFormat); StatisticExporter statisticExporter = new StatisticExporter(mock_par, mockResponse_par, maske); if (statisticExporter.xmlStatisticExport(ausgabeFormat)) { return; } } currentXml_local = maske.runQuery(user_par, mock_par, null).toString(); System.out.println("Ausgabe Maskenprotokoll\n" + SuperXManager.activityLog.toString()); log.info("Maskenergebnis wird lokalisiert"); currentXml_local = SxPools.get(mandantenID).localize(currentXml_local, desiredLocale); } catch (Exception e) { e.printStackTrace(); log.severe("Fehler beim Erstellen des XML für Maske " + e.toString()); } String ausgabedatei = ""; if (stylesheet.equals("tabelle_xml.xsl")) { ausgabedatei = writeTextFile(currentXml_local, outfile); } else if (contenttype.equals("text/csv")) try { ausgabedatei = writeTextFile(maske.getCSV(mandantenID).toString(), outfile); } catch (IOException e) { System.out.println("Fehler beim Erstellen der datei " + outfile); e.printStackTrace(); } else { ausgabedatei = writeTextFile(currentXml_local, outfile + ".xml"); if (stylesheet.endsWith(".jrxml")) exportJR(mock_par, mockResponse_par, maske, user_par, outfile, stylesheet, contenttype, mandantenID, myWEBINFFilePath + "/reports/", currentXml_local); else transformFile(maske, ausgabedatei, stylesheet, contenttype, outfile, mockResponse_par, method); } log.info("Datei " + ausgabedatei + " erzeugt"); } private static void exportJR(HttpServletRequest mock_par, HttpServletResponse mockResponse_par, Maske maske, SxUser user_par, String outfile, String stylesheet, String contentType, String mandantenID_par, String reports_dir, String currentXml_par) { if ((currentXml_par.indexOf("Insgesamt 0 Sätze gefunden") < 1 // im Makro immer ausführen: || currentXml_par.indexOf("ergebnis ordnr=\"1\"") > -1) && currentXml_par.indexOf("Zeilen liegen über max. Grenze") < 1) { try { de.superx.servlet.JasperCreator jc = new de.superx.servlet.JasperCreator(mock_par, mockResponse_par, user_par, maske, stylesheet, contentType, reports_dir); if (maske.getSelectedTableStylesheetFilename().indexOf("_xmlsource") > -1) { jc.perform(mandantenID_par, currentXml_par, outfile); } else { jc.perform(mandantenID_par, maske, outfile); } } catch (JRException | IOException e) { log.severe("Fehler bei JasperReport-Erzeugung " + e.toString()); } } else log.info("Keine Daten für JR verfügbar"); } /** * Erzeugt Text-Datei, bei temp. Datei wird der absolute Pfad zurückgegeben * * * @param content Inhalt der Text-Datei * @param filename Name Ausgabedatei * @return targetfilename Aboluter Pfad zur Datei, die erzeugt wurde */ private static String writeTextFile(String content, String filename) { String targetfilename = ""; try { File temp = File.createTempFile("tabelle", ".xml"); FileWriter f1 = new FileWriter(temp); f1.write(content == null ? "" : content); f1.flush(); f1.close(); targetfilename = temp.getAbsolutePath(); Path tmpPath = Paths.get(targetfilename); if (filename != null) { Path destPath = Paths.get(filename); Path resultPath = Files.move(tmpPath, destPath, StandardCopyOption.REPLACE_EXISTING); targetfilename = resultPath.toString(); } } catch (IOException e) { log.severe("Fehler beim Erstellen der Datei " + filename + " " + e.toString()); } return targetfilename; } /** * Erzeugt Excel- oder PDF aus XML-Datei * * * @param xmlfile Pfad zur XML-Datei * @param stylesheet Pfad zur XSL-Datei * @param contenttype MIME Type * @param filename Name Ausgabedatei * */ private static void transformFile(Maske maske, String xmlfile, String stylesheet, String contenttype, String filename, HttpServletResponse mockResponse_par, String method) { TransletUtils.initFactory("net.sf.saxon.TransformerFactoryImpl", null); String stylesheetPath = myWEBINFFilePath + File.separator + ".." + File.separator + "xml" + File.separator + stylesheet; if (method.equals("xls")) { try { XmlTransformer xt = new XmlTransformer(mockServletConfig, mock, mockResponse_par, null, null); xt.setMandantenID(mandantenID); xt.setMaske(maske); String xml = StringUtils.readFile(new File(xmlfile)); if (contenttype.indexOf("spreadsheetml") > -1) { if (maske.isNewExcelExport()) { xml = XmlTransformer.stripXml(xml); } xt.createExcelStandalone(xml, stylesheetPath, filename); log.info("Datei " + filename + " erzeugt"); } } catch (Exception e) { log.severe("Fehler bei der Transformation:"); e.printStackTrace(); } } else { SxTransformer myTransformer = new SxTransformer(log); try { myTransformer.quellstring = xmlfile; myTransformer.stylesheet = stylesheetPath; myTransformer.outfile = filename; myTransformer.mandantenID=mandantenID; if (fopxconfFile != null) myTransformer.setFopxconfFile(fopxconfFile); myTransformer.transformFile(method); } catch (Exception e) { log.severe("Fehler bei der Transformation:"); e.printStackTrace(); } } } public static String getMyWEBINFFilePath() { return myWEBINFFilePath; } public static void setMyWEBINFFilePath(String myWEBINFFilePath) { ExecuteMask.myWEBINFFilePath = myWEBINFFilePath; } public static String getMandantenID() { return mandantenID; } public static void setMandantenID(String mandantenID) { ExecuteMask.mandantenID = mandantenID; } public static GenericApplicationContext createContext() { /* * https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/AnnotationConfigApplicationContext.html * quote: * "In case of multiple @Configuration classes, @Bean methods defined in later classes will override those defined in earlier classes. * This can be leveraged to deliberately override certain bean definitions via an extra @Configuration class." * - so it's alright to override some beans via "CLIConfig" */ if (APPLICATION_CONTEXT == null) { APPLICATION_CONTEXT = new AnnotationConfigApplicationContext(BatchConfig.class, DataJdbcConfiguration.class, CLIConfig.class); } return APPLICATION_CONTEXT; } }