I am trying to use CDI in a servlet, here is a snippet of my servlet code
public class MyServlet extends HTTPServlet{
#Inject
#Any
Instance<MyProcedure> procedures;
public void handleRequest(ServletRequest req, ServletResponse res) throws Exception {
if (procedures == null) {
System.out.println("procedure list is NULL");
}
for (Object o : procedures) {
System.out.println("calling procedure " + o.toString());
}
}
}
MyProcedure is am interface and the appliation WAR contains a couple of classes that implement MyProcedure.
I always get a NullPointerException because the list of procedures is null...I do have a beans.xml file in my WEB-INF directory.
I found out what the problem was. The interface that was implemented by my beans had to be included in the war archive under WEB-INF/lib. After I did that everything started to work as expected.
Related
Following the Spring WebFlux document, I set up a WebFlux for FreeMarker configuration s the following:
#Configuration
#EnableWebFlux
public class WebConfig implements WebFluxConfigurer {
#Override
public void configureViewResolvers(ViewResolverRegistry registry) {
final FreeMarkerViewResolver freeMarkerViewResolver = new FreeMarkerViewResolver(
"", ".ftl");
registry.viewResolver(freeMarkerViewResolver);
}
#Bean
public FreeMarkerConfigurer freeMarkerConfigurer() {
FreeMarkerConfigurer configurer = new FreeMarkerConfigurer();
configurer.setTemplateLoaderPath("classpath:/templates");
return configurer;
}
// #Override <-- it causes a compile error
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer
.setUseCaseSensitiveMatch(true)
.setUseTrailingSlashMatch(false);
}
}
The first compile error I run into is
method does not override or implement a method from a supertype
I guess that it is a documentation error. After commenting the override annotation out, I get a run time error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of method httpHandler in org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration$AnnotationConfig required a bean of type 'org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties' that could not be found.
Action:
Consider defining a bean of type 'org.springframework.boot.autoconfigure.web.reactive.WebFluxProperties' in your configuration.
I, however, am unable to find out how WebFluxProperties shall be defined. Please let me know if you have the information.
I made an ExceptionMapper to catch and log all exceptions, like:
#Provider
public class CatchAllExceptionsMapper implements ExceptionMapper<Throwable> {
private static final Logger LOG = LoggerFactory.getLogger(CatchAllExceptionsMapper.class);
#Override
public Response toResponse(Throwable exception) {
LOG.error("Exception not catched!", exception);
return Response.serverError().build();
}
}
It catches the Exceptions my code throws, but if I send a Request with a JSON value that throws an IllegalStateException at my object's creation, this ExceptionMapper is ignored and I get a 400 Bad Request Response.
Funny thing is this Response is not the traditional Tomcat HTML formatted Response, its just plain text. It say just:
Cannot construct instance of `com.example.vo.AutoValue_Customer$Builder`, problem: First name is null or empty. at [Source: (org.glassfish.jersey.message.internal.ReaderInterceptorExecutor$UnCloseableInputStream); line: 14, column: 1]
I thought this might be something short-circuiting Jersey, but my #PreMatching ContainerRequestFilter is executed beforehand, so I really have no idea why the 400 Response is not the traditional HTML one from Tomcat.
Why is this happening? What can I do to catch this and return my own Response?
As stated by Paul Samsotha in the comments, JacksonFeature from the jersey-media-json-jackson package define some ExceptionMappers, like JsonMappingException and JsonParseException. The solution is to create our own, register them within the ResourceConfig and register JacksonFeature last, otherwise it won't work.
e.g.
#Provider
#Priority(1) // hack for overriding other implementations.
public class JsonMappingExceptionMapper implements ExceptionMapper<JsonMappingException> {
#Override
public Response toResponse(JsonMappingException exception) {
return Response.status(Status.BAD_REQUEST).build();
}
}
#Provider
#Priority(1) // hack for overriding other implementations.
public class JsonParseExceptionMapper implements ExceptionMapper<JsonParseException> {
#Override
public Response toResponse(JsonParseException exception) {
return Response.status(Status.BAD_REQUEST).build();
}
}
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
register(CatchAllExceptionsMapper.class);
register(JsonMappingExceptionMapper.class);
register(JsonParseExceptionMapper.class);
register(JacksonFeature.class);
}
}
I have read the documentation concerning the Jersey Test framework and have successfully used JerseyTest's target method to reach a #Path annotated endpoint within my own file. Simplified code is below.
public class TestApplication extends ResourceConfig {
public TestApplication() {
registerClasses(TestService.class);
}
}
#Override
protected Application configure() {
return new TestApplication();
}
#Path("create")
public static class TestService {
#POST
#Path("testObj")
#Consumes(APPLICATION_JSON)
public static Response createTestObj(final TestObj testObj) {
return Response.ok("testObj created").build();
}
}
#Test
private void ensureObjectCreated() {
JSONObject myObj = createNewObj();
final Response response = target("create/testObj").request(APPLICATION_JSON)
.post(Entity.json(myObj.toString()));
Assert.isEqual(response.status, 200);
}
Now I want to reach a #Path annotated endpoint in other files/directories. How do I do so? The problem may be that the other files are actual production code, so I cannot make the classes static. However the endpoints in the other paths are reachable.
Just register them in the resource config, either individually (depending on the scope of the test), or specify a package to scan with the packages method.
#Override
public ResourceConfig configure() {
return new ResourceConfig()
.register(SomeResource.class)
.packages("your.resource.package.to.scan");
}
The only reason the class in the example is static is because it is an inner class that needs to be instantiated by the framework.
When you access the resource, it will not include the root application path, only the #Path value on the class, and whatever sub path, just like in your code above.
I use Gucie 3.0 to intercept any methods that have my defined annotation #LogRequired. However for my application, some beans are initialized by Spring with injected fields values. After calling giuce injector.injectMembers(this), the beans gets proxied by guice but all original fields values are gone. Looks like Guice re-constucts the beans and throw away all old values. Is this expected behavior or how can I solve this issue?
Create a class extends AbstractModule
public class InterceptorModule extends AbstractModule{ public void configure()
{ LogInterceptor tracing = new LogInterceptor(); requestInjection(tracing); bindInterceptor(Matchers.any(), Matchers.annotatedWith(LogRequired.class), tracing); }
}
Define the interceptor business logic
public class LogInterceptor implements MethodInterceptor { //business logic here }
Create LogService class
Public class LogService { Injector injector = Guice.createInjector(new InterceptorModule()); }
I have one of the bean example below with the getName method wants to be intercepted:
public class UserImplTwo implements IUser {
private String name;
#LogRequired
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
which is initialized by Spring context:
Finally I have a consumer to consume the bean:
public class Consumer
{
#Inject
private UserImplTwo instance;
public void setInstance(UserImplTwo instance)
{
this.instance = instance;
}
public void init()
{
// the value of name is printed out as 'hello world'
System.out.println( this.instance.getName());
LogService.injector.injectMembers(this);
// the value of name is printed out as null, should be 'hello world'
System.out.println( this.instance.getName());
}
}
Then use Spring to initialized the bean:
<bean id="consumer" class="com.demo.Consumer" init-method="init">
<property name="instance" ref="userTwo"></property>
</bean>
Please let me know if this the the right approach or if I did something wrong, because I have to use Spring to initialize some beans.
A "right approach" is probably to keep things simple and use Spring's DI if you use Spring Framework, and not try to mix and match with Guice :-)
Having said that there seems no technical reason why they can't be mixed and matched together to some degree.
I think you will have more success with another approach. One that I have used before is to make use of Spring MVC Java-based configuration. Here is the basic approach.
Create a class that extends WebMvcConfigurationSupport:
#Configuration
#Import(BeansConfig.class)
public class Config extends WebMvcConfigurationSupport {
}
Separate out your beans config (probably it can be merged with the above but I guess it's quite dull code and you normally don't want want to see it). And use it to create your beans with your Guice injector before providing them to Spring.
#Configuration
public class BeansConfig {
#Bean
public Consumer getConsumer() {
return SomeGuiceInjectorFactory.newInstance(Consumer.class);
}
}
Include this in your spring.xml (or bootstrap other ways if your servlet container is newer than mine was)
<context:annotation-config/>
<bean id="extendedWebMvcConfig" class="Config"/>
Constructor injection and most/all? other Guice goodness should work also with such scenario.
Also you won't need to configure your beans in xml.
Here is my bean that is trying to inject a singleton bean InformationService :
#Path("/information/{name}")
#Stateless (name="InformationResource")
public class InformationResource {
#EJB
private InformationService appService;
#GET
#Produces(MediaType.APPLICATION_XML)
public Information getInfo(#PathParam("name") String name){
return appService.getMap().get(name);
}
#PUT
#POST
#Consumes(MediaType.APPLICATION_XML)
public Information putInfo(#PathParam("name") String name, Information info){
return appService.getMap().put(name,info);
}
#DELETE
public void deleteInfo(#PathParam("name") String name){
appService.getMap().remove(name);
}
}
This is the InformationService class
#Singleton
public class InformationService {
private Map<String,Information> map;
#PostConstruct
public void init(){
map = new HashMap<String,Information>();
map.put("daud", new Information("B.Tech","Lucknow"));
map.put("anuragh", new Information("M.Sc","Delhi"));
}
public Map<String,Information> getMap(){
return map;
}
}
Its part of a very simple JAX-RS implementation and I am deploying as war in JBoss 6.1 Final. The problem is that InformationService throwing a NullPointerException when I make the proper get request. If I initialize appService explicitly, everything works fine. Why is #EJB annotation not working ?
Are you using Jersey as REST implementation? If so, EJB injection is not supported out of the box.
This link provides more information on this and also a solution.
Check that your #Singleton is javax.ejb.Singleton.
Any other exceptions before NPE ?