Hi!
Vorweg eine kleine Beschreibung was in diesem Zusammenhang in der Web-Applikation passiert:
1. Benutzer loggt sich ein, 2. Wählt eine Selektion aus (welche Länderdaten und hierzu spezifische Werte für Umweltzeug beinhaltet). 3. Schränkt bspw. auf ein Land (Österreich) ein und 4. Läßt sich dann ein Ergebnis anzeigen (JSP). Danach hat der Benutzer dann die Möglichkeit sich dieses Ergebnis inkl einer Karte "auszudrucken", hierzu gibt es einen Button. Das Drucken selbst sieht dann so aus dass ein PDF Dokument im selben Browser Fenster angezeigt wird (mehrere Seiten) und man dann eben das Teil drucken kann.
Und wenn man das nun mit mehreren Benutzern macht, sowie per JMeter mehrmals durchührt, kommt irgendwann ein OutOfMemory von Tomcat. Nun habe ich die Vermutung dass dieser PDF Report einfach nie freigegeben wird und im Tomcat Speicher rumliegt.
Hier der Code:
1.
2.
3.
4.
Da diese Komponente nicht von mir entwickelt wurde tue ich mir ein wenig schwer bei der Analyse - vllt. fällt es einem von euch leichter.
Seht ihr irgendwo ein potentielles Speicherleck?
Vorweg eine kleine Beschreibung was in diesem Zusammenhang in der Web-Applikation passiert:
1. Benutzer loggt sich ein, 2. Wählt eine Selektion aus (welche Länderdaten und hierzu spezifische Werte für Umweltzeug beinhaltet). 3. Schränkt bspw. auf ein Land (Österreich) ein und 4. Läßt sich dann ein Ergebnis anzeigen (JSP). Danach hat der Benutzer dann die Möglichkeit sich dieses Ergebnis inkl einer Karte "auszudrucken", hierzu gibt es einen Button. Das Drucken selbst sieht dann so aus dass ein PDF Dokument im selben Browser Fenster angezeigt wird (mehrere Seiten) und man dann eben das Teil drucken kann.
Und wenn man das nun mit mehreren Benutzern macht, sowie per JMeter mehrmals durchührt, kommt irgendwann ein OutOfMemory von Tomcat. Nun habe ich die Vermutung dass dieser PDF Report einfach nie freigegeben wird und im Tomcat Speicher rumliegt.
Hier der Code:
1.
Code:
/**
* Ein Export mit der angegebnen Logik wird durchgefuehrt.
*
* @param exportLogik Logik die für den Export verwendet werden soll.
* @param exportIn Daten, die exportiert werden.
* @param title name of the selection
* @param layers relative paths to stored gif images, can be null
*/
public synchronized void process(final String exportLogik,
final DataTable exportIn, final String title, final List<String> layers) {
this.exportOut.reset();
ConfigExportItem config = ConfigExportLocator.getInstance().getConfigItem(exportLogik);
ExportLogik logik = (ExportLogik) this.findObjectForGivenClass(config.getExportKlasse());
Calendar c = Calendar.getInstance();
String fileNameDate = c.get(Calendar.YEAR) + "_"
+ (c.get(Calendar.MONTH) + 1) + "_"
+ c.get(Calendar.DAY_OF_MONTH); // january starts with 0
config.setFileName("eMoris"
+ "-" + title.replace(' ', '_')
+ "-" + fileNameDate
+ "." + logik.getFileExtensions());
logik.init(exportIn, this.exportOut, config, title, layers);
logik.serialisierung();
logik.aufbereitung();
boolean compressed = false;
if (this.exportOut.size() > config.getSchwellenWert()) {
try {
final ByteArrayOutputStream newOutStream = new ByteArrayOutputStream();
newOutStream.write(Zip.compress(this.exportOut.toByteArray(), config.getFileName()));
this.exportOut = newOutStream;
compressed = true;
} catch (IOException e) {
e.printStackTrace();
}
}
final SendLogik sl = (SendLogik) this.findObjectForGivenClass(
config.getSendClass());
new SendExport(sl).send(this.exportOut, config, compressed);
}
2.
Code:
* @see org.pcd.technologie.util.export.core.ExportLogik#serialisierung()
*/
public final synchronized void serialisierung() {
XMLProzessor process = new XMLProzessor(
this.data,
this.xml,
this.config,
this.title,
this.layers);
boolean columnSummary = false; // old xml version with column fields at bottom of file
if (this.config.getBezeichnung().equalsIgnoreCase("SPSS")) {
columnSummary = true;
}
process.processXML(columnSummary);
//this.saveFile("xml"); // debug
String xsltFilepath = "";
if (this.config.getInfoFile() == null) {
xsltFilepath = null;
} else if (this.config.getInfoFile().startsWith("http")) {
xsltFilepath = this.config.getInfoFile();
} else if (this.config.getInfoFile().contains("C:")) {
xsltFilepath = this.config.getInfoFile();
} else {
ServletContext ctx = (ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext();
xsltFilepath = ctx.getRealPath(this.config.getInfoFile());
}
XMLExport.LOG.debug("new xsltFilepath for export is: [" + xsltFilepath + "]");
if (xsltFilepath == null) {
if (this.config.isManuallyCreated()) {
// manually means print a pdf
// which needs a xslt file to process
XMLExport.LOG.fatal("no xslt-file defined for manually created configuration"
+ "[" + this.config.getBezeichnung() + "]");
throw new IllegalArgumentException("no xslt-file defined!");
}
} else {
XSLTTransformer transformer = new XSLTTransformer();
ByteArrayInputStream inputXml = new ByteArrayInputStream(this.xml.toByteArray());
transformer.transform(inputXml, this.xml, xsltFilepath);
//this.saveFile("fo"); // debug
}
}
3.
Code:
/**
* Erzeugt das XML und speichert es im übergebenen xml outputstream.
* @param columnSummary flag if columns should be summarized at the bottom of file or not
*/
public synchronized void processXML(final boolean columnSummary) {
Document doc = new Document();
Element rootElement = new Element("DataTableResult");
Namespace xsiNS = Namespace.getNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
rootElement.addNamespaceDeclaration(xsiNS);
if (this.validierungsFile != null) {
rootElement.setAttribute("noNamespaceSchemaLocation",
this.validierungsFile, Namespace.getNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance"));
}
Attribute attribute = new Attribute("name", this.name);
rootElement.setAttribute(attribute);
doc.setRootElement(rootElement);
this.processHeader(rootElement);
this.processRows(rootElement, columnSummary);
if (columnSummary) {
this.processDataColumns(rootElement);
}
XMLOutputter out = new XMLOutputter(Format.getPrettyFormat());
try {
out.output(doc, this.xml);
// Validierung.
ByteArrayInputStream inp = new ByteArrayInputStream(this.xml.toByteArray());
this.validierung(inp);
} catch (IOException e) {
e.printStackTrace();
this.xml = null;
} catch (final XmlNotValidException e) {
System.err.println(e.getMessage());
e.getStackTrace();
this.xml = null;
}
}
4.
Code:
/**
* Converts an FO file to a PDF file using FOP.
* @param inputStream the FO file
* @param outputStream the target PDF file
* @throws IOException In case of an I/O problem
* @throws FOPException In case of a FOP problem
*/
public void convertFO2PDF(final ByteArrayInputStream inputStream,
final ByteArrayOutputStream outputStream) throws IOException, FOPException {
try {
final FopFactory fopFactory = FopFactory.newInstance();
final FOUserAgent foUserAgent = fopFactory.newFOUserAgent();
outputStream.reset();
// Construct fop with desired output format
final Fop fop = fopFactory.newFop(MimeConstants.MIME_PDF,
foUserAgent, outputStream);
// Setup JAXP using identity transformer
final TransformerFactory factory = TransformerFactory.newInstance();
// identity transformer
final Transformer transformer = factory.newTransformer();
// Setup input stream
final Source src = new StreamSource(inputStream);
// Resulting SAX events (the generated FO) must be piped through
// to FOP
final Result res = new SAXResult(fop.getDefaultHandler());
// Start XSLT transformation and FOP processing
transformer.transform(src, res);
} catch (final Exception e) {
Fo2Pdf.LOG.fatal("Problem with fo 2 pdf transformation: "
+ e.getMessage());
System.err.println("error while creating new Fo2Pdf: " + e); // FIXME delete me!
} finally {
outputStream.flush();
}
}
Da diese Komponente nicht von mir entwickelt wurde tue ich mir ein wenig schwer bei der Analyse - vllt. fällt es einem von euch leichter.
Seht ihr irgendwo ein potentielles Speicherleck?