Initializing Jetty+Jersey - java

I'm trying to initialize Jetty with the following code:
URI baseUri = UriBuilder.fromUri("http://localhost/").port(config.getPort()).build();
ResourceConfig resConfig = new ResourceConfig(GetFutureTimetableCommand.class);
Server server = JettyHttpContainerFactory.createServer(baseUri, resConfig);
WebAppContext context = new WebAppContext();
context.setDescriptor("WebContent/WEB-INF/web.xml");
context.setResourceBase("WebContent");
context.setContextPath("rest/*");
context.setParentLoaderPriority(true);
server.setHandler(context);
server.start();
My Resource looks like this:
#Path("/timetable")
public class GetFutureTimetableCommand extends CMSCommand {
#GET
#Produces(MediaType.APPLICATION_JSON)
public CMSBean execute(#PathParam("param") String params) {
System.out.println("GOOD");
return new FutureTimetable(8202L, DateTime.now().plusDays(2));
}
}
And from the browser:
http://localhost:8080/rest/timetable
But nothing really happens, what am I doing wrong??

I have found that enabling MBeans with monitoring statistics invaluable when trying to determine why a resource isn't executing.
Add the below to your Jersey Servlet definition in your web.xml and connect JVisualVM or JConsole to see lots of data on deployed resources.
<init-param>
<param-name>jersey.config.server.monitoring.statistics.mbeans.enabled</param-name>
<param-value>true</param-value>
</init-param>
I appreciate this isn't an answer to your problem, but hopefully should help you find it.
Will

It seems only the root context path is taken into account when jersey is used together with jetty as per Jersey documentation (others are just ignored):
https://jersey.java.net/documentation/latest/deployment.html
You probably need to change the context path with:
context.setContextPath("/");

Related

Is it possible to forward a Request from a WebServlet to rest helper servlet (javax.ws.rs.core.Application)

Problem:
I have a WebFilter which forwards valid urls to either a proxy servlet or a servlet which handles a webpage for the admin to monitor recent requests and more.
The admin servlet is suppost to forward ajax requests to a REST service (after login.jsp from the webpage rendered by controlpannel.jsp ) but apparently the rest service has a different context as the WebFilter and WebServlets ?
Question:
So is it at all possible to forward from my WebServlet to the rest helper servlet (and its resource classes) ?
More specific Information:
This is how I use forwarding:
ServletContext sc = request.getServletContext();
RequestDispatcher dispatcher = sc.getRequestDispatcher(forwardURI);
dispatcher.forward(request, response);
I tried to forward to this uri:
forwardURI = /REST/proxy_client/newer_than
My rest helper servlet:
#Stateless
#ApplicationPath("/REST")
public class RestService extends Application {
#Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> restResourceClasses = new HashSet<Class<?>>();
restResourceClasses.add(ProxyClientResource.class);
return restResourceClasses;
}
}
And this resource class:
#Path("/proxy_client")
#Stateless
public class ProxyClientResource {
#EJB
private ProxyClientBean proxyClientBean;
#GET
#Produces("application/json")
#Path("/newer_than")
public String getNumberOfEntriesNewerThanTimestamp(#QueryParam("timestamp") Expression<Timestamp> indexTimestamp,
#QueryParam("numberOfclients") Integer numberOfclients) {
List<ProxyClient> pageData = proxyClientBean.getElementsNewerThan(numberOfclients, indexTimestamp);
return convertToJSONstring(pageData);
}
Solution attempt:
I found this question about how to call a rest web service from a servlet, but they use a client and no forwarding.
EDIT:
I had a configuration problem (might still have one), so now when I try to forward to my rest helper servlet (the one extending javax.ws.rs.core.Application) I get this error:
RestServlet is currently unavailable
(in the web.xml I call the Servlet RestServlet)
when accessing the REST api directly I get:
HTTP Status 500 - Authenticator.invoke() failed
but I can't find out what this means.
Edit2:
I will try repacing the subclass of Applicaton with a config in web.xml subclassing and #ApplicationPath dont seem to work for me. Also when I try to get the rest ServletsContext I get an error that no class has been specified, which is something you do when using the web.xml config.
Edit3:
I'm deploying my application on HCP and with the underlying problem beeing that I cant even access my REST service I found this SAP discussion.
When I get my REST service working without forwarding I will report back here.
Edit4:
This actually answers the question from Edit3
I had to add jersey 1.19.1 (not 2.x because im using Java EE6 which only supports up to DWP 3.0 not 3.1 as required) to by projects libraries otherwise It would say that I didn't specify a servlet class (but when I tried to add javax.ws.rs.core.Application it would tell me this is no Servlet class even though I have seen this configuration).
My real problem was that the javax.ws.rs.core.Application from the Java ee6 container on SAP Hana Cloud Platform did not work for a unkown reason.
The solution was to download and add the jersey-bundle-1.19.1.jar to WEB-INF/lib and the projects libraries.
There is no problem at all to forward a request from a vanilla servlet to the rest service! If it does not work in your case its most likely your setup or some unexpected reason like it was in my case.

Register context listener for jersey

I need to perform some clean up steps after shutting down a jersey server. To my mind this could be easily accomplished by implementing a ServletContextListener. The question of course is how to add this listener to the application. I have seen examples where this is done in the file web.xml like this:
<listener>
<listener-class>org.SomeCompany.SomePackage.server.MyListener</listener-class>
</listener>
where the MyListener class looks as follows:
#WebListener
public class MyListener implements ServletContextListener {
The problem is that this approach only works for deployment as a war file. However, I do also ship my software as a standalone jar file which creates a Grizzly web server to deploy the servlet:
HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(BASE_URI,
new MyServerConfig());
The class MyServerConfig subclasses from ResourceConfig and uses the various register methods. I would like to add the listener programmatically as well, but calling register doesn't seem to do the job. Any ideas how to fix this?
The first thing you are going to need to configure Grizzly as a servlet container. This is not the default behavior. You are only creating an HTTP Server. So the first thing you will need to is Grizzly servlet dependency
<dependency>
<groupId>org.glassfish.jersey.containers</groupId>
<artifactId>jersey-container-grizzly2-servlet</artifactId>
<version>${jersey2.version}</version>
</dependency>
So here's the deal with this. With this artifact, instead of the Grizzly HttpServer with GrizzlyHttpServerFactory, you would instead use the GrizzlyWebContainerFactory. The only thing is, if you look though the factory API methods, there really isn't a place to register any listeners, and from what I tested, the #WebListener annotation will not automatically get picked up. What we need access to is the Grizzly WebAppContext that Jersey uses to create the Grizzly servlet container.
The way I was able to get it to work, was just to grab some code from the GrizzlyWebContainerFactory.create source code, and just create the container myself. It's really not much code. Most of the source code does checks as it needs to be universal. But in a single use case (with no init-params), you can pretty much cut the code down to this
private static HttpServer create(URI u, Servlet servlet) throws IOException {
String path = u.getPath();
path = String.format("/%s", UriComponent.decodePath(u.getPath(), true)
.get(1).toString());
WebappContext context = new WebappContext("GrizzlyContext", path);
context.addListener(MyListener.class);
ServletRegistration registration;
registration = context.addServlet(servlet.getClass().getName(), servlet);
registration.addMapping("/*");
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(u);
context.deploy(server);
return server;
}
Then just call HttpServer server = create(BASE_URI, new ServletContainer(resourceConfig));
As an aside, for your use case (I just re-read the question :-), Jersey also has Event Listeners. You can write an ApplicationEventListener and listen for the destroy event, and do all your processing there.

How to configure ContextLoaderListener to not look for applicationContext.xml

I'm trying to steer clear of XML based configuration within my application and would like to maintain all configuration within the code. Currently the org.springframework.web.context.ContextLoaderListener looks for applicationContext.xml.
Is it possible for me to turn that off so it doesn't require that file?
Currently main() looks like this...
public static void main(String[] args) throws IOException {
final HttpServer server = HttpServer.createSimpleServer(".", 8181);
WebappContext ctx = new WebappContext("Socket", "/");
//allow spring to do all of it's stuff
ctx.addListener("org.springframework.web.context.ContextLoaderListener");
//enable web socket support
final WebSocketAddOn addon = new WebSocketAddOn();
for (NetworkListener listener : server.getListeners()) {
listener.registerAddOn(addon);
//if false, local files (html, etc.) can be modified without restarting the server
//#todo experiment with this setting in production vs development
listener.getFileCache().setEnabled(false);
}
//add jersey servlet support
/*ServletRegistration jerseyServletRegistration = ctx.addServlet("JerseyServlet", new ServletContainer());
jerseyServletRegistration.setInitParameter("com.sun.jersey.config.property.packages", "come.fettergroup.production.queue.resources");
jerseyServletRegistration.setLoadOnStartup(1);
jerseyServletRegistration.addMapping("/api/*");*/
//add atmosphere servlet support
AtmosphereServlet atmosphereServlet = new AtmosphereServlet();
AtmosphereFramework f = atmosphereServlet.framework();
ReflectorServletProcessor r = new ReflectorServletProcessor();
r.setServletClassName("com.sun.jersey.spi.spring.container.servlet.SpringServlet");
f.addAtmosphereHandler("/socket/*", r);
ServletRegistration atmosphereServletRegistration = ctx.addServlet("AtmosphereServlet", atmosphereServlet);
atmosphereServletRegistration.setInitParameter("org.atmosphere.websocket.messageContentType", "application/json");
atmosphereServletRegistration.setInitParameter("com.sun.jersey.api.json.POJOMappingFeature", "true");
atmosphereServletRegistration.setLoadOnStartup(1);
ctx.deploy(server);
//serve static assets
StaticHttpHandler staticHttpHandler = new StaticHttpHandler("src/main/web");
server.getServerConfiguration().addHttpHandler(staticHttpHandler, "/");
//start the production process
Production.init();
server.start();
System.in.read();
server.stop();
}
Haven't tried this yet but the org.springframework.web.context.ContextLoaderListener class needs to be configured to use an annotation scanning application context. This is done via context parameters.
So in a web.xml file, you would do something like this:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.yourbasepackage</param-value>
</context-param>
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
For a standalone Jetty, you need to do something similar using the WebAppContext.setInitParameter() method.
ctx.setInitParameter("contextClass", "org.springframework.web.context.support.AnnotationConfigWebApplicationContext");
ctx.setInitParameter("contextConfigLocation", "com.yourbasepackage");

Jersey with embedded Jetty + PostReplaceFilter

I am using Jersey with an embedded Jetty according to the documentation http://wiki.eclipse.org/Jetty/Tutorial/Embedding_Jetty. This works fine so far. What I'd like to add now is usage of the PostReplaceFilter in this configuration.
Normally this is done in the web.xml like this
<init-param>
<param-name>com.sun.jersey.spi.container.ContainerRequestFilters</param-name>
<param-value>com.sun.jersey.api.container.filter.PostReplaceFilter</param-value>
</init-param>
But using the embedded Jetty I have no web.xml. I tried setting the filter programmatically like this
ServletContextHandler sch = new ServletContextHandler(server, "myapp");
sch.setInitParameter("com.sun.jersey.spi.container.ContainerRequestFilters", "com.sun.jersey.api.container.filter.PostReplaceFilter");
But this does not show any effect. Can anyone shed light on how to install a PostReplaceFilter using Java code?
Thanks in advance
I looks like I missed to mention one important fact, which is that I am using Google Guice. Therefore there is no ServletHolder where I could set initialization parameters. Instead I could finally find the answer in the Guice documentation at http://code.google.com/p/google-guice/wiki/ServletRegexKeyMapping in the section on "Initialization Parameters". So for the PostReplaceFilter this would look like this:
Map<String, String> params = new HashMap<String, String>();
params.put("com.sun.jersey.spi.container.ContainerRequestFilters", "com.sun.jersey.api.container.filter.PostReplaceFilter");
serve("/*").with(GuiceContainer.class, params);
You should set it using the setInitParameter() on the ServletHolder instance that you use to register the Jersey servlet.

Modify Servlet parameters

I've a GWT Servlet running in a Tomcat 6.0 server. This server acts as a proxy to another service. This final service may be running in different IPs and/or ports in my network.
How can I configure my GWT Servlet to connect to any of my services without manually modifying the web.xml file?
I'm initializing my servlet with:
<!-- Servlets -->
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.proxy.MyServletServiceImpl</servlet-class>
<init-param>
<param-name>serverAddress</param-name>
<param-value>192.168.1.10</param-value>
</init-param>
<init-param>
<param-name>serverPort</param-name>
<param-value>55005</param-value>
</init-param>
</servlet>
From inside my MyServletSerciveImpl.java file I'm doing
private void loadConfig() {
ServletConfig config = this.getServletConfig();
serverAddress = config.getInitParameter("serverAddress");
serverPort = Integer.valueOf(config.getInitParameter("serverPort"));
}
My ideal case would be that this configuration is the default, but applying some configuration file (a properpies file, xml, ini, cfg, .....) I could overwrite the default web.xml values.
Any idea how to do that?
Thanks.
For true dynamic configuration, you can expose a configuration object as a jmx bean, and have your servlet use that bean.
An intermediate solution is to put the configuration in a different file, as xml or properties, or in a db table, and read from it periodically in a background thread.
For completeness:
public class MyServiceImpl extends RemoteServiceServlet implements
MyService {
private void loadConfig() {
InputStream inStream = this.getServletContext().getResourceAsStream("/WEB-INF/config.properties");
Properties properties = new Properties();
try {
properties.load(inStream);
// properties.getProperty("myValue");
} catch (IOException e) {
Log.error(e.getMessage());
e.printStackTrace();
}
}
....
}

Categories