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

Barcode4J native support #256

Merged
merged 1 commit into from
Oct 19, 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package io.quarkiverse.primefaces.deployment;

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

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

import io.quarkus.deployment.builditem.CombinedIndexBuildItem;
import io.quarkus.logging.Log;

abstract class AbstractJandexProcessor {

protected 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.debugf("Package Classes: %s", classes);
return classes;
}

protected 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.debugf("Package Interfaces: %s", classes);
return classes;
}

protected 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);
Log.debugf("Subclasses: %s", classes);
return classes;
}

protected List<String> collectImplementors(CombinedIndexBuildItem combinedIndex, String className) {
Set<String> classes = combinedIndex.getIndex()
.getAllKnownImplementors(DotName.createSimple(className))
.stream()
.map(ClassInfo::toString)
.collect(Collectors.toCollection(HashSet::new));
classes.add(className);
Set<String> subclasses = new HashSet<>();
for (String implementationClass : classes) {
subclasses.addAll(collectSubclasses(combinedIndex, implementationClass));
}
classes.addAll(subclasses);
Log.debugf("Implementors: %s", classes);
return new ArrayList<>(classes);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package io.quarkiverse.primefaces.deployment;

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

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.NativeImageResourceBuildItem;
import io.quarkus.deployment.builditem.nativeimage.NativeImageResourceBundleBuildItem;
import io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem;
import io.quarkus.deployment.builditem.nativeimage.RuntimeInitializedPackageBuildItem;
import io.quarkus.logging.Log;

/**
* Register Barcode4J library.
*/
public class Barcode4JProcessor extends AbstractJandexProcessor {

@BuildStep
void indexTransitiveDependencies(BuildProducer<IndexDependencyBuildItem> index) {
index.produce(new IndexDependencyBuildItem("org.primefaces.extensions", "barcode4j-light"));
}

@BuildStep
void runtimeBarcodeInitializedClasses(BuildProducer<RuntimeInitializedPackageBuildItem> runtimeInitializedPackages) {
//@formatter:off
List<String> classes = new ArrayList<>(
Stream.of(org.krysalis.barcode4j.output.bitmap.BitmapEncoderRegistry.class.getName(),
"javax.swing.plaf.metal",
"javax.swing.text.html",
"javax.swing.text.rtf",
"sun.datatransfer",
"sun.swing"
).toList());
//@formatter:on
Log.debugf("Barcode4J Runtime: %s", classes);
classes.stream()
.map(RuntimeInitializedPackageBuildItem::new)
.forEach(runtimeInitializedPackages::produce);
}

@BuildStep
void registerBarcodeForReflection(BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
CombinedIndexBuildItem combinedIndex) {
final List<String> classNames = new ArrayList<>();
classNames.add("javax.imageio.ImageIO");
classNames.add(org.krysalis.barcode4j.output.bitmap.ImageIOBitmapEncoder.class.getName());

Log.debugf("Barcode4J Reflection: %s", classNames);
// methods and fields
reflectiveClass.produce(
ReflectiveClassBuildItem.builder(classNames.toArray(new String[0])).methods().fields().serialization().build());
}

@BuildStep
void registerBarcodeResources(BuildProducer<NativeImageResourceBuildItem> nativeImageResourceProducer,
BuildProducer<NativeImageResourceBundleBuildItem> resourceBundleBuildItem) {
// Register individual resource files
nativeImageResourceProducer.produce(new NativeImageResourceBuildItem(
"org/krysalis/barcode4j/impl/fourstate/usps-4bc-bar-to-character-table.csv"));

// Register resource bundles
resourceBundleBuildItem
.produce(new NativeImageResourceBundleBuildItem("org.krysalis.barcode4j.impl.code128.EAN128AIs"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

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

import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.primefaces.model.file.CommonsUploadedFile;
import org.primefaces.util.Constants;
Expand All @@ -24,13 +22,11 @@
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.pkg.steps.NativeOrNativeSourcesBuild;
import io.quarkus.logging.Log;
import io.quarkus.primefaces.runtime.PrimeFacesFeature;
import io.quarkus.primefaces.runtime.PrimeFacesRecorder;
import io.quarkus.undertow.deployment.ServletInitParamBuildItem;

class PrimefacesProcessor {
class PrimeFacesProcessor extends AbstractJandexProcessor {

private static final String FEATURE = "primefaces";

Expand All @@ -39,7 +35,7 @@ FeatureBuildItem feature() {
return new FeatureBuildItem(FEATURE);
}

@BuildStep(onlyIf = NativeOrNativeSourcesBuild.class)
@BuildStep
NativeImageFeatureBuildItem nativeImageFeature() {
return new NativeImageFeatureBuildItem(PrimeFacesFeature.class);
}
Expand All @@ -50,7 +46,7 @@ void indexTransitiveDependencies(BuildProducer<IndexDependencyBuildItem> index)
index.produce(new IndexDependencyBuildItem("io.nayuki", "qrcodegen"));
index.produce(new IndexDependencyBuildItem("org.overviewproject", "mime-types"));
index.produce(new IndexDependencyBuildItem("org.primefaces", "primefaces"));
index.produce(new IndexDependencyBuildItem("org.primefaces.extensions", "barcode4j-light"));

index.produce(new IndexDependencyBuildItem("software.xdev", "chartjs-java-model"));
}

Expand Down Expand Up @@ -102,12 +98,6 @@ void substrateResourceBuildItems(BuildProducer<NativeImageResourceBuildItem> nat

// mime types
resourceBundleBuildItem.produce(new NativeImageResourceBundleBuildItem("mime.cache"));

// barcodes
nativeImageResourceProducer.produce(new NativeImageResourceBuildItem(
"org/krysalis/barcode4j/impl/fourstate/usps-4bc-bar-to-character-table.csv"));
resourceBundleBuildItem
.produce(new NativeImageResourceBundleBuildItem("org.krysalis.barcode4j.impl.code128.EAN128AIs"));
}

@BuildStep
Expand Down Expand Up @@ -154,10 +144,6 @@ void registerForReflection(PrimeFacesRecorder recorder, BuildProducer<Reflective
classNames.add(org.primefaces.component.organigram.OrganigramHelper.class.getName());
classNames.addAll(collectImplementors(combinedIndex, PropertyDescriptorResolver.class.getName()));

// Barcode
classNames.add("javax.imageio.ImageIO");
classNames.add(org.krysalis.barcode4j.output.bitmap.ImageIOBitmapEncoder.class.getName());

// Chart XDev models
classNames.addAll(collectClassesInPackage(combinedIndex, "software.xdev.chartjs.model"));

Expand All @@ -167,8 +153,7 @@ void registerForReflection(PrimeFacesRecorder recorder, BuildProducer<Reflective

// method reflection
reflectiveClass.produce(
ReflectiveClassBuildItem.builder(classNames.toArray(new String[0])).methods(true)
.fields(true).build());
ReflectiveClassBuildItem.builder(classNames.toArray(new String[0])).methods().fields().build());

// neither
reflectiveClass.produce(
Expand Down Expand Up @@ -202,41 +187,4 @@ void enforceInitParams(BuildProducer<ServletInitParamBuildItem> initParam) {
// only native uploading is supported no need for Commons FileUpload
initParam.produce(new ServletInitParamBuildItem(Constants.ContextParams.UPLOADER, "native"));
}

public 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.debugf("collectClassesInPackage: %s", classes);
return classes;
}

private 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;
}

public List<String> collectImplementors(CombinedIndexBuildItem combinedIndex, String className) {
List<String> classes = combinedIndex.getIndex()
.getAllKnownImplementors(DotName.createSimple(className))
.stream()
.map(ClassInfo::toString)
.collect(Collectors.toList());
classes.add(className);
Log.debugf("collectImplementors: %s", classes);
return classes;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@

import org.graalvm.nativeimage.hosted.Feature;
import org.graalvm.nativeimage.hosted.RuntimeClassInitialization;
import org.krysalis.barcode4j.output.bitmap.BitmapEncoderRegistry;

public class PrimeFacesFeature implements Feature {
private final static String REASON = "PrimeFaces runtime initialization";

@Override
public void afterRegistration(AfterRegistrationAccess access) {
// Barcode component is optional but must register this for native mode since it uses AWT
RuntimeClassInitialization.initializeAtRunTime(BitmapEncoderRegistry.class.getName());
// XDEV Charts.js uses SecureRandom
RuntimeClassInitialization.initializeAtRunTime("software.xdev.chartjs.model.color.Color");
RuntimeClassInitialization.initializeAtRunTime("software.xdev.chartjs.model.color.HSLAColor");
Expand All @@ -19,6 +15,6 @@ public void afterRegistration(AfterRegistrationAccess access) {

@Override
public String getDescription() {
return REASON;
return "PrimeFaces runtime initialization";
}
}
}