/* * Copyright 2017 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 * * http://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.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; /** * <p> * The CDI annotation <code>@</code>{@link Param} allows you to inject, convert and validate a HTTP request or path * parameter in a CDI managed bean. * <p> * For HTTP request parameters it's basically like <code><f:viewParam></code>, 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 <code><f:event type="preRenderView"></code> or <code><f:viewAction></code> in the * view. * * <h3>Usage</h3> * * <h4>Request parameters</h4> * <p> * The example below injects the request parameter with name <code>foo</code>. * <pre> * @Inject @Param * private String foo; * </pre> * <p> * 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 <code>name</code> attribute. The example below injects the request * parameter with name <code>foo</code> into a variable named <code>bar</code>. * <pre> * @Inject @Param(name="foo") * private String bar; * </pre> * <p> * The <code>name</code> attribute is only mandatory when using constructor injection as there is no information about * constructor parameter names. The example below injects the request parameter with name <code>foo</code> as a * constructor parameter. * <pre> * @Inject * public Bean(@Param(name="foo") String foo) { * // ... * } * </pre> * * <h4>Multi-valued request parameters</h4> * <p> * Multi-valued parameters are also supported by specifying a {@link List} or array type. The support was added in * OmniFaces 2.4. * <pre> * @Inject @Param(name="foo") * private List<String> foos; * * @Inject @Param(name="bar") * private String[] bars; * </pre> * * <h4>Path parameters</h4> * <p> * Path parameters can be injected by specifying the <code>pathIndex</code> attribute representing the zero-based index * of the path parameter. The support was added in OmniFaces 2.5. On an example request * <code>http://example.com/mypage/firstname.lastname</code>, which is mapped to <code>/mypage.xhtml</code>, the below * example injects the path parameter <code>firstname.lastname</code>. * <pre> * @Inject @Param(pathIndex=0) * private String user; * </pre> * <p> * This takes precedence over the <code>name</code> attribute. * * <h3>Conversion and validation</h3> * <p> * 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 <code>forClass</code>, can be injected without explicitly * specifying a converter. * <pre> * @Inject @Param * private Long id; * </pre> * <p> * Other types do need a converter. The following is an example of the injection of request parameter <code>user</code> * following a request such as <code>http://example.com/mypage?user=42</code>: * <pre> * @Inject @Param(converter="userConverter", validator="priviledgedUser") * private User user; * </pre> * <p> * This also works on multi-valued parameters. * <pre> * @Inject @Param(name="user", converter="userConverter") * private List<User> users; * </pre> * <p> * This also works on path parameters. The following is an example of the injection of path parameter <code>user</code> * following a request such as <code>http://example.com/mypage/42</code>: * <pre> * @Inject @Param(pathIndex=0, converter="userConverter", validator="priviledgedUser") * private User user; * </pre> * <p> * Note that the <code>converter</code> and <code>validator</code> attributes can be specified in 3 ways: * <ul> * <li>A string value representing the converter/validator ID like so <code>converter="userConverter"</code>. * <li>An EL expression returning the converter/validator ID string like so <code>converter="#{bean.converterId}"</code>. * <li>An EL expression returning the concrete converter/validator instance like so <code>converter="#{converterBean}"</code>. * </ul> * <p> * 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 <code>V</code> the actual type of the converted parameter. * Deserialization in this case works by converting from the original parameter again. * <pre> * @Inject @Param(converter="someIdToInputStreamConverter") * private ParamValue<InputStream> content; // Extreme example :) Be careful with resource leaking. * </pre> * <p> * If conversion or validation fails, <code>null</code> is injected if the injection target is <b>NOT</b> * {@link ParamValue}. Otherwise a {@link ParamValue} instance is injected, but it will contain a <code>null</code> * 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 <code>true</code>. * * @since 1.6 * @author Arjan Tijms * @see ParamValue * @see Attribute * @see ParamExtension * @see ParamProducer * @see DynamicParamValueProducer * */ @Qualifier @Retention(RUNTIME) @Target({ METHOD, FIELD, PARAMETER }) 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 <code>0</code>. * This takes precedence over <code>name</code> 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 <code>name</code> 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. * <p> * A converter can be specified in 3 ways: * <ol> * <li>A string value representing the <em>converter-id</em> as used by {@link * javax.faces.application.Application#createConverter(String)} * <li>An EL expression that resolves to a String representing the <em>converter-id</em> * <li>An EL expression that resolves to a {@link Converter} instance. * </ol> * <p> * 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. * * <p> * A validator can be specified in 3 ways: * <ol> * <li>A string value representing the <em>validator-id</em> as used by {@link * javax.faces.application.Application#createValidator(String)} * <li>An EL expression that resolves to a String representing the <em>validator-id</em> * <li>An EL expression that resolves to a {@link Validator} instance. * </ol> * <p> * 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<? extends Converter> converterClass() default Converter.class; /** * (Optional) Class of one ore more validators to be used for validating the (converted) parameter. * These will run <i>after</i> the ones specified in {@link #validators()}. * * @return The validator classes used to validate the (converted) parameter. */ @Nonbinding Class<? extends Validator>[] validatorClasses() default {}; /** * (Optional) Attributes that will be set on the converter instance obtained from {@link Param#converter()} or {@link Param#converterClass()}. * <p> * 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). * <p> * 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()}. * <p> * 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). * <p> * 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. * <p> * The value for which conversion failed is available as <code>{0}</code>. The label associated with this * parameter value (see the {@link Param#label()} attribute) is available as <code>{1}</code>. * * @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). * <p> * The value for which validation failed is available as <code>{0}</code>. The label associated with this * parameter value (see the {@link Param#label()} attribute) is available as <code>{1}</code>. * * @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}. * <p> * The (empty) value for which the required check failed is available as <code>{0}</code>. (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 <code>{1}</code>. * * @return The error message to be used on empty submit while {@link #required()} is <code>true</code>. */ @Nonbinding String requiredMessage() default ""; /** * (Optional) Flag that disables bean validation for this instance. * <p> * <b>NOTE:</b> 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. * <p> * If <code>true</code> no bean validation will be attempted. If <code>false</code> (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. * <p> * If <code>true</code> bean validation will be performed for this instance (given that bean validation is available) despite * it globally being disabled. If <code>false</code> (the default) no specific action is taken. * * @return Whether to override that JSF bean validation is globally disabled. */ @Nonbinding boolean overrideGlobalBeanValidationDisabled() default false; }