[Enjoy Netflix]4, Apache Commons Configuration 2.x File Locator and FileHandler

Keywords: Java encoding Apache Attribute

Writing code is a personal accomplishment, not a requirement of the boss, not to show your colleagues

->Return to Column Directory <-
Code download address: https://github.com/f641385712/netflix-learning

Catalog

Preface

The previous article described Commons Configuration 2.x, which is a new event-listening basis. On the one hand, you've experienced significant improvements over 1.x, and on the other hand, you can see that 2.x is scalable.

2.x is the current mainstream version, so this article is still looking at it.This article will describe its file positioning system, of which the two most important API s are FileLocator and FileHandler, which require attention.
The first article has already described that Commons Configuration can be configured from a variety of sources. Although it is not strongly required to come from files, in fact, we normally use it 99% of the time, and the content comes from files (files can be inside, outside, on the system, on the network, and so on), so the importance of file positioning is evident.

text

2.x also abstracts many new API s in terms of file-related aspects.The file is positioned in 1.x and only needs one sentence of code from ConfigurationUtils#locate(...), it has a fixed positioning policy and cannot be changed.

But 2.x has done a lot in this area, and while it may seem a little cumbersome to use, it gives you enough flexibility to provide strong personalized support when we need hot loading.

FileBased

File-based, this interface provides basic read and write capabilities.

public interface FileBased {
	void read(Reader in) throws ConfigurationException, IOException;
	void write(Writer out) throws ConfigurationException, IOException;
}

public interface FileBasedConfiguration extends FileBased, Configuration {
}

What does this interface do, as shown in the following screenshot:


You can see that all implementation classes are file-based Configuration.

File Location

How important is file location to Commons Configuration? No more, 2.x also abstracts a new set of API s for this, which we need to understand.

Explanation: Some of its API s are really a headache, there are some unreasonable designs, more to wait for ~

FileLocator

It is a class used to describe the location of a file.Instances of this class provide information on finding and accessing files.You can define a file location as a URL; it identifies a file in a unique way.

public final class FileLocator {
	
	// file name
	private final String fileName;
	private final String basePath;
	
	// URL
	private final URL sourceURL;
	private final String encoding; // Code
	
	private final FileSystem fileSystem;
	// It determines the file scanning strategy
	private final FileLocationStrategy locationStrategy;

	// Unique constructor assignment: must pass its own internal class Builder
	public FileLocator(final FileLocatorBuilder builder) { ... }
	... // Omit all get methods

	// Builder for building an instance of FileLocator, is public
    public static final class FileLocatorBuilder {
    	... // Omit attributes identical to the previous one

		// The only constructor, and Defualt, and the entry is FileLocator, there's a feeling that you have me and I have you
		// There are a large number of these designs in version 2.x, which I think is rather poor.
		// The only use of this constructor is the FileLocatorUtils#FileLocatorUtils() method
		FileLocatorBuilder(final FileLocator src) {
            if (src != null) {
            	// This method is to assign all src attributes to builder, nothing to say
                initBuilder(src);
            }
		}
	
		... // Omit all attribute assignment methods

		// Create a FileLocator, and the constructor passes in this
        public FileLocator create() {
            return new FileLocator(this);
        }
    }
	
}

This class cannot be changed and can therefore be shared securely.

Looking at the implementation of its Builder mode, I find it very cumbersome (I have you in me, you have my design in you, I find it really difficult to use), and it is easy to get confused.
Additionally, the builder pattern in Commons Configuration 2.x may not be the pattern you understand and needs to adapt.

FileLocatorUtils (Important)

A utility class that provides helper methods related to locating files.
From the design of FileLocator and FileLocatorBuilder, we know that if we want a FileLocator instance, we must use this tool class.

public final class FileLocatorUtils {

	// In most cases, use the default file system
    public static final FileSystem DEFAULT_FILE_SYSTEM = new DefaultFileSystem();
    // It is a CombinedLocationStrategy, and the order determines the scanning order
    public static final FileLocationStrategy DEFAULT_LOCATION_STRATEGY = initDefaultLocationStrategy();
    // This order determines the order of scans, such as the number of times the directory is first searched
    // Filesystem, in absolute path, in home directory, ** Find the classpath of the current project **
    private static FileLocationStrategy initDefaultLocationStrategy() {
        final FileLocationStrategy[] subStrategies = new FileLocationStrategy[] {
                        new ProvidedURLLocationStrategy(),
                        new FileSystemLocationStrategy(),
                        new AbsoluteNameLocationStrategy(),
                        new BasePathLocationStrategy(),
                        new HomeDirectoryLocationStrategy(true),
                        new HomeDirectoryLocationStrategy(false),
                        new ClasspathLocationStrategy()
                };
        return new CombinedLocationStrategy(Arrays.asList(subStrategies));
    }

	...
	// Defining these constants makes it easy for you to pass values using Map for each property of `FileLocator`
	// Why doesn't this write property go public?Some API designs for this library do slag~~~
	private static final String PROP_BASE_PATH = "basePath";
	private static final String PROP_ENCODING = "encoding";
	private static final String PROP_FILE_NAME = "fileName";
	...
	private static final String PROP_STRATEGY = "locationStrategy";

	// Attempt to convert the specified URL to a file object
	// It's just a fault-tolerant path, then a new File(fileName)
    public static File fileFromURL(final URL url) {
        return FileUtils.toFile(url);
    }

	// You can see that if you want a new fileLocator, you can have it without a sec, or all the attributes are null
    public static FileLocator.FileLocatorBuilder fileLocator() {
        return fileLocator(null);
    }
    public static FileLocator.FileLocatorBuilder fileLocator(final FileLocator src) {
        return new FileLocator.FileLocatorBuilder(src);
    }

	// Use Map to assign values to each property of FileLocator, such as map.get(PROP_BASE_PATH)
	public static FileLocator fromMap(final Map<String, ?> map) { ... }
	// Put all the attribute values of this locator in the map
	public static void put(final FileLocator locator, final Map<String, Object> map) { ... }

	// locator defines location only if fileName or sourceUrl has a value
	public static boolean isLocationDefined(final FileLocator locator) { ... }
	// fileName basePath souceURL has a value before returning true
	public static boolean isFullyInitialized(final FileLocator locator) { ... }

	// This method ensures that the FileLocator pointing to the file is set in a consistent manner.Don't have some that are short of arms and legs
	// Let this locator set completely - >All three attributes are assigned
	// FileLocationStrategy#locate() is used here to complete the url
	public static FileLocator fullyInitializedLocator(final FileLocator locator) { ... }


	// Note: FileLocator may be missing arms and legs at this time, even if only one FileName can find you.
	// So this method is very important, it's a reliable one, and it's often used
    public static URL locate(final FileLocator locator) {
        if (locator == null) {
            return null;
        }
        return obtainLocationStrategy(locator).locate(obtainFileSystem(locator), locator);
    }

}

This tool class is used a lot in encoding in peacetime and needs to master the common API s.

The difference between 1.x and 2.x locating a file
@Test
public void fun2(){
    // 1.x
    URL file1 = ConfigurationUtils.locate("1.properties");
    System.out.println(file1);

    // 2.x
    URL file2 = FileLocatorUtils.locate(FileLocatorUtils.fileLocator().fileName("1.properties").create());
    System.out.println(file2);
}

1.x Locating a file is easy, but at 2.x it's obviously a lot of trouble.I also wonder why not extract a more convenient API for such a common function???

FileLocatorAware

After learning Spring, you are no stranger to the Aware interface.

public interface FileLocatorAware {
	void initFileLocator(FileLocator locator);
}

The initFileLocator delegate passes you the description of the file. The only call is in the FileHandler, which injects the FileLocator file for you. All implementations of this interface are subclasses of Configuration:

FileLocationStrategy

File Location Policy Interface.

File Scanning Policies for 1.x and 2.x

1.x is to find and locate files in a fixed order:

  1. user home
  2. Current factory classpath
  3. System classpath

2.x makes this more flexible: it allows applications to customize the file location process, which is what this interface does.It has the following implementation classes:


These orders can be freely combined or even customized.For example:

List<FileLocationStrategy> subs = Arrays.asList(
        new ProvidedURLLocationStrategy(),
        new FileSystemLocationStrategy(),
        new ClasspathLocationStrategy());
FileLocationStrategy strategy = new CombinedLocationStrategy(subs);

This final positioning strategy is translated as follows:

  1. Locate using your own URL (such as having a complete URL path)
  2. file system
  3. Current project classpath (such as having only one name)

FileHandler (Important)

A class that manages the persistence actions of associated FileBased objects.Instances of this class can be used to easily load and save any object that implements the FileBased interface from different locations.

When constructing, you should pass in FileBased.Basically, this object is assigned a load location associated with a save location (although you can also temporarily specify a path location when loading ()/save ().

public class FileHandler {

	private final FileBased content;
	// FileLocator for Current File
	private final AtomicReference<FileLocator> fileLocator;
	private final List<FileHandlerListener> listeners = new CopyOnWriteArrayList<>();

	// Constructor, assigning values to attributes
    public FileHandler() {
        this(null);
    }
    // At this point, the FileLocator is empty and all data is null
    public FileHandler(final FileBased obj) {
        this(obj, emptyFileLocator());
    }
    // Here: FileLocator is retrieved from FileHandler
    public FileHandler(final FileBased obj, final FileHandler c) {
        this(obj, checkSourceHandler(c).getFileLocator());
    }

	... // Relevant get methods and methods to check for additions or deletions to listeners

	public String getFileName() {
		FileLocator locator = getFileLocator();
		if (locator.getFileName() != null)
			return locator.getFileName();
		if (locator.getSourceURL() != null)
			return FileLocatorUtils.getFileName(locator.getSourceURL());
		return null;
	}
	

	public void setFileName(final String fileName) { ... }
	public void setBasePath(final String basePath) { ... }
	// It is not null provided that fileName or sourceUrl is not null
	// Dependent methods are FileLocatorUtils.fileFromURL/FileLocatorUtils.getFile
	public File getFile() {
		return createFile(getFileLocator());
	}

	// ===============Static method for quickly building a FileHandler instance=========
	// Note, however, that FileBased is null, so it is used to build a FileLocator to facilitate the constructor to pass values
	// Have to spit out again: What garbage API was designed, isn't it fragrant for the constructor to pass FileLocator directly?
    public static FileHandler fromMap(final Map<String, ?> map) {
        return new FileHandler(null, FileLocatorUtils.fromMap(map));
    }

	// Determines whether the associated File exists and returns true
	public boolean locate() { ... }
	public void clearLocation() { ... .basePath(null).fileName(null).sourceURL(null); ... }
	
	...
	// Load files from FileLocator
	public void load() throws ConfigurationException { ... }
	// Load from this fileName you specified and fill it in FileBased
	public void load(final String fileName) throws ConfigurationException { ... }
	... // Omit other overload methods

	 // Write FileBased content into associated Files
	 public void save() throws ConfigurationException { ... }
	 public void save(final String fileName) throws ConfigurationException { ... }
	 ...
	
	// In the above load() and save processes, various events of the listener are triggered so that these actions can be heard externally
}

FileHandler concludes that it works by binding a relationship to FileBased (which is definitely a Configaration instance) and FileLocator, and then doing load/save reads and writes with ease.

Use examples

Read the contents of a file into PropertiesConfiguration without using Configurations.

@Test
public void fun3() throws ConfigurationException {
    // Configurations configs = new Configurations();
    // FileLocator fileLocator = FileLocatorUtils.fileLocator().fileName("1.properties").create();
    // fileLocator = FileLocatorUtils.fullyInitializedLocator(fileLocator);
    // PropertiesConfiguration config = configs.properties(fileLocator.getSourceURL());

    PropertiesConfiguration config = new PropertiesConfiguration();

    // Associate config with file
    Map<String, Object> map = new HashMap<>();
    map.put("fileName", "1.properties");
    FileHandler fileHandler = new FileHandler(config, FileHandler.fromMap(map));

    ConfigurationUtils.dump(config, System.out);
    System.out.println("\n==============Above is load before==============");
    fileHandler.load(); // Configuration can also be assigned via fileHandler
    ConfigurationUtils.dump(config, System.out);
    System.out.println("\n==============Above is load after==============");
    fileHandler.load();
    ConfigurationUtils.dump(config, System.out);
    System.out.println("\n==============Up to Again load One-time results==============");

    config.clear(); // ConfigurationEvent.CLEAR event will be sent
    fileHandler.load();
    ConfigurationUtils.dump(config, System.out);
    System.out.println("\n==============Above is clear Empty before load Results==============");
}

Print results:

==============Above is load before==============
common.name=YourBatman
common.age=18
common.addr=China
common.count=4
common.fullname=${common.name}-Bryant
java.version=1.8.123
==============Above is load after==============
common.name=[YourBatman, YourBatman]
common.age=[18, 18]
common.addr=[China, China]
common.count=[4, 4]
common.fullname=[${common.name}-Bryant, ${common.name}-Bryant]
java.version=[1.8.123, 1.8.123]
==============Up to Again load One-time results==============
common.name=YourBatman
common.age=18
common.addr=China
common.count=4
common.fullname=${common.name}-Bryant
java.version=1.8.123
==============Above is clear Empty before load Results==============

Note: For reasons why ${} placeholders are not parsed when printed, see article 2 for more details

The following conclusions can be drawn from the results:

  1. You can also write data to xxxConfiguration (file-based FileBased instance) without using Configurations, and you can write indefinitely
  2. New reading is still attached to the original (writing is also incremental writing...)
  3. If you want to read it again, you can do clear() before you read the latest
  4. For each step of operation, such as load() and save, a corresponding event is sent: FileHandlerListener (its built-in implementation only has AutoSaveListener)
    1. There's nothing to say about this listener. There are several ways to listen on a FileHandler: load, loaded, saved, saved, locationChanged for various file states~

summary

About Apache CommonsThis is the case with the Configuration 2.x version of the File Location System, which completely weakens the user's understanding of the positioning logic (blocking out the details of implementation) compared to 1.x. 2.x has done a lot of articles in this area so that users can even customize the FileLocation Strategy strategy at the expense of ease of use and difficulty in understanding.Without a silver bullet, trade-offs are the key.

statement

The original is not easy, the code is not easy. Thank you for your compliment, collection and attention.Sharing this article with your circle of friends is allowed, but you refuse to copy it.You can also join my family of Java engineers and architects to learn and communicate.

299 original articles published. 456 approved. 390,000 visits+
His message board follow

Posted by tech603 on Tue, 18 Feb 2020 18:15:44 -0800