Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can't @Value inject a JNDI value from the Environment?

I'm having trouble injecting JNDI values from my Tomcat into spring java config using the @Value annotation whereas I have no trouble retreiving the values via the Environment class. We are using Java 1.7.0_17, Spring 4.0.3, and Tomcat 7.0.52 .

I have in my context.xml the following variables defined:

<Environment name="USER_NAME" value="initech" type="java.lang.String" />
<Environment name="USER_PASSWORD" value="1n3+3ch!" type="java.lang.String" />

In my Java configuration file I have the following code working:

@Bean
public Foo foo( Environment env ){
    return new Foo( env.getProperty("USER_NAME"), env.getProperty("USER_PASSWORD") );
}

When I look into the server log I see:

12:50:45.214 [RMI TCP Connection(3)-127.0.0.1] DEBUG o.s.c.e.PropertySourcesPropertyResolver -> Searching for key 'USER_NAME' in [servletConfigInitParams]
12:50:45.214 [RMI TCP Connection(3)-127.0.0.1] DEBUG o.s.c.e.PropertySourcesPropertyResolver -> Searching for key 'USER_NAME' in [servletContextInitParams]
12:50:45.214 [RMI TCP Connection(3)-127.0.0.1] DEBUG o.s.c.e.PropertySourcesPropertyResolver -> Searching for key 'USER_NAME' in [jndiProperties]
12:50:45.214 [RMI TCP Connection(3)-127.0.0.1] DEBUG o.springframework.jndi.JndiTemplate -> Looking up JNDI object with name [java:comp/env/USER_NAME]
12:50:45.214 [RMI TCP Connection(3)-127.0.0.1] DEBUG o.s.jndi.JndiLocatorDelegate -> Located object with JNDI name [java:comp/env/USER_NAME]
12:50:45.214 [RMI TCP Connection(3)-127.0.0.1] DEBUG o.s.jndi.JndiPropertySource -> JNDI lookup for name [USER_NAME] returned: [initech]
12:50:45.214 [RMI TCP Connection(3)-127.0.0.1] DEBUG o.s.c.e.PropertySourcesPropertyResolver -> Found key 'USER_NAME' in [jndiProperties] with type [String] and value 'initech'

However, I would like to use @Value annotation to make the code cleaner if at all possible; something along the lines of

@Bean
public Foo foo(
        @Value( "#{USER_NAME}" ) String userName,
        @Value( "#{USER_PASSWORD}" ) String userPassword
    ){

    return new Foo( userName, userPassword );
}

but this code results in the error:

nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): Property or field 'USER_NAME' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public?; nested exception is org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): Property or field 'USER_NAME' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public?; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'oauthTokenRequestClient' defined in class path resource [com/initech/set/gw/api/config/GatewayRepositoryConfig.class]: Unsatisfied dependency expressed through constructor argument with index 0 of type [java.net.URI]: : Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): Property or field 'USER_NAME' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public?; nested exception is org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 0): Property or field 'USER_NAME' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public?
org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:747)
org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:462)

I have tried "#{systemEnvironment.USER_NAME}, "#{systemProperties.USER_NAME}, "#{jndiProperties.USER_NAME}, and "#{java:comp/env/USER_NAME} as well: yet, none of them work.

PS- I've tried to simplify the question so if one of these should have worked let me know because I may have made an error in my posting.

like image 904
Sled Avatar asked Jun 10 '14 17:06

Sled


People also ask

Can we use @value inside method?

no :) you can use annotations to annotate classes, fields, methods and their arguments. but not in methods, since there is no way, to get method- local variables using reflection in order to process these annotations. Use @Value in your field, and read the value from your method.

What does the @value annotation do?

Spring @Value annotation is used to assign default values to variables and method arguments. We can read spring environment variables as well as system variables using @Value annotation.

What does @value annotation do in Java?

@Value is a Java annotation that is used at the field or method/constructor parameter level and it indicates a default value for the affected argument. It is commonly used for injecting values into configuration variables - which we will show and explain in the next part of the article.

How does @value works in spring boot?

The annotation @Value is used to automatically assign a value from multiple environment such as spring environment, system environment, property files, bean objects, default value etc. The annotation @Value in spring boot is commonly used to inject the configuration values to the spring boot application.


3 Answers

I solved it using the selected answer from the question Java Spring: How to use @Value annotation to inject an Environment property?.

So my code looks like:

@Bean
public Foo foo(
        @Value( "#{environment.USER_NAME}" ) String userName,
        @Value( "#{environment.USER_PASSWORD}" ) String userPassword
    ){

    return new Foo( userName, userPassword );
}

I'm assuming from the content of the most upvoted answer (ie "assuming that you have a PropertySourcesPlaceHolderConfigurer registered") to that quesiton that this means we are lacking a configured PropertySourcesPlaceHolderConfigurer.

like image 91
Sled Avatar answered Oct 03 '22 12:10

Sled


If you are using Spring 4 you should change

  @Value( "#{USER_PASSWORD}" ) String userPassword

To

@Value( "${USER_PASSWORD}" ) String userPassword

Try that and see what happens.

like image 20
Bryce Easley Avatar answered Oct 03 '22 13:10

Bryce Easley


For Spring 3.1+ (tested on 4.2.5.RELEASE), you can use @Value like this:

@Value("${property.name}")
private String jndiProperty;

But you also need to define a PropertySourcesPlaceholderConfigurer bean in a static method and configure a JndiPropertySource (I have this in an @Configuration class):

@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(ConfigurableEnvironment env) {
    PropertySourcesPlaceholderConfigurer placeholderConfigurer = new PropertySourcesPlaceholderConfigurer();

    JndiPropertySource jndiPropertySource = new JndiPropertySource("java:comp");
    env.getPropertySources().addFirst(jndiPropertySource);

    return placeholderConfigurer;
}

Note the use of addFirst() which ensures property values from JNDI take precedence in case of a clash with other property sources, which may or may not be desired, in which case one can use other methods like addLast() etc.

like image 29
rohitvats Avatar answered Oct 03 '22 13:10

rohitvats