You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@felix.apache.org by "Kass, Samuel-P58051" <Sa...@gdc4s.com> on 2008/06/12 22:29:11 UTC

OSGI-ification of resource finding

I'm investigating OSGI-ifying a large codebase, and am trying to pin
down how we're going to do resource loading.  At the bottom of this
message is a bit of code that's called many times in the codebase.  It
can take anything from a filename to a URL string to a resource path,
and turn it into a canonical URL that can be passed to anything in the
Java libraries that take a URL.

Questions:
1. I think ClassLoader.getSystemResource(spec) is going to get me into
trouble in OSGi-land.  I'm not sure the
Thread.currentThread().getContextClassLoader().getResource(spec); is any
better.  However, this is what was recommended by Sun in
http://java.sun.com/j2se/1.5.0/docs/guide/javaws/developersguide/faq.htm
l#211 when trying to make our code work in JNLP-land.  Would
AnyInputStream.class.getResource(spec) work any better?
2. Felix sees the string "C:\dir\file" as a URL with protocol "C:" (and
throws malformed URL exception since it doesn't recognize that protocol)
when it tries to to look it up as a resource.  Moving the attempt to
stat the File to before the resource calls solves that problem, but the
common case is that we store the file in a resource, so we're doing a
lot of extra file stats if I make that change.
3. I'd like to avoid OSGI-specific code in the core codebase and rely on
the classpath-mangling magic to accomplish this... but do I need to move
away from static calls to do it?  Would a singleton pattern work better
here, or are singletons, too, evil in a multi-classloader world?

	--Sam Kass

class AnyInputStream
{
//...
  public static URL makeURL(String spec)
    throws IllegalArgumentException
  {
    // For applets/webstart, we should use this thread's ClassLoader,
which
    //   will default to the system classloader if it hasn't been
overridden
    URL url =
Thread.currentThread().getContextClassLoader().getResource(spec);

    // Call back to the SystemResouce if we can't find it
    if (null == url)
      url = ClassLoader.getSystemResource(spec);

    // Try parsing it as a pre-formed URL
    if (null == url)
    {
      try
      {
        url = new URL(spec);
      }
      catch (MalformedURLException e)
      {
        url = null;
      }
    }

    // If we couldn't find it from the classloader, try the local
filesystem
    if (url == null)
    {
      try
      {
        File file = new File(spec);
        if (file.isFile())
        {
          url = file.toURL();
        }
      }
      catch (MalformedURLException e)
      {
        //throw new IllegalArgumentException("Unable to open: " +
onedoc);
      }
    }

    if (url == null)
    {
      throw new IllegalArgumentException(
        "Unable to create URL from \"" + spec + "\"");
    }

    return url;
  }
}

Re: OSGI-ification of resource finding

Posted by Stuart McCulloch <st...@jayway.net>.
2008/6/13 Kass, Samuel-P58051 <Sa...@gdc4s.com>:

> I'm investigating OSGI-ifying a large codebase, and am trying to pin
> down how we're going to do resource loading.  At the bottom of this
> message is a bit of code that's called many times in the codebase.  It
> can take anything from a filename to a URL string to a resource path,
> and turn it into a canonical URL that can be passed to anything in the
> Java libraries that take a URL.
>
> Questions:
> 1. I think ClassLoader.getSystemResource(spec) is going to get me into
> trouble in OSGi-land.  I'm not sure the
> Thread.currentThread().getContextClassLoader().getResource(spec); is any
> better.  However, this is what was recommended by Sun in
> http://java.sun.com/j2se/1.5.0/docs/guide/javaws/developersguide/faq.htm
> l#211 when trying to make our code work in JNLP-land.  Would
> AnyInputStream.class.getResource(spec) work any better?


using the TCCL allows clients to swap in their own classloaders and
doesn't rely on any OSGi specific API, but all OSGi clients must then
remember to set the TCCL to their bundle classloader before calling
your library (this isn't done automatically in Felix)

this is what a lot of legacy libraries do - the alternative is to use some
sort of injection/plugin approach to swap in an OSGi specific resource
implementation when using the library inside an OSGi framework.

2. Felix sees the string "C:\dir\file" as a URL with protocol "C:" (and
> throws malformed URL exception since it doesn't recognize that protocol)
> when it tries to to look it up as a resource.


this problem is not specific to Felix, or even OSGi - it always happens
when you have a string which might be a URL or a filename, because
there's an overlap between the path syntax on some OS's and the URL
syntax - you often have to fall back and query the OS to see whether it
represents a valid file location before trying it as a URL (or vice-versa)

this is made more difficult by the varying slashes needed with file URIs:

  http://blogs.msdn.com/ie/archive/2006/12/06/file-uris-in-windows.aspx

I've basically ended up doing the same sort of thing in the past for URIs
ie. load the string as a URI and use toURL() method to check the syntax,
otherwise fall back to loading it as a file and use the toURI() method.

 Moving the attempt to
> stat the File to before the resource calls solves that problem, but the
> common case is that we store the file in a resource, so we're doing a
> lot of extra file stats if I make that change.


you'll find the core Java classes perform similar checks to distinguish
between URLs and filenames - it's surprising how often "stat" is called.


> 3. I'd like to avoid OSGI-specific code in the core codebase and rely on
> the classpath-mangling magic to accomplish this... but do I need to move
> away from static calls to do it?  Would a singleton pattern work better
> here, or are singletons, too, evil in a multi-classloader world?
>

singletons should work fine, along as your exports and imports are ok.

another solution (if you want to keep the static calls) is a thread local,
which you then just need to set to the right implementation - this can
be a very clean solution, especially if you provide utility methods to
do the setup.

( just remember to clean up thread locals when you're done, as they
  can be a cause of memory leaks )

HTH

       --Sam Kass
>
> class AnyInputStream
> {
> //...
>  public static URL makeURL(String spec)
>    throws IllegalArgumentException
>  {
>    // For applets/webstart, we should use this thread's ClassLoader,
> which
>    //   will default to the system classloader if it hasn't been
> overridden
>    URL url =
> Thread.currentThread().getContextClassLoader().getResource(spec);
>
>    // Call back to the SystemResouce if we can't find it
>    if (null == url)
>      url = ClassLoader.getSystemResource(spec);
>
>    // Try parsing it as a pre-formed URL
>    if (null == url)
>    {
>      try
>      {
>        url = new URL(spec);
>      }
>      catch (MalformedURLException e)
>      {
>        url = null;
>      }
>    }
>
>    // If we couldn't find it from the classloader, try the local
> filesystem
>    if (url == null)
>    {
>      try
>      {
>        File file = new File(spec);
>        if (file.isFile())
>        {
>          url = file.toURL();
>        }
>      }
>      catch (MalformedURLException e)
>      {
>        //throw new IllegalArgumentException("Unable to open: " +
> onedoc);
>      }
>    }
>
>    if (url == null)
>    {
>      throw new IllegalArgumentException(
>        "Unable to create URL from \"" + spec + "\"");
>    }
>
>    return url;
>  }
> }
>



-- 
Cheers, Stuart