org.omnifaces.cdi.Param Maven / Gradle / Ivy
/*
* Copyright 2020 OmniFaces
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*/
package org.omnifaces.cdi;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.PARAMETER;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.beans.PropertyEditor;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.List;
import javax.annotation.PostConstruct;
import javax.enterprise.util.Nonbinding;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;
import javax.faces.validator.BeanValidator;
import javax.faces.validator.RequiredValidator;
import javax.faces.validator.Validator;
import javax.inject.Inject;
import javax.inject.Qualifier;
import org.omnifaces.cdi.param.Attribute;
import org.omnifaces.cdi.param.DynamicParamValueProducer;
import org.omnifaces.cdi.param.ParamExtension;
import org.omnifaces.cdi.param.ParamProducer;
import org.omnifaces.cdi.param.ParamValue;
import org.omnifaces.util.Utils;
/**
*
* The CDI annotation @
{@link Param} allows you to inject, convert and validate a HTTP request or path
* parameter in a CDI managed bean.
*
* For HTTP request parameters it's basically like <f:viewParam>
, but with the major difference that
* the injected parameter is directly available during {@link PostConstruct}, allowing a much easier way of processing
* without the need for a <f:event type="preRenderView">
or <f:viewAction>
in the
* view.
*
*
Usage
*
* Request parameters
*
* The example below injects the request parameter with name foo
.
*
* @Param
* private String foo;
*
*
* By default the name of the request parameter is taken from the name of the variable into which injection takes place.
* The name can be optionally specified via the name
attribute. The example below injects the request
* parameter with name foo
into a variable named bar
.
*
* @Param(name="foo")
* private String bar;
*
*
* The name
attribute is only mandatory when using constructor injection in OmniFaces 3.5 or older as there
* is no information about constructor parameter names. The example below injects the request parameter with name
* foo
as a constructor parameter.
*
* @Inject
* public Bean(@Param(name="foo") String foo) {
* // ...
* }
*
*
* Since OmniFaces 3.6 it is not necessary anymore if the parameter has a name according to the class file as per
* Parameter#isNamePresent()
.
*
* @Inject
* public Bean(@Param String foo) {
* // ...
* }
*
*
* Multi-valued request parameters
*
* Multi-valued parameters are also supported by specifying a {@link List} or array type. The support was added in
* OmniFaces 2.4.
*
* @Param(name="foo")
* private List<String> foos;
*
* @Param(name="bar")
* private String[] bars;
*
*
* Path parameters
*
* Path parameters can be injected by specifying the pathIndex
attribute representing the zero-based index
* of the path parameter. The support was added in OmniFaces 2.5. On an example request
* https://example.com/mypage/firstname.lastname
, which is mapped to /mypage.xhtml
, the below
* example injects the path parameter firstname.lastname
.
*
* @Param(pathIndex=0)
* private String user;
*
*
* This takes precedence over the name
attribute.
*
*
Conversion and validation
*
* Standard types for which JSF already has a build in converter like {@link String}, {@link Long}, {@link Boolean}, etc
* or for which there's already a converter registered via forClass
, can be injected without explicitly
* specifying a converter.
*
* @Param
* private Long id;
*
*
* Other types do need a converter. The following is an example of the injection of request parameter user
* following a request such as https://example.com/mypage?user=42
:
*
* @Param(converter="userConverter", validator="priviledgedUser")
* private User user;
*
*
* This also works on multi-valued parameters.
*
* @Param(name="user", converter="userConverter")
* private List<User> users;
*
*
* This also works on path parameters. The following is an example of the injection of path parameter user
* following a request such as https://example.com/mypage/42
:
*
* @Param(pathIndex=0, converter="userConverter", validator="priviledgedUser")
* private User user;
*
*
* Note that the converter
and validator
attributes can be specified in 3 ways:
*
* - A string value representing the converter/validator ID like so
converter="userConverter"
.
* - An EL expression returning the converter/validator ID string like so
converter="#{bean.converterId}"
.
* - An EL expression returning the concrete converter/validator instance like so
converter="#{converterBean}"
.
*
*
* Instead of converter
or validator
you can also use converterClass
or
* validatorClass
:
*
* @Param(converterClass=UserConverter.class, validatorClass=PriviledgedUser.class)
* private User user;
*
*
* Note that this is ignored when converter
or validator
is also specified.
*
* In case you want to specify converter or validator attributes, then you can use converterAttributes
or
* validatorAttributes
respectively. They accept an array of {@link Attribute} arguments whose value
* can be a string literal or an EL expression such as value = "#{bean.property}"
.
*
* @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.")
* private Date date;
*
*
* Yes, you can use converterMessage
and validatorMessage
to customize the error message.
*
* In case the converted parameter value is not serializable, while the managed bean is serializable, you could inject
* it into a field of type {@link ParamValue}, with V
the actual type of the converted parameter.
* Deserialization in this case works by converting from the original parameter again.
*
* @Inject @Param(converter="someIdToInputStreamConverter")
* private ParamValue<InputStream> content; // Extreme example :) Be careful with resource leaking.
*
*
* If conversion or validation fails, null
is injected if the injection target is NOT
* {@link ParamValue}. Otherwise a {@link ParamValue} instance is injected, but it will contain a null
* value. In both cases, the conversion and validation messages (if any) will be set in the JSF context then, and
* {@link FacesContext#isValidationFailed()} will return true
.
*
*
Historical note
*
* Before OmniFaces 3.6, the @
{@link Param} which is not of type {@link ParamValue} also required
* @
{@link Inject} as in:
*
* @Inject @Param
* private String foo;
*
*
* But this is not needed anymore since OmniFaces 3.6. This has the following advantages:
*
* - Less code
*
- Not anymore confusing "No bean is eligible for injection to the injection point [JSR-365 §5.2.2]"
* warnings in IDEs like Eclipse (caused by the dynamic/generic type of the injection point).
*
* These will not anymore use the {@link DynamicParamValueProducer}. Instead the injection is "manually" done while
* creating the bean.
*
* @since 1.6
* @author Arjan Tijms
* @author Bauke Scholtz
* @see ParamValue
* @see Attribute
* @see ParamExtension
* @see ParamProducer
* @see DynamicParamValueProducer
*/
@Qualifier
@Retention(RUNTIME)
@Target({ METHOD, FIELD, PARAMETER })
@SuppressWarnings("rawtypes")
public @interface Param {
/**
* (Optional) The name of the request parameter. If not specified the name of the injection target field will be used.
*
* @return The name of the request parameter.
*/
@Nonbinding String name() default "";
/**
* (Optional) The index of the path parameter. If specified the parameter will be extracted from the request path
* info on the given index instead of as request parameter. The first path parameter has an index of 0
.
* This takes precedence over name
attribute.
*
* @return The index of the path parameter.
* @since 2.5
*/
@Nonbinding int pathIndex() default -1;
/**
* (Optional) the label used to refer to the parameter.
*
* @return The label used to refer the parameter, defaults to the name
attribute.
*/
@Nonbinding String label() default "";
/**
* (Optional/Required) The converter to be used for converting the parameter to the type that is to be injected.
* Optional if the target type is String, otherwise required.
*
* A converter can be specified in 3 ways:
*
* - A string value representing the converter-id as used by {@link
* javax.faces.application.Application#createConverter(String)}
*
- An EL expression that resolves to a String representing the converter-id
*
- An EL expression that resolves to a {@link Converter} instance.
*
*
* If this attribute is specified in addition to {@link Param#converterClass()}, this attribute takes precedence.
*
* @return The converter used to convert the parameter to model value.
*/
@Nonbinding String converter() default "";
/**
* (Optional) Flag indicating if this parameter is required (must be present) or not. The required check is done
* after conversion and before validation. A value is said to be not present if it turns out to be empty according to
* the semantics of {@link Utils#isEmpty(Object)}.
*
* @return Whether the absence of the parameter should cause a validation error.
*/
@Nonbinding boolean required() default false;
/**
* (Optional) The validators to be used for validating the (converted) parameter.
*
*
* A validator can be specified in 3 ways:
*
* - A string value representing the validator-id as used by {@link
* javax.faces.application.Application#createValidator(String)}
*
- An EL expression that resolves to a String representing the validator-id
*
- An EL expression that resolves to a {@link Validator} instance.
*
*
* If this attribute is specified in addition to {@link Param#validatorClasses()} then the validators from both
* attributes will be added to the final collection of validators. The validators from this attribute will however
* be called first.
*
* @return The validators used to validate the (converted) parameter.
*/
@Nonbinding String[] validators() default {};
/**
* (Optional) Class of the converter to be used for converting the parameter to the type that is to be injected.
* This is ignored when {@link #converter()} is specified.
*
* @return The converter class used to convert the parameter to model value.
*/
@Nonbinding Class converterClass() default Converter.class;
/**
* (Optional) Class of one ore more validators to be used for validating the (converted) parameter.
* These will run after the ones specified in {@link #validators()}.
*
* @return The validator classes used to validate the (converted) parameter.
*/
@Nonbinding Class[] validatorClasses() default {};
/**
* (Optional) Attributes that will be set on the converter instance obtained from {@link Param#converter()} or {@link Param#converterClass()}.
*
* For each attribute the converter instance should have a writable JavaBeans property with the same name. The value can be a string literal
* or an EL expression. String literals are coerced if necessary if there's a {@link PropertyEditor} available (the JDK provides these for
* the primitive types and their corresponding boxed types).
*
* Attributes for which the converter doesn't have a property (setter) are silently ignored.
*
* @return The attributes which need to be set on the converter.
*/
@Nonbinding Attribute[] converterAttributes() default {};
/**
* (Optional) Attributes that will be set on each of the validator instances obtained from {@link Param#validators()} and {@link Param#validatorClasses()}.
*
* For each attribute the validator instances should have a writable JavaBeans property with the same name. The value can be a string literal
* or an EL expression. String literals are coerced if necessary if there's a {@link PropertyEditor} available (the JDK provides these for
* the primitive types and their corresponding boxed types).
*
* Attributes for which any given validator doesn't have a property (setter) are silently ignored.
*
* @return The attributes which need to be set on the validators.
*/
@Nonbinding Attribute[] validatorAttributes() default {};
/**
* (Optional) A message that will be used if conversion fails instead of the message set by the converter.
*
* The value for which conversion failed is available as {0}
. The label associated with this
* parameter value (see the {@link Param#label()} attribute) is available as {1}
.
*
* @return The error message to be used when the {@link #converter()} or {@link #converterClass()} fail.
*/
@Nonbinding String converterMessage() default "";
/**
* (Optional) A message that will be used if validation fails instead of the message set by the validator(s).
*
* The value for which validation failed is available as {0}
. The label associated with this
* parameter value (see the {@link Param#label()} attribute) is available as {1}
.
*
* @return The error message to be used when any of the {@link #validators()} or {@link #validatorClasses()} fail.
*/
@Nonbinding String validatorMessage() default "";
/**
* (Optional) A message that will be used if a non-empty value is submitted instead of the default message associated
* with the {@link RequiredValidator}.
*
* The (empty) value for which the required check failed is available as {0}
. (this will be either null or the empty string)
* The label associated with this parameter value (see the {@link Param#label()} attribute) is available as {1}
.
*
* @return The error message to be used on empty submit while {@link #required()} is true
.
*/
@Nonbinding String requiredMessage() default "";
/**
* (Optional) Flag that disables bean validation for this instance.
*
* NOTE: bean validation at the moment (OmniFaces 1.6) is done against the {@link ParamValue} that is injected. In many cases this will
* be of limited use. We hope to directly inject the converted type in OmniFaces 1.7 and then bean validation will make more sense.
*
* If true
no bean validation will be attempted. If false
(the default) no specific action is taken, and it
* will depend on the availability of bean validation and the global {@link BeanValidator#DISABLE_DEFAULT_BEAN_VALIDATOR_PARAM_NAME} setting
* whether bean validation is attempted or not.
*
* @return Whether to disable bean validation or not.
*/
@Nonbinding boolean disableBeanValidation() default false;
/**
* (Optional) Flag that overrides the global {@link BeanValidator#DISABLE_DEFAULT_BEAN_VALIDATOR_PARAM_NAME} setting.
*
* If true
bean validation will be performed for this instance (given that bean validation is available) despite
* it globally being disabled. If false
(the default) no specific action is taken.
*
* @return Whether to override that JSF bean validation is globally disabled.
*/
@Nonbinding boolean overrideGlobalBeanValidationDisabled() default false;
}