Class AudioSources
AudioSourceProvider instances via ServiceLoader,
runs content-based scoring against every registered provider, and returns
an AudioSource produced by the provider with the strictly greatest
SPI Priority Score. Ties on score are broken by priority() (Requirement 5.6).
AudioSources is the dtmf-io module's façade over the
provider SPI: callers hand in a Path, a markable
InputStream (plus optional file-name / URL-segment / MIME-type
hint), or a URL, and get back an AudioSource without
having to know which format module is on the classpath. Non-markable
streams are transparently wrapped in a BufferedInputStream sized
to 16384 bytes before scoring, so providers always
see a markable stream they can rewind after reading the header prefix
(Requirement 5.12, 11.2).
Dispatch
For every public open(...) call, the facade:
- Discovers providers via
ServiceLoader.load(AudioSourceProvider.class, loader)on the context class loader (falling back toAudioSources's own class loader when the context loader isnull), caching the result for the lifetime of the JVM (Requirement 5.5). - Invokes
canOpen(...)on every cached provider, catchingIOExceptionand logging it atWARNINGbefore treating the provider as having returned-1(Requirement 5.9). - Picks the winner as the provider with the strictly greatest
(score, priority())lexicographic pair over all scores>= 0(Requirement 5.6). - Delegates the actual open to the winner's
open(...)method.
Error handling
If every registered provider returns -1 (including the case
where each one's canOpen threw an IOException), this
facade throws UnsupportedAudioFormatException with
UnsupportedAudioFormatException.providersConsulted() and
UnsupportedAudioFormatException.providerScores() populated from
the scoring loop (Requirements 5.7, 6.4, 6.5, 6.6). If no providers are
registered at all, the thrown exception carries a distinct message
pointing the caller at the dtmf-io-wav / dtmf-io-mp3
modules (Requirement 5.8).
File-not-found special case. On open(Path),
if every provider returned -1 because each
one's canOpen(Path) threw an IOException, the facade
re-throws the first captured IOException instead of throwing
UnsupportedAudioFormatException. This preserves NoSuchFileException (and its siblings — permission
errors, access-denied, etc.) as distinct error signals rather than
masquerading as "format not supported" (Requirement 12.3, 12.4). The
special case does not apply to open(InputStream, String)
because an IOException from canOpen(InputStream, ...)
is a read-failure during header inspection, not a missing-source
signal.
Thread safety
AudioSources is thread-safe. The provider cache is a
volatile List<AudioSourceProvider> field populated under
double-checked lazy initialization guarded by the class monitor; the
ServiceLoader iterator — which is not itself thread-safe — is
consumed exclusively inside the synchronized block. The cache is
computed once per JVM and returned immutably for every subsequent
call. Provider instances themselves may be called concurrently from
multiple threads; see AudioSourceProvider for that contract.
- Since:
- 2.1.0
- See Also:
-
Method Summary
Modifier and TypeMethodDescriptionstatic AudioSourceopen(InputStream stream, String hint) Open anAudioSourcefor the givenInputStream, scoring every registered provider against it and dispatching to the winner (Requirement 5.3).static AudioSourceOpen anAudioSourcefor the resource aturl.static AudioSourceOpen anAudioSourcefor the file atpathby scoring every registeredAudioSourceProvideragainst it and dispatching to the winner (Requirement 5.2).Names of every loadedAudioSourceProviderinServiceLoaderdiscovery order (Requirement 5.11).
-
Method Details
-
open
Open anAudioSourcefor the file atpathby scoring every registeredAudioSourceProvideragainst it and dispatching to the winner (Requirement 5.2).See the class Javadoc for the dispatch algorithm and error handling — including the special case where the sole error signal from every provider is an
IOException(the first such exception is re-thrown verbatim to preserveNoSuchFileExceptionand permission errors).- Parameters:
path- the file to open; must be non-null- Returns:
- an opened
AudioSourceproduced by the winning provider - Throws:
UnsupportedAudioFormatException- if no provider is applicable, or if no providers are registered at allIOException- on any other I/O failure, including the pass-through of a capturedIOExceptionwhen every provider rejectedpathby throwing oneNullPointerException- ifpathisnull
-
open
Open anAudioSourcefor the givenInputStream, scoring every registered provider against it and dispatching to the winner (Requirement 5.3).The optional
hintargument — a file name, URL path segment, or MIME type — is forwarded verbatim to every provider'scanOpenand (on dispatch)openmethods. Providers may use it as a fallback signal when content-based detection is ambiguous or when the stream is not markable; content-based scoring always takes precedence on a markable stream.If
streamdoes not supportmark/reset, it is wrapped in aBufferedInputStreamsized to at least 16384 bytes before being forwarded to providers (Requirement 5.12, 11.2). Providers can therefore always assume the stream they receive is markable.The returned
AudioSourcedoes not closestreamwhen its ownAudioSource.close()is invoked; ownership of the caller-supplied stream stays with the caller (Requirement 4.10). If the facade wrapped the stream in aBufferedInputStream, that wrapper is also not closed on the caller's behalf.- Parameters:
stream- markable (or wrappable) stream to open; must be non-nullhint- optional caller-supplied hint (file name, URL path segment, or MIME type); may benull- Returns:
- an opened
AudioSourceproduced by the winning provider - Throws:
UnsupportedAudioFormatException- if no provider is applicable, or if no providers are registered at allIOException- on any other I/O failureNullPointerException- ifstreamisnull
-
open
Open anAudioSourcefor the resource aturl. Opens the URL viaurl.openStream(), derives the hint from the last'/'-separated segment ofurl.getPath(), and delegates toopen(InputStream, String)(Requirement 5.4, 11.1).If
open(InputStream, String)throws, the stream opened byurl.openStream()is closed before the exception propagates so the underlying URL connection does not leak (Requirement 11.4). On a successful return, the stream's lifecycle is governed by the returnedAudioSource: the caller should close theAudioSourceto release the connection.- Parameters:
url- the URL to open; must be non-null- Returns:
- an opened
AudioSourceproduced by the winning provider - Throws:
UnsupportedAudioFormatException- if no provider is applicable, or if no providers are registered at allIOException- on any other I/O failure, includingURL.openStream()failing to connectNullPointerException- ifurlisnull
-
registeredFormats
Names of every loadedAudioSourceProviderinServiceLoaderdiscovery order (Requirement 5.11). Provider discovery is cached, so repeated calls return the same list.- Returns:
- immutable list of provider format names in discovery order
-