@CleanupObligation public final class TConfig extends net.java.truecommons.shed.Resource<IllegalStateException>
At any time, a thread can call current()
to get access to the
mutable current configuration.
If no configuration has been pushed onto the inheritable thread local
configuration stack before, then this will return the
global configuration which is shared by all threads.
As an implication, accessing the global configuration may not be thread-safe.
To create an inheritable thread local configuration, a thread may
call open()
.
This will copy the current configuration (which may be the global
configuration) and push the copy on top of the inheritable thread
local configuration stack.
Later, the thread can use close()
to pop this configuration
off the top of the inheritable thread local configuration stack again.
Finally, whenever a child thread gets started, it will share the
same current configuration with its parent thread.
If the parent's inheritable thread local configuration stack is empty, then
the child will share the global configuration as its current configuration
with its parent.
Note that the child thread cannot close()
the inherited current
configuration - this would result in an IllegalStateException
.
If the thread local configuration stack is empty, i.e. no open()
without a corresponding close()
has been called before, then the
current()
method will return the global configuration.
This feature is intended to get used during application setup to change some
configuration options with global scope like this:
class MyApplication extends TApplication<IOException> {
@Override
protected void setup() {
// This should obtain the global configuration.
TConfig config = TConfig.current();
// Configure custom application file format.
config.setArchiveDetector(new TArchiveDetector("aff", new JarDriver()));
// Set FsAccessOption.GROW for appending-to rather than reassembling
// existing archive files.
config.setAccessPreference(FsAccessOption.GROW, true);
}
...
}
If an application needs to change the configuration for just the current
thread rather than changing the global configuration, then the
open()
method needs to get called like this:
TFile file1 = new TFile("file.aff");
assert !file1.isArchive();
// First, push a new current configuration onto the inheritable thread local
// stack.
try (TConfig config = TConfig.open()) {
// Configure custom application file format "aff".
config.setArchiveDetector(new TArchiveDetector("aff", new JarDriver()));
// Now use the current configuration.
TFile file2 = new TFile("file.aff");
assert file2.isArchive();
// Do some I/O here.
...
}
By default, TrueVFS is configured to produce the smallest possible archive files. This is achieved by selecting the maximum compression ratio in the archive drivers and by performing an archive update whenever an existing archive entry is going to get overwritten with new contents or updated with new meta data in order to avoid the writing of redundant data to the resulting archive file. An archive update is basically a copy operation where all archive entries which haven't been written yet get copied from the input archive file to the output archive file. However, while this strategy produces the smallest possible archive files, it may yield bad performance if the number and contents of the archive entries to create or update are pretty small compared to the total size of the resulting archive file.
Therefore, you can change this strategy by setting the
FsAccessOption.GROW
output option preference when writing archive
entry contents or updating their meta data.
When set, this output option allows archive files to grow by appending new
or updated archive entries to their end and inhibiting archive update
operations.
You can set this preference in the global configuration as shown above or
you can set it on a case-by-case basis as follows:
// We are going to append "entry" to "archive.zip".
TFile file = new TFile("archive.zip/entry");
// First, push a new current configuration on the inheritable thread local
// stack.
try (TConfig config = TConfig.open()) {
// Set FsAccessOption.GROW for appending-to rather than reassembling
// existing archive files.
config.setAccessPreference(FsAccessOption.GROW, true);
// Now use the current configuration and append the entry to the archive
// file even if it's already present.
try (TFileOutputStream out = new TFileOutputStream(file)) {
// Do some output here.
...
}
}
Note that it's specific to the archive file system driver if this output option preference is supported or not. If it's not supported, then it gets silently ignored, thereby falling back to the default strategy of performing a full archive update whenever required to avoid writing redundant archive entry data.
As of TrueVFS 0.11, the support is like this:
Using the thread local inheritable configuration stack comes in handy when unit testing, e.g. with JUnit. Consider this pattern:
public class AppTest {
private TConfig config;
@Before
public void setUp() {
config = TConfig.open();
// Let's just recognize ZIP files.
config.setArchiveDetector(new TArchiveDetector("zip"));
}
@After
public void shutDown() {
config.close();
}
@Test
public void testMethod() {
// Test accessing some ZIP files here.
...
}
}
Disclaimer: Although this class internally uses an
InheritableThreadLocal
, it does not leak memory in multi class
loader environments when used appropriately.
Modifier and Type | Method and Description |
---|---|
void |
close() |
static TConfig |
current()
Returns the current configuration.
|
boolean |
equals(Object other) |
boolean |
getAccessPreference(FsAccessOption option)
Returns
true if and only if the given access option is set in
the access preferences. |
net.java.truecommons.shed.BitField<FsAccessOption> |
getAccessPreferences()
Returns the access preferences to apply for file system operations.
|
TArchiveDetector |
getArchiveDetector()
Returns the
TArchiveDetector to use for scanning path names for
prospective archive files. |
int |
hashCode() |
boolean |
isLenient()
Returns the value of the property
lenient , which is true
if and only if the access preference FsAccessOption.CREATE_PARENTS
is set in the access preferences. |
protected void |
onBeforeClose()
Pops this configuration off the inheritable thread local configuration
stack.
|
static TConfig |
open()
Creates a new current configuration by copying the current configuration
and pushing the copy onto the inheritable thread local configuration
stack.
|
void |
setAccessPreference(FsAccessOption option,
boolean set)
Sets or clears the given access option in the access preferences.
|
void |
setAccessPreferences(net.java.truecommons.shed.BitField<FsAccessOption> preferences)
Sets the access preferences to apply for file system operations.
|
void |
setArchiveDetector(TArchiveDetector detector)
Sets the default
TArchiveDetector to use for scanning path
names for prospective archive files. |
void |
setLenient(boolean lenient)
Sets the value of the property
lenient . |
String |
toString() |
@DischargesObligation public void close() throws IllegalStateException
close
in interface AutoCloseable
close
in class net.java.truecommons.shed.Resource<IllegalStateException>
IllegalStateException
public static TConfig current()
open()
ed yet, the global
configuration gets returned.
Note that accessing the global configuration is not thread-safe!open()
public boolean getAccessPreference(FsAccessOption option)
true
if and only if the given access option is set in
the access preferences.option
- the access option to test.true
if and only if the given access option is set in
the access preferences.public net.java.truecommons.shed.BitField<FsAccessOption> getAccessPreferences()
public TArchiveDetector getArchiveDetector()
TArchiveDetector
to use for scanning path names for
prospective archive files.TArchiveDetector
to use for scanning path names for
prospective archive files.getArchiveDetector()
public boolean isLenient()
lenient
, which is true
if and only if the access preference FsAccessOption.CREATE_PARENTS
is set in the access preferences.
This property controls whether archive files and their member
directories get automatically created whenever required.
Consider the following path: a/outer.zip/b/inner.zip/c
.
Now let's assume that a
exists as a plain directory in the
platform file system, while all other segments of this path don't, and
that the module TrueVFS Driver ZIP is present on the run-time class path
in order to enable the detection of outer.zip
and
inner.zip
as prospective ZIP files.
Now, if this property is set to false
, then a client application
needs to call new TFile("a/outer.zip/b/inner.zip").mkdirs()
before it can actually create the innermost entry c
as a file
or directory.
More formally, before an application can access an entry in an archive
file system, all its parent directories need to exist, including archive
files.
This emulates the behaviour of the platform file system.
If this property is set to true
however, then any missing
parent directories (including archive files) up to the outermost archive
file outer.zip
get automatically created when using operations
to create the innermost entry c
.
This enables applications to succeed with doing this:
new TFile("a/outer.zip/b/inner.zip/c").createNewFile()
,
or that:
new TFileOutputStream("a/outer.zip/b/inner.zip/c")
.
A most desirable side effect of being lenient is that it will
safe space in the target archive file.
This is because the directory entry b
in this exaple does not
need to get output because there is no meta data associated with it.
This is called a ghost directory.
Note that in either case the parent directory of the outermost archive
file a
must exist - TrueVFS does not automatically create
directories in the platform file system!
lenient
, which is true
if and only if the access preference
FsAccessOption.CREATE_PARENTS
is set in the
access accessPreferences.protected void onBeforeClose() throws IllegalStateException
onBeforeClose
in class net.java.truecommons.shed.Resource<IllegalStateException>
IllegalStateException
- If this configuration is not the
current configuration.@CreatesObligation public static TConfig open()
current()
public void setAccessPreference(FsAccessOption option, boolean set)
option
- the access option to set or clear.set
- true
if you want the option to be set or
false
if you want it to be cleared.public void setAccessPreferences(net.java.truecommons.shed.BitField<FsAccessOption> preferences)
preferences
- the access preferences.IllegalArgumentException
- if an option is present in
accessPreferences
which is not present in
FsAccessOptions.ACCESS_PREFERENCES_MASK
or if both
FsAccessOption.STORE
and
FsAccessOption.COMPRESS
have been set.public void setArchiveDetector(TArchiveDetector detector)
TArchiveDetector
to use for scanning path
names for prospective archive files.
Changing this property will show effect when a new TFile
or
TPath
gets created.detector
- the default TArchiveDetector
to use for
scanning path names for prospective archive files.getArchiveDetector()
public void setLenient(boolean lenient)
lenient
.
Changing this property will show effect upon the next access to the
virtual file system.lenient
- the value of the property lenient
.Copyright © 2005–2018 Schlichtherle IT Services. All rights reserved.