views:

163

answers:

6

I wanted to consolidate two functions.

After getting a viable solution, I decided to play with the code a bit further, and came up with this:

package hu.flux.helper;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.Writer;
import javax.servlet.jsp.JspWriter;
import com.objectmentor.library.web.framework.mocks.*;


// A holder for formatting data 
public class NameAndAddress 
{
    public String firstName;
    public String middleName;
    public String lastName;
    public String address1;
    public String address2;
    public String city;
    public String state;
    public String zip;

    public String FormattedString()
    {
        String formattedString = "<PRE>\n" + firstName;

        // Add the middle name only if it contains data.
        if ((middleName != null) && (middleName.length() > 0)) 
            {formattedString += " " + middleName;}

        formattedString += " " + lastName + "\n";

        formattedString += address1  + "\n";

        if ((address2 != null) && (address2.length() > 0))
            formattedString += address2 + "\n";

        formattedString += city + ", " + state + " " + zip + "\n</PRE>";
        return formattedString;
    }

    // Print out the name and address.
    public void print(Writer writer) {
    long now = System.currentTimeMillis();
    System.out.println("--Entering-- " + now);
    PrintWriter p = new PrintWriter (writer);
        p.write(this.FormattedString());
            now = System.currentTimeMillis();
        System.out.println("--Exiting-- "  + now);
    }

    /*
    public void print(JspWriter out) throws java.io.IOException 
    { print (new PrintWriter(out)); }
    */

    @SuppressWarnings("deprecation")
    public static void main (String args[])
    {
        NameAndAddress naa = new NameAndAddress();
        naa.firstName = "Brian";
        naa.middleName = "Matthew";
        naa.lastName = "Kessler";
        naa.address1 = "Tatra u. 15/b V/3";
        naa.city = "Budapest";
        naa.state = "Hungary";
        naa.zip = "HU-1136";

        System.out.println("\nTesting PrintWriter...");
        PrintWriter p = null;
        try { p = new PrintWriter("d:/temp/pwriter_text.txt"); } 
        catch (FileNotFoundException e) 
        { 
            System.err.print ("Can not create new PrintWriter: " + e);
            e.printStackTrace();
        }
        naa.print(p);
        p.flush();

        FileInputStream fis;
        DataInputStream dis;
        try 
        { 
            fis = new FileInputStream("d:/temp/pwriter_text.txt");
            dis = new DataInputStream (fis);
            while (dis.available() != 0)
                { System.out.println(dis.readLine()); }
            dis.close();
        }
        catch (Exception e)
        {
            System.err.println("File input error");
        }


        System.out.println("\nTested PrintWriter...");
        System.out.println("---------------------");

        System.out.println("\nTesting JSPWriter...");
        JspWriter j = null;
        naa.print(j);
        System.out.print("\nTested JSPWriter...");
        System.out.println("---------------------");

        System.out.println("\nTesting MockJspWriter");
        MockJspWriter m = null;
        m = new MockJspWriter(255, true);
        naa.print(m);
        System.out.print(m.getContent());
        System.out.println("\nTested MockJSPWriter...");
        System.out.println("---------------------");
    }
}

I expected that the print() method would catch both JspWriter and PrintWriter.

While this solution worked fine for PrintWriter, when I tried to run this as a console application, I get this output:

Testing PrintWriter...
--Entering-- 
--Exiting-- 
<PRE>
Brian Matthew Kessler
Tatra u. 15/b V/3
Budapest, Hungary HU-1136
</PRE>

Tested PrintWriter...
---------------------

Testing JSPWriter...
--Entering-- 
Exception in thread "main" java.lang.NullPointerException
    at hu.flux.helper.NameAndAddress.print(NameAndAddress.java:46)
    at hu.flux.helper.NameAndAddress.main(NameAndAddress.java:101)

I get a different error, however, if I try to access print(Writer writer) from a JSP:

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: javax.servlet.ServletException: java.lang.NoSuchMethodError: hu.flux.helper.NameAndAddress.print(Ljavax/servlet/jsp/JspWriter;)V
    org.apache.jasper.servlet.JspServletWrapper.handleJspException(JspServletWrapper.java:492)
    org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:407)
    org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:320)
    org.apache.jasper.servlet.JspServlet.service(JspServlet.java:265)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
root cause

javax.servlet.ServletException: java.lang.NoSuchMethodError: hu.flux.helper.NameAndAddress.print(Ljavax/servlet/jsp/JspWriter;)V
    org.apache.jasper.runtime.PageContextImpl.doHandlePageException(PageContextImpl.java:898)
    org.apache.jasper.runtime.PageContextImpl.handlePageException(PageContextImpl.java:827)
    org.apache.jsp.Address_jsp._jspService(Address_jsp.java:92)
    org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:68)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
    org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:376)
    org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:320)
    org.apache.jasper.servlet.JspServlet.service(JspServlet.java:265)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
root cause

java.lang.NoSuchMethodError: hu.flux.helper.NameAndAddress.print(Ljavax/servlet/jsp/JspWriter;)V
    org.apache.jsp.Address_jsp._jspService(Address_jsp.java:81)
    org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:68)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
    org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:376)
    org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:320)
    org.apache.jasper.servlet.JspServlet.service(JspServlet.java:265)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:722)
note The full stack trace of the root cause is available in the Apache Tomcat/7.0.2 logs.

Apache Tomcat/7.0.2

When calling from the JSP, I can make a call to the class with JspWriter work by adding this code:

public void print(JspWriter out) throws java.io.IOException 
    { print (new PrintWriter(out)); }

However, when attempting to use JspWriter from a console application (for testing -- I don't think anyone would ever need to use JspWriter in the console!), the above console error is moved to this function.

If print(JspWriter out) can fix the problem for JSPs, shouldn't it also fix the problem for console apps?

Moreover, if JspWriter is a Writer object, shouldn't it always be a Writer object, regardless whether it is called from the console or a JSP?

A: 

JspWriter is not a PrintWriter

you can wrap the original writer inside a print writer like this:

if (out instance of Writer) {
 PrintWriter p = new PrintWriter((Writer) out));
 p.print...
}
mohammad shamsi
JspWriter can be cast to PrinterWriter though. If I add this little method, the problem will go away: public void print(JspWriter out) throws java.io.IOException { print (new PrintWriter(out)); }
Brian Kessler
@brian kessler - the problem doesn't go away. If you change the IOException to an Exception, the problem is merely hidden. The comment you posted above will fail at runtime if you try it with a PrintWriter.
Tony Ennis
The code above works fine with PrintWriter. Only JspWriter is failing, at least initially because Java isn't recognizing JspWriter as an Object (though I thought every class inherited from object?). If I add the immediately above method, both PrintWriter and JspWriter work ... but I'm trying to understand why Java doesn't seem to consider JspWriter an object and whether it would be possible to make the code above work withouth using another method to handle JspWriter.
Brian Kessler
@Brian Kessler JspWriter and PrintWriter both are extending from Writer. how do you want cast them to each other?
mohammad shamsi
@mohammad shamsi: I'm trying to see how far I can consolidate code which was identical for both types. As the code is otherwise identical, I'd like to catch both types with a single function and process them both using a single condition. Right now, catching them both with a single function seems the bigger headache.
Brian Kessler
+1  A: 

This is because the Java compiler tries to find the PrintWriter.print method on the JspWriter object. Although it has a print method, this method does not match because it is from another class. Java does not support duck-typing and goes to great lengths to prevent it.

It is also considered bad practice to use exceptions in programming logic.

You'll have to do sthg like

    try
    { 
       if (out instanceof PrintWriter) {
          ((PrintWriter) out).print(this.formattedString()); 
       } else if (out instanceof JspWriter) {
          ((JspWriter) out).print(this.formattedString()); 
       } else {
          throw new IllegalArgumentException("NameAndAddress.print expected ether a PrintWriter or a JspWriter but received a " + out.getClass().getName());
       }
    catch (Exception ex)
    { System.err.println("\"out\" is not a printable type: " + ex); }

BTW: methods in Java should start with a lowercase letter by convention.

Peter Tillemans
Tried this, but Java still doesn't find the method to execute these conditions.
Brian Kessler
I double checked : JspWriter.print(String s) and PrintWriter.print(String s) both exist. It should work. What is the error you're getting?
Peter Tillemans
The error is, as above. The crux of it is: org.apache.jasper.JasperException: javax.servlet.ServletException: java.lang.NoSuchMethodError: hu.flux.helper.NameAndAddress.print(Ljavax/servlet/jsp/JspWriter;)V which, if I understand correctly, means that Java isn't recognizing JspWriter as an Object
Brian Kessler
It could be that the object you're passing is loaded in a different classloader from the one the NameAndAddress class has been loaded. Is the NameAndAddress code loaded from a servlet or from the JspPage?
Peter Tillemans
I'm calling the class from two places. I have a servlet calling with PrintWriter; this works fine. I have a Jsp page calling with JspWriter; this is the one that causes the problem if I don't have a method explicitly for JspWriter.
Brian Kessler
I am at the end of my rope here, sorry. Good luck hunting the bug.
Peter Tillemans
+3  A: 

This exception says that your JSP code haven't been recompiled after you changed print(JspWriter) to print(Object), so it still tries to call print(JspWriter) and can't find it.

In order to force a recompilation you may modify your JSP page.

axtavt
Right, either recompile or just move all that Java code out into a servlet where it belongs. Instead of modifying you can also choose to clean the work directory. Most IDE's offer a *Clean* context menu option in server entry.
BalusC
Recompiled multiple times... that's not it, but cheers for the response.
Brian Kessler
@Brian: Stacktrace remains the same after recompilation? If so, recompilation doesn't actually occur for some reason.
axtavt
Not identical... Besides, I can make changes which will have the code functional (though not functioning as I'm trying to get it to function) and it works and then change things so that Java must realize that JspWriter inherits from either Object or Writer and it breaks again.
Brian Kessler
something stagnant in your environment. do a clean build. restart your machine. format disk.
irreputable
A: 

Well you start your method by casting to a PrintWriter, so maybe the jvm optimized the method for you. Since JspWriter does not subclass from PrintWriter, your best bet is to write two methods:

public void print(JspWriter out) 
{ 
    if (out == null) return;
    try {
        out.print(this.FormattedString());
    } Except (IOException e) {
        // handle error 
    }
}

public void print(PrintWriter out) 
{ 
    if (out == null) return;
    try {
        out.print(this.FormattedString());
    } Except (IOException e) {
        // handle error 
    }
}

If only JspWriter and PrintWriter had a common interface... sigh.

Mike Axiak
They both descend from Writer.
Tony Ennis
JspWriter _can_ be cast to PrinterWriter though. If I add this little method, the problem will go away: public void print(JspWriter out) throws java.io.IOException { print (new PrintWriter(out)); }
Brian Kessler
@brian kessler - Java will let us cast classes to other classes, but that doesn't mean that when it's time for that code to be executed that it will run. In the case of passing in an Object and casting whatever it _really_ is to a PrintWriter, it means we're simply turning off the error checking that the designers put in to the language.
Tony Ennis
@Tony Ennis: Writer does not have a print() method, so using the writer interface wouldn't work.
Mike Axiak
A: 

Your print() casts Object to a PrintWriter. But when you pass in a JspWriter which isn't PrintWriter, the cast fails.

However, PrintWriter and JspWriter are both descended from Writer. Can you change your print() method to accept a Writer instead of an Object, and then use the Writer.write() method within print()? This is common to both classes.

This:

public void print(Writer writer) {
    try {
        writer.write(this.FormattedString());
    } catch (IOException e) {
        // log something...
    }
}
Tony Ennis
Tried this, but the result seems the same: the method isn't found so it never gets to casting.
Brian Kessler
Wait, which method isn't found.
Tony Ennis
As above: org.apache.jasper.JasperException: javax.servlet.ServletException: java.lang.NoSuchMethodError: hu.flux.helper.NameAndAddress.print(Ljavax/servlet/jsp/JspWriter;)V
Brian Kessler
Insert the code I posted, and comment out the `print(Object out)` method that's currently there. See what happens. Consider putting a main() in your class so you can run tests from the IDE/command line (much faster) or even better, add junit tests.
Tony Ennis
Still recognizes only PrintWriter, not JspWriter.
Brian Kessler
Inconceivable! Verify the JspWriter you're using is a subclass of java.io.Writer please.
Tony Ennis
+1  A: 

What you posted should work since both PrintWriter and JspWriter are subclasses of Writer (and of course both are subclasses of Object). Something seems to be wrong either with your test code or with your environment.

Maybe you can try a simplified example and see if that works, then build up from there. I can suggest starting here:

public class Test
{
    public void print(Writer writer) throws IOException
    {
        if (writer == null)
            System.out.println("Null writer");
        else
        {
            writer.write("hello");
            writer.flush();
        }
    }

    public static void main(String args[]) throws IOException
    { 
        Test test = new Test();

        System.out.print("Testing PrintWriter...");
        PrintWriter p = new PrintWriter("d:/temp/pwriter_text.txt");
        test.print(p);
        System.out.print("Tested PrintWriter...");

        System.out.print("Testing JspWriter...");
        JspWriter j = null;
        test.print(j);
        System.out.print("Tested JspWriter...");
    }
}

This should compile and run. The second time test.print() is called, the JspWriter being passed in will be null, but you should not get any NoSuchMethodError. If this works, take the code and test it from your servlet / JSP pages. Hopefully this will help you find the problem.

Grodriguez
That will compile, but I'll get errors when it hits test.print(j) cause its not instantiated, which it can't be cause its abstract.
Brian Kessler
@brian Kessler - that's exactly his point. The error you're getting looks to be a _can't find an appropriate method_ error. So Grod's solution, while it won't produce the output you want, _will_ test the idea that the method isn't found. If it still isn't found, then you have a different problem than the one we're trying to solve.
Tony Ennis
You will not get any errors. As I explained in my answer, when you call `test.print(j)`, `j` will be `null`, but this is checked in the implementation of the `print` method. You should not get any errors or exceptions. The only purpose of this is to demonstrate that the `test` method gets called if a `JspWriter` is passed in.
Grodriguez
@Tony Ennis: Yes, exactly.
Grodriguez
+1 good idea...
Tony Ennis
I _do_ get errors: Exception in thread "main" java.lang.NullPointerException at java.io.Writer.<init>(Unknown Source) at java.io.PrintWriter.<init>(Unknown Source) at java.io.PrintWriter.<init>(Unknown Source) at hu.flux.helper.NameAndAddress.print(NameAndAddress.java:51) at hu.flux.helper.NameAndAddress.main(NameAndAddress.java:97)
Brian Kessler
@brian Kessler - you have something askew. post your latest and greatest main() if you haven't done so. We can't tell which line is #97, btw. Do you really have doubled doubled-quotes in your code where you specify the PrintWriter output file or is that a formatting issue?
Tony Ennis
@Brian: Those errors you are getting do not come from the code snippet I suggested. There is no `NameAndAddress` class in my code, and that was intentional. Please try to run the code snippet from my answer. You should not be getting any errors.
Grodriguez
Actually, the above error is from trying to test in the NameAndAddress class. I didn't expect anything different with the Test class, but I decided to try it anyway. After adding imports, I discovered while it gets errors, the errors are far from what I expected. Eclipse gives me "Launch Error: Editor does not contain a main type" and running from the command line, I get Exception in thread "main" java.lang.NoClassDefFoundError: Could not find the main class: build\classes\hu\flux\helper\Test.class. ... But I see the main class, so I have no idea why it is complaining about it.
Brian Kessler
@Tony Ennis: I've posted the "latest and greatest" above ... not sure what you mean about the doubled doubled-quoted code...
Brian Kessler
@Brian: Something seems to be wrong with your environment. Are you able to compile and run a simple "hello world"-like example with a Java class containing a main method?
Grodriguez
@Grodriguez: I am able to compile and run many Java classes, from a simple "hello world" to the more complex NameAndAddress class we have been discussing. I have never before seen this problem that I'm seeing now with your test method. It has me flabbergasted.
Brian Kessler
If you can compile a simple hello world then you should certainly be able to compile and run the `Test` class I posted. I can compile and run it without problems. That's why I am suspecting something wrong with your environment, and looks like I'm not the only one. I suggest creating a simple hello world example, perhaps in a class named `Test`, in a new Eclipse project (or you can do without Eclipse, and compile and run from the command line). If that works, replace the `Test` class with the code I posted. If that works, then we'd be getting somewhere.
Grodriguez
@Grodriguez: Something does seem to be very wrong... While I can still get old classes to work, if I just create a new class that says nothing but public class Test2 { public static void main(String[] args) { System.out.print("Hello! World"); }} I am getting this problem with the main method... going to try turning it off and on again...
Brian Kessler
OK.. now we're in the right direction.
Grodriguez
Created a new project and am able to run your test in it... your right, no errors.... this is the output: Testing PrintWriter...Tested PrintWriter...Testing JspWriter...Null writerTested JspWriter...
Brian Kessler
Very good. So this demonstrates that the `print` method is actually being called for a `JspWriter` argument. In my test code, the `JspWriter` being passed in is `null`, but this will not be the case when this is called from your servlet or JSP. I assume that your question is now solved.
Grodriguez
@Grodriguez -- Not exactly... After this, I went back to trying to call the method from the JSP pages and there I still get a NoSuchMethod if I rely on print(Writer writer) to catch the reference... so it seems that printer(Writer writer) will catch a JspWriter object from the console, but not a jsp page?
Brian Kessler
`print(Writer)` will be always called if the parameter passed in is a subclass of `Writer`. If this does not happen in your case, then the problem is clearly somewhere else. I suggest you start from a tiny JSP example (different project, etc etc.) and build up from there.
Grodriguez
There isn't really much to this code to begin with... I'm just playing with some code from a JSP tutorial (I learn better by engaging than by just staring at or typing code). I've modified the code so that the console will display "--Entering--" timestamp immediately when it is called, even from a JSP page. This code never gets called if print (JspWriter writer) isn't present to catch the call.
Brian Kessler
Brian, there is something else wrong with your code or setup. As I said (and you were able to verify) the `print(Writer)` method will be called if the argument is a subclass of `Writer`. If this does not work in your tests, then something else is wrong. I'm afraid I cannot help further than this.
Grodriguez