@ThreadSafe @CleanupObligation public final class TConfig extends Resource<RuntimeException> implements Closeable
A thread can call get()
to get access to the
current configuration at any time .
If no configuration has been pushed onto the inheritable thread local
configuration stack before, this will return the global configuration
which is shared by all threads (hence its name).
Mind that access to the global configuration is not synchronized.
To create an inheritable thread local configuration, a thread can
simply call push()
.
This will copy the current configuration (which may be identical to
the global configuration) and push the copy on top of the inheritable thread
local configuration stack.
Later, the thread can use pop()
or Resource.close()
to
pop the current configuration or this
configuration respectively
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.
This is achieved by copying the top level element of the parent's
inheritable thread local configuration stack.
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.
As an implication, pop()
or Resource.close()
can be called at most
once in the child thread.
If the thread local configuration stack is empty, i.e. no push()
without a corresponding Resource.close()
or pop()
has been called
before, then the get()
method will return the global configuration.
This feature is intended to get used during the application setup to change
some configuration options with global scope like this:
class MyApplication extends TApplication {
@Override
protected void setup() {
// This should obtain the global configuration.
TConfig config = TConfig.get();
// Configure custom application file format.
config.setArchiveDetector(new TArchiveDetector("aff",
new JarDriver(IOPoolLocator.SINGLETON)));
// Set FsOutputOption.GROW for appending-to rather than reassembling
// existing archive files.
config.setOutputPreferences(
config.getOutputPreferences().set(FsOutputOption.GROW));
}
...
}
If an application needs to change the configuration of just the current
thread rather than changing the global configuration, then the
push()
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.
TConfig config = TConfig.push();
try {
// Configure custom application file format "aff".
config.setArchiveDetector(new TArchiveDetector("aff",
new JarDriver(IOPoolLocator.SINGLETON)));
// Now use the current configuration.
TFile file2 = new TFile("file.aff");
assert file2.isArchive();
// Do some I/O here.
...
} finally {
// Pop the current configuration off the inheritable thread local stack.
config.close();
}
Using try-with-resources in JSE 7, this can get shortened to:
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.push()) {
// Configure custom application file format "aff".
config.setArchiveDetector(new TArchiveDetector("aff",
new JarDriver(IOPoolLocator.SINGLETON)));
// Now use the current configuration.
TFile file2 = new TFile("file.aff");
assert file2.isArchive();
// Do some I/O here.
...
}
By default, TrueZIP 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
FsOutputOption.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.
TConfig config = TConfig.push();
try {
// Set FsOutputOption.GROW for appending-to rather than reassembling
// existing archive files.
config.setOutputPreferences(
config.getOutputPreferences().set(FsOutputOption.GROW));
// Now use the current configuration and append the entry to the archive
// file even if it's already present.
TFileOutputStream out = new TFileOutputStream(file);
try {
// Do some output here.
...
} finally {
out.close();
}
} finally {
// Pop the current configuration off the inheritable thread local stack.
config.close();
}
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 TrueZIP 7.5, 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 {
@Before
public void setUp() {
TConfig config = TConfig.push();
// Let's just recognize ZIP files.
config.setArchiveDetector(new TArchiveDetector("zip"));
}
@After
public void shutDown() {
TConfig.pop();
}
@Test
public void testMethod() {
// Test accessing some ZIP files here.
...
}
}
Note that it's not necessary to save the reference to the new pushed
configuration in setUp()
.
shutDown()
will just pop the top configuration off the inheritable
thread local configuration stack.
Disclaimer: Although this classes internally uses an
InheritableThreadLocal
, it does not leak memory in multi class
loader environments when used appropriately.
Modifier and Type | Field and Description |
---|---|
static BitField<FsInputOption> |
DEFAULT_INPUT_PREFERENCES
The default value of the
input preferences property, which is
FsInputOptions.NONE . |
static BitField<FsOutputOption> |
DEFAULT_OUTPUT_PREFERENCES
The default value of the
output preferences property, which is
. |
Modifier and Type | Method and Description |
---|---|
static TConfig |
get()
Returns the current configuration.
|
TArchiveDetector |
getArchiveDetector()
Returns the default
TArchiveDetector to use for scanning path
names for prospective archive files if no TArchiveDetector has
been explicitly provided to a constructor. |
BitField<FsInputOption> |
getInputPreferences()
Returns the input preferences.
|
BitField<FsOutputOption> |
getOutputPreferences()
Returns the output preferences.
|
boolean |
isLenient()
Returns the value of the property
lenient . |
protected void |
onClose() |
static void |
pop()
Pops the
current configuration off the inheritable thread
local configuration stack. |
static TConfig |
push()
Creates a new current configuration by copying the current configuration
and pushing the copy onto the inheritable thread local configuration
stack.
|
void |
setArchiveDetector(TArchiveDetector detector)
Sets the default
TArchiveDetector to use for scanning path
names for prospective archive files if no TArchiveDetector has
been explicitly provided to a TFile constructor. |
void |
setInputPreferences(BitField<FsInputOption> preferences)
Sets the input preferences.
|
void |
setLenient(boolean lenient)
Sets the value of the property
lenient . |
void |
setOutputPreferences(BitField<FsOutputOption> preferences)
Sets the output preferences.
|
public static final BitField<FsInputOption> DEFAULT_INPUT_PREFERENCES
input preferences
property, which is
FsInputOptions.NONE
.public static final BitField<FsOutputOption> DEFAULT_OUTPUT_PREFERENCES
output preferences
property, which is
BitField
.of(FsOutputOption.CREATE_PARENTS
)
.public static TConfig get()
pushed
yet, the global
configuration is returned.
Mind that the global configuration is shared by all threads.push()
public TArchiveDetector getArchiveDetector()
TArchiveDetector
to use for scanning path
names for prospective archive files if no TArchiveDetector
has
been explicitly provided to a constructor.TArchiveDetector
to use for scanning
path names for prospective archive files.setArchiveDetector(de.schlichtherle.truezip.file.TArchiveDetector)
public BitField<FsInputOption> getInputPreferences()
public BitField<FsOutputOption> getOutputPreferences()
public boolean isLenient()
lenient
.lenient
.setLenient(boolean)
protected void onClose()
onClose
in class Resource<RuntimeException>
@DischargesObligation public static void pop()
current configuration
off the inheritable thread
local configuration stack.IllegalStateException
- If the current configuration
is the global configuration.@CreatesObligation public static TConfig push()
get()
public void setArchiveDetector(TArchiveDetector detector)
TArchiveDetector
to use for scanning path
names for prospective archive files if no TArchiveDetector
has
been explicitly provided to a TFile
constructor.
Because the TFile
class is immutable, changing the value of this
property will affect the scanning of path names of subsequently
constructed TFile
objects only - existing TFile
objects
will not be affected.detector
- the default TArchiveDetector
to use for scanning
path names for prospective archive files.getArchiveDetector()
public void setInputPreferences(BitField<FsInputOption> preferences)
preferences
- the input preferences.IllegalArgumentException
- if an option is present in
preferences
which is not present in
FsInputOptions.INPUT_PREFERENCES_MASK
.public void setLenient(boolean lenient)
lenient
.
This property controls whether archive files and their member
directories get automatically created whenever required.
By default, the value of this class property is true
!
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 TrueZIP Driver ZIP is present on the run-time class path
in order to detect outer.zip
and inner.zip
as ZIP files
according to the initial setup.
Now, if this property is set to false
, then an application
needs to call new TFile("a/outer.zip/b/inner.zip").mkdirs()
before it can actually create the innermost c
entry as a file
or directory.
More formally, before an application can access an entry in a federated 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 element of the path 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")
.
Note that in either case the parent directory of the outermost archive
file a
must exist - TrueZIP does not automatically create
directories in the platform file system!
lenient
- the value of the property lenient
.isLenient()
public void setOutputPreferences(BitField<FsOutputOption> preferences)
preferences
- the output preferences.IllegalArgumentException
- if an option is present in
preferences
which is not present in
FsOutputOptions.OUTPUT_PREFERENCES_MASK
or if both
FsOutputOption.STORE
and
FsOutputOption.COMPRESS
have been set.Copyright © 2005–2018 Schlichtherle IT Services. All rights reserved.