Spring Boot - access component inside a servlet - spring

I have a spring boot application that serves my jersey based api. I have a requirement to have the services layer serve blob data to a client as a stream. I wrote a servlet to do that and configured it as follows.
#Bean
public ServletRegistrationBean servletRegistrationBean(){
return new ServletRegistrationBean(new BlobReaderServlet(),"/blobReader/*");
}
However, in the servlet code I can't seem to inject any components (they are all null). I need to inject a component that actually loads the blob data from the database.
#WebServlet(name = "BlobReaderServlet",
urlPatterns = {"/blobreader"})
#Component
public class BlobReaderServlet extends HttpServlet {
Logger logger = Logger.getLogger(this.getClass().getName());
#Inject
DocumentLoaderComponent blobLoader;
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
I strongly suspect the servlet isn't a spring managed component after all and dependency injection isn't working. How can I get access to a component from the context?
UPDATE
It was much simpler than I thought.
#Override
public void init() throws ServletException {
ApplicationContext ac = (ApplicationContext) getServletConfig().getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
this.documentBlob = (DocumentBlob) ac.getBean("documentBlobBean");
}

You are correct that your servlet isn't a Spring managed bean. That is because you are instantiating the instance directly (i.e., calling new BlobReaderServlet() in your bean method). Another solution is to change your configuration class as follows:
#Bean
public ServletRegistrationBean servletRegistrationBean(){
return new ServletRegistrationBean(blobReaderServlet(),"/blobReader/*");
}
#Bean
public BlobReaderServlet blobReaderServlet(){
return new BlobReaderServlet();
}
This will allow Spring to manage the instance and perform autowiring on it.

Related

Use org.json with Spring Boot

I am using Spring Boot framework and trying to create a structure where the developer can only return org.json.JSONObject instance. I have this endpoint declaration.
#RequestMapping(path = "/hello", method = RequestMethod.POST)
#ResponseBody
public org.json.JSONObject hello(HttpServletRequest request, HttpServletResponse response) throws IOException
This always returns {"empty":false} because Jackson used by the framework does not know how to serialize the org.json instance. I am trying to tell Jackson how to serialize the org.json instance by using the following dependency.
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-json-org</artifactId>
<version>2.13.0</version>
</dependency>
But I cannot get it work unless I change the return type to Map value which is not possible. Using
ObjectMapper mapper = JsonMapper.builder()
.addModule(new JsonOrgModule())
.build()
does not help. Is there a global ObjectMapper object that is used by Spring Boot where I can register the JsonOrgModule at the application startup? How can I use org.json.JSONObject return type using Spring Boot framework.
Thanks!
As per Spring Boot docs 4.3 Customize the Jackson ObjectMapper section:
Any beans of type com.fasterxml.jackson.databind.Module are
automatically registered with the auto-configured
Jackson2ObjectMapperBuilder and are applied to any ObjectMapper
instances that it creates. This provides a global mechanism for
contributing custom modules when you add new features to your
application.
Therefore, if you provide a #Bean of type JsonOrgModule it will be automatically applied to the default ObjectMapper created at startup.
For exmaple:
#SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public JsonOrgModule jsonOrgModule() {
return new JsonOrgModule();
}
}
#SpringBootTest
class ObjectMapperTests {
#Autowired
ObjectMapper defaultObjectMapper;
#Test
void defaultObjectMapperShouldWriteJsonObject() throws JSONException, JsonProcessingException {
// Given
var jsonObject = new JSONObject().put("username", "eHayik");
// When
var json = defaultObjectMapper.writeValueAsString(jsonObject);
// Then
assertThat(json).isEqualTo("{\"username\":\"eHayik\"}");
}
}

Why Servlet Filter can be Spring Bean?

What i know about Filter and Interceptor is that Filters as J2EE Specifications are part of the webserver and not the Spring framework. So some older articles explain that it is impossible to register filters as Spring Bean while Interceptor is possible.
But the results I got when I tested today is that Filters can be Spring Bean and also inject Spring Bean on Filters are possible too like Interceptors.
(I tested on SpringBoot Framework)
#Component
public class CustomFilterTest implements Filter {
#Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws ServletException, IOException {
chain.doFilter(request, response);
}
#Override
public void init(final FilterConfig filterConfig) throws ServletException {
Filter.super.init(filterConfig);
}
#Override
public void destroy() {
Filter.super.destroy();
}
}
#RestController
#RequiredArgsConstructor
public class ProductController {
private final CustomFilterTest customFilterTest;
#GetMapping("/test")
public ResponseEntity<Void> temp() {
System.out.println(customFilterTest);
return ResponseEntity.noContent().build();
}
}
Can anyone please explain to me?
We have to make a distinction between a regular Spring application and a Spring Boot application here. As with both, you can register a servlet filter as a bean, but the mechanism is a bit different.
Spring Framework
In plain Spring use the DelegatingFilterProxy to achieve this. The task of the DelegatingFilterProxy is to look for a bean with the same name as the filter in the root application context (the ApplicationContext registered through the ContextLoaderListener). This bean has to be your managed servlet filter.
#Configuration
#EnableWebMvc
public class WebConfiguration {
#Bean
public void YourFilter myFilter() { ... }
}
Then for the web application you would register a DelegatingFilterProxy with the name myFilter to make this work.
public class MyWebApplicationInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
public void onStartup(ServletContext servletContext)
throws ServletException {
super.onStartup(servletContext);
servletContext.addFilter("myFilter", DelegatingFilterProxy.class);
}
Spring Boot
In Spring Boot it is a bit different as Spring Boot is also in control of your servlet container, like Tomcat. It basically means that Tomcat is also a managed bean in your ApplicationContext and Spring Boot can inject dependencies into it. So when Spring Boot detects a bean for the servlet filter it will automatically add it to the filter chain (without the need of a DelegatingFilterProxy).
Which means only an #Bean for your filter is needed.
#Configuration
public class WebConfiguration {
#Bean
public YourFilter myFilter() { ... }
}
Additionally you can configure things like URLs etc. by adding an additional FilterRegistrationBean for this filter.
Conclusion
For plain Spring the DelegatingFilterProxy has been around since Spring 1.2 which was released in 2005. This means if you are reading really, really, really old articles (before 2005) this was true, however with the addition of the DelegatingFilterProxy, this isn't anymore. With the release of Spring Boot, this became even a lesser issue, is it more or less is the only way to register a filter (as a managed bean).

Grpc Spring Boot Starter

I am trying to integrate a grpcService to my spring boot application. In this service class i have a jpaRepository which is #Autowired. When i run the server this repository is not injected( is null when i'm trying to use it).
#Service
public class MedicationPlanService extends medicationPlanGrpc.medicationPlanImplBase {
#Autowired
private MedicationPlanRepository medicationPlanRepository;
#Override
public void hello(MedicationPlan.HelloRequest request, StreamObserver<MedicationPlan.HelloResponse> responseObserver) {
List<MedicationPlan> medicationPlans = medicationPlanRepository.findAll();
MedicationPlan.HelloResponse.Builder response = MedicationPlan.HelloResponse.newBuilder();
response.setResponseMessage("hello");
responseObserver.onNext(response.build());
responseObserver.onCompleted();
}
}
#Component
public class GrpcServerStartConfiguration {
#PostConstruct
public void startGrpcServer() throws IOException, InterruptedException {
Server server = ServerBuilder.forPort(9091).addService(new MedicationPlanService()).build();
server.start();
System.out.println("gRPC server started");
server.awaitTermination();
}
}
When i try to use the medicationPlanRepository i realized that it is null.
Thanks in advance :).
Because you're creating MedicationPlanService with "new", it becomes a simple object, not a bean. And things such as DI don't work in this way.
Initialize these 2 classes correctly (via #ComponentScan or #Bean in #Configuration class). Then inject MedicationPlanService into GrpcServerStartConfiguration. The latter you can mark as #Configuration for better understaning btw.

Springboot>WebServlet - Pass spring container

I have springBoot standalone application. I used #SpringBootApplication, #ServletComponentScan annotations in my standalone application. All my components, beans getting initialized in spring container and prints in the application startup.
Inside my servlet, i invoke handler and beans were coming as null. How do i pass spring container through my servlet ?
#SpringBootApplication
#ServletComponentScan
public class AStandaloneApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(AStandaloneApplication.class, args);
}
}
#WebServlet("/ba")
public class BAServlet extends SpeechletServlet {
#Autowired
private BASpeechletHandler bASpeechletHandler;
#Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
this.setSpeechlet(bASpeechletHandler);
}
}
public class BASpeechletHandler implements Speechlet {
#Autowired
private BSEngine bSEngine;
#Autowired
private IBotResponseObjToAlexaSpeechletResponseObj botResponseObjToAlexaSpeechletResponseObj;
}
The bASpeechletHandler is null in servlet, if i instatiate object in my servlet for bASpeechletHandler and move on then components, services and repository inside bASpeechletHandler also null.
Thanks.
1.Add the packages to component scan - similar to this
#ServletComponentScan(basePackages="org.my.pkg")
2.Add one of the #Component annotations into your BASpeechletHandler class.
This will make that class eligible for auto-discovery of beans.
May be i little complication in asking. I found the solution. In Web applicationContext i pinged the spring context and got the bean.
private ApplicationContext appContext;
private BASpeechletHandler bASpeechletHandler;
public void init(ServletConfig config) throws ServletException {
super.init();
appContext = (ApplicationContext) config.getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
bASpeechletHandler = (bASpeechletHandler) appContext.getBean("bASpeechletHandler");
}
Thanks.

How to use CDI into JAX-RS client

I have searched a while on SO and official documentation but I cannot found a way to use directly CDI injection into a JAX-RS client.
I retrieve a client using the builder method and I want to register a WriterInterceptor (or any filter like component) which uses injection to retrieve another bean.
I want to use CDI injection and avoid registering each bean with HK2.
ClientBuilder.newBuilder()
.register(MyWriter.class)
.build();
And MyWriter with the injected class.
#Provider
public class MyWriter implements WriterInterceptor {
private final MyRepo repo;
#Inject
public MyWriter(MyRepo repo) {
this.repo = repo;
}
#Override
public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {
context.proceed();
}
}
public class MyRepo {
}
I am running in an embedded jetty with Jersey 2 and Weld SE.
Its possible to inject in java se application using wield .
#Singleton
public class Application {
private static Logger logger = LoggerFactory.getLogger(Application.class);
#inject
private SomeOtherBean injectedBean;
public void run() {
logger.debug("application initialized");
injectedBean.doSomething();
}
}
inside main initialize weild
import java.io.IOException;
import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;
public class EntryPoint {
public static void main(String[] args) throws IOException {
Weld weld = new Weld();
WeldContainer container = weld.initialize();
Application application = container.instance().select(Application.class).get();
application.run();
weld.shutdown();
}
}
Have a look at below doc
https://docs.jboss.org/weld/reference/latest/en-US/html/environments.html#_java_se
also below tutorial
https://randling.wordpress.com/2011/08/15/cdi-in-java-se/
If I understand everything correctly, this has already been asked and answered. In a nutshell: you have to override the default behaviour of the H2K Binder, so it reaches for the Weld Bean Manager. You don't have to register every Bean with H2K later on.
Edit: to contain everything in the post, so you don't have to read the comments:
The linked answer is for the server-side, not the client.
With standard tools (Jersey Client-side injection providers and the Weld bridge), it seems to be a too big overhead/impossible to do
Apparently in the Dropwizard project they managed to do custom client-side injection.

Resources