Я пошел немного дальше и решил расширить фнкциональность до исполнения любых сервлетов тем самым Jetty, который встроен в RWT приложение. И, самое главное интегрировать JAX-WS веб-сервисы туда же. Сочувствующим, добро пожаловать под кат.
Подраземевается, что приложение запускается из OSGi платфопмы, у меня это Equinox.
Для начала, из документации известно, что можно сделать некоторый Jetty customizer и, передав системным свойством его название, получить некоторую кастомизацию. Так и сделаем: при запуске всего приложения передадим
-Dorg.eclipse.equinox.http.jetty.customizer.class=ru.futurelink.jetty.customizer.MyJettyCustomizer
Код кастомайзера:
public class MyJettyCustomizer extends JettyCustomizer { @Override public Object customizeContext(Object context, Dictionary<String, ?> settings) { ServletContextHandler c = (ServletContextHandler) context; // Сервлет, раздающий файлы c.getServletHandler().addServletWithMapping( MyFileServlet.class, "/files/*"); // Сервлет, обслуживающий веб-сервисы JAX-WS // import com.sun.xml.ws.transport.http.servlet.WSServlet; c.getServletHandler().addServletWithMapping( WSServlet.class, "/service/mobile"); // Сервлет-обработчик страниц ошибок ErrorHandler errorHandler = new MyJettyErrorHandler(); errorHandler.setShowStacks(true); c.setErrorHandler(errorHandler); // Добавляем веб-сервисы JAX-WS (самое интересное внизу) c.getServletContext().getContextHandler(). addEventListener(new MyServletContextListener()); return c; } }
Тут мы делаем следующее:
1) Мапим какие-то сервлеты, по определенным урлам, теперь они там будут доступны, тут все просто.
2) Устанавливаем кастомный обработчиик для ошибочных страниц.
3) Добавляем обслуживание веб-сервисов JAX-WS — об этом подробнее в конце, это интересно 😉
Если с первым пунктом все понятно и просто — пишем сервлет, мапим его на URL и вуаля, он доступен, то второй требует примера кода обработчика, вот он:
public class MyJettyErrorHandler extends ErrorHandler { @Override protected void handleErrorPage(HttpServletRequest request, Writer writer, int code, String message) throws IOException { if (code == 404) { writer.write("No,no,no!!! This page does not exist!"); return; } super.handleErrorPage(request, writer, code, message); } }
Теперь можно обработать любой HTTP response code нужным нам способом.
Теперь самое интересное, интергируем JAX-WS в наше RWT прлиожение. Сложность тут возникает при реализации самого веб-сервиса. Нам надо сделать XML описание, которое разместить в WEB-INF/sun-jaxws.xml.
<?xml version="1.0" encoding="UTF-8"?> <endpoints xmlns="http://java.sun.com/xml/ns/jax-ws/ri/runtime" version="2.0"> <endpoint name="ClientService" implementation="ru.futurelink.mo.mobile.server.ClientService" url-pattern="/service/mobile"/> </endpoints>
Но наше приложение никак не хочет видеть этот файл описание и создавать эти самые конечные точки. А стандартное решение, которое предлагается для JAX-WS для создания конечных точек в рантайме не подходит, т. к. порождает еще один веб-сервер com.sun.net.httpserver. Надо вешать его на отдельный порт, да и вообще, как-то коряво это. Надо другое решение, которое будет использовать уже существующий Jetty для обработки запросов к веб-сервисам.
Мы уже добавили сам сервлет, который будет обрабатывать запросы к веб-сервису, теперь надо чтобы заработали конечные точки (endpoints). Однако, так, как наш проект работает в OSGi фреймворке, то файл описание, который мы только что создали JAX-WS не видит. Все дело в том, что JAX-WS не является совместимым OSGi бандлом, хотя мы его перепаковали (надеюсь это так?) в бандл из простого JAR и добавили ему манифест. Надо заставить его скушать этот файл, при этом сделать все правильно в стиле OSGi.
Весь наш кастомайзер мы упаковываем во фрагмент к хосту org.eclipse.equinox.http.jetty. Это обязательно, как известно фрагмент расширяет classpath, тем самым позволяет хосту находить и загружать какие-то кастомные части из classpath фрагмента, как из своего собственного. Мы заставим JAX-WS использовать jetty как HTTP транспорт, вместо обычного дефолтового com.sun.net.httpserver.
Далее, берем несколько классов из JAX-WS (к сожалению, архитектура этого пакета не позволяет корректно наследоваться и нам придется переопределить все), из com/sun/xml/ws/transport/http/servlet. Вот пример:
public final class WSServletContextListener {…}
Не знаю зачем они так сделали… теперь нам придется переписать кусок JAX-WS к себе во фрагмент. Переписанные файлы можно найти в github: github.com/futurelink/habrahabr
Всего нам понадобится 4 класса:
WSServletContextListener — MyServletContextListener,
ServletResourceLoader — MyServletResourceLoader,
ServletContainer — MyServletContainer,
DeploymentDescriptorParser — MyDeploymentDescriptorParser
Все это нужно нам было ради изменения одной единственной строки кода в WSServletContextListener:
static final String JAXWS_RI_RUNTIME = "/WEB-INF/sun-jaxws.xml";
заменили на:
static final String JAXWS_RI_RUNTIME = "/META-INF/sun-jaxws.xml";
Проблема в том, что теперь, с равертыванием новой версии JAX-WS, надо следить и за этим фрагментом, чтобы он нормально работал. Но другого способа интегрироваться в Jetty я не обнаружил. Если кто-то захочет пройти этот путь с начала, будет интересно почитать иное решение.
ссылка на оригинал статьи http://habrahabr.ru/post/206088/
Добавить комментарий