0

Before asking this question I tried to follow the following questions which are similar:

Injecting Properties using Spring & annotation @Value

How can I inject a property value into a Spring Bean which was configured using annotations?

Loading up properties file to a class in Spring

However, in my case I am not using any web applications or Tomcat; I'm just trying to load a cluster.properties file into a regular Java project via Spring so I can then ingest dummy data into Accumulo. Also, I'm trying to load properties from a cluster.properties file, not from key value pairs defined in an xml file.

Using what I learned from the links above and lots of reading on Spring, here's what I have:

I created the following context.xml file:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

      <!-- Define the Spring Bean to load our cluster properties -->
      <bean id="props" class="accumuloIngest.LoadProperties"></bean>

    </beans>  

And here is a small snippet of what my cluster.properties file looks like:

    cluster.instance=instance
    cluster.username=user

    etc...

Next, I created the following Spring main method under the class MainApp.java:

    package accumuloIngest;

    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;

    public class MainApp {

        // Spring main method used to load a cluster.properties file with the Spring framework
        public static void main(String[] args) {
            ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml");
            LoadProperties myObj = LoadProperties.class.cast(ctx.getBean("props"));

            // Now print out the cluster.properties loaded by Spring to verify they aren't null
            StringBuffer springPropsBuffer = new StringBuffer();
            springPropsBuffer.append("Printing out cluster.properties read via Spring...");
            springPropsBuffer.append("\n\n");
            springPropsBuffer.append("instanceName= ");
            springPropsBuffer.append(myObj.getInstanceName());
            springPropsBuffer.append("\n");
            springPropsBuffer.append("userName= ");
            springPropsBuffer.append(myObj.getUserName());
            springPropsBuffer.append("\n");
            springPropsBuffer.append("password= ");
            springPropsBuffer.append(myObj.getPassword());
            springPropsBuffer.append("\n");
            springPropsBuffer.append("zooServers= ");
            springPropsBuffer.append(myObj.getZooServers());
            springPropsBuffer.append("\n");
            springPropsBuffer.append("tableName= ");
            springPropsBuffer.append(myObj.getTableName());
            springPropsBuffer.append("\n");
            springPropsBuffer.append("dataFile= ");
            springPropsBuffer.append(myObj.getDataFile());
            springPropsBuffer.append("\n");
            springPropsBuffer.append("dataDelim= ");
            springPropsBuffer.append(myObj.getDataDelim());
            springPropsBuffer.append("\n");
            springPropsBuffer.append("rowCount= ");
            springPropsBuffer.append(myObj.getRowCount());  
            springPropsBuffer.append("\n");
            System.out.println(springPropsBuffer.toString());

            // now start data ingest
            myObj.startIngest(); // method that calls Ingester class to start data ingest
        } // end of main method

    } // end of MainApp class

Spring loads my context.xml file and loads the Bean I called "props", but the values are still null. It seems that my @Value annotations aren't working in my LoadProperties class:

    package accumuloIngest;
    import java.io.IOException;

    import org.apache.accumulo.core.client.AccumuloException;
    import org.apache.accumulo.core.client.AccumuloSecurityException;
    import org.apache.accumulo.core.client.TableExistsException;
    import org.apache.accumulo.core.client.TableNotFoundException;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
    import org.springframework.context.annotation.Bean;
    import org.springframework.core.io.ClassPathResource;
    import org.springframework.core.io.Resource;

    public class LoadProperties {

        // this class defines the Spring Bean and loads the cluster properties
        // using the SpringFramework

        @Bean
        public static PropertyPlaceholderConfigurer props(){
        PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
        Resource[] resource = new ClassPathResource[ ]
                { new ClassPathResource("/EclipseProjectName/src/cluster.properties") };
        ppc.setLocations(resource);
        ppc.setIgnoreUnresolvablePlaceholders(true);
        return ppc;
}

// Now load the properties from cluster.properties using the Spring Framework
private @Value("${cluster.instance}") String instanceName;
private @Value("${cluster.username}") String userName;
private @Value("${cluster.password}") String password;
private @Value("${cluster.zooServers}") String zooServers;
private @Value("${cluster.TableName}") String tableName;
private @Value("${cluster.DataFile}") String dataFile;
private @Value("${cluster.DataDelimiter}") String dataDelim;
private @Value("${cluster.rowCount}") int rowCount;

// Getters for the other Java classes to access properties loaded by Spring
public String getInstanceName() {
    return instanceName;
}
public String getUserName() {
    return userName;
}
public String getPassword() {
    return password;
}
public String getZooServers() {
    return zooServers;
}
public String getTableName() {
    return tableName;
}
public String getDataFile() {
    return dataFile;
}
public String getDataDelim() {
    return dataDelim;
}
public int getRowCount() {
    return rowCount;
}

        // method to kick off the ingest of dummy data
        void startIngest() {
            Ingester ingestObject = new Ingester();
            try {
                ingestObject.ingestData();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (TableNotFoundException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (TableExistsException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (AccumuloException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } catch (AccumuloSecurityException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            } // end of try-catch block
        } // end of startIngest method

    } // end of LoadProperties class

Yet when I run MainApp.java in Eclipse the values are null when my Ingester.java class calls the getters.

Here's the console output when I run MainApp.java in Eclipse:

    13/09/24 14:08:24 INFO support.ClassPathXmlApplicationContext: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@191f667c: startup date [Tue Sep 24 14:08:24 EDT 2013]; root of context hierarchy
    13/09/24 14:08:24 INFO xml.XmlBeanDefinitionReader: Loading XML bean definitions from class path resource [context.xml]
    13/09/24 14:08:24 INFO support.DefaultListableBeanFactory: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@3cdd17f5: defining beans [props]; root of factory hierarchy
    Printing out cluster.properties read via Spring...

    instanceName= null
    userName= null
    password= null
    zooServers= null
    tableName= null
    dataFile= null
    dataDelim= null
    rowCount= 0

    Exception in thread "main" java.lang.IllegalArgumentException: argument was null:Is null- arg1? true arg2? true
        at org.apache.accumulo.core.util.ArgumentChecker.notNull(ArgumentChecker.java:36)
        at org.apache.accumulo.core.client.ZooKeeperInstance.<init>(ZooKeeperInstance.java:99)
        at org.apache.accumulo.core.client.ZooKeeperInstance.<init>(ZooKeeperInstance.java:85)
        at accumuloIngest.Ingester.ingestData(Ingester.java:65)
        at accumuloIngest.LoadProperties.startIngest(LoadProperties.java:69)
        at accumuloIngest.MainApp.main(MainApp.java:44)

Am I missing a piece of the Spring framework that loads the properties in my cluster.properties file? I had tried adding @AutoWired to both my MainApp and LoadProperties java classes but that didn't seem to help.

Community
  • 1
  • 1
erj2code
  • 301
  • 5
  • 9
  • 19
  • There are a number of issues here. First, why does your context namespace declare Spring beans version 2.0 when you are using `@Bean` which was introduced in 3.0? – Sotirios Delimanolis Sep 24 '13 at 18:59
  • Yes, that was a copy/paste mistake on my part. I defined Spring Framework 3.2.4 in my pom.xml, so I modified my context.xml to use Spring beans version 3.2 – erj2code Sep 24 '13 at 19:06

1 Answers1

0

If you're going to use @Bean, you'll need @Configuration. You shouldn't declare a xml context to include an annotation context. You also shouldn't use your @Configuration class instance as a bean. ClassPathXmlApplicationContext is no good for processing annotation based configurations.

Use something like the following

@Configuration
@ComponentScan(basePackageClasses = LoadProperties.class)
public static class Config {
    @Bean
    public static PropertyPlaceholderConfigurer props() {
        PropertyPlaceholderConfigurer ppc = new PropertyPlaceholderConfigurer();
        Resource[] resource = new ClassPathResource[] { new ClassPathResource(
                "/EclipseProjectName/src/cluster.properties") };
        ppc.setLocations(resource);
        ppc.setIgnoreUnresolvablePlaceholders(true);
        return ppc;
    }  

    @Bean
    public LoadProperties loadProperties() {
        return new LoadProperties();
    }
}

public static class LoadProperties {
    private @Value("${cluster.zooServers}") String zooServers;
    ... // getters and setters
}

public static void main(String[] args) throws Exception {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
    LoadProperties load = (LoadProperties) context.getBean(LoadProperties.class);
    System.out.println(load.getZooServers());
}

A few things to note:

  1. In your ClassPathResource you need to specify a classpath resource. Do you really have a resource /EclipseProjectName/src/cluster.properties at the root of your classpath? I very much doubt it.
  2. In this case you won't need a @ComponentScan, but familiarize yourself with it.
  3. A PropertyPlaceholderConfigurer needs to be declared static so it can be initialized before the other @Bean declarations. You should use PropertySourcesPlaceholderConfigurer as explained in the javadoc.
Sotirios Delimanolis
  • 274,122
  • 60
  • 696
  • 724
  • Yes, you are correct, I don't have a resource at the root of my classpath. I read the Spring API talking about the ClassPathResource i.e., http://docs.spring.io/spring/docs/3.2.4.RELEASE/javadoc-api/ and realized that I should have specified a java Class above. But how do I determine which ClassPathResource to specify? – erj2code Sep 24 '13 at 19:34
  • @user2160928 Here I assume you want a `.properties` file so specify the path to that. You'll need to figure out how your program is compiled and built to figure out what that path is or should be. – Sotirios Delimanolis Sep 24 '13 at 19:35
  • Ok, so what I have is correct. My cluster.properties file is located under the src directory under my Eclipse Project (which I called EclipseProjectName above) – erj2code Sep 24 '13 at 19:42
  • @user2160928 It might be there on your file system, but eclipse builds your application in the `bin` folder (afaik). Also, where do you think it would be if you ran it from a `war` file? – Sotirios Delimanolis Sep 24 '13 at 19:45
  • I went under the Properties menu for my Eclipse project, and under Java Build Path it includes the Source folder /src on my build path. Also, I tried the code changes you suggested above, but it can't seem to find my cluster.properties file. – erj2code Sep 24 '13 at 20:01
  • @user2160928 Yes, so it'll take everything in `/src`, compile it if it is a `.java` file and copy it over if its anything else. In the properties you can check the `Deployment Assembly` to see where the application is deployed. – Sotirios Delimanolis Sep 24 '13 at 20:03
  • I should add that I think you have a typo when defining your classes above as I got an error when trying to define the Config class as public static class Config – erj2code Sep 24 '13 at 20:09
  • @user2160928 This was just an SSCCE, you can declare the classes in their own files, ie. non-static. – Sotirios Delimanolis Sep 24 '13 at 20:11
  • Yes, that was what I actually did. – erj2code Sep 24 '13 at 20:12
  • @user2160928 Can't really do anything unless you tell me the error :) – Sotirios Delimanolis Sep 24 '13 at 20:13
  • I declared them as their own files, and non-static. I also tried moving my cluster.properties file to the /bin directory and adjusting the path to my .properties file but I had the same errors. I also modified my context.xml to point to the props bean under the Config class. Here's the error I see: Exception in thread "main" org.springframework.beans.factory.BeanInitializationException: Could not load properties; nested exception is java.io.FileNotFoundException: class path resource [EclipseProjectName/src/cluster.properties] cannot be opened because it does not exist – erj2code Sep 24 '13 at 20:26
  • @user2160928 That's because the root of your classpath is probably `bin`. Change your resource path to `cluster.properties`. – Sotirios Delimanolis Sep 24 '13 at 20:27
  • Btw, this error was after I moved the cluster.properties back under the /src directory. – erj2code Sep 24 '13 at 20:29
  • Hey, I changed my resource path to cluster.properties and it worked! It printed out my value for Zookeepers from my cluster.properties file. – erj2code Sep 24 '13 at 20:33
  • @user Start with [this wiki article](http://en.wikipedia.org/wiki/Classpath_(Java)) and then search some more on how the classpath works. – Sotirios Delimanolis Sep 24 '13 at 20:35