My goal is to configure a Jetty server to work with the Vaadin 11 framework.
What I was trying was the following:
public static void main(final String[] args) {
final Server server = new Server();
final ServerConnector httpConnector = new ServerConnector(server);
httpConnector.setPort(8080);
final ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
contextHandler.setContextPath("/");
final ServletHolder servletHolder = new ServletHolder(new VaadinServlet());
contextHandler.addServlet(servletHolder, "/*");
final WebAppContext context = new WebAppContext();
context.setServer(server);
context.setContextPath("/");
context.setClassLoader(Thread.currentThread().getContextClassLoader());
server.addConnector(httpConnector);
server.setHandler(contextHandler);
server.setHandler(context);
try {
server.start();
} catch (final Exception e) {
e.printStackTrace();
}
}
#Route("/")
public class MainView extends Div {
public MainView() {
System.out.println("main");
setText("aha bye");
}
}
But the MainView is never called.
How can I tell Jetty to forward the requests to Vaadin?
Answering my own question here.
But this will still not work as you expect. Why? Because you have 2 different contexts (contextHandler and context) on the same contextPath /.
But again, this seems backwards, as your WebAppContext has no resource base or war declared, so it's not doing anything for you in your sample code.
Joakim Erdfelt is absolutely right. I messed up the code with two contexts and I either have to set a resource base or a war-file. Many thanks to you as you pointed out the way to the solution!
Thanks to Leif Åstrand too for the hint of finding all classes with the #Route annotation!
I separated the server code from the "client" code and build the war-file for the jetty server.
What I use as a working solution is the following:
final WebAppContext context = new WebAppContext();
context.setServer(httpServer);
context.setContextPath("/");
context.addServlet(new ServletHolder(new TestServlet()), "/*");
context.setWar(warFile.getAbsolutePath());
context.setExtractWAR(true);
context.setClassLoader(Thread.currentThread().getContextClassLoader());
context.setInitParameter("ui", MainUI.class.getCanonicalName());
httpServer.setHandler(context);
In the Testservlet (extending VaadinServlet) I have the following two methods:
#SuppressWarnings("unchecked")
private void findRouteClasses() {
final Set<Class<?>> routeClasses = ReflectionUtils.findClassesWithAnnotation(Route.class,
"com.example.myproject");
this.routeClasses = new HashSet<>();
for (final Class<?> clazz : routeClasses)
this.routeClasses.add((Class<? extends Component>) clazz);
}
#Override
protected void servletInitialized() throws ServletException {
final ServletContext context = getServletContext();
final Object routeRegistryObject = context.getAttribute(RouteRegistry.class.getName());
if (routeRegistryObject == null)
throw new ServletException("routeRegistryObject is null");
if ((routeRegistryObject instanceof RouteRegistry) == false)
throw new ServletException("routeRegistryObject is not of type RouteRegistry");
final RouteRegistry routeRegistry = (RouteRegistry) routeRegistryObject;
findRouteClasses();
try {
routeRegistry.setNavigationTargets(routeClasses);
} catch (final InvalidRouteConfigurationException e) {
throw new ServletException(e);
}
super.servletInitialized();
}
All classes with the annotation com.vaadin.flow.router.Route are seached and then set as the navigation targets.
server.setHandler(contextHandler);
server.setHandler(context);
You replaced your contextHandler with the context with that code.
Try this instead ...
HandlerList handlers = new HandlerList();
handlers.addHandler(contextHandler);
handlers.addHandler(context);
handlers.addHandler(new DefaultHandler()); // to report errors if nothing above matches
server.setHandler(handlers);
But this will still not work as you expect.
Why? Because you have 2 different contexts (contextHandler and context) on the same contextPath /.
The first context in your HandlerList will be used, and the next one will never be called.
Why? Because once you enter a context, you do not exit it (that context MUST serve a response, even an error).
You could just modify your WebAppContext to include the VaadinServlet (eliminating the need for ServletContextHandler entirely)
eg:
final WebAppContext context = new WebAppContext();
final ServletHolder servletHolder = new ServletHolder(new VaadinServlet());
context.addServlet(servletHolder, "/*");
But again, this seems backwards, as your WebAppContext has no resource base or war declared, so it's not doing anything for you in your sample code.
If it were me, and I'm using embedded-jetty, I would avoid using a WebAppContext entirely and just stick with the ServletContextHandler only.
Related
I write a jettyserver.java as start service,here is the code,
public class jettyserver {
public static void main(String[] args) throws Exception {
Server server = new Server(8080);
ServletHolder servletHolder = new ServletHolder(ServletContainer.class);
Map<String, String> parameterMap = new HashMap<String, String>();
parameterMap.put("jersey.config.server.provider.packages", "com.heu.cs.mavenproject3");
servletHolder.setInitParameters(parameterMap);
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/jettyproject/*");
context.addServlet(servletHolder, "/*");
server.setHandler(context);
server.start();
server.join();
}
}
And I write a demo.java as a test demo,
#Path("/demo")
public class demo {
#GET
#Produces({"text/html"})
public String index(){
return "OK";
}
}
and then i run jettyserver.java,I got OK like:
we can see OK,that's right
but wen I enter localhost:8080/jettyproject/index.html or localhost:8080/index.html in the browser, I got a 404 error like this:
what should I do ? What'wrong with the code????
I am in process of migrating jetty 7 to jetty 8,
I am getting this error:
no suitable method found for
addFilter(Class,String,int)
servletContext.addFilter(JsonTokenFilter.class, "/*", FilterMapping.REQUEST);
What I am trying to do is:
// servlet context
ServletContextHandler servletContext = new ServletContextHandler( ServletContextHandler.SESSIONS | ServletContextHandler.SECURITY);
// set session manager
SessionHandler sessionHandler = new GatewaySessionHandler(sessionManager);
servletContext.setSessionHandler(sessionHandler);
sessionHandler.addEventListener(new SessionListener());
sessionHandler.addEventListener(new CsrfGuardHttpSessionListener());
sessionManager.setSecureRequestOnly(Utilities.getConfigBoolean("fievel.jetty.secureCookies", true));
sessionManager.setHttpOnly(Utilities.getConfigBoolean( "fievel.jetty.httpCookies", true));
setSessionSettings();
// add json rpc signal if session token invalid
servletContext.addFilter(JsonTokenFilter.class, "/*", FilterMapping.REQUEST);
The last line is the one that is giving error.
If I refer ServletContextHandler for jetty 8,
addFilter(FilterHolder holder, String pathSpec, EnumSet<DispatcherType> dispatches)
is ideally the type of code that I should write.
Can anybody give me a clue where am I going wrong?
i think you should be writing something like this:
servletContext.addFilter(JsonTokenFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
since it expects an EnumSet instead of int
I've tried the tutorial from eclipse jetty tutorial
public static void main(String[] args) throws Exception
{
Server server = new Server(8080);
ServletContextHandler servletContext = new ServletContextHandler(ServletContextHandler.SESSIONS);
servletContext.setContextPath("/");
server.setHandler(servletContext);
servletContext.addServlet(new ServletHolder(new HelloServlet()),"/*");
servletContext.addFilter(TestFilter.class, "/*", EnumSet.of(DispatcherType.REQUEST));
server.start();
server.join();
}
the output is:
2016-08-10 15:13:58.054:INFO:oejs.Server:jetty-8.1.19.v20160209
TestFilter Init
2016-08-10 15:13:58.186:INFO:oejs.AbstractConnector:Started SelectChannelConnector#0.0.0.0:8080
TestFilter doFilter running
so the filter is registered for sure in this way
I have an endpoint with:
#POST
#Path("/test")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public String canaryTest(String JSON) {
return JSON;
}
When I register it in Jetty using Jersey
ServletHolder holder = new ServletHolder(new ServletContainer());
everything seems to work fine.
But in case I try to specify explictly the default config, it stops working (returning a media type error from the endpoint). Even by just passing a default instance of a ResourceConfig to the ServletContainer, it stops working.
ResourceConfig config = new ResourceConfig();
//config.property(x,defaultX)
//config.property(y,defaultY)
ServletHolder holder = new ServletHolder(new ServletContainer(config));
I'd like to emulate the default configuration behavior manually and explicitly, so what I am asking here is how should I configure ResourceConfig so the behavior keeps working (i.e, what properties to set)
P.S: i'm using Jetty 9.2.6.v20141205 and Jersey 2.14.
Dependencies in Maven:
org.eclipse.jetty.jetty-server org.eclipse.jetty.jetty-servlet
org.eclipse.jetty.jetty-servlets
org.glassfish.jersey.containers.jersey-container-servlet-core
com.sun.jersey.jersey-json
org.glassfish.jersey.media.jersey-media-json-jackson
I don't know how you got this to work
ServletHolder holder = new ServletHolder(new ServletContainer());
I could not produce a working example simply instantiating the ServletContainer(). Though I was about to get it to work with the following code
public class TestJerseyServer {
public static void main(String[] args) throws Exception {
ResourceConfig config = new ResourceConfig();
config.packages("jetty.practice.resources");
ServletHolder jerseyServlet
= new ServletHolder(new ServletContainer(config));
Server server = new Server(8080);
ServletContextHandler context
= new ServletContextHandler(server, "/");
context.addServlet(jerseyServlet, "/*");
server.start();
server.join();
}
}
Using all your dependencies, excluding the com.sun.jersey:jersey-json, as it's not needed. No other configuration. The resource class
#Path("test")
public class TestResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
public Response getTest() {
Hello hello = new Hello();
hello.hello = "world";
return Response.ok(hello).build();
}
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response postHello(Hello hello) {
return Response.ok(hello.hello).build();
}
public static class Hello {
public String hello;
}
}
in the jetty.practice.resources package.
I'm curious to see how you got it to work without the ResourceConfig
Another thing I should mention is that jersey-container-servlet-core should be switched out for jersey-container-servlet. The former is for 2.5 container support, but the latter is recommended for 3.x containers. It not have any effect though, with my example
cURL
C:\>curl http://localhost:8080/test -X POST -d "{\"hello\":\"world\"}" -H "Content-Type:application/json"
world
C:\>curl http://localhost:8080/test
{"hello":"world"}
I've got some test code with Jersey (2.7) and Jetty (9.2.5.v2014112) that I'm trying to port to CXF (3.0.x) and Jetty (same version). There's one part that I can't figure out, the equivalent of the rc.register(resource) line below:
ServletContextHandler sch = new ServletContextHandler();
sch.setContextPath("/xxx");
resource = new TheResource();
ResourceConfig rc = new ResourceConfig();
rc.register(resource);
ServletContainer sc = new ServletContainer(rc);
ServletHolder holder = new ServletHolder(sc);
sch.addServlet(holder, "/*");
Server server = new Server(port);
server.setHandler(sch);
server.start();
server.join();
For the CXF/Jetty case I know I need to do something like:
CXFNonSpringJaxrsServlet context = new CXFNonSpringJaxrsServlet();
// EQUIVALENT OF:
// ResourceConfig rc = new ResourceConfig();
// rc.register(myresource)
// SOMETHING.setResourceConfig(rc)
ServletHolder holder = new ServletHolder(context);
ServletContextHandler handler = new ServletContextHandler("/restroot");
handler.addServlet(holder, "/*");
// Using ContextHandlerCollection cos my jetty needs to serve more
// than just this REST service
ContextHandlerCollection collection = new ContextHandlerCollection();
collection.addHandler(handler);
Server server = new Server(8080);
server.setHandler(collection);
server.start();
server.join();
Does anybody know how/if this is possible?
Just to clarify what I'm trying to do here: I'm trying to publish a JAX-RS service on an embedded Jetty instance, and I need full control over the lifecycle of the JAX-RS bean.
Maarten
From CXF version 3.0.4 onwards, there are additional constructors on the CXFNonSpringJaxrsServlet that make this a lot easier:
public SNAPSHOTApp(int port) throws Exception {
resource = new TheResource();
Set<Object> resourceSingletons = new HashSet<>();
resourceSingletons.add(resource);
CXFNonSpringJaxrsServlet context = new CXFNonSpringJaxrsServlet(resourceSingletons);
ServletHolder servlet = new ServletHolder(context);
ServletContextHandler handler = new ServletContextHandler();
handler.addServlet(servlet, "/*");
handler.setContextPath("/snapshot");
Server server = new Server(port);
server.setHandler(handler);
server.start();
server.join();
}
Big thanks to #Sergey Beryozkin for adding this based on my question on the CXF user mailing list.
Look at the source for CXFNonSpringJaxrsServlet, it is configured via init params from web.xml. However you're free to override that functionality of course
I've managed to get something working by:
extending CXFNonSpringJaxrsServlet
Overriding getResourceProviders()
Providing my own implementation of ResourceProvider
It all looks a bit complex, and I feel that there should be a standard API for this in CXF, but anyways, here's my solution:
public class TheApp {
private static final Logger LOG = LoggerFactory.getLogger(TheApp.class);
TheResource resource;
public static class BeanResourceProvider implements ResourceProvider {
private final Object theBean;
public BeanResourceProvider(Object theBean) {
this.theBean = theBean;
}
#Override
public Object getInstance(Message m) {
return theBean;
}
#Override
public void releaseInstance(Message m, Object o) {
}
#Override
public Class<?> getResourceClass() {
return theBean.getClass();
}
#Override
public boolean isSingleton() {
return true;
}
};
public TheApp(int port) throws Exception {
resource = new TheResource();
CXFNonSpringJaxrsServlet context = new CXFNonSpringJaxrsServlet() {
public void configureSingleton(Object o) {
LOG.info("configureSingleton() called");
}
public Map<Class<?>, ResourceProvider>
getResourceProviders(ServletConfig servletConfig,
Map<Class<?>, Map<String, List<String>>> resourceClasses) throws ServletException {
LOG.info("getResourceProviders called");
Map<Class<?>, ResourceProvider> map = new HashMap<Class<?>, ResourceProvider>();
for (Map.Entry<Class<?>, Map<String, List<String>>> entry : resourceClasses.entrySet()) {
Class<?> c = entry.getKey();
LOG.info("getting provider for {}", c.getName());
map.put(c, new BeanResourceProvider(resource));
}
return map;
}
};
ServletHolder holder = new ServletHolder(context);
holder.setInitParameter("jaxrs.serviceClasses", "com.test.cxfjetty.TheResource");
ServletContextHandler handler = new ServletContextHandler();
handler.addServlet(holder, "/*");
handler.setContextPath("/theroot");
Server server = new Server(port);
server.setHandler(handler);
server.start();
server.join();
}
public static void main(String[] args) throws Exception {
TheApp app = new TheApp(8122);
}
}
Fun exercise this was...
I am trying to launch a Jetty server from a class. When I try to run it from eclipse it works fine. If I embed it to a JAR the jetty server starts, but when I make a request it return a 500 code response. Here is my class:
#GET
#Path("test")
#Produces(MediaType.APPLICATION_JSON)
public String echo(#QueryParam("testParam")String test){
return test;
}
private Server server;
public synchronized void start(int port) throws Exception {
if (server != null) {
throw new IllegalStateException("Server is already running");
}
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
Map<String,Object> initMap = new HashMap<String, Object>();
initMap.put("com.sun.jersey.api.json.POJOMappingFeature", "true");
initMap.put("com.sun.jersey.config.property.packages", "the.class.package");
context.addServlet(new ServletHolder(new ServletContainer(new PackagesResourceConfig(initMap))), "/*");
this.server = new Server(port);
this.server.setHandler(context);
this.server.start();
}
public static void main(String[] args) throws Exception {
if(args.length != 1) {
System.out.println("AnnotatorServer <port>");
System.exit(-1);
}
JettyServer server = new JettyServer();
server.start(Integer.parseInt(args[0]));
}
When I try to launch it embedded in a JAR, the server starts, but when I access the method, I get the following exception:
Caused by: com.sun.jersey.api.MessageException: A message body writer for Java class java.lang.String, and Java type class java.lang.String, and MIME media type application/octet-stream was not found
Do you have any idea why is this happening?? Thank you!
The problem was on the POM file. The dependency was overwriting some files in Meta-Inf/service. Found the solution to Jersey exception only thrown when depencencies assembled into a single jar