This specification standardizes the mechanism to extend the Java run-time with new URL schemes and content handlers through bundles. Dynamically extending the URL schemes that are supported in an OSGi framework is a powerful concept.
This specification is necessary because the
standard Java mechanisms for extending the URL
class with new
schemes and different content types is not compatible with the dynamic
aspects of an OSGi framework. The registration of a new scheme or content
type is a one time only action in Java, and once registered, a scheme or
content type can never be revoked. This singleton approach to registration
makes the provided mechanism impossible to use by different, independent
bundles. Therefore, it is necessary for OSGi Framework implementations to
hide this mechanism and provide an alternative mechanism that can be
used.
-
Multiple Access - Multiple bundles should be allowed to register
ContentHandler
objects andURLStreamHandler
objects. -
Existing Schemes Availability - Existing schemes in an OSGi framework should not be overridden.
-
life cycle Monitored - The life cycle of bundles must be supported. Scheme handlers and content type handlers must become unavailable when the registering bundle is stopped.
-
Simplicity - Minimal effort should be required for a bundle to provide a new URL scheme or content type handler.
-
Scheme - An identifier for a specific protocol. For example,
"http"
is a scheme for the Hyper Text Transfer Protocol. A scheme is implemented in ajava.net.URLStreamHandler
sub-class. -
Content Type - An identifier for the type of the content. Content types are usually referred to as MIME types. A content type handler is implemented as a
java.net.ContentHandler
sub-class. -
Uniform Resource Locator (URL) - An instance of the
java.net.URL
class that holds the name of a scheme with enough parameters to identify a resource for that scheme. -
Factory - An object that creates other objects. The purpose is to hide the implementation types (that may vary) from the caller. The created objects are a subclass/implementation of a specific type.
-
Proxy - The object that is registered with Java and that forwards all calls to the real implementation that is registered with the service registry.
-
java.net.URLStreamHandler - An instance of the
java.net.URLStreamHandler
class that can createURLConnection
objects that represent a connection for a specific protocol. -
Singleton Operation - An operation that can only be executed once.
-
URLStreamHandlerService - An OSGi service interface that contains the methods of the
URLStreamHandler
class with public visibility so they can be called from the Framework. -
AbstractURLStreamHandlerService - An implementation of the
URLStreamHandlerService
interface that implements the interface's methods by calling the implementation of the super class (java.net.url.URLStreamHandler
). This class also handles the setting of thejava.net.URL
object via thejava.net.URLStreamHandlerSetter
interface. -
URLStreamHandlerSetter - An interface needed to abstract the setting of the
java.net.URL
object. This interface is related to the use of a proxy and security checking. -
java.net.URLStreamHandlerFactory - A factory, registered with the
java.net.URL
class, that is used to findjava.net.URLStreamHandler
objects implementing schemes that are not implemented by the Java environment. Only onejava.net.URLStreamHandlerFactory
object can be registered with Java. -
java.net.URLConnection - A connection for a specific, scheme-based protocol. A
java.net.URLConnection
object is created by ajava.net.URLStreamHandler
object when thejava.net.URL.openConnection
method is invoked. -
java.net.ContentHandler - An object that can convert a stream of bytes to a Java object. The class of this Java object depends on the MIME type of the byte stream.
-
java.net.ContentHandlerFactory - A factory that can extend the set of
java.net.ContentHandler
objects provided by thejava.net.URLConnection
class, by creating new ones on demand. Only onejava.net.ContentHandlerFactory
object can be registered with thejava.net.URLConnection
class. -
MIME Type - A namespace for byte stream formats. See [2] MIME Multipurpose Internet Mail Extension.
The following class diagram is surprisingly complex due to the complicated strategy that Java uses to implement extendable stream handlers and content handlers.
A bundle that can implement a new URL scheme should register a
service object under the URLStreamHandlerService
interface
with the OSGi Framework. This interface contains public versions of the
java.net.URLStreamHandler
class methods, so that these
methods can be called by the proxy (the object that
is actually registered with the Java run-time).
The OSGi Framework implementation must make this service object
available to the underlying java.net
implementation. This
must be supported by the OSGi Framework implementation because the
java.net.URL
.setStreamHandlerFactory
method
can only be called once, making it impossible to
use by bundles that come and go.
Bundles that can convert a content-typed stream
should register a service object under the name
java.net.ContentHandler
. These objects should be made
available by the OSGi Framework to the
java.net.URLConnection
class.
Java provides the java.net.URL
class which is used by
the OSGi Framework and many of the bundles that run on the OSGi framework.
A key benefit of using the URL
class is the ease with which a
URL string is translated into a request for a resource.
The extensibility of the java.net.URL
class allows new
schemes (protocols) and content types to be added dynamically using
java.net.URLStreamHandlerFactory
objects. These new handlers
allow existing applications to use new schemes and content types in the
same way as the handlers provided by the Java run-time environment. This
mechanism is described in the Javadoc for the
URLStreamHandler
and ContentHandler
class.
For example, the URL http://www.osgi.org/sample.txt
addresses a file on the OSGi web server that is obtained with the HTTP
scheme (usually a scheme provided by the Java run-time). A URL such as
rsh://www.acme.com/agent.zip
is addressing a ZIP file that
can be obtained with the non-built-in RSH scheme. A
java.net.URLStreamHandlerFactory
object must be registered
with the java.net.URL
class prior to the successful use of an
RSH scheme.
There are several problems with using only the existing Java
facilities for extending the handlers used by the
java.net.URL
class:
-
Factories Are Singleton Operations - One
java.net.URLStreamHandlerFactory
object can be registered once with thejava.net.URL
class. Similarly, onejava.net.ContentHandlerFactory
object can be registered once with thejava.net.URLConnection
class. It is impossible to undo the registration of a factory or register a replacement factory. -
Caching Of Schemes - When a previously unused scheme is first used by the
java.net.URL
class, thejava.net.URL
class requests ajava.net.URLStreamHandler
object for that specific scheme from the currently registeredjava.net.URLStreamHandlerFactory
object. A returnedjava.net.URLStreamHandler
object is cached and subsequent requests for that scheme use the samejava.net.URLStreamHandler
object. This means that once a handler has been constructed for a specific scheme, this handler can no longer be removed, nor replaced, by a new handler for that scheme. This caching is likewise done forjava.net.ContentHandler
objects.
Both problems impact the OSGi operating model, which allows a bundle to go through different life cycle stages that involve exposing services, removing services, updating code, replacing services provided by one bundle with services from another, etc. The existing Java mechanisms are not compatible when used by bundles.
The OSGi Framework must register a
java.net.URLStreamHandlerFactory
object and a
java.net.ContentHandlerFactory
object with the
java.net.URL.setURLStreamHandlerFactory
and
java.net.URLConnection.setContentHandlerFactory
methods,
respectively.
When these two factories are registered, the OSGi Framework service
registry must be tracked for the registration of
URLStreamHandlerService
services and
java.net.ContentHandler
services.
A URL Stream Handler Service must be associated with a service
registration property named URL_HANDLER_PROTOCOL. The value of this
url.handler.protocol
property must be an array of scheme
names (String[]
or String
).
A Content Handler service must be associated with a service
registration property named URL_CONTENT_MIMETYPE. The value of the URL_CONTENT_MIMETYPE property must be an array of MIME types names
(String[]
or String
) in the form type/subtype.
See [2] MIME Multipurpose Internet Mail Extension.
When a URL is used with a previously unused scheme, it must query
the registered java.net.URLStreamHandlerFactory
object
(that should have been registered by the OSGi Framework). The OSGi
Framework must then search the service registry for services that are
registered under URLStreamHandlerService
and that match the
requested scheme.
If one or more service objects are found, a proxy object must be
constructed. A proxy object is necessary because the service object that
provides the implementation of the
java.net.URLStreamHandler
object can become unregistered
and Java does not provide a mechanism to withdraw a
java.net.URLStreamHandler
object once it is returned from a
java.net.URLStreamHandlerFactory
object.
Once the proxy is created, it must track the service registry for registrations and unregistrations of services matching its associated scheme. The proxy must be associated with the service that matches the scheme and has the highest ranking (first in ranking order) at any moment in time. If a proxy is associated with a URL Stream Handler Service, it must change the associated handler to a newly registered service when that service has a higher ranking.
The proxy object must forward all method requests to the associated URL Stream Handler Service until this service object becomes unregistered.
Once a proxy is created, it cannot be withdrawn because it is
cached by the Java run-time. However, service objects can be withdrawn
and it is possible for a proxy to exist without an associated
URLStreamHandlerService
/java.net.ContentHandler
object.
In this case, the proxy must handle subsequent requests until another appropriate service is registered. When this happens, the proxy class must handle the error.
In the case of a URL Stream Handler proxy, it
must throw a java.net.MalformedURLException
exception if
the signature of a method allows throwing this exception. Otherwise, a
java.lang.IllegalStateException
exception is thrown.
In the case of a Content Handler proxy, it must return InputStream to the data.
Bundles must ensure that their
URLStreamHandlerService
or
java.net.ContentHandler
service objects throw these
exceptions also when they have become unregistered.
Proxies for Content Handler services operate slightly differently
from URL Stream Handler Service proxies. In the case that
null
is returned from the registered
ContentHandlerFactory
object, the factory will not get
another chance to provide a ContentHandler
object for that
content-type. Thus, if there is no built-in handler, nor a registered
handler for this content-type, a ContentHandler
proxy must
be constructed that returns the InputStream
object from the
URLConnection
object as the content object until a handler
is registered.
Implementations of Java provide a number of sub-classes of
java.net.URLStreamHandler
classes that can handle protocols
like HTTP, FTP, NEWS etc. Most Java implementations provide a mechanism
to add new handlers that can be found on the class path through class
name construction.
If a registered java.net.URLStreamHandlerFactory
object returns null
for a built-in handler (or one that is
available through the class name construction mechanism), it will never
be called again for that specific scheme because the Java implementation
will use its built-in handler or uses the class name
construction.
As a result, even though it is not forbidden for URL Handlers
Service implementations to override built-in handlers, it is not
possible to guarantee that a registered
URLStreamHandlerService
object will be used when it is
overriding a built-in handler. For consistency reasons, it is therefore
recommended to never override built-in handlers.
The Content Handler Factory is implemented using a similar technique and has therefore the same problems.
To facilitate the discovery of built-in handlers that are available through the name construction, the method described in the next section must be used by the Framework before any handlers are searched for in the service registry.
If the system properties java.protocol.handler.pkgs
or java.content.handler.pkgs
are defined, they must be used
to locate built-in handlers. Each property must be defined as a list of
package names that are separated by a vertical line ('|'
\u007C
) and that are searched in the left-to-right order (the
names must not end in a full stop ('.'
\u002E
). For example:
org.osgi.impl.handlers | com.acme.url
The package names are the prefixes that are put in front of a scheme or content type to form a class name that can handle the scheme or content-type.
A URL Stream Handler name for a scheme is formed by appending the
string ".Handler"
to the scheme name. Using the packages in
the previous example, the rsh
scheme handler class is
searched by the following names:
org.osgi.impl.handlers.rsh.Handler
com.acme.url.rsh.Handler
MIME type names contain the solidus ('/'
\u002F
) character and can contain other characters that must not
be part of a Java class name. A MIME type name must be processed as
follows before it can be converted to a class name:
-
First, all solidi in the MIME name must be converted to a full stop (
'.' \u002E
). All other characters that are not allowed in a Java class name must be converted to an underscore ('_' \u005F
).application/zip application.zip text/uri-list text.uri_list image/vnd.dwg image.vnd_dwg
-
After this conversion, the name is appended to the list of packages specified in
java.content.handler.pkgs
. For example, if the content type isapplication/zip
, and the packages are defined as in the previous example, then the following classes are searched:org.osgi.impl.handlers.application.zip com.acme.url.application.zip
The Java run-time specific packages should be listed in the appropriate properties so that implementations of the URL Stream Handler Factory and Content Handler Factory can be made aware of these packages.
Implementations of
java.net.URLStreamHandler
class cannot be registered in the
service registry for use by the proxy because the methods of the
URLStreamHandler
class are protected and thus not available
to the proxy implementation. Also, the URLStreamHandler
class checks that only the URLStreamHandler
object that was
returned from the URLStreamHandlerFactory
object can invoke
the setURL
method. This means that
URLStreamHandler
objects in the service registry would be
unable to invoke the setURL
method. Invoking this method is
necessary when implementing the parseURL
method.
Therefore, the URLStreamHandlerService
and
URLStreamHandlerSetter
interfaces were created. The
URLStreamHandlerService
interface provides public versions
of the URLStreamHandler
methods, except that the
setURL
method is missing and the parseURL
method has a new first argument of type
URLStreamHandlerSetter
. In general, sub-classes of the
URLStreamHandler
class can be converted to
URLStreamHandlerService
classes with minimal code changes.
Apart from making the relevant methods public, the parseURL
method needs to be changed to invoke the setURL
method on
the URLStreamHandlerSetter
object that the
URLStreamHandlerService
object was passed, rather then the
setURL
method of URLStreamHandler
class.
To aid in the conversion of URLStreamHandler
implementation classes, the AbstractURLStreamHandlerService
has been provided. Apart from making the relevant methods public, the
AbstractURLStreamHandlerService
stores the
URLStreamHandlerSetter
object in a private variable. To
make the setURL
method work properly, it overrides the
setURL
method to invoke the setURL
method on
the saved URLStreamHandlerSetter
object rather then the
URLStreamHandler.setURL
method. This means that a subclass
of URLStreamHandler
should be changed to become a sub-class
of the AbstractURLStreamHandlerService
class and be
recompiled.
Normally, the parseURL
method will have the following
form:
class URLStreamHandlerImpl {
...
protected URLStreamHandlerSetter realHandler;
...
public void parseURL(
URLStreamHandlerSetter realHandler,
URL u, String spec, int start, int limit) {
this.realHandler = realHandler;
parseURL(u, spec, start, limit);
}
protected void setURL(URL u,
String protocol, String host,
int port, String authority,
String userInfo, String path,
String query,String ref) {
realHandler.setURL(u, protocol, host,
port, authority, userInfo, path,
query, ref);
}
...
}
The URLStreamHandler.parseURL
method will call the
setURL
method which must be invoked on the proxy rather
than this
. That is why the setURL
method is
overridden to delegate to the URLStreamHandlerSetter
object
in realHandler
as opposed to super
.
Java 1.5 introduced a new method on the
URLStreamHandler
class: URLConnection
openConnection(URL,Proxy)
. Adding this method to the URL
Stream Handler service poses the following problems:
-
It would have broken all existing implementations.
-
The references to the java.net.Proxy class would make the API dependent on Java 1.5
Therefore, scheme providers can optionally implement the
openConnection(URL,Proxy)
method as a public method. If the
scheme provider implements this method, then the framework must call it
(using reflection). If this method is not implemented in the URL Stream
Handler service an Unsupported Operation Exception must be
thrown.
Framework implementations should be careful not to create unwanted dependencies on Java 1.5. This will require two different implementation classes for the URLStreamHandler class that is used to proxy the URL Stream Handler services.
The following example provides a scheme that
returns the path part of the URL. The first class that is implemented is
the URLStreamHandlerService
. When it is started, it registers
itself with the OSGi Framework. The OSGi Framework calls the
openConnection
method when a new
java.net.URLConnection
must be created. In this example, a
DataConnection
object is returned.
public class DataProtocol
extends AbstractURLStreamHandlerService
implements BundleActivator {
public void start( BundleContext context ) {
Hashtable properties = new Hashtable();
properties.put( URLConstants.URL_HANDLER_PROTOCOL,
new String[] { "data" } );
context.registerService(
URLStreamHandlerService.class.getName(),
this, properties );
}
public void stop( BundleContext context ) {}
public URLConnection openConnection( URL url ) {
return new DataConnection(url);
}
}
The following example DataConnection
class extends java.net.URLConnection
and overrides the
constructor so that it can provide the URL
object to the
super class, the connect
method, and the
getInputStream
method. This last method returns the path part
of the URL as an java.io.InputStream
object.
class DataConnection extends java.net.URLConnection{
DataConnection( URL url ) {super(url);}
public void connect() {}
public InputStream getInputStream() throws IOException {
String s = getURL().getPath();
byte [] buf = s.getBytes();
return new ByteArrayInputStream(buf,1,buf.length-1);
}
public String getContentType() {
return "text/plain";
}
}
A Content Handler should extend the
java.net.ContentHandler
class and implement the
getContent
method. This method must get the
InputStream
object from the
java.net.URLConnection
parameter object and convert the bytes
from this stream to the applicable type. In this example, the MIME type is
text/plain
and the return object is a String
object.
public class TextPlainHandler extends ContentHandler
implements BundleActivator {
public void start( BundleContext context ) {
Hashtable properties = new Hashtable();
properties.put( URLConstants.URL_CONTENT_MIMETYPE,
new String[] { "text/plain" } );
context.registerService(
ContentHandler.class.getName(),
this, properties );
}
public void stop( BundleContext context ) {}
public Object getContent( URLConnection conn )
throws IOException {
InputStream in = conn.getInputStream();
InputStreamReader r = new InputStreamReader( in );
StringBuffer sb = new StringBuffer();
int c;
while ( (c=r.read()) >= 0 )
sb.append( (char) c );
r.close(); in.close();
return sb.toString();
}
}
The ability to specify a protocol and add content handlers makes it
possible to directly affect the behavior of a core Java VM class. The
java.net.URL
class is widely used by network applications and
can be used by the OSGi Framework itself.
Therefore, care must be taken when providing the ability to register
handlers. The two types of supported handlers are
URLStreamHandlerService
and
java.net.ContentHandler
. Only trusted bundles should be
allowed to register these services and have
ServicePermission[URLStreamHandlerService|ContentHandler,
REGISTER]
for these classes. Since these services are made
available to other bundles through the java.net.URL
class and
java.net.URLConnection
class, it is advisable to deny the use
of these services (ServicePermission[<name>, GET]
) to
all, so that only the Framework can get them. This prevents the
circumvention of the permission checks done by the
java.net.URL
class by using the
URLStreamHandlerServices
service objects directly.
URL Stream and Content Handlers Package Version 1.0.
Bundles wishing to use this package must list the package in the Import-Package header of the bundle's manifest.
Example import for consumers using the API in this package:
Import-Package: org.osgi.service.url; version="[1.0,2.0)"
-
AbstractURLStreamHandlerService
- Abstract implementation of theURLStreamHandlerService
interface. -
URLConstants
- Defines standard names for property keys associated with URLStreamHandlerService andjava.net.ContentHandler
services. -
URLStreamHandlerService
- Service interface with public versions of the protectedjava.net.URLStreamHandler
methods. -
URLStreamHandlerSetter
- Interface used byURLStreamHandlerService
objects to call thesetURL
method on the proxyURLStreamHandler
object.
Abstract implementation of the URLStreamHandlerService
interface. All
the methods simply invoke the corresponding methods on
java.net.URLStreamHandler
except for parseURL
and
setURL
, which use the URLStreamHandlerSetter
parameter.
Subclasses of this abstract class should not need to override the
setURL
and parseURL(URLStreamHandlerSetter,...)
methods.
Thread-safe
The URLStreamHandlerSetter
object passed to the parseURL method.
This method calls super.equals(URL,URL)
.
java.net.URLStreamHandler.equals(URL,URL)
This method calls super.getDefaultPort
.
java.net.URLStreamHandler.getDefaultPort
This method calls super.getHostAddress
.
java.net.URLStreamHandler.getHostAddress
This method calls super.hashCode(URL)
.
java.net.URLStreamHandler.hashCode(URL)
This method calls super.hostsEqual
.
java.net.URLStreamHandler.hostsEqual
java.net.URLStreamHandler.openConnection
The object on which the setURL
method must be
invoked for the specified URL.
Parse a URL using the URLStreamHandlerSetter
object. This method
sets the realHandler
field with the specified
URLStreamHandlerSetter
object and then calls
parseURL(URL,String,int,int)
.
java.net.URLStreamHandler.parseURL
This method calls super.sameFile
.
java.net.URLStreamHandler.sameFile
This method calls
realHandler.setURL(URL,String,String,int,String,String)
.
java.net.URLStreamHandler.setURL(URL,String,String,int,String,String)
This method is only for compatibility with handlers written for JDK 1.1.
This method calls
realHandler.setURL(URL,String,String,int,String,String,String,String)
.
java.net.URLStreamHandler.setURL(URL,String,String,int,String,String,String,String)
Defines standard names for property keys associated with
URLStreamHandlerService and java.net.ContentHandler
services.
The values associated with these keys are of type java.lang.String[]
or java.lang.String
, unless otherwise indicated.
Consumers of this API must not implement this type
Service property naming the MIME types serviced by a java.net.ContentHandler. The property's value is a MIME type or an array of MIME types.
Service interface with public versions of the protected
java.net.URLStreamHandler
methods.
The important differences between this interface and the
URLStreamHandler
class are that the setURL
method is absent
and the parseURL
method takes a URLStreamHandlerSetter object
as the first argument. Classes implementing this interface must call the
setURL
method on the URLStreamHandlerSetter
object received
in the parseURL
method instead of URLStreamHandler.setURL
to
avoid a SecurityException
.
AbstractURLStreamHandlerService
Thread-safe
java.net.URLStreamHandler.openConnection
The object on which setURL
must be invoked for
this URL.
Parse a URL. This method is called by the URLStreamHandler
proxy,
instead of java.net.URLStreamHandler.parseURL
, passing a
URLStreamHandlerSetter
object.
java.net.URLStreamHandler.parseURL
Interface used by URLStreamHandlerService
objects to call the
setURL
method on the proxy URLStreamHandler
object.
Objects of this type are passed to the
URLStreamHandlerService.parseURL(URLStreamHandlerSetter, URL, String, int, int)
method. Invoking the setURL
method on the
URLStreamHandlerSetter
object will invoke the setURL
method
on the proxy URLStreamHandler
object that is actually registered with
java.net.URL
for the protocol.
Thread-safe
java.net.URLStreamHandler.setURL(URL,String,String,int,String,String)
This method is only for compatibility with handlers written for JDK 1.1.
[2]MIME Multipurpose Internet Mail Extensionhttps://www.mhonarc.org/~ehood/MIME/MIME.html
[3]Assigned MIME Media Typeshttps://www.iana.org/assignments/media-types