Ali Kamali created FELIX-5759:
---------------------------------

             Summary: StackOverflowError thrown during URL construction
                 Key: FELIX-5759
                 URL: https://issues.apache.org/jira/browse/FELIX-5759
             Project: Felix
          Issue Type: Bug
            Reporter: Ali Kamali
            Priority: Critical


I get the following callstack resulting in a stack overflow error when building 
a URL object:
{code}
[info]   at 
org.apache.felix.framework.URLHandlersStreamHandlerProxy.parseURL(URLHandlersStreamHandlerProxy.java:401)
[info]   at java.net.URL.<init>(URL.java:622)
[info]   at java.net.URL.<init>(URL.java:490)
[info]   at 
org.apache.felix.framework.URLHandlersStreamHandlerProxy.parseURL(URLHandlersStreamHandlerProxy.java:401)
[info]   at java.net.URL.<init>(URL.java:622)
[info]   at java.net.URL.<init>(URL.java:490)
[info]   at 
org.apache.felix.framework.URLHandlersStreamHandlerProxy.parseURL(URLHandlersStreamHandlerProxy.java:401)
[info]   at java.net.URL.<init>(URL.java:622)
[info]   at java.net.URL.<init>(URL.java:490)
[info]   at 
org.apache.felix.framework.URLHandlersStreamHandlerProxy.parseURL(URLHandlersStreamHandlerProxy.java:401)
[info]   at java.net.URL.<init>(URL.java:622)
[info]   at java.net.URL.<init>(URL.java:490)
[info]   at 
org.apache.felix.framework.URLHandlersStreamHandlerProxy.parseURL(URLHandlersStreamHandlerProxy.java:401)
[info]   at java.net.URL.<init>(URL.java:622)
[info]   at java.net.URL.<init>(URL.java:490)
[info]   at 
org.apache.felix.framework.URLHandlersStreamHandlerProxy.parseURL(URLHandlersStreamHandlerProxy.java:401)
[info]   at java.net.URL.<init>(URL.java:622)
[info]   at java.net.URL.<init>(URL.java:490)
[info]   at 
org.apache.felix.framework.URLHandlersStreamHandlerProxy.parseURL(URLHandlersStreamHandlerProxy.java:401)
{code}

Using org.apache.felix.framework 4.4.1 and Java 1.8.0_111 running on Ubuntu.

We started getting this exception after upgrading to Spark 2.2.0, after some 
investigation we realized Spark 2.2.0 registers its own 
{{URLStreamHandlerFactory}}:
{code}
package org.apache.spark.sql.internal

object SharedState extends Logging {
  try {
    URL.setURLStreamHandlerFactory(new FsUrlStreamHandlerFactory())
  } catch {
    case e: Error =>
      logWarning("URL.setURLStreamHandlerFactory failed to set 
FsUrlStreamHandlerFactory")
  }
...
{code}

Looks like the bug is related to line 128 in {{URLHandlers.java}}:
{code}
            URLStreamHandler handler = getBuiltInStreamHandler(protocol, 
factory);
            if (handler != null)
            {
                URL url = new URL(protocol, null, -1, "", handler);
                m_handlerToURL.put(handler, url);
            }
{code}
This code assumes there is a unique mapping from handlers to protocols, which 
doesn't seem to be a valid assumption, at least not with Spark. Spark URL 
handler factory is returning the same handler instance for both {{file}} and 
{{ftp}} protocols. When {{URLHandlers}} is initializing it first tries to 
register a handler for {{file}} and then for {{ftp}}, but because the factory 
returns the same handler we end up replacing the URL object we have for 
{{file}} with {{ftp}} in {{m_handlerToURL}}.

Later when a URL is being constructed it calls {{createURLStreamHandler}} from 
URLHandlers, at the end of this method:
{code}
        // If built-in content handler, then create a proxy handler.
        return addToStreamCache(protocol,
            new URLHandlersStreamHandlerProxy(protocol, m_secureAction,
                handler, (URL) m_handlerToURL.get(handler)));
{code}

Note that it's trying to use {{m_handlerToURL}} to look up the protocol for the 
handler, and in case of {{file}} instead of returning a URL with protocol set 
to {{file}} it returns a URL with protocol set to {{ftp}}, so 
{{URLHandlersStreamHandlerProxy}} gets constructed with {{m_builtInURL}} set to 
{{ftp}}.

Later in URL.java we have this code:
{code}
            if ((context != null) && ((newProtocol == null) ||
                            newProtocol.equalsIgnoreCase(context.protocol))) {
                // inherit the protocol handler from the context
                // if not specified to the constructor
                if (handler == null) {
                    handler = context.handler;
                }
{code}
This code only uses the handler if protocols match, but in this case protocols 
don't match because it's expected to be {{file}} but we receive {{ftp}} that 
comes from {{m_builtInURL}}, and the code falls back to asking the factory to 
create a new handler:
{code}
            if (handler == null &&
                (handler = getURLStreamHandler(protocol)) == null) {
                throw new MalformedURLException("unknown protocol: "+protocol);
            }
{code}

The factory returns a handler with protocol set to {{ftp}} again and we get 
stuck in a loop.

Looking at the newest Felix code looks like the assumption of having a unique 
handler per protocol is still there, so I believe this bug still exists in the 
newest Felix as well.

To reproduce this bug before starting a bundle you only need to register a 
factory that returns the same handler instance for {{file}} and {{ftp}}.



--
This message was sent by Atlassian JIRA
(v6.4.14#64029)

Reply via email to