views:

875

answers:

6

I need programmaticaly render JSP page. As far as I understand JSP should have some compiler. The question is can I use this compiller dirrectly without JspServlet and others? All I need is documentation how to use JSP compiler (Jasper, for example).

Some additional information would clarify situation, I think. I can not use standart JspServlet. I want to change source JSP before compilation in some way (merge two JSP together to be precise), so I need a way to compile JSP result from InputStream (or Reader) using JSP compiller directly.

Merging of two JSP is layout requirement. You can ask: "But why this guy just doesn't use SiteMesh or something like this?". One of JSP pages is not static. It's provided by user and stored in database. We sanitify and validates this JSP layout (users are able to use only subset of tags, and all of them are not standart but created specially for them), cache them and so on. But now we need a way to use these JSP pages (which are stored in memory) as layouts for all JSP pages that user request.

A: 

Perhaps you could use Tomcat's JspC ant task?

Jonathan Feinberg
Not exactly. I need to compile JSP on server side by myself. So ant task is not what I looking for. But I will look source code of this task. Maybe it will be helpfull. Thank you.
dotsid
A: 

JSTL is just a library of tags used inside JSP files. So it really doesn't matter in this context.

Since a JSP compiler transforms JSP files into Java Servlets, I doubt you can have it directly run(a compiler doesn't actually run anything!) or render for that matter a JSP file.

It's actually quite hard to understand from your question what you're really looking for.

Edit: I would recommend jsp:include for the job

Marius Burz
Yes, mention for JSTL is inconcvenient in this context, agree.And you right, JSP pages becomes servlets after compilation. What I need, is a way to compile JSP pages to the servlets by myself. I can explain my task a little bit deeper. I need to merge two JSP files on some way (from sources) and then compile result.
dotsid
Isn't it enough to include one of the JSP files in the other? See jsp:include and <%@include%>
Marius Burz
Unfortunately, no. The rules for JSP merging are non trivial. Of cource this is problem by it self, but...
dotsid
+3  A: 

I need programmaticaly render JSP page.

What's the functional requirement after all? You're clearly looking for a solution in the wrong direction. What is it, the problem/requirement for which you think that this is the solution? We may come up with better suggestions.

Do you for example need just its output? If so, then java.net.URLConnection may suffice.

Edit: you edited your question:

I want to change source JSP before compilation in some way (merge two JSP together to be precise), so I need a way to compile JSP result from InputStream (or Reader) using JSP compiller directly.

OK, that's a bit more clear. But what for do you need this? What does those JSPs actually represent? What is the final result supposed to be used for?

Do you for example just want to include the one JSP in the other? E.g. including a head.jsp in a main.jsp? If so, then <jsp:include> may suffice. Or is it more worse, do they contain raw Java code with some specific code which you wanted to reuse? If so, then you should use plain Java classes and if necessary taglibs for this.

Edit 2: as you commented:

But now we need a way to use these JSP pages (which are store is in memory by the way) as layouts for all JSP pages that user request

Just store the JSP files on the disk file system inside the webapp's webcontent (the ServletContext#getRealPath() may come into rescue here) and forward the request to your own main JSP file which includes the two JSP files using for example:

<jsp:include page="${page1}" />
<jsp:include page="${page2}" />

Edit 3: I created an SSCCE to prove its working.

package mypackage;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TestServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException
    {
        File root = new File(getServletContext().getRealPath("/"));

        String main = "<jsp:include page=\"${page1}\" /><jsp:include page=\"${page2}\" />";
        write(main, new File(root, "main.jsp"));

        String page1 = "<p>We are in ${data1}";
        write(page1, new File(root, "page1.jsp"));
        request.setAttribute("page1", "page1.jsp");
        request.setAttribute("data1", "first jsp");

        String page2 = "<p>We are in ${data2}";
        write(page2, new File(root, "page2.jsp"));
        request.setAttribute("page2", "page2.jsp");
        request.setAttribute("data2", "second jsp");

        request.getRequestDispatcher("main.jsp").forward(request, response);
    }

    private static void write(String content, File file) throws IOException {
        BufferedWriter writer = null;
        try {
            writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), "UTF-8"));
            writer.write(content);
        } finally {
            if (writer != null) try { writer.close(); } catch (IOException ignore) {}
        }
    }

}

Execute it at http://localhost:8080/playground/test (or whatever host/contextname you're using) and you'll see

We are in first jsp
We are in second jsp

To make it more efficient I would cache every resource and make use of File#exists() to check if the particular page is already saved on disk.

BalusC
I'm sorry for inconvenient. I'm clarified requirements in question.
dotsid
Ok, let's dive into requirements. Merging of two JSP is layout requirement. You can ask: "But why this guy just doesn't use SiteMesh or something like this?". One of JSP pages are not static. It's provided by user and stored in database. We sanitify and validates this JSP layout (users are able to use only subset of tags, and all of them are not standart but created specially for them), cache them and so on. But now we need a way to use these JSP pages (which are store is in memory by the way) as layouts for all JSP pages that user request.
dotsid
Ok, it will work. But I want my JSP to look a bit more clean. Like <m:page title="Page title">...</m:page> and childs of page tag describe page. Page tag by itself replaces with layout on compilation time. Is this possible in some way?
dotsid
Just include taglibs at top of JSP content. Just code JSP the usual way. All you need to do is to store it in webapp's webcontent. See the SSCCE which I've added last.
BalusC
Would the sample code work if the either page1.jsp or page2.jsp was modified after the appserver is started? In most production environments, the appservers do not recompile these files.
Thimmayya
That requires then a different approach. Give it another filename and so on. I however don't think that this applies on his specific case.
BalusC
@BalusC: over and above the call of duty!!
George Jempty
A: 

If the JSP has already been precompiled by the appserver then you could look for the generated .class file . In Tomcat this should be under the $CONTEXT_ROOT/org/apache/jsp/ directory. You might be able to run this class somehow and generate your output.

EDIT: Missed your edit about modifying the JSP source.

Take a look at org.apache.jasper.compiler.AntCompiler (included in jasper.jar in Tomcat). There is a protected method called generateClass which you might be able to override and mess around with :)

Thimmayya
A: 

Which reason you have to do that? If you need merge 2 jsp file to process, maybe using include Or you need other ideas? Can you give sample about your request?

misamap
+1  A: 

I'm not totally sure if this is what you are looking for but the DWR framework contains a method called WebContext.forwardToString that forwards the current request plus a fake response object to a URL and then reads the contents of the buffer into memory. Here's a sample of the code:

StringWriter sout = new StringWriter();
StringBuffer buffer = sout.getBuffer();

HttpServletResponse realResponse = getHttpServletResponse();
HttpServletResponse fakeResponse = new SwallowingHttpServletResponse(realResponse, sout, realResponse.getCharacterEncoding());

HttpServletRequest realRequest = getHttpServletRequest();
realRequest.setAttribute(WebContext.ATTRIBUTE_DWR, Boolean.TRUE);

getServletContext().getRequestDispatcher(url).forward(realRequest, fakeResponse);

return buffer.toString();

You could use this to get the results of the jsp rednering and store them in memory. You can download the source from the above link to see how SwallowingHttpServletResponse works.

Jason Gritman
Thanks a lot for link. It's seems promising
dotsid