3

is there a way to autowire a list containing Strings inside another list read from a properties-file? The difficulty I found is that the property-values need to be split to become a String list (or Array) and shall then be autowired to. My properties-file looks something like this:

jobFolders1=C:/Temp/originFolder, C:/Temp/trafoIn, C:/Temp/trafoOut, C:/Temp/destinationFolder
jobFolders2=C:/Temp/originFolder2, C:/Temp/trafoIn2, C:/Temp/trafoOut2, C:/Temp/destinationFolder2

Now I want my users to be able to add lines to that file, whenever there are new jobs. So I never know the names of the keys, nor the amount of lines. Is there some way to autowire the file-entries to a List which itself contains a List containing the 4 Strings (split by ",")? Probably this entire approach is not the best. If so, please feel free to tell me so.

Wingie
  • 111
  • 1
  • 2
  • 10
  • To clarify: I imagine a mix of the answer here: [link](http://stackoverflow.com/a/30561609/2118899) and this one: [link](http://stackoverflow.com/a/12580260/2118899). It´s a map in this case, but that would still be alright. – Wingie Feb 03 '16 at 10:39

3 Answers3

2

Alright, following is a quite "springy" solution, although I think one could solve this more elegant (without writing custom code at all):

Write a PropertyMapper:

@Component("PropertyMapper")
public class PropertyMapper {

@Autowired
ApplicationContext context;
@Autowired
List<List<String>> split;

public List<List<String>> splitValues(final String beanname) {
((Properties) this.context.getBean(beanname)).values().forEach(v -> {
final List<String> paths = Arrays.asList(((String) v).split(","));
paths.forEach(p -> paths.set(paths.indexOf(p), p.trim()));
this.split.add(paths);
});
return this.split;
}

Load properties in context.xml like this:

<util:properties id="testProps" location="classpath:test.properties"/>

And then wire the values to the field, using Spring EL 'tweaking' the original values by calling splitValues method on them:

@Value("#{PropertyMapper.splitValues('testProps')}")
private List<List<String>> allPaths;
Wingie
  • 111
  • 1
  • 2
  • 10
  • Please see [my answer to a similar question](http://stackoverflow.com/questions/28369458/how-to-fill-hashmap-from-java-property-file-with-spring-value/28370899#28370899) posted months ago. In particular, see the `groupedList()` method of the `PropertySplitter` class. – fps Feb 03 '16 at 16:54
  • @Federico Peralta Schaffner I reviewed your answer before, when I did my research on this, but didn´t want to add a 3rd party lib to my project. Although I must confesss your answer helped me to get on track with my solution. – Wingie Feb 04 '16 at 08:30
  • Oh, glad to hear that :) You don't have to actually use Guava, just code the splitting functionality yourself and you'll be OK. Let me know if you need some help with that. – fps Feb 05 '16 at 13:15
  • I already did so. It´s in my answer: `final List paths = Arrays.asList(((String) v).split(",")); paths.forEach(p -> paths.set(paths.indexOf(p), p.trim()));` – Wingie Feb 05 '16 at 14:37
0

You could use Apache Commons Configuration. The getKeys() method will give you the list of the keys contained in the configuration. The refresh strategy ensures that you will be able to dynamically load new properties added during runtime. I've written a sample example for your convenience but haven't tested it.

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.configuration.PropertiesConfiguration;
import org.apache.commons.configuration.reloading.FileChangedReloadingStrategy;
import org.apache.log4j.Logger;

public class SystemProperties {

    private static final Logger LOG = Logger.getLogger(SystemProperties.class.getSimpleName());;

    private static SystemProperties singleton = new SystemProperties();

    private PropertiesConfiguration conf = null;
    private final FileChangedReloadingStrategy strategy = new FileChangedReloadingStrategy();

    private static final long REFRESH_INTERVAL = 2000;

    private final String configurationFilePath = "/my-properties-files/system.properties";

    private SystemProperties() 
    {
        conf = new PropertiesConfiguration();
        conf.setDelimiterParsingDisabled(true);

        if (conf.getFile() == null) 
        {
            try {
                URL url = getClass().getResource(configurationFilePath);

                File configurationFile = new File(url.getFile());
                InputStream in = new FileInputStream(url.getFile());
                conf.load(in);
                conf.setFile(configurationFile);
            } catch (final Exception ex) {
                LOG.error("SystemProperties: Could not load properties file ", ex);
            }
        }

        if (conf.getFile() == null) 
        {
            LOG.warn("File could not be loaded");
        }

        strategy.setRefreshDelay(REFRESH_INTERVAL);
        conf.setReloadingStrategy(strategy);
        conf.setAutoSave(true);
    }

    public static SystemProperties getInstance() 
    {
        return singleton;
    }

    public String get(String s) 
    {
        return conf.getString(s);
    }

    public List<String> getKeys() 
    {
        @SuppressWarnings("unchecked")
        final Iterator<String> keysIterator = conf.getKeys();
        if (keysIterator != null) {
            final List<String> keysList = new ArrayList<String>();
            while(keysIterator.hasNext()) {
                keysList.add(keysIterator.next());
            }
            return keysList; 
        }
        return Collections.emptyList();
    }

    public void setProperty(String property, String value) throws Exception 
    {
        conf.setProperty(property, value);
    }
}
Apostolos Emmanouilidis
  • 7,179
  • 1
  • 24
  • 35
  • Thanks for the comment. I appreciate the effort of your example, although it is pretty much not autowired and therefor of not much use for me. I basically know how to find a solution for this, but would like to do so using spring. – Wingie Feb 03 '16 at 10:11
0

What you could do is storing all directories of all jobs in a single value. The jobs would be separated by a special character that is certainly not used in any of the directory names.

To keep it readable, you can use a multiline property value. Example, using | (pipe) as separator:

# Starting with a line break for readability.
# Looks nice, but the first array value will be empty.
# Just leave it out if you don't want this.
jobFolders=|\
    C:/Temp/originFolder, C:/Temp/trafoIn, C:/Temp/trafoOut, C:/Temp/destinationFolder|\
    C:/Temp/originFolder2, C:/Temp/trafoIn2, C:/Temp/trafoOut2, C:/Temp/destinationFolder2

In your code, you can simply apply

String[] jobs = jobFolders.split("|");

to the value to get an array of all job configurations.


Side note: The separator can be any of the reserved filename characters, but make sure to choose one that is certainly invalid on your target platform. Read the Wikipedia article for more information.

I used | in the example, which is not allowed in filenames by commom operating systems, but may be allowed in Unix filenames.

Community
  • 1
  • 1
Hexaholic
  • 3,299
  • 7
  • 30
  • 39