📜 ⬆️ ⬇️

RWT / RAP + Jetty + JAX-WS Integration

When developing RAP / RCP applications, I had a problem: how to make custom error-pages and integrate them into embedded jetty. There are many tutorials on the Internet on how to do this if you build Jetty into your application. But with RWT everything is more complicated. RWT itself runs the Jetty and starts it as a managed service. At the same time, the Jetty interface itself is hidden from other bundles and it will not work directly with it.
I went a little further and decided to extend the functionality to the execution of any servlets, thereby Jetty, which is embedded in the RWT application. And, most importantly, integrate JAX-WS web services in the same place. Sympathetic, welcome under cat.



It is assumed that the application is launched from OSGi platform, I have it Equinox.
')
To begin with, it is known from the documentation that it is possible to make some Jetty customizer and, passing the name of the system property, to get some customization. So let's do it: when launching the entire application, we will pass

-Dorg.eclipse.equinox.http.jetty.customizer.class = en.futurelink.jetty.customizer.MyJettyCustomizer

Customizer code:

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; } } 

Here we do the following:
1) Mapim some servlets, for certain urla, now they will be available there, everything is simple.
2) Install custom process for erroneous pages.
3) Add JAX-WS web services maintenance - more on this in the end, this is interesting;)

If everything is clear and simple with the first item - we write the servlet, map it to the URL and voila, it is available, then the second requires an example of the handler code, here it is:

 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); } } 

Now we can handle any HTTP response code in the way we want.

Now the most interesting, we integrate JAX-WS into our RWT application. The difficulty here arises in the implementation of the web service itself. We need to make an XML description that is placed in 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> 

But our application does not want to see this file description and create these same endpoints. And the standard solution proposed for JAX-WS for creating endpoints in runtime is not suitable, since it generates another com.sun.net.httpserver web server. We need to hang it on a separate port, and indeed, somehow it is clumsy. We need another solution that will use the already existing Jetty to process requests for web services.

We have already added the servlet itself, which will process requests to the web service, now we need to earn endpoints. However, since our project works in the OSGi framework, the file description that we just created is not seen by JAX-WS. The thing is that JAX-WS is not a compatible OSGi bundle, although we repacked it (I hope so?) Into a bundle from a simple JAR and added a manifest to it. It is necessary to force him to eat this file, while doing everything correctly in the style of OSGi.

We pack all our customizer in a fragment to the host org.eclipse.equinox.http.jetty . It is necessary, as is known, the fragment extends the classpath, thereby allowing the host to find and load some custom parts from the classpath fragment, as if from its own. We will make JAX-WS use jetty as an HTTP transport, instead of the usual default com.sun.net.httpserver.

Next, we take several classes from JAX-WS (unfortunately, the architecture of this package does not allow to inherit correctly and we will have to override everything), from com / sun / xml / ws / transport / http / servlet . Here is an example:

 public final class WSServletContextListener {…} 

I don’t know why they did it like this ... now we’ll have to rewrite a piece of JAX-WS into our fragment. Overwritten files can be found in github: github.com/futurelink/habrahabr

In total, we need 4 classes:
WSServletContextListener - MyServletContextListener,
ServletResourceLoader - MyServletResourceLoader,
ServletContainer - MyServletContainer,
DeploymentDescriptorParser - MyDeploymentDescriptorParser

All this we needed was to change a single line of code in the WSServletContextListener:

 static final String JAXWS_RI_RUNTIME = "/WEB-INF/sun-jaxws.xml"; 

replaced by:

 static final String JAXWS_RI_RUNTIME = "/META-INF/sun-jaxws.xml"; 

The problem is that now, with the unwrapping of the new version of JAX-WS, it is necessary to monitor this fragment so that it works normally. But I found no other way to integrate into Jetty. If someone wants to go this way from the beginning, it will be interesting to read another solution.

Source: https://habr.com/ru/post/206088/


All Articles