Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Native Compilation #99

Merged
merged 1 commit into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion deployment/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,4 @@
</plugin>
</plugins>
</build>
</project>
</project>
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;
}

}

Large diffs are not rendered by default.

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
@@ -0,0 +1,38 @@
package io.quarkiverse.jasperreports.deployment;

import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedClassBuildItem;

/**
* A build step class responsible for registering classes related to the Saxon library
* for reflection and runtime initialization during the Quarkus build process.
* <p>
* Borrowed from Camel Quarkus XSLT Saxon extension.
* </p>
*/
class SaxonProcessor {

/**
* Registers the required Saxon and XML resolver classes for reflection and runtime initialization.
*
* @param reflectiveClasses the build producer for registering classes for reflection
* @param runtimeInitializedClasses the build producer for marking classes as needing runtime initialization
*/
@BuildStep
void registerSaxon(BuildProducer<ReflectiveClassBuildItem> reflectiveClasses,
BuildProducer<RuntimeInitializedClassBuildItem> runtimeInitializedClasses) {
//@formatter:off
reflectiveClasses.produce(ReflectiveClassBuildItem.builder(
net.sf.saxon.Configuration.class,
net.sf.saxon.functions.String_1.class,
net.sf.saxon.functions.Tokenize_1.class,
net.sf.saxon.functions.StringJoin.class)
.build());
reflectiveClasses.produce(ReflectiveClassBuildItem.builder(org.xmlresolver.loaders.XmlLoader.class).build());

runtimeInitializedClasses.produce(new RuntimeInitializedClassBuildItem("org.apache.hc.client5.http.impl.auth.NTLMEngineImpl"));
melloware marked this conversation as resolved.
Show resolved Hide resolved
//@formatter:on
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package io.quarkiverse.jasperreports.deployment;

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

import io.quarkiverse.jasperreports.XalanTransformerFactory;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.deployment.builditem.nativeimage.ExcludeConfigBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBundleBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem;

/**
* Xalan native processor from Camel.
*
* @see <a href=
* "https://github.com/apache/camel-quarkus/blob/ff092e50666465f93c0ac71f607886e397e81598/extensions-support/xalan/deployment/src/main/java/org/apache/camel/quarkus/support/xalan/deployment/XalanNativeImageProcessor.java">XalanNativeImageProcessor.java</a>
*/
class XalanProcessor {
private static final String TRANSFORMER_FACTORY_SERVICE_FILE_PATH = "META-INF/services/javax.xml.transform.TransformerFactory";

@BuildStep
List<ReflectiveClassBuildItem> reflectiveClasses() {
return Arrays.asList(
ReflectiveClassBuildItem.builder(io.quarkiverse.jasperreports.XalanTransformerFactory.class.getName(),
"org.apache.xalan.xsltc.dom.ObjectFactory",
"org.apache.xalan.xsltc.dom.XSLTCDTMManager",
"org.apache.xalan.xsltc.trax.ObjectFactory",
"org.apache.xalan.xsltc.trax.TransformerFactoryImpl",
"org.apache.xml.dtm.ObjectFactory",
"org.apache.xml.dtm.ref.DTMManagerDefault",
"org.apache.xml.serializer.OutputPropertiesFactory",
"org.apache.xml.serializer.CharInfo",
"org.apache.xml.utils.FastStringBuffer").methods().build(),
ReflectiveClassBuildItem.builder("org.apache.xml.serializer.ToHTMLStream",
"org.apache.xml.serializer.ToTextStream",
"org.apache.xml.serializer.ToXMLStream").build());
}

@BuildStep
List<NativeImageResourceBundleBuildItem> resourceBundles() {
return Arrays.asList(
new NativeImageResourceBundleBuildItem("org.apache.xalan.xsltc.compiler.util.ErrorMessages"),
new NativeImageResourceBundleBuildItem("org.apache.xml.serializer.utils.SerializerMessages"),
new NativeImageResourceBundleBuildItem("org.apache.xml.serializer.XMLEntities"),
new NativeImageResourceBundleBuildItem("org.apache.xml.res.XMLErrorResources"));
}

@BuildStep
void resources(BuildProducer<NativeImageResourceBuildItem> resource) {

Stream.of(
"html",
"text",
"xml",
"unknown")
.map(s -> "org/apache/xml/serializer/output_" + s + ".properties")
.map(NativeImageResourceBuildItem::new)
.forEach(resource::produce);

}

@BuildStep
void installTransformerFactory(
BuildProducer<ExcludeConfigBuildItem> excludeConfig,
BuildProducer<ServiceProviderBuildItem> serviceProvider) {

excludeConfig
.produce(new ExcludeConfigBuildItem("xalan\\.xalan-.*\\.jar", "/" + TRANSFORMER_FACTORY_SERVICE_FILE_PATH));
serviceProvider.produce(new ServiceProviderBuildItem("javax.xml.transform.TransformerFactory",
XalanTransformerFactory.class.getName()));

}

}
8 changes: 7 additions & 1 deletion docs/modules/ROOT/pages/index.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,13 @@ For instance, with Maven, add the following dependency to your POM file:
</dependency>
----

== Docker
== Compiling Reports

Jasper does not recommend compiling `.jrxml` reports on the fly, but if necessary, it can be done only in JVM mode. This approach will NOT work in native mode, as there is no Java compiler available and the required classes for compilation are missing. The best practice is to use precompiled `.jasper` files instead.

CAUTION: Compiling `.jrxml` files will not work in Native mode!

== Native

When building native images in Docker using the standard Quarkus Docker configuration files some additional features need to be installed to support fonts. Specifically font information is not included in https://developers.redhat.com/products/rhel/ubi[Red Hat's ubi-minimal images]. To install it
simply add these lines to your `DockerFile.native` file:
Expand Down
Loading