Get ServletContext in Application - java

Could you possibly explain how I can get the ServletContext instance in my Application's sub-class? Is it possible? I have tried to do it like in the following snippet but it does not seem to work - the ctx is not set:
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
//...
#ApplicationPath("/")
public class MainApplication extends Application {
#Context ServletContext ctx;
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<Class<?>>();
//...
return classes;
}
}
web.xml:
<web-app ...>
<context-param>
<param-name>environment</param-name>
<param-value>development</param-value>
</context-param>
<filter>
<filter-name>jersey-filter</filter-name>
<filter-class>org.glassfish.jersey.servlet.ServletContainer</filter-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>my.MainApplication</param-value>
</init-param>
</filter>
...
</web-app>
The problem is that I need to get context parameters from it. If there is another way, I would be grateful if somebody gave a hint.
I understand that Context annotation might not be purposed for this. Actually, I do not need ServletContext itself. If only I could get context params from web.xml, I would be absolutely happy.
Here is an example of what I really need:
import java.util.HashSet;
import java.util.Set;
import javax.servlet.ServletContext;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
public class MainApplication extends Application {
#Context ServletContext ctx;
#Override
public Set<Object> getSingletons() {
Set<Object> set = new HashSet<Object>();
final String environment = ctx.getInitParameter("environment");
//final String environment = ... get context parameter from web xml
set.add(new AbstractBinder() {
#Override
protected void configure() {
bind(new BaseDataAccess(environment)).to(DataAccess.class);
}
});
//...
return set;
}
}
Thanks.

Since Jersey 2.5, ServletContext can be injected directly in constructor:
https://java.net/jira/browse/JERSEY-2184
public class MyApplication extends ResourceConfig {
public MyApplication(#Context ServletContext servletContext) {
// TODO
}
}

#Context can be made available on ResoureConfig by injecting it as a constructor parameter using #Context. Another way to access it is through an event handler.
Try the below code.
#ApplicationPath("...")
public class MyApplication extends ResourceConfig {
public MyApplication() {
register(StartupHandler.class);
}
private static class StartupHandler extends AbstractContainerLifecycleListener {
#Context
ServletContext ctx;
#Override
public void onStartup(Container container) {
// You can put code here for initialization.
}
}
// ...

Injection happens when you enter service method. Check if this is a problem.

There is interesting statement in documentation for Jersey version 1.18 for class
com.sun.jersey.spi.container.servlet.ServletContainer
The servlet or filter may be configured to have an initialization
parameter "com.sun.jersey.config.property.resourceConfigClass" or
"javax.ws.rs.Application" and whose value is a fully qualified name of
a class that implements ResourceConfig or Application. If the concrete
class has a constructor that takes a single parameter of the type Map
then the class is instantiated with that constructor and an instance
of Map that contains all the initialization parameters is passed as
the parameter.
If my understanding is correct the following constructor must be invoced with "an instance of Map that contains all the initialization parameters"
public class ExampleApplication extends Application {
public ExampleApplication(Map initParams) {
}
...
}
Here is appropriate part of web.xml:
<servlet>
<servlet-name>Jersey Web Application</servlet-name>
<servlet-class>com.sun.jersey.spi.container.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>experiment.service.ExampleApplication</param-value>
</init-param>
</servlet>
But somehow it failed for me with the following message:
SEVERE: Missing dependency for constructor public
experiment.service.ExampleApplication(java.util.Map) at parameter
index 0
And for current version of Jersey (2.5.1) there are no such statement in documentstion:
https://jersey.java.net/apidocs/latest/jersey/org/glassfish/jersey/servlet/ServletContainer.html

You can use the ApplicationEventListener interface to get the ServletContext. After initialization has finished, you can 'catch' an ApplicationEvent and use the injected ServletContext to work with.
Works fine with: org.glassfish.jersey : 2.12
For additional versions, pls use comments - i dont know, sry.
Jersey Docs - 20.1.2. Event Listeners
Your MainApplication:
#ApplicationPath("/")
public class MainApplication extends Application {
#Override
public Set<Class<?>> getClasses() {
Set<Class<?>> set = new HashSet<Class<?>>();
set.add(MainApplicationListener.class);
return classes;
}
}
... or alternative MainResourceConfig (I prefer to use this one):
public class MainResourceConfig extends ResourceConfig {
public MainResourceConfig() {
register(MainApplicationListener.class);
}
}
And the ApplicationEventListener:
public class MainApplicationListener implements ApplicationEventListener {
#Context
private ServletContext ctx; //not null anymore :)
#Override
public void onEvent(ApplicationEvent event) {
switch (event.getType()) {
case INITIALIZATION_FINISHED:
// do whatever you want with your ServletContext ctx
break;
}
#Override
public RequestEventListener onRequest(RequestEvent requestEvent) {
return null;
}
}

Don't use #Context in your Application but in a Resource class.
#Path("/foos")
public class FooResource {
#Context
ServletContext ctx;
#GET
public Response getFoos() {
return Response.ok().build();
}
}

Related

Registering a provider programmatically in jersey which implements exceptionmapper

How do I register my provider programmatically in jersey which implements the Exceptionmapper provided by jersey API? I don't want to use #Provider annotation and want to register the provider using ResourceConfig, how can I do that?
For example:
public class MyProvider implements ExceptionMapper<WebApplicationException> extends ResourceConfig {
public MyProvider() {
final Resource.Builder resourceBuilder = Resource.builder();
resourceBuilder.path("helloworld");
final ResourceMethod.Builder methodBuilder = resourceBuilder.addMethod("GET");
methodBuilder.produces(MediaType.TEXT_PLAIN_TYPE)
.handledBy(new Inflector<ContainerRequestContext, String>() {
#Override
public String apply(ContainerRequestContext containerRequestContext) {
return "Hello World!";
}
});
final Resource resource = resourceBuilder.build();
registerResources(resource);
}
#Override
public Response toResponse(WebApplicationException ex) {
String trace = Exceptions.getStackTraceAsString(ex);
return Response.status(500).entity(trace).type("text/plain").build();
}
}
Is this the correct way to do this?
I'm guessing you don't have a ResourceConfig, since you seem to not be sure how to use it. For one, it is not required. If you do use it, it should be it's own separate class. There you can register the mapper.
public class AppConfig extends ResourceConfig {
public AppConfig() {
register(new MyProvider());
}
}
But you are probably using a web.xml. In which case, you can register the provider, with the following <init-param>
<servlet>
<servlet-name>MyApplication</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>jersey.config.server.provider.classnames</param-name>
<param-value>
org.foo.providers.MyProvider
</param-value>
</init-param>
</servlet>
Have a look at What exactly is the ResourceConfig class in Jersey 2?
for more information on different deployment models. There are a few different ways to deploy applications. You can even mix and match (web.xml and ResourceConfig).
While #paul-samsotha's answer is correct, still there is implementation trick. I want to share it and hope it will help someone.
a) Implement your mapper:
public class MyExceptionMapper implements ExceptionMapper<Throwable>, ResponseErrorMapper {
...
b) make sure you declare generic type, otherwise your mapper will never be called
public class MyExceptionMapper implements ExceptionMapper/* no generic declaration */, ResponseErrorMapper {
...
and may trigger
javax.ws.rs.ProcessingException: Could not find exception type for given ExceptionMapper class: class com...MyExceptionMapper.
c) Register it as resource:
ResourceConfig config = new ResourceConfig();
config.register(new MyExceptionMapper());
or
config.register(MyExceptionMapper.class);
d) make sure you enforce processing errors as well:
config.setProperties(new LinkedHashMap<String, Object>() {{
put(org.glassfish.jersey.server.ServerProperties.PROCESSING_RESPONSE_ERRORS_ENABLED, true);
}});
If you're using Spring and want to register the providers programmatically based on the presence of #Path and #Provider annotation you can use the following technique
#Component
public class JerseyConfig extends ResourceConfig {
#Autowired
private ApplicationContext applicationContext;
#PostConstruct
public init() {
applicationContext.getBeansWithAnnotation(Path.class).values().forEach(
component -> register(component.getClass())
);
applicationContext.getBeansWithAnnotation(Provider.class).values().forEach(
this::register
);
}
}

Share variables between JAX-RS requests

I have what I think is a very basic question about JAX-RS but I somehow can't easily find the answer.
I am trying to refactor a REST service which uses a "standard" Javax servlet -- routing requests to methods by hand -- into an "cleaner" JAX-RS implementation. The current application sets some variables during the servlet init(). It assigns those as attributes of the HttpServlet class so they are available during each doGet() and can be passed as parameters to request processing methods. For clarity, one of these is a ConcurentHashMap that acts as a cache.
Now, with JAX-RS, I can extend Application to set my resource classes. I can also use the #Context annotation in each resource implementation to inject things like ServletContext when processing a request. But I do not know how to similarly inject variables set during application initialization.
I am using the Apache Wink 1.3.0 implementation of JAX-RS.
You can use a listener for init the cache and set to the context as attribute before the web application start. something like the following:
package org.paulvargas.shared;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class CacheListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
Map<String, String> dummyCache = new HashMap<String, String>();
dummyCache.put("greeting", "Hello Word!");
ServletContext context = sce.getServletContext();
context.setAttribute("dummyCache", dummyCache);
}
public void contextDestroyed(ServletContextEvent sce) {
ServletContext context = sce.getServletContext();
context.removeAttribute("dummyCache");
}
}
This listener is configured in the web.xml.
<listener>
<listener-class>org.paulvargas.shared.CacheListener</listener-class>
</listener>
<servlet>
<servlet-name>restSdkService</servlet-name>
<servlet-class>
org.apache.wink.server.internal.servlet.RestServlet
</servlet-class>
<init-param>
<param-name>applicationConfigLocation</param-name>
<param-value>/WEB-INF/application</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>restSdkService</servlet-name>
<url-pattern>/rest/*</url-pattern>
</servlet-mapping>
You can use the #Context annotation for inject the ServletContext and retrieving the attribute.
package org.apache.wink.example.helloworld;
import java.util.*;
import javax.servlet.ServletContext;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import org.apache.wink.common.model.synd.*;
#Path("/world")
public class HelloWorld {
#Context
private ServletContext context;
public static final String ID = "helloworld:1";
#GET
#Produces(MediaType.APPLICATION_ATOM_XML)
public SyndEntry getGreeting() {
Map<String, String> dummyCache =
(Map<String, String>) context.getAttribute("dummyCache");
String text = dummyCache.get("greeting");
SyndEntry synd = new SyndEntry(new SyndText(text), ID, new Date());
return synd;
}
}

Instantiate value only once in a Bean

I'm new to using Servlets so please forgive me if I use incorrect terminology. I have an Object called "Provider" in JSF Bean Class "Detector" which needs to be instantiated once and then can be used for all other requests. I've done some searching and found the ServletContextListener interface which seems to do what I need. Ive mentioned it in my web.xml file like so:
<listener>
<listener-class>
p1.ContextListener
</listener-class>
</listener>
and the class looks like this:
package p1;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class ContextListener implements ServletContextListener{
#Override
public void contextInitialized(ServletContextEvent sce) {
Detector.startProvider();
}
#Override
public void contextDestroyed(ServletContextEvent sce) {
Provider.dispose();
}
}
And here is my Detector Class:
package p1;
import javax.faces.bean.ManagedBean;
import javax.faces.context.FacesContext;
import javax.servlet.http.HttpServletRequest;
#ManagedBean
public class Detector{
private static Provider p;
FacesContext context;
String userAgent;
public Detector() {
context = FacesContext.getCurrentInstance();
}
public String getValue() {
return p.getValue();
}
public String getUserAgent() {
return ((HttpServletRequest) context.getExternalContext().getRequest()).getHeader("User-Agent");
}
public static void startProvider(){
p = Creater.create();
}
}
My code all works, but the only way that seems right to me is to have the Provider Object as a static but that seems like a bad idea in an Bean that will be used for different requests. My question is whether it is right to have the Provider Object as a static?
Using "static" is a bad idea. If you want an object in your servlet to be shared between all the HTTP requests processed by this servlet then simply made it a field of your servlet class. The best place for initialization of that field variable is init() method.
public class MyServlet extends HttpServlet {
private MyProdiver provider;
public void init() throws ServletException {
this.provider = new MyProdiver();
// do init
}
}
Unless your servlet class implements SingleThreadModel there is only one servlet instance per servlet declaration in your deployment descriptor (web.xml)
I found the answer I need on this question JSF initialize application-scope bean when context initialized. I set the Provider Object as an attribute of the ServletContextEvent in my "ContextListener" and retrieved it in my Detector class from my FacesContext Object "context". (This is shown in more detail in the accepted answer of the link provided)

Method interception in Jersey using Guice AOP

Is it possible to use Guice AOP to intercept an annotated method on a Jersey resource?
I have a successfully configured Guice integration working with Jersey with respect to Dependency Injection without any problems, however my configured Interceptor is not intercepting my annotated method at all.
web.xml
<listener>
<listener-class>my.package.GuiceConfig</listener-class>
</listener>
<filter>
<filter-name>guiceFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>guiceFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
GuiceConfig configuration module
public class GuiceConfig extends GuiceServletContextListener {
#Override
protected Injector getInjector() {
return Guice.createInjector(new JerseyServletModule() {
#Override
protected void configureServlets() {
bindInterceptor(Matchers.any(),
Matchers.annotatedWith(RequiredAuthority.class),
new AuthorisationInterceptor());
Map<String, String> params = new HashMap<String, String>();
params.put(JSP_TEMPLATES_BASE_PATH, "/WEB-INF/jsp");
params.put(FEATURE_FILTER_FORWARD_ON_404, "true");
params.put(PROPERTY_PACKAGES, "my.service.package");
filter("/*").through(GuiceContainer.class, params);
}
});
}
}
RequiredAuthority annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface RequiredAuthority {
String value();
}
AuthorisationInterceptor aspect
public class AuthorisationInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
// Allow invocation to process or throw an appropriate exception
}
}
TempResource JAX-RS resource class
#Path("/temp")
public class TempResource {
#GET
#Produces(MediaType.APPLICATION_JSON)
#RequiredAuthority("PERMISSION")
public String getTemp() {
// Return resource normally
}
}
Looks like configureServlets() isn't calling:
bind(TempResource.class);

Guice3 Singleton is never instantiated in GAE project

I'm new to Guice and already stuck :)
I pretty much copied classes GuiceConfig, OfyFactory and slightly modified Ofy from Motomapia project (which you can browse) using it as s sample.
I created GuiceServletContextListener which looks like this
public class GuiceConfig extends GuiceServletContextListener
{
static class CourierServletModule extends ServletModule
{
#Override
protected void configureServlets()
{
filter("/*").through(AsyncCacheFilter.class);
}
}
public static class CourierModule extends AbstractModule
{
#Override
protected void configure()
{
// External things that don't have Guice annotations
bind(AsyncCacheFilter.class).in(Singleton.class);
}
#Provides
#RequestScoped
Ofy provideOfy(OfyFactory fact)
{
return fact.begin();
}
}
#Override
public void contextInitialized(ServletContextEvent servletContextEvent)
{
super.contextInitialized(servletContextEvent);
}
#Override
protected Injector getInjector()
{
return Guice.createInjector(new CourierServletModule(), new CourierModule());
}
}
I added this listener into my web.xml
<web-app>
<listener>
<listener-class>com.mine.courierApp.server.GuiceConfig</listener-class>
</listener>
<!-- GUICE -->
<filter>
<filter-name>GuiceFilter</filter-name>
<filter-class>com.google.inject.servlet.GuiceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>GuiceFilter</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
</filter-mapping>
<!-- My test servlet -->
<servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>com.mine.courierApp.server.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>
</web-app>
OfyFactory looks like this
#Singleton
public class OfyFactory extends ObjectifyFactory
{
Injector injector;
#Inject
public OfyFactory(Injector injector)
{
this.injector = injector;
register(Pizza.class);
register(Ingredient.class);
}
#Override
public <T> T construct(Class<T> type)
{
return injector.getInstance(type);
}
#Override
public Ofy begin()
{
return new Ofy(super.begin());
}
}
Ofy doesn't have any Guice annotations at all...
public class Ofy extends ObjectifyWrapper<Ofy, OfyFactory>
{
// bunch of helper methods here
}
And finally test servlet where I'm trying to use injected field looks like this
public class TestServlet extends HttpServlet
{
#Inject Ofy ofy;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
ofy.save(new Pizza());
}
}
Ofy ofy is always null. It's never injected. And it's not injected because OfyFactory is never instantiated, its constructor is never called.
Could you please point what I'm doing wrong? Why my singleton is never created?
Thanks a lot.
Instead of defining TestServlet in the web.xml file, try deleting its mapping from web.xml and adding this line in the configureServlets() method:
serve("/test").with(TestServlet.class);
You may also need to bind TestServlet as a Singleton either by annotating the class with #Singleton or by adding a
bind(TestServlet.class).in(Singleton.class);
line to one of the modules.
What's happening is that Guice is not actually creating your servlet so it isn't able to inject the Ofy object. Guice will only create servlets if it is instructed to do so using a serve(...).with(...) binding. Any servlets defined in the web.xml are outside of Guice's control.

Categories