Uploaded image for project: 'JBoss ClassLoader'
  1. JBoss ClassLoader
  2. JBCL-177

ProtectionDomain.getCodeSource().getLocation() returns VFS URL with trailing slash, subsequently interpreted as path when creating new URL

    Details

    • Steps to Reproduce:
      Hide

      TestServlet code that demonstrates issue:

      {{{
      import java.io.*;
      import java.net.*;
      import java.security.CodeSource;
      import javax.servlet.ServletException;
      import javax.servlet.http.*;

      /**

      • Sample servlet that illustrats JBoss problem with VFS and creating an URL to
      • a file residing in the same location as the servlet jar file.
        *
      • @author jons
        */
        public class TestServlet extends HttpServlet
        {
        private String mCodeSourceURL;
        private String mTestFileURL;
        private String mTestFileString;
        private boolean mErrorOccurred;

      public void init() throws ServletException

      { super.init(); }

      /**

      • This method illustrates the problem.
        */
        private void loadTestFile()
        {
        try
        {
        // CodeSource.getLocation() returns an URL to the location of the jar file
        // containing this class. In JBoss, the location is a VFS URL to the servlet
        // jar file, but the URL has a trailing '/' after the jar name.
        CodeSource cs = getClass().getProtectionDomain().getCodeSource();
        URL csurl = cs.getLocation();
        mCodeSourceURL = csurl.toString();

      // Creating a new URL from the VFS jar location results in an incorrect URL
      // to the test data file. With this URL constructor, a new URL is created
      // by parsing the second parameter (the spec) in the context of the first
      // parameter (the context). If the spec does not start with a slash, it's considered
      // a relative location to the context, and the URL class will truncate the context
      // at the last slash in order to determine the parent directory under which the
      // relative filespec is located. In JBoss, the last slash is after the jar filename,
      // resulting in an invalid URL to the test file.
      URL testfileurl = new URL( csurl, "testfile.txt" );
      mTestFileURL = testfileurl.toString();

      // If URL is valid, read from it as an URLConnection inputstream.
      URLConnection urlconn = testfileurl.openConnection();
      InputStream is = urlconn.getInputStream();
      ByteArrayOutputStream baos = new ByteArrayOutputStream();
      byte[] buffer = new byte[1024];
      while( true )
      {
      final int numread = is.read( buffer, 0, buffer.length );
      if( numread == -1 )

      { break; }

      baos.write( buffer, 0, numread );
      }
      mTestFileString = new String(baos.toByteArray());
      is.close();
      }
      catch ( Exception e )
      {
      // If data cannot be read, make some pretty output for display
      // in the browser page that's generated.
      mErrorOccurred = true;
      StringBuffer sb = new StringBuffer();
      sb.append( "<b>" );
      sb.append( e.getClass() );
      sb.append( ": " );
      sb.append( e.getMessage() );
      sb.append( "</b>" );
      sb.append( "<br/>" );
      StackTraceElement[] stack = e.getStackTrace();
      for ( int i = 0; i < stack.length; i++ )

      { StackTraceElement ste = stack[i]; sb.append( ste.toString() ); sb.append( "<br/>" ); }

      mTestFileString = sb.toString();
      }
      }

      /**

      • doGet is invoked when a servlet request is made. Here, it's just used to call the method
      • that illustrates the problem, then some data resulting from the test method is output
      • to the browser page.
      • @param request the request.
      • @param response the response.
      • @throws IOException on IOException
      • @throws ServletException on ServletException
        */
        public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
        {
        mErrorOccurred = false;

      loadTestFile();

      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      out.println("<html>");
      out.println("<head>");
      out.println("<title>TestServlet Results</title>");
      out.println("</head>");
      out.println("<body>");
      out.println( "<h3>CodeSource URL</h3>" );
      out.println( mCodeSourceURL );
      out.println( "<h3>Test file URL</h3>" );
      out.println( mTestFileURL );
      if ( !mErrorOccurred )

      { out.println( "<h3>Test file data</h3>" ); out.println( mTestFileString ); }

      else

      { out.println( "<h3>Error occurred</h3>"); out.println( mTestFileString ); }

      out.println("</body>");
      out.println("</html>");
      }
      }
      }}}

      Compile test servlet code, package TestServlet.class into a TestServlet.jar file. Create a TestServlet.war file containing these files:

      WEB-INF/web.xml
      WEB-INF/lib/TestServlet.jar
      WEB-INF/lib/testfile.txt (a plain text file containing some random text is fine)

      Deploy war file, visit url of web app, results displayed in browser page.

      Show
      TestServlet code that demonstrates issue: {{{ import java.io.*; import java.net.*; import java.security.CodeSource; import javax.servlet.ServletException; import javax.servlet.http.*; /** Sample servlet that illustrats JBoss problem with VFS and creating an URL to a file residing in the same location as the servlet jar file. * @author jons */ public class TestServlet extends HttpServlet { private String mCodeSourceURL; private String mTestFileURL; private String mTestFileString; private boolean mErrorOccurred; public void init() throws ServletException { super.init(); } /** This method illustrates the problem. */ private void loadTestFile() { try { // CodeSource.getLocation() returns an URL to the location of the jar file // containing this class. In JBoss, the location is a VFS URL to the servlet // jar file, but the URL has a trailing '/' after the jar name. CodeSource cs = getClass().getProtectionDomain().getCodeSource(); URL csurl = cs.getLocation(); mCodeSourceURL = csurl.toString(); // Creating a new URL from the VFS jar location results in an incorrect URL // to the test data file. With this URL constructor, a new URL is created // by parsing the second parameter (the spec) in the context of the first // parameter (the context). If the spec does not start with a slash, it's considered // a relative location to the context, and the URL class will truncate the context // at the last slash in order to determine the parent directory under which the // relative filespec is located. In JBoss, the last slash is after the jar filename, // resulting in an invalid URL to the test file. URL testfileurl = new URL( csurl, "testfile.txt" ); mTestFileURL = testfileurl.toString(); // If URL is valid, read from it as an URLConnection inputstream. URLConnection urlconn = testfileurl.openConnection(); InputStream is = urlconn.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte [1024] ; while( true ) { final int numread = is.read( buffer, 0, buffer.length ); if( numread == -1 ) { break; } baos.write( buffer, 0, numread ); } mTestFileString = new String(baos.toByteArray()); is.close(); } catch ( Exception e ) { // If data cannot be read, make some pretty output for display // in the browser page that's generated. mErrorOccurred = true; StringBuffer sb = new StringBuffer(); sb.append( "<b>" ); sb.append( e.getClass() ); sb.append( ": " ); sb.append( e.getMessage() ); sb.append( "</b>" ); sb.append( "<br/>" ); StackTraceElement[] stack = e.getStackTrace(); for ( int i = 0; i < stack.length; i++ ) { StackTraceElement ste = stack[i]; sb.append( ste.toString() ); sb.append( "<br/>" ); } mTestFileString = sb.toString(); } } /** doGet is invoked when a servlet request is made. Here, it's just used to call the method that illustrates the problem, then some data resulting from the test method is output to the browser page. @param request the request. @param response the response. @throws IOException on IOException @throws ServletException on ServletException */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { mErrorOccurred = false; loadTestFile(); response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<html>"); out.println("<head>"); out.println("<title>TestServlet Results</title>"); out.println("</head>"); out.println("<body>"); out.println( "<h3>CodeSource URL</h3>" ); out.println( mCodeSourceURL ); out.println( "<h3>Test file URL</h3>" ); out.println( mTestFileURL ); if ( !mErrorOccurred ) { out.println( "<h3>Test file data</h3>" ); out.println( mTestFileString ); } else { out.println( "<h3>Error occurred</h3>"); out.println( mTestFileString ); } out.println("</body>"); out.println("</html>"); } } }}} Compile test servlet code, package TestServlet.class into a TestServlet.jar file. Create a TestServlet.war file containing these files: WEB-INF/web.xml WEB-INF/lib/TestServlet.jar WEB-INF/lib/testfile.txt (a plain text file containing some random text is fine) Deploy war file, visit url of web app, results displayed in browser page.

      Description

      Webapp has a non-jar auxiliary file located in WEB-INF/lib, alongside main application jar. To load contents of aux file, webapp calls <currentClass>.getProtectionDomain().getCodeSource().getLocation() to get URL of main application jar. JBoss returns a VFS URL to the application jar inside the webapp .war or .ear file, but VFS URL has a slash at the end. Subsequent call to new URL( jarurl, auxfilename ) results in a new VFS URL with the aux filename appended after the trailing slash, rather than locating the aux file relative to the application jar.

      For example, CodeSource.getLocation() returns this URL:

      {{

      { vfs:/C:/Development/jboss/jboss-6.0.0.Final/server/default/deploy/testservlet.war/WEB-INF/lib/TestServlet.jar/ }

      }}

      Creating a new URL using "new URL( aboveurl, "testfile.txt" )" results in this URL:

      {{

      { vfs:/C:/Development/jboss/jboss-6.0.0.Final/server/default/deploy/testservlet.war/WEB-INF/lib/TestServlet.jar/testfile.txt }

      }}

      A subsequent attempt to read from the testfile.txt using URL.openConnection().getInputStream() fails with a FileNotFoundException.

      Because of the trailing slash on the VFS URL to the application jar, the context used by the URL class to construct the new relative URL to the auxiliary file is incorrect, making the auxiliary file inaccessible.

        Gliffy Diagrams

          Attachments

            Activity

              People

              • Assignee:
                johnbailey John Bailey
                Reporter:
                jonschuster Jon Schuster
              • Votes:
                0 Vote for this issue
                Watchers:
                1 Start watching this issue

                Dates

                • Created:
                  Updated: