Skip to content

Commit

Permalink
Native work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
melloware committed Oct 9, 2024
1 parent eb89d88 commit f7434a7
Show file tree
Hide file tree
Showing 6 changed files with 232 additions and 48 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package io.quarkiverse.jasperreports.deployment;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;

import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
import io.quarkus.deployment.builditem.IndexDependencyBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedPackageBuildItem;
import io.quarkus.logging.Log;

public class BatikProcessor {

@BuildStep
void indexTransitiveDependencies(BuildProducer<IndexDependencyBuildItem> index) {
index.produce(new IndexDependencyBuildItem("org.apache.xmlgraphics", "batik-bridge"));
index.produce(new IndexDependencyBuildItem("org.apache.xmlgraphics", "batik-gvt"));
}

/**
* Registers classes and packages that need to be initialized at runtime.
*
* @param runtimeInitializedPackages Producer for runtime initialized packages
*/
@BuildStep
void runtimeInitializedClasses(BuildProducer<RuntimeInitializedPackageBuildItem> runtimeInitializedPackages) {
//@formatter:off
List<String> classes = new ArrayList<>(Stream.of("javax.swing",
"javax.swing.plaf.metal",
"javax.swing.text.html",
"javax.swing.text.rtf",
"sun.datatransfer",
"sun.swing",
org.apache.batik.anim.values.AnimatableTransformListValue.class.getPackageName(),
org.apache.batik.bridge.CSSUtilities.class.getPackageName(),
org.apache.batik.css.engine.SystemColorSupport.class.getPackageName(),
org.apache.batik.dom.svg.AbstractSVGTransform.class.getPackageName(),
org.apache.batik.ext.awt.MultipleGradientPaint.class.getPackageName(),
org.apache.batik.ext.awt.image.GraphicsUtil.class.getPackageName(),
org.apache.batik.ext.awt.image.rendered.TurbulencePatternRed.class.getPackageName(),
org.apache.batik.ext.awt.image.spi.DefaultBrokenLinkProvider.class.getName(),
org.apache.batik.gvt.CompositeGraphicsNode.class.getPackageName(),
org.apache.batik.gvt.renderer.MacRenderer.class.getPackageName(),
org.apache.batik.script.jpython.JPythonInterpreter.class.getPackageName(),
org.apache.batik.script.InterpreterPool.class.getName()).toList());
//@formatter:on
Log.debugf("Runtime: %s", classes);
classes.stream()
.map(RuntimeInitializedPackageBuildItem::new)
.forEach(runtimeInitializedPackages::produce);
}

@BuildStep
void registerForReflection(BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
CombinedIndexBuildItem combinedIndex) {
final List<String> classNames = new ArrayList<>(
collectClassesInPackage(combinedIndex, org.apache.batik.gvt.font.AWTGVTFont.class.getPackageName()));

// methods and fields
reflectiveClass.produce(
ReflectiveClassBuildItem.builder(classNames.toArray(new String[0])).methods().fields().build());
}

private List<String> collectClassesInPackage(CombinedIndexBuildItem combinedIndex, String packageName) {
final List<String> classes = new ArrayList<>();
final List<DotName> packages = new ArrayList<>(combinedIndex.getIndex().getSubpackages(packageName));
packages.add(DotName.createSimple(packageName));
for (DotName aPackage : packages) {
final List<String> packageClasses = combinedIndex.getIndex()
.getClassesInPackage(aPackage)
.stream()
.map(ClassInfo::toString)
.toList();
classes.addAll(packageClasses);
}
Log.tracef("Package: %s", classes);
return classes;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class JasperReportsProcessor {

private static final String FEATURE = "jasperreports";
private static final String EXTENSIONS_FILE = "jasperreports_extension.properties";
private static final String DEFAULT_ROOT_PATH = "src";
private static final String DEFAULT_ROOT_PATH = "src/main/resources";

@BuildStep
FeatureBuildItem feature() {
Expand All @@ -61,7 +61,7 @@ FeatureBuildItem feature() {

/**
* Merges the JasperReports extension properties file into the Uber JAR.
*
* <p>
* This build step ensures that the JasperReports extension properties file
* is included in the final Uber JAR, allowing JasperReports to properly
* load its extensions at runtime.
Expand All @@ -75,7 +75,7 @@ UberJarMergedResourceBuildItem mergeResource() {

/**
* Indexes transitive dependencies required for JasperReports functionality.
*
* <p>
* This method adds various JasperReports modules and related dependencies
* to the build index, ensuring they are properly processed during the build.
*
Expand All @@ -85,19 +85,16 @@ UberJarMergedResourceBuildItem mergeResource() {
@BuildStep
void indexTransitiveDependencies(BuildProducer<IndexDependencyBuildItem> index) {
index.produce(new IndexDependencyBuildItem("net.sf.jasperreports", "jasperreports"));
index.produce(new IndexDependencyBuildItem("net.sf.jasperreports", "jasperreports-pdf"));
index.produce(new IndexDependencyBuildItem("net.sf.jasperreports", "jasperreports-data-adapters"));
index.produce(new IndexDependencyBuildItem("net.sf.jasperreports", "jasperreports-excel-poi"));
index.produce(new IndexDependencyBuildItem("net.sf.jasperreports", "jasperreports-jdt"));
index.produce(new IndexDependencyBuildItem("net.sf.jasperreports", "jasperreports-pdf"));
index.produce(new IndexDependencyBuildItem("net.sf.jasperreports", "jasperreports-xalan"));
index.produce(new IndexDependencyBuildItem("net.sf.jasperreports", "jasperreports-excel-poi"));
index.produce(new IndexDependencyBuildItem("net.sf.jasperreports", "jasperreports-data-adapters"));
index.produce(new IndexDependencyBuildItem("com.github.javaparser", "javaparser-core"));
index.produce(new IndexDependencyBuildItem("org.apache.xmlgraphics", "batik-bridge"));
index.produce(new IndexDependencyBuildItem("org.apache.xmlgraphics", "batik-gvt"));
}

/**
* Registers classes for reflection in the native image.
*
* <p>
* This method collects various classes used by JasperReports and registers them
* for reflection. This is necessary for proper functioning in a native image context.
*
Expand Down Expand Up @@ -150,7 +147,6 @@ void registerForReflection(BuildProducer<ReflectiveClassBuildItem> reflectiveCla
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.renderers.util.SvgFontProcessor.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.repo.DefaultRepositoryExtensionsRegistryFactory.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.util.JsonLoader.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, org.apache.batik.gvt.font.AWTGVTFont.class.getPackageName()));

// basic Java classes found in reports
classNames.addAll(collectImplementors(combinedIndex, java.util.Collection.class.getName()));
Expand Down Expand Up @@ -239,23 +235,12 @@ void runtimeInitializedClasses(BuildProducer<RuntimeInitializedPackageBuildItem>
net.sf.jasperreports.xalan.data.XalanXmlDataSource.class.getPackageName(),
net.sf.jasperreports.xalan.util.XalanNsAwareXPathExecuter.class.getPackageName(),
org.apache.batik.anim.values.AnimatableTransformListValue.class.getPackageName(),
org.apache.batik.bridge.CSSUtilities.class.getPackageName(),
org.apache.batik.css.engine.SystemColorSupport.class.getPackageName(),
org.apache.batik.dom.svg.AbstractSVGTransform.class.getPackageName(),
org.apache.batik.ext.awt.MultipleGradientPaint.class.getPackageName(),
org.apache.batik.ext.awt.image.GraphicsUtil.class.getPackageName(),
org.apache.batik.ext.awt.image.rendered.TurbulencePatternRed.class.getPackageName(),
org.apache.batik.ext.awt.image.spi.DefaultBrokenLinkProvider.class.getName(),
org.apache.batik.gvt.CompositeGraphicsNode.class.getPackageName(),
org.apache.batik.gvt.renderer.MacRenderer.class.getPackageName(),
org.apache.batik.script.InterpreterPool.class.getName(),
org.apache.xmlbeans.impl.schema.TypeSystemHolder.class.getName()).toList());
//@formatter:on
Log.debugf("Runtime: %s", classes);
classes.stream()
.map(RuntimeInitializedPackageBuildItem::new)
.forEach(runtimeInitializedPackages::produce);

}

/**
Expand Down Expand Up @@ -312,7 +297,7 @@ void registerReports(BuildProducer<NativeImageResourcePatternsBuildItem> nativeI
nativeImageResourcePatterns.produce(builder.build());

Path startDir = findProjectRoot(outputTarget.getOutputDirectory());
Log.tracef("Start Directory: %s", startDir);
Log.debugf("JasperReport Source Directory: %s", startDir);
Set<Path> foundFiles = new HashSet<>();

try {
Expand All @@ -323,14 +308,14 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
// Check if the file has one of the desired extensions
String filePath = file.toString();
if (filePath.endsWith(ReportFileBuildItem.EXT_COMPILED)) {
Log.info(filePath);
Log.debugf("Jasper compiled report: %s", filePath);
foundFiles.add(file);
}
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
Log.error("Error looking for report files.", e);
Log.error("Error looking for JasperReport files.", e);
}

foundFiles.forEach((file) -> {
Expand All @@ -343,7 +328,7 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
String reportDataClass = mainData.getCompileName();
if (StringUtils.isNotBlank(reportDataClass)) {
byte[] bytes = (byte[]) mainData.getCompileData();
Log.infof("Report Data Class: %s Size: %d", reportDataClass, bytes.length);
Log.debugf("JasperReport Data Class: %s Size: %d", reportDataClass, bytes.length);

reflectiveClassProducer
.produce(ReflectiveClassBuildItem.builder(reportDataClass).constructors().methods().serialization()
Expand All @@ -352,14 +337,14 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
}

} catch (JRException e) {
Log.error("Error loading report file class.", e);
Log.error("Error loading JasperReport file class.", e);
}
});
}

/**
* Registers JasperReports proxy classes for native image compilation.
*
* <p>
* This method collects classes from the CsvExporterConfiguration and PdfExporterConfiguration
* packages and registers them as proxy definitions for the native image build process.
*
Expand All @@ -370,9 +355,9 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
void registerJasperReportsProxies(BuildProducer<NativeImageProxyDefinitionBuildItem> proxyDefinitions,
CombinedIndexBuildItem combinedIndex) {
// Register the proxy for exporters
List<String> classes = new ArrayList<>(collectClassesInPackage(combinedIndex,
List<String> classes = new ArrayList<>(collectInterfacesInPackage(combinedIndex,
net.sf.jasperreports.export.CsvExporterConfiguration.class.getPackageName()));
classes.addAll(collectClassesInPackage(combinedIndex,
classes.addAll(collectInterfacesInPackage(combinedIndex,
net.sf.jasperreports.pdf.PdfExporterConfiguration.class.getPackageName()));
for (String proxyClassName : classes) {
proxyDefinitions.produce(new NativeImageProxyDefinitionBuildItem(proxyClassName));
Expand All @@ -381,7 +366,7 @@ void registerJasperReportsProxies(BuildProducer<NativeImageProxyDefinitionBuildI

/**
* Registers font resources for inclusion in the native image.
*
* <p>
* This method adds a glob pattern to include DejaVu fonts in the native image.
* These fonts are commonly used by JasperReports for PDF generation.
*
Expand Down Expand Up @@ -453,6 +438,23 @@ private List<String> collectClassesInPackage(CombinedIndexBuildItem combinedInde
return classes;
}

private List<String> collectInterfacesInPackage(CombinedIndexBuildItem combinedIndex, String packageName) {
final List<String> classes = new ArrayList<>();
final List<DotName> packages = new ArrayList<>(combinedIndex.getIndex().getSubpackages(packageName));
packages.add(DotName.createSimple(packageName));
for (DotName aPackage : packages) {
final List<String> packageClasses = combinedIndex.getIndex()
.getClassesInPackage(aPackage)
.stream()
.filter(ClassInfo::isInterface) // Filter only interfaces
.map(ClassInfo::toString)
.toList();
classes.addAll(packageClasses);
}
Log.tracef("Package: %s", classes);
return classes;
}

private List<String> collectSubclasses(CombinedIndexBuildItem combinedIndex, String className) {
List<String> classes = combinedIndex.getIndex()
.getAllKnownSubclasses(DotName.createSimple(className))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package io.quarkiverse.jasperreports.deployment;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;

import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
import io.quarkus.deployment.builditem.IndexDependencyBuildItem;
import io.quarkus.deployment.builditem.NativeImageEnableAllCharsetsBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBundleBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourcePatternsBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedPackageBuildItem;
import io.quarkus.logging.Log;

public class OpenPdfProcessor {

@BuildStep
void indexTransitiveDependencies(BuildProducer<IndexDependencyBuildItem> index) {
index.produce(new IndexDependencyBuildItem("com.github.librepdf", "openpdf"));
}

@BuildStep
NativeImageEnableAllCharsetsBuildItem enableAllCharsetsBuildItem() {
return new NativeImageEnableAllCharsetsBuildItem();
}

@BuildStep
void registerFonts(BuildProducer<NativeImageResourcePatternsBuildItem> nativeImageResourcePatterns) {
final NativeImageResourcePatternsBuildItem.Builder builder = NativeImageResourcePatternsBuildItem.builder();
builder.includeGlob("**/pdf/fonts/**");
builder.includeGlob("**/font-fallback/**");
nativeImageResourcePatterns.produce(builder.build());
}

@BuildStep
void registerResources(BuildProducer<NativeImageResourceBundleBuildItem> resourceBundleBuildItem) {
// Register resource bundles
resourceBundleBuildItem.produce(new NativeImageResourceBundleBuildItem("com/lowagie/text/error_messages/en.lng"));
resourceBundleBuildItem.produce(new NativeImageResourceBundleBuildItem("com/lowagie/text/error_messages/nl.lng"));
resourceBundleBuildItem.produce(new NativeImageResourceBundleBuildItem("com/lowagie/text/error_messages/pt.lng"));
}

/**
* Registers classes and packages that need to be initialized at runtime.
*
* @param runtimeInitializedPackages Producer for runtime initialized packages
*/
@BuildStep
void runtimeInitializedClasses(BuildProducer<RuntimeInitializedPackageBuildItem> runtimeInitializedPackages) {
//@formatter:off
List<String> classes = new ArrayList<>();
classes.add(com.lowagie.text.pdf.PdfPublicKeySecurityHandler.class.getName());

Log.debugf("Runtime: %s", classes);
classes.stream()
.map(RuntimeInitializedPackageBuildItem::new)
.forEach(runtimeInitializedPackages::produce);
//@formatter:on
}

@BuildStep
void registerForReflection(BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
CombinedIndexBuildItem combinedIndex) {
final List<String> classNames = new ArrayList<>(
collectSubclasses(combinedIndex, com.lowagie.text.Image.class.getName()));
classNames.add(com.lowagie.text.PageSize.class.getName());
classNames.add(com.lowagie.text.Utilities.class.getName());
classNames.add(com.lowagie.text.pdf.PdfName.class.getName());

// methods and fields
reflectiveClass.produce(
ReflectiveClassBuildItem.builder(classNames.toArray(new String[0])).methods().fields().build());
}

public List<String> collectSubclasses(CombinedIndexBuildItem combinedIndex, String className) {
List<String> classes = combinedIndex.getIndex()
.getAllKnownSubclasses(DotName.createSimple(className))
.stream()
.map(ClassInfo::toString)
.collect(Collectors.toList());
classes.add(className);
return classes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,22 +163,17 @@ public static boolean isRunningInContainer() {
@GET
@Path("csv")
@APIResponse(responseCode = "200", description = "Document downloaded", content = @Content(mediaType = MediaType.APPLICATION_OCTET_STREAM, schema = @Schema(type = SchemaType.STRING, format = "binary")))
public Response csv() {
public Response csv() throws JRException {
long start = System.currentTimeMillis();
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
try {

JasperPrint jasperPrint = fill();
JRCsvExporter exporter = new JRCsvExporter();
exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
exporter.setExporterOutput(new SimpleWriterExporterOutput(outputStream));
JasperPrint jasperPrint = fill();
JRCsvExporter exporter = new JRCsvExporter();
exporter.setExporterInput(new SimpleExporterInput(jasperPrint));
exporter.setExporterOutput(new SimpleWriterExporterOutput(outputStream));

exporter.exportReport();
exporter.exportReport();

} catch (JRException e) {
Log.error("CSV Error", e);
throw new RuntimeException(e);
}
Log.infof("CSV creation time : %s", (System.currentTimeMillis() - start));
final Response.ResponseBuilder response = Response.ok(outputStream.toByteArray());
response.header("Content-Disposition", "attachment;filename=jasper.csv");
Expand Down
Loading

0 comments on commit f7434a7

Please sign in to comment.