Tomcat JSP / JSTL без HTTP - PullRequest
       15

Tomcat JSP / JSTL без HTTP

5 голосов
/ 16 декабря 2011

У меня есть довольно стандартное веб-приложение, работающее под Tomcat 7.

То, что я сейчас пытаюсь сделать, - это использовать JSP / JSTL в качестве языка шаблонов, независимого от аспектов Tomcat, работающих с HTTP / веб, для создания HTML, который можно отправить по электронной почте и преобразовать в PDF.

Кто-нибудь еще пытался сделать это раньше и мог бы помочь мне с некоторыми указателями?

Заранее спасибо.

Ответы [ 2 ]

9 голосов
/ 16 декабря 2011

В отличие от того, что сказал Стивен С. Да, JSP - это сервлеты и т. Д. И т. Д. (И Velocity довольно хорош и прост в использовании)

Но что такое сервлет?

Это интерфейс. Интерфейс с одним основным методом:

service(ServletRequest req, ServletResponse res)

Найдите класс JSP, приведите его к сервлету, создайте реализации ServletRequest и ServletResponse, а затем ...

String jspClassName = findJspClassForJSP("your.jsp");
Class jspClass = Class.forName(jspClassName);
Servlet jspServlet = (Servlet)jspClass.newInstance();
MyServletRequest req = new MyServletRequest();
MyServletResponse resp = new MyServletResponse();
jspServlet.init();
jspServlet.service(req, resp);
jspServlet.destroy();
String results = reps.getContent();

Будет ли это работать? Ну, после некоторой работы это будет. Очевидно, вам нужно реализовать минимальные фасады ServletRequest / Response, а также то, что когда-либо понадобится вашим JSP. Но, вероятно, вам, вероятно, понадобится немного больше, чем атрибуты и потоки. Если ваш ответ возвращает StringWriter, вы на полпути.

Следующая часть создает сервлет из JSP. Удобно, компилятор Jasper сделает это за вас - игра вызывает его. Я никогда не делал этого напрямую, но это, безусловно, можно сделать, так как это делает и контейнер сервлета, и файл сценария / bat JSPC, задача ant, а также большинство контейнеров сервлета, использующих Jasper. Итак, это можно сделать. Как только вы узнаете, как это вызывать, вы узнаете окончательное имя сгенерированного класса для JSP. (См. Первую строку образца.)

Я когда-нибудь делал это? Нет. Но я держу пари, что меньше чем через день возни, ты узнаешь, выполнимо это или нет. Могу поспорить, что это так, особенно если вы не столкнетесь с махинациями в классе. Возможно, у вас возникнет проблема, если вы позволите своим пользователям изменять и восстанавливать JSP (поэтому MyEmail.jsp скомпилируется в MyEmail.class, MyEmail_2.class и т. Д.). Но если вы сами призовете Джаспера, вы, вероятно, будете иметь больше контроля над этим. Другая сложная часть - это определение имени класса JSP. Здесь большинство контейнеров следуют базовому шаблону, поэтому, если вы покопаетесь в сгенерированном коде из WAR, вы, скорее всего, найдете его.

Сделайте JSP достаточно простыми (шаблон электронной почты не должен слишком усложняться встроенной Java или чем-либо, выполняющим произвольные вызовы), и это еще более вероятно, что он будет работать.

Возможно, ваше решение не является переносимым из коробки из Tomcat, но, скорее всего, вам все равно. Люди, с которыми я говорил, которые используют JSP для шаблонов, просто открыли сокет для своего собственного сервера и сделали запрос. Они тоже не зашли так далеко.

Но на поверхности, за исключением какого-то дурацкого чёрного дырочного загрузчика класса, держу пари, вы можете заставить это работать довольно быстро. Реализуйте столько запросов и ответов, сколько вам нужно, сразитесь с несколькими NPE, как JSP и JSTL, которые вы не планировали, и, как говорит Санта,

Хак прочь, Хак прочь, хак прочь все!

Addenda:

Итак, для всех скептиков ...

public void runJsp() {
    JspC jspc = new JspC();
    jspc.setUriroot("/tmp/app");
    jspc.setOutputDir("/tmp/dest");
    jspc.setJspFiles("newjsp.jsp");
    jspc.setCompile(true);
    try {
        jspc.execute();
        Class cls = Class.forName("org.apache.jsp.newjsp_jsp");
        Servlet s = (Servlet) cls.newInstance();
        MyRequest req = new MyRequest();
        MyResponse resp = new MyResponse();

        s.init(getServletConfig());
        s.service(req, resp);
        s.destroy();
        System.out.println(resp.getSw().toString());
    } catch (JasperException ex) {
        throw new RuntimeException(ex);
    } catch (ClassNotFoundException ex) {
        throw new RuntimeException(ex);
    } catch (InstantiationException ex) {
        throw new RuntimeException(ex);
    } catch (IllegalAccessException ex) {
        throw new RuntimeException(ex);
    } catch (ServletException ex) {
        throw new RuntimeException(ex);
    } catch (IOException ex) {
        throw new RuntimeException(ex);
    }
}

Удивительно, что исходный код и полчаса в отладчике сделают для вас.

Я создал простой JSP в /tmp/app/newjsp.jsp.

jspc.setUriroot сообщает компилятору, где находится база вашего "веб-приложения". jspc.setOutputDir сообщает jspc, куда поместить сгенерированные файлы Java и Class. jspc.setJspFiles сообщает jspc, какие файлы компилировать, основываясь на корне URI. jspc.setCompile сказал ему на самом деле скомпилировать код. Наконец, jspc.execute () делает дело.

По умолчанию Jasper использует пакет org.apache.jsp и создает новый класс на основе имени файла JSP. Для моего простого эксперимента я просто поместил "/ tmp / dest" в путь классов моего контейнера Glassfish, чтобы контейнер мог найти сгенерированные классы.

Я загружаю класс и получаю экземпляр.

Наконец, я создал MyRequest, MyRequest и, в конечном счете, MySession. В моей IDE удобно созданы заглушки для соответствующих интерфейсов. В этом случае я реализовал: MyRequest.getSession (), MyResponse.setContentType (), MyResponse.setBufferSize () и MyResponse.getWriter ().

public PrintWriter getWriter() throws IOException {
    if (sw == null) {
        sw = new StringWriter();
        pw = new PrintWriter(sw);
    }
    return pw;
}

Очевидно, что sw и pw являются переменными экземпляра MyResponse.

MyRequest вернул экземпляр MySession.Моя реализация MySession не работает - ничего.Но среда выполнения хотела сессию, она просто не использовала ее для моего очень простого JSP, и я не был заинтересован в том, чтобы заполнить тот из сервлета.

Я тестировал это на Glassfishv2.1.Я просто добавил appserv_rt.jar (из glassfish / lib) в мой путь к классу сборки (чтобы он мог найти jspC jars), но я не связываю его в WAR (так как он уже находится в контейнере).

И, шазам, это сработало.В «реальной жизни», предполагая, что процесс, который хотел использовать JSP, на самом деле был получен из веб-запроса, я просто создал бы HttpServletResponseWrapper и переопределил три предыдущих метода, остальные, вероятно, просто работали.Если веб-запроса вообще нет на картинке, вам нужно создать собственную реализацию Session (на самом деле ничего страшного, это просто карта).

Я бы также использовал частный URLClassLoaderзагрузить искусственные классы JSP.Если бы я ЗНАЛ, что я никогда не перезагрузил бы JSP, то просто сделал бы назначение моим каталогом WEB-INF / classes и предоставил бы ему свой собственный пакет и позволил бы системе загрузить их.

Но, да, это сработало.Ничего страшного.Это просто Java.

3 голосов
/ 16 декабря 2011

Это на самом деле не имеет смысла.JSP - это хороший синтаксис, который приводит к генерации класса сервлета Java EE.Действительно, природа «сервлета» / «http» JSP тщательно переплетается через API и семантическую модель JSP и JSTL.

Если вы хотите генерировать HTML независимо от веб-запросов, вам лучшеиспользования другой шаблонизирующей технологии;например, Velocity или FreeMarker.Если вы затем хотите, чтобы HTML также доставлялся в виде веб-ответов, организуйте, чтобы ваши сервлеты вызывали механизм шаблонов для генерации ответов.(Если вы используете Spring, для этого существует существующая инфраструктура. Другие платформы могут иметь аналогичную поддержку, но если нет, то для этого не составит труда самостоятельно реализовать некоторый склеивающий код.)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...