quarkusio / quarkus

Quarkus: Supersonic Subatomic Java.
https://quarkus.io
Apache License 2.0
13.36k stars 2.56k forks source link

Problem with @ConfigProperty in null when using a process in Apache Camel Quarkus #41594

Open cesarjv opened 4 days ago

cesarjv commented 4 days ago

Good morning, I am currently working with Quarkus and Apache Camel and I am using a variable from the application.properties in a processor, I try to bring it in using the @ConfigProperty annotation, but it is giving me a null error, this is how I have it configured:

The variable is called encryptionKey

UserProfileProcessorUserIdParamReq


@ApplicationScoped
@Slf4j
public class UserProfileProcessorUserIdParamReq implements Processor {

    @ConfigProperty(name="aes.encrypt.key")
    String encryptionKey;

    private final IValidationFieldsUserID validationFields;

    public UserProfileProcessorUserIdParamReq() {
        validationFields = new ValidationFieldsUserID();
    }

    @Override
    public void process(Exchange exchange) throws Exception {

        String documentType;
        String documentNumber;
        String userId;
        String correlatorId;
        String tokenInfo;
        String userIdDecodedBase64;
        String userIdDecrypt;

        try {
            correlatorId= Optional.ofNullable(exchange.getIn().getHeader("x-correlator")).map(Object::toString).orElse("");
            exchange.setProperty("correlatorId",correlatorId);
            userId = exchange.getIn().getHeader("user_id").toString();
            tokenInfo= Optional.ofNullable(exchange.getIn().getHeader("x-token-info")).map(Object::toString).orElse("");
            validationFields.validateUserId(exchange);
            userId=validateUserIdValue(userId, tokenInfo);
            exchange.setProperty("userIdEncrypt", userId);
            userIdDecodedBase64= EncodeBase64.decrypt(userId);
            log.info("userIdDecodedBase64" + userIdDecodedBase64);
            userIdDecrypt= EncryptUtil.decrypt(userIdDecodedBase64,encryptionKey);
            exchange.setProperty("userId", userIdDecrypt);
            validateTokenInfo(exchange,userId, tokenInfo);
            validateUserIdDecrypt(userIdDecrypt);
            documentType = userIdDecrypt.split("-")[0];
            documentNumber = userIdDecrypt.split("-")[1];
            exchange.setProperty("documentType", documentType);
            exchange.setProperty("documentNumber", documentNumber);
            exchange.setProperty("isSearchByQueryParam","false");

        } catch (RequiredValueException | NullPointerException e) {
            throw new RequiredValueException(e.getMessage());
        }
        catch (NotFoundDataException e) {
            throw new NotFoundDataException(e.getMessage());
        }
        catch (PermissionDeniedException e) {
            throw new PermissionDeniedException(e.getMessage());
        }
        catch (Exception e){
            if( e.getMessage().contains("Input byte array"))
                throw new NotFoundDataException(e.getMessage());
            throw new Exception(e.getMessage());
        }
    }

    private static void validateTokenInfo(Exchange exchange, String userId, String tokenInfoValidated) {
        if (!tokenInfoValidated.isEmpty()) {
            log.info("Valor del x-token-info: {}", tokenInfoValidated);
            /* Se obtiene el Objeto JSON con el valor del campo x-token-info */
            JSONObject jsonObject = new JSONObject(tokenInfoValidated);
            /*String subValue=jsonObject.get("sub").toString(); */
            /* Mediante el optString obtenemos el valor sub si viene (token 3 patas) o no viene (token 2 patas), para este ultimo caso el valor es vacio */
            String subValue = jsonObject.optString("sub");
            log.info("Valor sub que esta llegando en el valor x-token-info: {}", subValue);
            if (!subValue.isEmpty()) {
                if(!subValue.equals(userId)) {
                    throw new PermissionDeniedException("Error validando el campo sub de la autenticacion en el campo x-token-info, no hace match con userId enviado");
                }
            }
        }
    }

    /* Se valida que el UserId sea un valor valido para venezuela, sea enviado cifrado o no*/
    private static void validateUserIdDecrypt (String userId) {
        if(!Pattern.matches("^(CI|RIF|P|CD|NIT)(-).*",userId)){
            throw  new NotFoundDataException("UserId enviado esta en formato incorrecto");
        }
    }
    /*Se valida el valor del campo UserId, si el mismo pudiera contener el valor de me, en este caso se debe extraer el valor de UserId del json token-info, en especifico del campo sub */
    private String validateUserIdValue(String userId,String tokenInfo) {
        if(userId.equals("me")){
            if(!tokenInfo.isEmpty()){
                JSONObject jsonObject = new JSONObject(tokenInfo);
                userId = jsonObject.optString("sub");
            }
            else {
                throw new RequiredValueException("Se requiere parametro x-token-info (autenticacion 3 patas) para campo userId=me");
            }
        }
        return userId;
    }
}

And this is the error it gives:

14:52:44 INFO traceId=2a0a8e8cd93ddb947e2ab7206ef4f25d, parentId=, spanId=394c6d08dec8d551, sampled=true [route23] (vert.x-worker-thread-0) [2024-07-01 14:52:44.0] Descripcion de la Exception: Cannot invoke "String.getBytes()" because "key" is null

This is how it is in my application.properties:

aes.encrypt.key=${AES_ENCRYPT_KEY:xxxxxxxxxxxx}

Like from ResRoute call to processor without constructor:

from("direct:usersQueryParam")
                /*.removeHeaders("CamelHttp*") */
                .doTry()
                .process(new UserProfileProcessorQueryParamReq())
                .choice()
                    .when(simple("${exchangeProperty[isSearchByQueryParamIdDocumentValueAndIdDocumentType]} == 'true'"))
                        .to(DIRECT_PIPELINE)
                    .otherwise()
                        .log(DATE_LOG+"Identity: ${exchangeProperty[identityQueryParam]}")
                        .log(DATE_LOG+"IdentityType: ${exchangeProperty[identityTypeQueryParam]}")
                        .process(new FindCustomerDocumentBySubscriberIdReq())
                        .log(DATE_LOG+"Entrada Microservicio FindCustomerDocumentBySubscriberId: ${exchangeProperty[findCustomerDocumentBySubscriberIdRequest]}")
                        .to(configureSsl.setupSSLContext(getCamelContext(), findCustomerDocumentBySubscriberId))
                        .process(new FindCustomerDocumentBySubscriberIdRes())
                        .log(DATE_LOG+"Salida FindCustomerDocumentBySubscriberId: ${exchangeProperty[findCustomerDocumentBySubscriberIdResponseStatus]}")
                        .log(DATE_LOG+"User ID Mediante QueryParam: ${exchangeProperty[userId]}")
                        .to(DIRECT_PIPELINE)
                .endDoTry()

How can I do here so that it takes the value of the @ConfigProperty?

quarkus-bot[bot] commented 4 days ago

/cc @radcortez (config)

geoand commented 3 days ago

This is almost certainly not a Quarkus issue, but either a misuse of Camel Quarkus or a bug with that project.

cc @ppalaga

ppalaga commented 2 days ago

The injection does not work if you call new UserProfileProcessorQueryParamReq() directly. You have to let the CDI container to create the bean:

cesarjv commented 2 days ago

@ppalaga Thank you for your response, could you apply the example of what you tell me in the code to try to replicate it on my side?

ppalaga commented 2 days ago

Ups, sorry, I thought, my suggestion was clear enough. Which part requires more details so that you can do the 3 changes yourself?

cesarjv commented 2 days ago

@ppalaga I go point by point, to see if I understood:

Does it mean adding the @ApplicationScoped annotation to my ReaRoute class? It has the same one, just don't put the complete code:

@ApplicationScoped
public class ResRoute extends RouteBuilder {

/*code */

 from("direct:usersQueryParam")
                /*.removeHeaders("CamelHttp*") */
                .doTry()
                .process(new UserProfileProcessorQueryParamReq()
                .choice()
                    .when(simple("${exchangeProperty[isSearchByQueryParamIdDocumentValueAndIdDocumentType]} == 'true'"))
                        .to(DIRECT_PIPELINE)
                    .otherwise()
                        .log(DATE_LOG+"Identity: ${exchangeProperty[identityQueryParam]}")
                        .log(DATE_LOG+"IdentityType: ${exchangeProperty[identityTypeQueryParam]}")
                        .process(new FindCustomerDocumentBySubscriberIdReq())
                        .log(DATE_LOG+"Entrada Microservicio FindCustomerDocumentBySubscriberId: ${exchangeProperty[findCustomerDocumentBySubscriberIdRequest]}")
                        .to(configureSsl.setupSSLContext(getCamelContext(), findCustomerDocumentBySubscriberId))
                        .process(new FindCustomerDocumentBySubscriberIdRes(encryptionKey))
                        .log(DATE_LOG+"Salida FindCustomerDocumentBySubscriberId: ${exchangeProperty[findCustomerDocumentBySubscriberIdResponseStatus]}")
                        .log(DATE_LOG+"User ID Mediante QueryParam: ${exchangeProperty[userId]}")
                        .to(DIRECT_PIPELINE)
                .endDoTry()
                /*.to(DIRECT_PIPELINE) */
                .doCatch(EmptyDataExceptionQueryParam.class)
                .process(new UserProfileEmptyDataSubscriberQueryParam())
                .log(DATE_LOG+MSG_EXCEPTION)
                .log(DATE_LOG+SALIDA_BSS_EXCEPTION);
                /* code /*

What does this mean? How can I do it? In the previous point I show how my processor is called, I don't know if I have to modify the way of defining it

I also have a doubt regarding this point, I lack more details on how to apply it because it is not clear to me.

Thanks in advance and sorry if it's not clear to me.

ppalaga commented 8 hours ago

@ppalaga I go point by point, to see if I understood:

Make your RouteBuilder @ApplicationScoped:

Does it mean adding the @ApplicationScoped annotation to my ReaRoute class? It has the same one, just don't put the complete code:

Exactly

Add @Inject UserProfileProcessorUserIdParamReq processor; to your route builder

What does this mean? How can I do it? In the previous point I show how my processor is called, I don't know if I have to modify the way of defining it

You need to add the following code to your ResRoute class or any other class where you want to use the UserProfileProcessorUserIdParamReq:

@Inject 
UserProfileProcessorUserIdParamReq processor;

Change the route definition: .process(processor)

I also have a doubt regarding this point, I lack more details on how to apply it because it is not clear to me.

Hm... reading your code again, it actually does not show any usage of UserProfileProcessorUserIdParamReq. Where do you use it then? I originally thought, there is .process(new UserProfileProcessorUserIdParamReq()) somewhere in ResRoute, but there are only invocations of other processors like .process(new UserProfileProcessorQueryParamReq()), `.process(new FindCustomerDocumentBySubscriberIdReq()) etc.

So if you had .process(new UserProfileProcessorUserIdParamReq()) somewhere in your route builder, you would have to replace it with .process(processor) and the NPE should disappear.