Skip to content

Commit

Permalink
Add @param processing, create synth, beans as needed. (#166)
Browse files Browse the repository at this point in the history
* @param testing

* Process @param injection points, create synth. beans for them.

---------

Co-authored-by: melloware <[email protected]>
  • Loading branch information
manovotn and melloware committed Sep 17, 2024
1 parent 92a0ccb commit 0c4e74d
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,24 @@
package io.quarkiverse.omnifaces.deployment;

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

import jakarta.enterprise.context.ApplicationScoped;
import jakarta.enterprise.context.Dependent;
import jakarta.enterprise.inject.Produces;
import jakarta.enterprise.inject.spi.InjectionPoint;
import jakarta.inject.Inject;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationTarget;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.ClassType;
import org.jboss.jandex.DotName;
import org.jboss.jandex.Type;
import org.jboss.logging.Logger;
import org.omnifaces.cdi.ContextParam;
import org.omnifaces.cdi.Cookie;
Expand All @@ -31,12 +39,15 @@
import io.quarkus.arc.deployment.AdditionalBeanBuildItem;
import io.quarkus.arc.deployment.AnnotationsTransformerBuildItem;
import io.quarkus.arc.deployment.BeanDefiningAnnotationBuildItem;
import io.quarkus.arc.deployment.BeanDiscoveryFinishedBuildItem;
import io.quarkus.arc.deployment.ContextRegistrationPhaseBuildItem;
import io.quarkus.arc.deployment.ContextRegistrationPhaseBuildItem.ContextConfiguratorBuildItem;
import io.quarkus.arc.deployment.CustomScopeBuildItem;
import io.quarkus.arc.deployment.KnownCompatibleBeanArchiveBuildItem;
import io.quarkus.arc.deployment.SyntheticBeanBuildItem;
import io.quarkus.arc.deployment.UnremovableBeanBuildItem;
import io.quarkus.arc.processor.AnnotationsTransformer;
import io.quarkus.arc.processor.InjectionPointInfo;
import io.quarkus.deployment.IsDevelopment;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
Expand All @@ -52,6 +63,7 @@
import io.quarkus.deployment.pkg.steps.NativeOrNativeSourcesBuild;
import io.quarkus.omnifaces.runtime.OmniFacesFeature;
import io.quarkus.omnifaces.runtime.OmniFacesRecorder;
import io.quarkus.omnifaces.runtime.ParamBeanCreator;
import io.quarkus.omnifaces.runtime.scopes.OmniFacesQuarkusViewScope;
import io.quarkus.undertow.deployment.ServletInitParamBuildItem;

Expand All @@ -62,6 +74,10 @@ class OmnifacesProcessor {
private static final String FEATURE = "omnifaces";
static final DotName OMNIFACES_STARTUP = DotName.createSimple(Startup.class.getName());
static final DotName OMNIFACES_EAGER = DotName.createSimple(Eager.class.getName());
static final DotName OMNIFACES_PARAM = DotName.createSimple(Param.class.getName());
static final DotName INJECT_ANNOTATION = DotName.createSimple(Inject.class.getName());
static final DotName PRODUCES_ANNOTATION = DotName.createSimple(Produces.class.getName());
static final DotName INJECTION_POINT = DotName.createSimple(InjectionPoint.class.getName());

private static final Class[] BEAN_CLASSES = {
EagerBeansRepository.class,
Expand Down Expand Up @@ -227,6 +243,31 @@ public void transform(AnnotationsTransformer.TransformationContext ctx) {
});
}

@BuildStep
public void inspectInjectionPoints(BeanDiscoveryFinishedBuildItem beanDiscoveryFinishedBuildItem,
BuildProducer<SyntheticBeanBuildItem> syntheticBeanBuildItemBuildProducer) {
// go through all injection points, look for those with @Param, store required types
Set<Type> requiredParamTypes = new HashSet<>();
for (InjectionPointInfo ipInfo : beanDiscoveryFinishedBuildItem.getInjectionPoints()) {
if (ipInfo.getRequiredQualifier(OMNIFACES_PARAM) != null) {
requiredParamTypes.add(ipInfo.getRequiredType());
}
}

// for each required type, register a synthetic bean
for (Type requiredType : requiredParamTypes) {
SyntheticBeanBuildItem synthBean = SyntheticBeanBuildItem.configure(DotName.createSimple(Object.class))
.scope(Dependent.class)
.addType(requiredType)
.addQualifier(Param.class)
.unremovable()
.addInjectionPoint(ClassType.create(INJECTION_POINT))
.creator(ParamBeanCreator.class)
.done();
syntheticBeanBuildItemBuildProducer.produce(synthBean);
}
}

public List<String> collectClassesInPackage(CombinedIndexBuildItem combinedIndex, String packageName) {
final List<String> classes = new ArrayList<>();
final List<DotName> packages = new ArrayList<>(combinedIndex.getIndex().getSubpackages(packageName));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package io.quarkiverse.omnifaces.it;

import static org.omnifaces.util.Faces.isValidationFailed;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;
import java.util.List;

import jakarta.annotation.PostConstruct;
import jakarta.enterprise.context.RequestScoped;
import jakarta.faces.FacesException;
import jakarta.faces.convert.DateTimeConverter;
import jakarta.faces.validator.LengthValidator;
import jakarta.inject.Inject;
import jakarta.inject.Named;
import jakarta.validation.constraints.NotNull;

import org.omnifaces.cdi.Param;
import org.omnifaces.cdi.param.Attribute;
import org.omnifaces.util.Messages;

@Named
@RequestScoped
public class CdiParamBean {

// Like <f:viewParam name="text1" value="#{bean.text1}" required="true">
@Inject
@Param(required = true)
String text1;

// Like <f:viewParam name="text2" value="#{bean.text2}" validatorMessage="..."><f:validateLength minimum="3">
@Inject
@Param(validatorClasses = LengthValidator.class, validatorAttributes = @Attribute(name = "minimum", value = "3"), validatorMessage = "{1}: Value is too too small! Please enter a minimum of 3 characters.")
String text2;

// Multi-valued parameters are not possible with <f:viewParam>; using JSR303 bean validation via the @NotNull constraint.
@Inject
@Param
@NotNull(message = "{0} is required")
List<String> text3;

// Like <f:viewParam name="number" value="#{bean.number}"> using implicit Faces integer converter.
@Param
Integer number;

// Like <f:viewParam name="date" value="#{bean.date}" converterMessage="..."><f:convertDateTime pattern="yyyyMMdd">
@Inject
@Param(converterClass = DateTimeConverter.class, converterAttributes = {
@Attribute(name = "pattern", value = "yyyyMMdd") }, converterMessage = "{1}: \"{0}\" is not the date format we had in mind! Please use the format yyyyMMdd.")
Date date;

private String result;

@PostConstruct
public void init() {
if (isValidationFailed()) {
result = "Validation has failed!";
return;
}

result = String.format("You entered text1 '%s', text2 '%s', text3 '%s', number '%d', date '%5$tY%5$tm%5$td'", text1,
text2, text3, number, date);

Messages.addGlobalInfo("Yes, no validation errors!");
}

public String getResult() {
return result;
}

@SuppressWarnings("unchecked")
public static <T> T copy(T source) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
T copy = null;

try (ObjectOutputStream oos = new ObjectOutputStream(baos)) {
oos.writeObject(source);
} catch (IOException e) {
throw new FacesException(e);
}

try (ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()))) {
copy = (T) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
throw new FacesException(e);
}

return copy;
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.quarkus.omnifaces.runtime;

import jakarta.enterprise.inject.spi.InjectionPoint;

import org.omnifaces.cdi.param.ParamProducer;
import org.omnifaces.cdi.param.ParamValue;

import io.quarkus.arc.BeanCreator;
import io.quarkus.arc.SyntheticCreationalContext;

public class ParamBeanCreator implements BeanCreator<Object> {

@Override
public Object create(SyntheticCreationalContext<Object> context) {
InjectionPoint injectionPoint = context.getInjectedReference(InjectionPoint.class);
if (injectionPoint == null) {
throw new IllegalStateException("No current injection point found");
}
// delegate to the original producer
ParamValue<?> paramValue = new ParamProducer().produce(injectionPoint);
return paramValue.getValue();
}
}

0 comments on commit 0c4e74d

Please sign in to comment.