0

Here's my app:

public static void main( String[] args ) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Config.class);

    //run the importer
    final ImportNewOrders importer = (ImportNewOrders) ApplicationContextProvider.getApplicationContext().getBean("importNewOrders");
    importer.run();
    //importer.runInBackground();
}

Here's my config:

@Configuration
@ComponentScan(basePackages = {
        "com.production"
})
@PropertySource(value = {
        "classpath:/application.properties",
        "classpath:/environment-${MY_ENVIRONMENT}.properties"
})
@EnableJpaRepositories("com.fettergroup.production.repositories")
@EnableTransactionManagement
public class Config {

    .... skipping things that aren't relevant

    @Bean
    public ImportNewOrders importNewOrders() {
        return new ImportNewOrders();
    }

Here's my class...

@Component
public class ImportNewOrders implements Task {

    private final static Logger logger = Logger.getLogger(ImportNewOrders.class.getName());

    @Autowired
    private OrderService orderService;

    @Autowired
    private ImportOrderRequest importOrderRequest;

    @Value("${api.user}")
    private String apiUser;

    @Value("${api.password}")
    private String apiPassword;

    @Value("${api.orders.pingFrequency}")
    private String pingFrequency;

And finally the application.properties:

# ------------------- Application settings -------------------

#Base URL to the API application
api.baseUrl=http://localhost:9998

#Unique token used to authenticate this vendor
api.vendor.token=asdf

#API credentials
api.user=myuser
api.password=mypassword

#How often to check for new orders; frequency is in seconds
api.orders.pingFrequency=60

This worked an hour or two ago, now it's decided it doesn't like these values. I'm at a loss as to why. Everything looks correct to me.

Update

@Configuration
@ComponentScan(basePackages = {
        "com.production"
})
@PropertySource(value = {
        "classpath:/application.properties",
        "classpath:/environment-${MY_ENVIRONMENT}.properties"
})
@EnableJpaRepositories("com.production.repositories")
@EnableTransactionManagement
public class Config {
    @Value("${db.url}")
    private static String PROPERTY_DATABASE_URL;

    @Bean
    public DataSource dataSource() {
        MysqlDataSource dataSource = new MysqlDataSource();

        dataSource.setUrl(PROPERTY_DATABASE_URL); //is null
        /*dataSource.setUser(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_USER));
        dataSource.setPassword(environment.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));*/

        return dataSource;
    }

    @Bean
    public PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer () {
        return new PropertySourcesPlaceholderConfigurer();
    }
}
Ben
  • 60,438
  • 111
  • 314
  • 488

1 Answers1

6

Your properties file is found by your @Configuration and is using it for your database properties within that class because of @PropertySource. But @Value fields and ${} evaluation need more than that.

From Javadoc for @PropertySource

In order to resolve ${...} placeholders in definitions or @Value annotations using properties from a PropertySource, one must register a PropertySourcesPlaceholderConfigurer. This happens automatically when using in XML, but must be explicitly registered using a static @Bean method when using @Configuration classes. See the "Working with externalized values" section of @Configuration Javadoc and "a note on BeanFactoryPostProcessor-returning @Bean methods" of @Bean Javadoc for details and examples.

So declare a

@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
    PropertySourcesPlaceholderConfigurer p = new PropertySourcesPlaceholderConfigurer();
    p.setLocation(new ClassPathResource("your properties path"));
    // other properties
    return p;
}

in your config class, or as ach has aptly mentioned in the comments if you use @PropertySource your can omit setLocation altogether:

@Configuration
@PropertySource(value="classpath:your_file.properties")
public class MyConfiguration{

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer()    {
        PropertySourcesPlaceholderConfigurer p = new PropertySourcesPlaceholderConfigurer();
    return p;
    }
}

You shouldn't need the environment when you have the PropertySourcesPlaceholderConfigurer

In most cases, however, application-level beans should not need to> interact with the Environment directly but instead may have to have ${...} property values replaced by a property placeholder configurer such as PropertySourcesPlaceholderConfigurer, which itself is EnvironmentAware and as of Spring 3.1 is registered by default when using < context:property-placeholder/>.

Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • 2
    You don't even need to set the location, you can just `return new PropertySourcesPlaceholderConfigurer()` as long as you have `@PropertySources` pointing at your properties files. – ach Mar 13 '13 at 20:32
  • Sweet, I did not know this. – Sotirios Delimanolis Mar 13 '13 at 20:34
  • When I add a bean for this, it no longer autowires `Environment environment;` into my config class. – Ben Mar 14 '13 at 13:26
  • @Webnet See above, unless you are using Environment for something else, properties are now available with `@Value` because of `PropertySourcesPlaceholderConfigurer`. Update your post with full (or parts) configuration. – Sotirios Delimanolis Mar 14 '13 at 13:30
  • Read this answer too http://stackoverflow.com/questions/14169834/spring-3-1-environment-does-not-work-with-user-property-files – Sotirios Delimanolis Mar 14 '13 at 13:36
  • @SotiriosDelimanolis - I've updated my configuration. I was only using `Environment` for the properties. If I understand this correctly, I don't need to specify a location for my properties files because it should be loaded from the `@PropertySource` annotation. When I do try to specify a location anyways, I use `p.setLocation(new ClassPathResource("classpath:/application.properties"));` and it says it can't find it. But that error doesn't appear when that path is used in `@PropertySource` – Ben Mar 14 '13 at 13:50
  • @Webnet So don't use it. For the ClassPathResource, I don't think you need to specify the `classpath:` prefix. – Sotirios Delimanolis Mar 14 '13 at 14:01
  • I've read through the documentation and I'm doing everything according to their examples... – Ben Mar 14 '13 at 14:06
  • @SotiriosDelimanolis - At the end of my post you'll see my current config. I'm trying to get `@Value("${db.url}")` mapped to a value. When I implement `PropertySourcesPlaceholderConfigurer` as you mentioned it's null, when I don't implement that bean it becomes a string, with the value of `"${db.url}"` – Ben Mar 14 '13 at 14:23
  • 1
    @Webnet Try to make the `PropertySourcesPlaceholderConfigurer` bean method `static`. You're on Spring 3.1+? – Sotirios Delimanolis Mar 14 '13 at 14:44
  • 1
    @SotiriosDelimanolis - That did it... Thank You!! I'm on 3.2.1 – Ben Mar 14 '13 at 14:47