views:

219

answers:

2

Hi All,

I have a web app consisting of some JSPs that were previously running on a Linux box. I need to get this running on a Windows XP SP3 machine running Tomcat 5.5.29. Most everything is working now but this one item: The app has a capability to write a configuration file to its Windows directory (i.e. C:\Program Files\Apache\Tomcat\webapps\myapp). But when it tries to do this, the app cannot open the FileOutputStream (returns null). If I drop off the path specifier, and just let it open the file, it successfully opens it in the Tomcat directory.

The first line fails, but the second one succeeds:

//  outputFile2 = new PrintWriter(new FileOutputStream(basePathName + "programAll.txt", false));
    outputFile2 = new PrintWriter(new FileOutputStream("programAll.txt", false));

Here is the code that creates the basePathName:

    String basePathName = getBaseFilePath();  
    ...
    public String getBaseFilePath()
            {
            String curDir = System.getProperty("catalina.home");  
            curDir = curDir + "/webapps/pubmed/"; 
            curDir = "file:///" + formatPathNameForOS(curDir);
            return curDir;
            }

   public String formatPathNameForOS(String pathName)
        {
        if (codeIsOnWindows())
            {
            pathName = pathName.replace('/','\\');
            }
        else
            {
            pathName = pathName.replace('\\','/');
            }
        return pathName;
    }

Here's the exception message:

HTTP Status 500 -

type Exception report

message

description The server encountered an internal error () that prevented it from fulfilling this request.

exception

org.apache.jasper.JasperException
    org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java:460)
    org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:373)
    org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:321)
    org.apache.jasper.servlet.JspServlet.service(JspServlet.java:257)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
root cause

java.lang.NullPointerException
    org.apache.jsp.updateMemberLists_jsp._jspService(updateMemberLists_jsp.java:1492)
    org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:98)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:331)
    org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:321)
    org.apache.jasper.servlet.JspServlet.service(JspServlet.java:257)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:729)

Here's the stack trace:

Aug 6, 2010 1:20:39 PM org.apache.catalina.core.StandardWrapperValve invoke
SEVERE: Servlet.service() for servlet jsp threw exception
java.lang.NullPointerException
    at org.apache.jsp.updateMemberLists_jsp._jspService(updateMemberLists_jsp.java:1492)
    at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:98)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:331)
    at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:321)
    at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:257)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:729)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:269)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:213)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:172)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:117)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:108)
    at org.globus.tomcat.coyote.valves.HTTPSValve55.invoke(HTTPSValve55.java:45)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:174)
    at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:873)
    at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:665)
    at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:528)
    at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:81)
    at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:689)
    at java.lang.Thread.run(Thread.java:595)

I've looked at the updateMembersLists_jsp.java, and here is the section where the null pointer is occurring:

//create a file to write to the output
PrintWriter outputFile2 = null;
try
    {
    outputFile2 = new PrintWriter(new FileOutputStream(basePathName + "programAll.txt", false));
//  outputFile2 = new PrintWriter(new FileOutputStream("programAll.txt", false));
    }
    catch (FileNotFoundException e)
        {
        out.write("<p><font color='red'>Error Saving</font></p>");
        }
outputFile2.print(output);  // THIS IS LINE 1492 -- NULL POINTER
outputFile2.close();

Also: We are now running Tomcat off of the root directory instead of Program Files. I have checked the basePathName and it is being calculated correctly.

Now I figured out that Tomcat's policy settings might need to be adjusted. I added the following lines to catalina.policy hoping it would set things right:

// The permissions granted to the pubmed webapp
grant codeBase "file:${catalina.home}/webapps/pubmed/-" {
        permission java.io.FilePermission "${catalina.home}/webapps/pubmed/-", "read, write";
        permission java.io.FilePermission "${catalina.home}/webapps/pubmed/*", "read, write";
};

This didn't seem to have any effect. The Windows user that tomcat is running under has read/write permissions on the pubmed directory. Aside from the fact that it seems questionable for this webapp to be writing files to its own directory, what am I missing?

Sean

+1  A: 

You should not use relative paths in Java IO. The path would become relative to the current working directory which depends on how you started the webserver/webapplication. When Tomcat is started in Eclipse for example, it may be the Eclipse project directory. When Tomcat is started from CMD, it may be the currently opened folder in CMD. When Tomcat is started as service, it may be /bin folder of Tomcat. To determine it yourself, do as follows:

System.out.println(new File("programAll.txt").getAbsolutePath()); 

And you'll see that it's completely different than you'd expect.

You need to specify absolute paths instead. You can use ServletContext#getRealPath() to convert a webcontent-relative path to an absolute disk file system path.

String relativeWebPath = "programAll.txt";
String absoluteDiskPath = getServletContext().getRealPath(relativeWebPath);
output = new FileOutputStream(absoluteDiskPath);
// ...

The getServletContext() method is available inside every servlet.


That said, did you take into consideration that all modified files in the webcontent will be wiped/overriden whenever you redeploy/restart the webapp/server? If you'd like to have a bit more permanent storage, I strongly recommend to store them outside the webcontent (of course in an absolute path, e.g. /var/webapp/files or so).

BalusC
I added the getServletContext code as you describe, and it returns the same path I've been using. So, while I appreciate the nuances now much better about relative paths, I don't think that was the problem I'm having. Isn't the basePathName string I listed in the comments above already absolute?
SeanG
Oh, I see. Can you please update your question to include the complete exception message and trace? I think there's ambiguity where you said that it returns null. Literally taken, this can't be the case. The part `new FileOutputStream(path)` can only either return a fullworthy instance or throw an exception, not null.
BalusC
@BalusC thats right, thats what i thought, I thought that the basePathname was null
naikus
Ok, question updated with msg and trace.
SeanG
Oh geez, that doesn't help much. You wrote raw [Java code in a JSP file](http://stackoverflow.com/questions/3177733/howto-avoid-java-code-in-jsp-files). Do you know for sure what exactly is null? If so, please point it in the code. If not, you should now open the server-generated `updateMemberLists_jsp.java` file in an editor and head to line 1492 and tell us what exactly is null. At least, this does still not seem to be a permission problem.
BalusC
Yeah, I'm not exactly a Java expert. Please remember I didn't write this, I'm just trying to get it installed and running at my site. It might be a little while before I can answer this question because my admin is reinstalling tomcat in a better location (as recommended by Romain). When I can get back on, I'll give you more info. Thanks for your help, by the way!
SeanG
Are you able to edit JSP code? If so, bring in some poor man's logging with help of `System.out.println()` at strategic locations printing the data and variables of strategic interest. Run the JSP once again and investigate the log so that you can trackback the line where the `NullPointerException` has been thrown and then paste the details here.
BalusC
Good advice on looking in the .java file to find the proper line. Also, the System.out.println was helpful. It was a path syntax problem all along.
SeanG
+1  A: 

Try obtaining the path in the following way:

String path = getServletContext().getRealPath("/");

This will convert your path (to your webapp) to the actual path on your disk.

naikus
It seems like this returns the same path as the absolute path I had listed above.
SeanG
@SeanG the path you mentioned is a URL (a file:/ url), the getRealPath("/") will not return a URL-like string. Two options to try: 1) Change your path to a proper URL (with forward slashes) and see or 2) use the path that you get from getRealPath(). Also like Balus mentions, any logs/stack traces would be helpful
naikus
It worked! Putting getRealPath in there did the trick. I think the slashes were the wrong way when I first tried this. Thank you --
SeanG