I'm setting up and application using Undertow, I've set up a ResourceHandler for static files, and Servlet to be used by apache-camel to expose rest services.
I've done this using spring and servlet3.0 in an app container.
In a class extending org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer
#Override
public void onStartup(ServletContext servletContext) throws ServletException
{
super.onStartup(servletContext);
ServletRegistration.Dynamic servlet = servletContext.addServlet("RestServlet", new CamelHttpTransportServlet());
servlet.setLoadOnStartup(1);
servlet.addMapping("/rest/*");
}
And in camel route
restConfiguration()
.component("RestServlet")
.bindingMode(RestBindingMode.json)
.dataFormatProperty("prettyPrint", "true");
Pretty close to whats described in http://camel.apache.org/servlet.html
But if I do this in Undertow as an embedded I get org.apache.camel.NoSuchBeanException: No bean could be found in the registry for: RestServlet of type: org.apache.camel.spi.RestConsumerFactory as I guess Guice never finds the servlets created by Undertow. I tried to manually expose the CamelHttpTransportServlet as a Guice Binding but that doesn't seem to change things.
ClassLoader classLoader = getClass().getClassLoader();
ResourceHandler staticHandler = new ResourceHandler(new ClassPathResourceManager(classLoader, STATIC_RESOURCE_ROOT))
.addWelcomeFiles(INDEX_HTML);
DeploymentInfo deploymentInfo = Servlets.deployment()
.setClassLoader(classLoader)
.setContextPath(ROOT_MAPPING)
.setDeploymentName(DEPLOYMENT_NAME)
.addServlet(
Servlets.servlet("RestServlet", CamelHttpTransportServlet.class)
.addMapping(REST_MAPPING)
.setLoadOnStartup(1)
);
DeploymentManager manager = Servlets.defaultContainer().addDeployment(deploymentInfo);
manager.deploy();
PathHandler path = Handlers.path()
.addPrefixPath(REST_MAPPING, manager.start())
.addPrefixPath(ROOT_MAPPING, staticHandler);
undertow = Undertow.builder()
.addHttpListener(port, LOCALHOST)
.setHandler(path)
.build();
undertow.start();
The static resource work as expected, and it also seems the rest servlet is running and getting the responses but CamelContext won't start up.
I can't use restlet or anything in camel as then the port will be in use so I need to use different port for static files and rest.
Is there any way to have camel identify the Servlet initiated by Undertow?
Ok I finally found out where it went wrong.
I suspect I always used .component("servlet") and not .component("RestServlet"), but Camel wouldn't link this automatically before.
I changed this section to
restConfiguration()
.bindingMode(RestBindingMode.json)
.component("servlet")
.dataFormatProperty("prettyPrint", "true")
.endpointProperty("servletName", "RestServlet);
And the deployment I changed the servlets mapping to /* or else request.getPathInfo() would return null inside CamelHttpTransportServlet.
NB I encountered a problem beause I initially set contextPath to /rest/* which messed up sessions and cookies
DeploymentInfo deploymentInfo = Servlets.deployment()
.setClassLoader(classLoader)
.setContextPath("/rest/")
.setDeploymentName(DEPLOYMENT_NAME)
.addServlet(
Servlets.servlet("RestServlet", CamelHttpTransportServlet.class)
.addMapping("/*")
.setLoadOnStartup(1)
);
Related
I am looking for wild card support in Embedded Jetty WebAppContext contextPath but could not work out any.
I have following code for my shopping webapp:
String path = "/shopping";
WebAppContext wac = new WebAppContext(WEBAPP_PATH, path);
contexts.addHandler(wac);
I want that if access http://ip:port/shopping/anything, it should load my above webapp. But it does not. I also tried with path = "/shopping/*" and it did not work either.
Can anyone here please help me if it is possible or not or am I doing something wrong here.
EDIT:
I am deploying a webapp (flatten content) on Jetty and can access it successfully using http://host:port/shopping.
To achieve this, I created a WebAppContext(wac) with webappPath to resource folder and context-path as /shopping. I added this wac to handler list and attached it to server (Jetty Server).
Now, what I want is to access this webapp using http://host:port/shopping/. Whatever I provide after shopping in url, my same webapp should get loaded.
UPDATE:
Further following on this, I tried below but it did not work out.
ServletContextHandler context = new
ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/shopping");
ServletHolder staticHolder = new ServletHolder(new DefaultServlet());
staticHolder.setInitParameter("resourceBase", webAppPath);
staticHolder.setInitParameter("pathInfoOnly", "true");
context.addServlet(staticHolder, "/*");
context.setWelcomeFiles(new String[] { "index.html", "index.htm", "index.jsp" });
contexts.addHandler(context);
Now, if I try /shopping, it works but /shopping/abc does not. It seems wildcard matching is not working. If I use /home instead of /, /shopping/home works. Can anyone please help me here.
My requirement is to server weabAppPath for any /shopping/ url.
Finally, I got it working. Below will serve webapp for /shopping and /shopping/anything/* requests.
String path = "/shopping";
WebAppContext wac = new WebAppContext(WEBAPP_PATH, path);
contexts.addHandler(wac);
wac.addServlet(new ServletHolder(new WebappServlet()), "/anything/*");
public class WebappServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.getRequestDispatcher("/index.html").forward(request, response);
}
}
Based on your short code snippet (really there's almost nothing to work with here), your WebAppContext has no servlets declared.
Nothing in that WebAppContext will be called to handle the incoming request.
A WebAppContext is assigned to a context-path, which has a syntax of /<path>, there is no wildcard support within a context-path.
What you are looking for is wildcard support in a url-pattern, which only exists for Servlets and Filters.
Do this, create a ShoppingAnythingServlet and set it to the url-pattern of /* and then you'll get all requests to that new Servlet.
From within that code, use the HttpServletRequest.getPathInfo() to obtain what that /anything is.
I am new to Jetty and trying to understand by online example program. Here is the sample program I used:
public class EmbeddedJettyMain {
public static void main(String[] args) throws Exception {
Server server = new Server(7070);
ServletContextHandler handler = new ServletContextHandler(server, "/example");
handler.addServlet(ExampleServlet.class, "/");
server.start();
}
}
With that I can use:
http://localhost:7070/example/
Now I want to add one more servlet URI
http://localhost:7070/example2
How can I do this ?
I can see some reference such as webapp, looking for a good approach.
Server server = new Server(7070);
ServletContextHandler handler = new ServletContextHandler(server, "/");
handler.addServlet(ExampleServlet.class, "/example");
handler.addServlet(ExampleServlet.class, "/example2");
Each addServlet creates a mapping. Jetty will create an instance of the Servlet that will be a singleton for each mapping, meaning that init(ServletConfig config) will only be called once in each instance and all requests to a mapping go to the same instance.
Jetty provides a Web server and javax.servlet container.
Your servlets are stored and served via jetty's embedded container to serve when needed.
I have a Jersey REST 2.5.1 service which is served through a Grizzly server. Everything works fine so far. I want to add some static content, which is also served through Grizzly and provided from my JAR file. Therefore I use CLStaticHttpHandler. When I access static resources, such as my index.html explicitly (e.g. http://localhost:8080/index.html), everything works fine. However, when I try to access the root http://localhost:8080, I get a 404. The code looks like this:
ObjectMapper mapper = new ObjectMapper();
// some configuration stuff here
JacksonJaxbJsonProvider provider = new JacksonJaxbJsonProvider();
provider.setMapper(mapper);
ResourceConfig resourceConfig = new ResourceConfig()
.packages("my.restapi.package")
.register(provider);
HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), resourceConfig);
HttpHandler httpHandler = new CLStaticHttpHandler(HttpServer.class.getClassLoader(), "/static/");
httpServer.getServerConfiguration().addHttpHandler(httpHandler, "/");
As far as I can tell from debugging, org.glassfish.grizzly.http.server.CLStaticHttpHandler.handle(String, Request, Response) never gets called. Any hints, how I can make the index.html accessible as default page?
After some wasted hours, I feel a bit stupid now, but the simple solution was, to specify a path in BASE_URI (http://localhost:8080/api/ instead of http://localhost:8080/). Now, when accessing /, I get the index.html and the REST methods are under /api.
I am migrating my Jersey web application from Jersey 1.17.1 to 2.3.1, using Jetty 9.0.6. With Jersey 1.17.1, the Resource would load during startup. However, pretty much the same code (below) only starts when a request comes in. But I would like to make sure it is loaded during startup (i.e., the constructor of the annotated resource class is called). Any idea what's wrong?
public static void startServer(){
org.eclipse.jetty.server.Server server = new Server();
org.eclipse.jetty.servlet.ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");
server.setHandler(context);
org.glassfish.jersey.servlet.ServletContainer servletContainer = new ServletContainer();
org.eclipse.jetty.servlet.ServletHolder servletHolder = new ServletHolder(servletContainer);
servletHolder.setInitParameter(ServerProperties.PROVIDER_PACKAGES, "mypackage");
servletHolder.setInitOrder(0); // this does not seem to work
context.addServlet(servletHolder, "/*");
server.start();
}
And roughly:
package mypackage;
#Singleton
#Path("/foo")
public class MyResource {
public MyResource(){
// I want this to be called when I start the server
}
#Path("bar")
public Response receivePayload(#Context HttpServletRequest request, String payloadString){
// do some stuff
return response;
}
}
Uli,
The servletHolder.setInitOrder(0) that you refer to is related to the org.glassfish.jersey.servlet.ServletContainer class. The unit tests for servlet init ordering in jetty-9.0.6 are passing, so I assume that the jersey ServletContainer class is in fact being loaded on startup as expected.
Perhaps jersey has changed behaviour between version 1.17 and 2.3 and only lazily initializes the resources mapped using the #Path annotation?
Or perhaps jersey has changed to use some servlet 3.0 features for initialization, such as ServletContainerInitializers? If that is the case, then you'll need to upgrade from a ServletContextHandler to a WebAppContext, and ensure that the support for these servlet 3.0 features are enabled (see jetty docs on using annotations with embedded jetty here: http://www.eclipse.org/jetty/documentation/current/using-annotations-embedded.html)
cheers
Jan
I have a Restlet (2.0.10) application, I start with the following code:
public static void main(final String[] args) {
try {
// Create a new Component
final Component component = new Component();
// Add a new HTTP server listening on port 8182
component.getServers().add(Protocol.HTTP, SERVER_PORT);
// Add a client protocol connector for static files
component.getClients().add(Protocol.FILE);
// Attach the sample application.
component.getDefaultHost().attach("/myApp", new MyApplication(Context.getCurrent()));
// Start the component.
component.start();
} catch (Exception e) {
LOGGER.error(e);
}
}
Now I require the applications root (i.e. /myApp) inside the application and I try to get this according to Java accessing ServletContext from within restlet Resource:
Client serverDispatcher = context.getServerDispatcher();
ServletContext servletContext = (ServletContext)serverDispatcher.getContext().getAttributes()
.get("org.restlet.ext.servlet.ServletContext");
String contextPath = servletContext.getContextPath();
This works perfectly fine while deploying my application to a Tomcat Server, but as soon as I start the server using a Component as shown above, my Context is always null. Can someone please tell me how to get a properly initialized context using restlets internal server capabilities?
Seems logic.
You want a servlet context but you are not running in a servlet container, so the servlet context is NULL.
When doing component.start() you are using the Restlet connectors to server HTTP/HTTPS requests, not a servlet container like Tomcat.
You would need to pick up the context from the Component Class:
component.getDefaultHost().attach("/myApp",
new MyApplication(component.getContext().createChildContext());
That would just give you the Restlet context, but Servlet Context still won't be available since this is a standalone Java application.