6 Stimmen

JSP-Kompilierung in einen String oder ein Bytearray im Speicher mit Tomcat/Websphere

Ich mache eine Konvertierung in ein Bild und eine PDF-Ausgabe. Ich benötige ein HTML-Eingabedokument, das von unseren Anwendungs-JSPs generiert wird. Im Wesentlichen muss ich das endgültige Ausgabeprodukt einer JSP-basierten Anwendung in eine Zeichenfolge oder einen Speicher übertragen und diese Zeichenfolge dann für eine andere Verarbeitung verwenden.

Welche Möglichkeiten gibt es, den JSP-Renderer einfach aufzurufen, um den endgültigen HTML-Inhalt zu erhalten, der normalerweise an den Benutzer ausgegeben wird? Idealerweise suche ich nach etwas, das für mehrere Anwendungsserver wie Websphere funktioniert. Aber etwas, das Tomcat-spezifisch ist, wird auch funktionieren.

Es gibt noch eine Reihe anderer Ansätze, aber ich denke, dass das Rendern der JSP (die auch Sub-JSPs enthalten kann) der beste Ansatz ist.

Optionale Pfade, von denen ich mich lieber fernhalten würde.

  1. Ich könnte eine Netzwerkanforderung an die Seite mithilfe der Socket-APIs durchführen und dann die endgültige Ausgabe lesen, die von dieser bestimmten Seite gerendert wird. Dies ist wahrscheinlich die nächstbeste Option, aber wir arbeiten mit mehreren Servern und JVMs, und die Suche nach der benötigten Seite wäre kompliziert.

  2. Verwenden Sie einen Filter, um die endgültige Seitenausgabe zu erhalten. Das ist in Ordnung, aber ich hatte schon immer Probleme mit Filtern und illegalen Statusausnahmen. Es scheint nie zu 100% so zu funktionieren, wie ich es brauche.

Das sollte eigentlich ganz einfach sein. Der JSP-Compiler ist im Wesentlichen nur eine Bibliothek zum Parsen eines JSP-Eingabedokuments und seiner Unterdokumente und zur Ausgabe von HTML-Inhalten. Ich würde diesen Prozess gerne über Java-Code aufrufen. Auf dem Server und möglicherweise als eigenständige Konsolenanwendung.

8voto

skaffman Punkte 389758

Dies ist ein ausgesprochen ärgerliches Problem, mit dem ich schon einige Male zu tun hatte und für das ich nie eine zufriedenstellende Lösung gefunden habe.

Das Grundproblem ist, dass die Servlet-API hier nicht weiterhilft, so dass man sie austricksen muss. Meine Lösung besteht darin, eine Unterklasse von HttpServletResponseWrapper zu schreiben, die die Methoden getWriter() und getOutput() außer Kraft setzt und die Daten in einem Puffer speichert. Anschließend leiten Sie Ihre Anfrage an die URI der JSP weiter, die Sie erfassen möchten, und ersetzen die ursprüngliche Antwort durch Ihre Wrapper-Antwort. Anschließend extrahieren Sie die Daten aus dem Puffer, manipulieren sie und schreiben das Endergebnis zurück in die ursprüngliche Antwort.

Hier ist mein Code, der dies tut:

public class CapturingResponseWrapper extends HttpServletResponseWrapper {

    private final OutputStream buffer;

    private PrintWriter writer;
    private ServletOutputStream outputStream;

    public CapturingResponseWrapper(HttpServletResponse response, OutputStream buffer) {
        super(response);
        this.buffer = buffer;
    }

    @Override
    public ServletOutputStream getOutputStream() {
        if (outputStream == null) {
            outputStream = new DelegatingServletOutputStream(buffer);
        }
        return outputStream;
    }

    @Override
    public PrintWriter getWriter() {
        if (writer == null) {
            writer = new PrintWriter(buffer);
        }
        return writer;
    }

    @Override
    public void flushBuffer() throws IOException {
        if (writer != null) {
            writer.flush();
        }
        if (outputStream != null) {
            outputStream.flush();
        }
    }

}

Der Code zur Verwendung kann etwa so aussehen:

HttpServletRequest originalRequest = ...
HttpServletResponse originalResponse = ...

ByteArrayOutputStream bufferStream = new ByteArrayOutputStream();
CapturingResponseWrapper responseWrapper = new CapturingResponseWrapper(originalResponse, bufferStream);

originalRequest.getRequestDispatcher("/my.jsp").forward(originalRequest, responseWrapper);

responseWrapper.flushBuffer();
byte[] buffer = bufferStream.toByteArray();
// now use the data

Es ist sehr hässlich, aber die beste Lösung, die ich gefunden habe. Falls Sie sich wundern: Die Wrapper-Antwort muss die ursprüngliche Antwort enthalten, da die Servlet-Spezifikation besagt, dass Sie bei der Weiterleitung kein völlig anderes Anfrage- oder Antwortobjekt ersetzen können, Sie müssen die Originale oder umhüllte Versionen davon verwenden.

0 Stimmen

Ich habe das Gefühl, dass alle die gleiche grauenhafte Lösung haben.

0 Stimmen

Das ist ein guter Ansatz. Ähnlich wie der "filterorientierte" Ansatz. Aber das einzige Problem dabei ist, dass es zwei Probleme gibt. 1. Sie können dies nicht als eigenständiges System ausführen. Sie können z.B. nicht tun: main() { compileJSP() } 2. Wenn Sie dies innerhalb von Filtercode verwenden, sind einige Anwendungsserver pingelig, was den Status des Requestwrappers angeht, und ich neige dazu, illegale Statusausnahmen zu erhalten und kann die JSP nie dazu bringen, einen String zu rendern (wenn ich dies tue, ist es im Grunde nie einfach). Aber ja, das ist eine gute Lösung.

0 Stimmen

An skaffman. Der Ansatz der Netzwerkanforderung ist auch nicht so schlecht. new Socket()/URL() { localhost/myjsppage ... die Ausgabe der Anfrage lesen } Aber das erfordert eine Netzwerkanfrage und wahrscheinlich mehr Code.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X