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 8, 2024
1 parent d0ac2f7 commit eb89d88
Show file tree
Hide file tree
Showing 8 changed files with 217 additions and 46 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import io.quarkus.deployment.builditem.GeneratedClassBuildItem;
import io.quarkus.deployment.builditem.HotDeploymentWatchedFileBuildItem;
import io.quarkus.deployment.builditem.IndexDependencyBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageProxyDefinitionBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBundleBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourcePatternsBuildItem;
Expand All @@ -41,6 +42,12 @@
import net.sf.jasperreports.engine.design.JRReportCompileData;
import net.sf.jasperreports.engine.util.JRLoader;

/**
* Processor class for JasperReports integration in Quarkus.
* This class handles various build steps required for JasperReports functionality,
* including feature registration, resource merging, dependency indexing,
* and reflection configuration.
*/
class JasperReportsProcessor {

private static final String FEATURE = "jasperreports";
Expand All @@ -52,11 +59,29 @@ FeatureBuildItem feature() {
return new FeatureBuildItem(FEATURE);
}

/**
* Merges the JasperReports extension properties file into the Uber JAR.
*
* 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.
*
* @return A {@link UberJarMergedResourceBuildItem} representing the merged resource
*/
@BuildStep
UberJarMergedResourceBuildItem mergeResource() {
return new UberJarMergedResourceBuildItem(EXTENSIONS_FILE);
}

/**
* Indexes transitive dependencies required for JasperReports functionality.
*
* This method adds various JasperReports modules and related dependencies
* to the build index, ensuring they are properly processed during the build.
*
* @param index The BuildProducer for IndexDependencyBuildItem, used to add
* dependencies to the build index.
*/
@BuildStep
void indexTransitiveDependencies(BuildProducer<IndexDependencyBuildItem> index) {
index.produce(new IndexDependencyBuildItem("net.sf.jasperreports", "jasperreports"));
Expand All @@ -70,6 +95,16 @@ void indexTransitiveDependencies(BuildProducer<IndexDependencyBuildItem> index)
index.produce(new IndexDependencyBuildItem("org.apache.xmlgraphics", "batik-gvt"));
}

/**
* Registers classes for reflection in the native image.
*
* This method collects various classes used by JasperReports and registers them
* for reflection. This is necessary for proper functioning in a native image context.
*
* @param reflectiveClass The BuildProducer for ReflectiveClassBuildItem, used to register
* classes for reflection.
* @param combinedIndex The CombinedIndexBuildItem containing information about indexed classes.
*/
@BuildStep
void registerForReflection(BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
CombinedIndexBuildItem combinedIndex) {
Expand All @@ -94,36 +129,41 @@ void registerForReflection(BuildProducer<ReflectiveClassBuildItem> reflectiveCla
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.components.ComponentsExtensionsRegistryFactory.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.data.xmla.XmlaDataAdapterImpl.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.engine.JasperReport.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.engine.base.ElementStore.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.engine.design.JRDesignQuery.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.engine.export.DefaultExporterFilterFactory.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.engine.export.ExporterNature.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.engine.fill.JRFillVariable.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.engine.fonts.SimpleFontFace.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.engine.part.DefaultPartComponentsBundle.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.engine.type.ColorEnum.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.engine.util.ImageUtil.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.engine.util.xml.JRXPathExecuter.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.engine.xml.ReportLoader.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.export.CsvExporterConfiguration.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.extensions.DefaultExtensionsRegistryFactory.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.governors.GovernorExtensionsRegistryFactory.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.jackson.util.JacksonUtil.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.parts.PartComponentsExtensionsRegistryFactory.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.pdf.classic.ClassicPdfProducerFactory.class.getPackageName()));
classNames.addAll(collectClassesInPackage(combinedIndex, net.sf.jasperreports.renderers.AbstractSvgDataToGraphics2DRenderer.class.getPackageName()));
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()));
classNames.addAll(collectImplementors(combinedIndex, java.time.temporal.TemporalAccessor.class.getName()));
classNames.addAll(collectImplementors(combinedIndex, java.util.Map.Entry.class.getName()));

classNames.add(byte.class.getName());
classNames.add(byte[].class.getName());
classNames.add(java.awt.color.ColorSpace.class.getName());
classNames.add(java.awt.Color.class.getName());
classNames.add(java.io.Serializable.class.getName());
classNames.add(java.lang.Boolean.class.getName());
classNames.add(java.lang.Byte.class.getName());
classNames.add(java.lang.Enum.class.getName());
classNames.add(java.lang.Byte.class.getName());
classNames.add(java.lang.Character.class.getName());
classNames.add(java.lang.Double.class.getName());
Expand Down Expand Up @@ -165,6 +205,12 @@ void registerForReflection(BuildProducer<ReflectiveClassBuildItem> reflectiveCla
.serialization().build());
}

/**
* Registers classes and packages that need to be initialized at runtime.
*
* @param runtimeInitializedPackages Producer for runtime initialized packages
* @param combinedIndex Combined index of classes in the application
*/
@BuildStep
void runtimeInitializedClasses(BuildProducer<RuntimeInitializedPackageBuildItem> runtimeInitializedPackages,
CombinedIndexBuildItem combinedIndex) {
Expand Down Expand Up @@ -212,9 +258,18 @@ void runtimeInitializedClasses(BuildProducer<RuntimeInitializedPackageBuildItem>

}

/**
* Registers various resource build items for native image compilation.
*
* @param nativeImageResourcePatterns Producer for native image resource patterns
* @param nativeImageResourceProducer Producer for native image resources
* @param resourceBundleBuildItem Producer for resource bundles
*/
@BuildStep
void registerResourceBuildItems(BuildProducer<NativeImageResourceBuildItem> nativeImageResourceProducer,
void registerResourceBuildItems(BuildProducer<NativeImageResourcePatternsBuildItem> nativeImageResourcePatterns,
BuildProducer<NativeImageResourceBuildItem> nativeImageResourceProducer,
BuildProducer<NativeImageResourceBundleBuildItem> resourceBundleBuildItem) {
// Register individual resource files
nativeImageResourceProducer.produce(new NativeImageResourceBuildItem(
"jasperreports.properties",
EXTENSIONS_FILE,
Expand All @@ -224,14 +279,31 @@ void registerResourceBuildItems(BuildProducer<NativeImageResourceBuildItem> nati
"metadata_messages-defaults.properties",
"properties-metadata.json"));

// Register resource bundles
resourceBundleBuildItem.produce(new NativeImageResourceBundleBuildItem("jasperreports_messages"));
resourceBundleBuildItem.produce(new NativeImageResourceBundleBuildItem("metadata_messages"));
resourceBundleBuildItem.produce(new NativeImageResourceBundleBuildItem("metadata_messages-defaults"));

// Register resource patterns for OOXML export
final NativeImageResourcePatternsBuildItem.Builder builder = NativeImageResourcePatternsBuildItem.builder();
builder.includeGlob("**/export/ooxml/docx/**");
builder.includeGlob("**/export/ooxml/pptx/**");
builder.includeGlob("**/export/ooxml/xlsx/**");
nativeImageResourcePatterns.produce(builder.build());
}

/**
* Registers report files for native image compilation and processes compiled report files.
*
* @param nativeImageResourcePatterns Producer for native image resource patterns
* @param additionalClasses Producer for additional generated classes
* @param reflectiveClassProducer Producer for reflective classes
* @param outputTarget The output target build item
*/
@BuildStep
void registerReports(BuildProducer<NativeImageResourcePatternsBuildItem> nativeImageResourcePatterns,
BuildProducer<GeneratedClassBuildItem> additionalClasses,
BuildProducer<ReflectiveClassBuildItem> reflectiveClassProducer,
OutputTargetBuildItem outputTarget) {
final NativeImageResourcePatternsBuildItem.Builder builder = NativeImageResourcePatternsBuildItem.builder();
builder.includeGlob("*." + ReportFileBuildItem.EXT_REPORT);
Expand Down Expand Up @@ -272,7 +344,11 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
if (StringUtils.isNotBlank(reportDataClass)) {
byte[] bytes = (byte[]) mainData.getCompileData();
Log.infof("Report Data Class: %s Size: %d", reportDataClass, bytes.length);
additionalClasses.produce(new GeneratedClassBuildItem(false, reportDataClass, bytes));

reflectiveClassProducer
.produce(ReflectiveClassBuildItem.builder(reportDataClass).constructors().methods().serialization()
.build());
additionalClasses.produce(new GeneratedClassBuildItem(true, reportDataClass, bytes));
}

} catch (JRException e) {
Expand All @@ -281,17 +357,36 @@ public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
});
}

static Path findProjectRoot(Path outputDirectory) {
Path currentPath = outputDirectory.getParent();
Log.tracef("Current Directory: %s", currentPath);
Path root = Paths.get(DEFAULT_ROOT_PATH);
if (Files.exists(currentPath.resolve(root))) {
return currentPath.resolve(root).normalize();
} else {
return outputDirectory;
/**
* Registers JasperReports proxy classes for native image compilation.
*
* This method collects classes from the CsvExporterConfiguration and PdfExporterConfiguration
* packages and registers them as proxy definitions for the native image build process.
*
* @param proxyDefinitions The producer for NativeImageProxyDefinitionBuildItem
* @param combinedIndex The combined index of classes for the build
*/
@BuildStep
void registerJasperReportsProxies(BuildProducer<NativeImageProxyDefinitionBuildItem> proxyDefinitions,
CombinedIndexBuildItem combinedIndex) {
// Register the proxy for exporters
List<String> classes = new ArrayList<>(collectClassesInPackage(combinedIndex,
net.sf.jasperreports.export.CsvExporterConfiguration.class.getPackageName()));
classes.addAll(collectClassesInPackage(combinedIndex,
net.sf.jasperreports.pdf.PdfExporterConfiguration.class.getPackageName()));
for (String proxyClassName : classes) {
proxyDefinitions.produce(new NativeImageProxyDefinitionBuildItem(proxyClassName));
}
}

/**
* Registers font resources for inclusion in the native image.
*
* This method adds a glob pattern to include DejaVu fonts in the native image.
* These fonts are commonly used by JasperReports for PDF generation.
*
* @param nativeImageResourcePatterns The producer for NativeImageResourcePatternsBuildItem
*/
@BuildStep
void registerFonts(BuildProducer<NativeImageResourcePatternsBuildItem> nativeImageResourcePatterns) {
final NativeImageResourcePatternsBuildItem.Builder builder = NativeImageResourcePatternsBuildItem.builder();
Expand Down Expand Up @@ -384,4 +479,15 @@ public List<String> collectImplementors(CombinedIndexBuildItem combinedIndex, St
Log.tracef("Implementors: %s", classes);
return new ArrayList<>(classes);
}

static Path findProjectRoot(Path outputDirectory) {
Path currentPath = outputDirectory.getParent();
Log.tracef("Current Directory: %s", currentPath);
Path root = Paths.get(DEFAULT_ROOT_PATH);
if (Files.exists(currentPath.resolve(root))) {
return currentPath.resolve(root).normalize();
} else {
return outputDirectory;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,13 @@
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ServiceProviderBuildItem;

class XalanNativeImageProcessor {
/**
* 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
Expand Down
50 changes: 26 additions & 24 deletions integration-tests/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,57 +57,59 @@
<execution>
<goals>
<goal>build</goal>
<goal>generate-code</goal>
<goal>generate-code-tests</goal>
<goal>native-image-agent</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<parameters>true</parameters>
</configuration>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<systemPropertyVariables>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
<plugin>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
<configuration>
<systemPropertyVariables>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner
</native.image.path>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</execution>
</executions>
<configuration>
<systemPropertyVariables>
<native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
<java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
<maven.home>${maven.home}</maven.home>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</build>
<profiles>
<profile>
<id>native-image</id>
<id>native</id>
<activation>
<property>
<name>native</name>
</property>
</activation>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skipTests>${native.surefire.skip}</skipTests>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<skipITs>false</skipITs>
<quarkus.native.enabled>true</quarkus.native.enabled>
<quarkus.native.additional-build-args>
--trace-object-instantiation=org.openxmlformats.schemas.wordprocessingml.x2006.main.STZoom$Enum
</quarkus.native.additional-build-args>
</properties>
</profile>
<profile>
Expand Down
Loading

0 comments on commit eb89d88

Please sign in to comment.