Grizzly Server with static content and REST resource - java

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.

Related

wild card support in Embedded Jetty WebAppContext

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.

Jersey+Grizzly load-on-startup without web.xml

according to this (servlet response time is slow for first request) SO Question I can use the load-on-startup parameter in web.xml to create the services classes on startup and not on the first client request, which causes better first-response times for clients.
However I'm using Grizzly+Jersey, how can I configure this behaviour in grizzly, or is this completetly impossible? Then what would be alternatives to grizzly without using a full blown Java EE Application Server
EDIT: Main main method is this:
public static void main(String... args){
//Packages which contain service classes
final ResourceConfig rc = new ResourceConfig()
.packages("de.danielr1996.flamingoapi.services");
//Logging Aktivieren
rc.register(new LoggingFilter());
//Dependency Injection konfigurieren
rc.register(new DependencyBinder());
//Jackson hinzufügen
rc.register(JacksonFeature.class);
//Datenbank initialisieren
DatabaseUtil.getEntityManagerFactory();
// Server erstellen
return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
}
Thanks in Advance
ServletAdapter adapter =new ServletAdapter();
...
adapter.setProperty( "load-on-startup", 1 );
UPDATE
I don't now how to configure instance created from Factory. I assume it does not allow you to provide complex configuration.
But you can create server manually this way :
GrizzlyWebServer ws = new GrizzlyWebServer(80);
ServletAdapter adapter = new ServletAdapter();
Then configure adapter Javadocs
adapter.setContextPath("...");
adapter.setRootFolder("...");
adapter.setHandleStaticResources(true);
adapter.setProperty("load-on-startup","1");
Add adapter to server
ws.addGrizzlyAdapter(adapter, new String[]{"/uri"});

Spring Cloud Zuul: RedirectView redirecting to service and not gateway

I'm a bit new to microservices and Spring. I have Spring Cloud microservices (ports: 8xxx-8xxx) with a Zuul gateway running on port 9000. There's a method inside a controller on a UI service which should do a login and then return to a index.html page:
#RequestMapping(value="/do-login", method = RequestMethod.POST)
public RedirectView doLogin (#ModelAttribute("authEntity") final AuthEntity authEntity, final Model model) {
model.addAttribute(VERSION, applicationVersion);
model.addAttribute("authEntity", new AuthEntity());
authenticatedStatus = true;
model.addAttribute(AUTHENTICATED, authenticatedStatus);
return new RedirectView("index");
}
The problem is that when above method completes it returns an url of the microservice itself localhost:8888/index but not localhost:9000/services/ui/.
If I use a simpler method:
#RequestMapping(value="/do-login", method = RequestMethod.POST)
public String doLogin (#ModelAttribute("authEntity") final AuthEntity authEntity, final Model model) {
model.addAttribute(VERSION, applicationVersion);
model.addAttribute("authEntity", new AuthEntity());
authenticatedStatus = true;
model.addAttribute(AUTHENTICATED, authenticatedStatus);
return "index";
}
This returns correctly an url of gateway localhost:9000/services/ui/do-login but with a /do-login which I do not need.
Maybe I can get rid of /do-login/ part of url? Or maybe there is a solution for the incorrect redirect?
Thanks in advance!
If you use relative path like in return "index"; the result of the POST request sent to http://localhost:9000/services/ui/do-login will include URLs to http://localhost:9000/... unless coded otherwise in the jsp / freemarker / thymeleaf file.
If you want to get rid of the do-login, you would need to implement what's called a Redirect After Post (or redirect after form submit) approach so that a page refresh doesn't resubmit the form. If you take this approach, which seem what you were doing when using: return new RedirectView("index");, I can think of a couple ways of fixing the URL and set it to the proxy host.
1) http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/servlet/view/RedirectView.html, there are a couple of constructors that takes a host parameter, you would need to inject the proxy host in the controller class and most-likely in every controller class that implements Redirect After Post.
2) http://tuckey.org/urlrewrite/, include UrlRewriteFilter and configure rules to rewrite from webapp host to proxy host when webapp http status code response is 302. With this approach it would only be once rule and no need to inject proxy host to controller classes or change the return new RedirectView("index");`
3) Maybe this rewriting is implemented in Zuul and you don't need include and configure UrlRewriteFilter as suggested in 2).
As a side note, I have configured Nginx's proxy_pass to a Java webapps (where I implemented Redirect After Post) in the past and I don't recall having this issue. Will have to take a look at both UrlRewriteFilter and Nginx config files to expand on this.
I found that this (thanks to answer in here: Spring redirect url issue when behind Zuul proxy) seems to work as required (but is considered a 'workaround'):
#RequestMapping(value="/do-login", method = RequestMethod.POST)
public void doLogin (#ModelAttribute("authEntity") final AuthEntity authEntity,
final Model model,
HttpServletResponse servletResponse) throws IOException {
...
String rUrl = ServletUriComponentsBuilder.fromCurrentContextPath().path("/").build().toUriString();
servletResponse.sendRedirect(rUrl);
}

Using rest Servlet in Camel with Guice and Undertow

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

DropWizard Serving Assets help needed

Help! I've been trying for hours, googling anything I could think of. I have a problem, that I would like to show my static content instead of my application on my site.
I modified a simple hello-world application:
public static void main(String[] args) throws Exception {
Class.forName("org.sqlite.JDBC");
new HelloWorldApplication().run(args);
}
#Override
public String getName() {
return "hello-world";
}
#Override
public void initialize(Bootstrap<HelloWorldConfiguration> bootstrap) {
bootstrap.addBundle(new AssetsBundle("/assets/*", "/"));
}
#Override
public void run(HelloWorldConfiguration configuration, Environment environment) {
final HelloWorldResource resource = new HelloWorldResource(
configuration.getTemplate(),
configuration.getDefaultName()
);
final AddResource addResource = new AddResource();
final DeleteResource deleteResource = new DeleteResource();
final TemplateHealthCheck healthCheck = new TemplateHealthCheck(configuration.getTemplate());
environment.healthChecks().register("template", healthCheck);
environment.jersey().register(resource);
environment.jersey().register(addResource);
environment.jersey().register(deleteResource);
}
Here's my hello-world.yml:
server:
type: simple
applicationContextPath: /application/hello-world
template: Hello, %s!
defaultName: Stranger
I applied everything, what the DropWizard docs (http://dropwizard.readthedocs.org/en/latest/manual/core.html#serving-assets) said. But I just cannot manage to reach the index.html
I have not seen an actual example that proves that the documented way actually works.
And when looking in the Dropwizard source, I conclude that this is in fact not possible: the Jetty application context is set by the configuration parameter applicationContextPath in SimpleServerFactory:103:
environment.getApplicationContext().setContextPath(applicationContextPath);
And after that, the AssetBundles are registered into this applicationContext upon run() (AssetBundle:109):
environment.servlets().addServlet(assetsName, createServlet()).addMapping(uriPath + '*');
So, assetbundles are always served within the applicationContextPath that is set in the application's YAML file, so serving them outside this applicationContextPath is not possible (despite the docs saying so)
A better way to get this working, is to configure the application to use the / path:
applicationContextPath: /
And then, in your application's code, in the bootstrap() and run() methods, explicitly override the path for Jersey resources and add AssetBundles to your liking:
bootstrap.addBundle(new AssetsBundle("/static", "/"));
environment.jersey().setUrlPattern("/application/*");
I got it working by using the default constructor for the AssetsBundle() class.
With the default constructor your resources will gets looked up in a directory on the java classpath e.g.
/src/main/resources/assets/
and your have to name your applicationContextPath only /application
Point your browser to the folling location for static content
localhost:8080/application/assets/index.htm
For Dropwizard 0.8.0 and newer this is achieved from this configuration:
applicationContextPath: /
rootPath: /application
Where applicationContextPath is Jetty's Context path, and rootPath is Jersey's.
As Geert mentioned, the asset bundle needs to be served from within the applicationContextPath. However, if you add the AssetsBundle in the bootstrap method, and set the contextPath from the run method, the AssetServlet gets added after the contextPath is set.
My fix is to avoid using the AssetsBundle and add the AssetsServlet directly in the run method (after contextPath is set):
environment.getApplicationContext().setContextPath("/");
environment.servlets().addServlet("assets", new AssetServlet("/assets", "/", "index.html", StandardCharsets.UTF_8)).addMapping("/*");

Categories

Resources