I want to create a servlet dispatcher capable of doing some JPA operations. This servlet must not have any dependencies on Spring or EJB. So, my intention is to inject EntityManagerFactory to it programatically. Then, the dispatcher must handle transactions and create/open/close EntityManager when required.
So I did this code in a sample application, using Spring 3 and EclipseLink:
public class Initializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext context) {
AnnotationConfigWebApplicationContext appContext = new AnnotationConfigWebApplicationContext();
appContext.scan(...);
appContext.refresh();
EntityManagerFactory emf = appContext.getBean(EntityManagerFactory.class);
MyDispatcher dispatcher = new MyDispatcher(emf);
context.addServlet("my", dispatcher).addMapping("/my/*");
}
}
public class MyDispatcher extends HttpServlet {
...
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
emf.createEntityManager();
// java.lang.IllegalStateException: Attempting to execute an operation on a closed EntityManagerFactory.
}
}
The problem is when this dispatcher needs to create an EntityManager an IllegalStateExceptionis thrown. Debugging I have seen that EntityManagerFactoryImpl.close() is called automatically after all beans are instantiated.
What am I doing wrong? Is there some way of achieving what I need?
Thanks.
Related
I am trying to inject a auth service to a Filter -
#Autowired
AuthRequestService authService;
And use it in doFiler method -
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
System.out.println("SAPServiceFilter: "+ req.getRequestURI());
//TODO - create auth sender
authService.isAuthnticate((HttpServletRequest)request); //null
chain.doFilter(request, response);
}
My filter class in sub-package of my #SpringBootApplication class and annotated with #service -
#Service
public class AuthRequestService {
#PostConstruct
public void init() {
System.out.println("AuthRequestService #PostConstruct");
}
public boolean isAuthnticate(HttpServletRequest request) {
System.out.println("isAuthnticate");
return true;
}
}
The class also appears when listing all my beans using -
for (String name : applicationContext.getBeanDefinitionNames()) {
System.out.println(name);
}
Still when debugging authService is null, one last thing the filter is registered with FilterRegistrationBean -
FilterRegistrationBean<SAPServiceFilter> filterRegBean = new FilterRegistrationBean<>();
filterRegBean.setFilter(new SAPServiceFilter());
You could use constructor injection. Supposed your filter registration bean lives in a component and has access to the service you could autowire it there and pass it with the constructor
#Autowired
AuthRequestService authRequestService;
[...]
FilterRegistrationBean<SAPServiceFilter> filterRegBean = new FilterRegistrationBean<>();
filterRegBean.setFilter(new SAPServiceFilter(authRequestService));
Your filter is not under the control of spring. That´s why the autowired dependencies are not being injected.
In your filter init code add this line:
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
or
SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,getServletContext());
But there are more other ways to register a servlet filter in spring context:
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-embedded-container-servlets-filters-listeners-beans
I need to create a new EmployeeInfoCache instance (not a singleton) from info in the HttpServletRequest that is used to get info from an external app. I then want to give this object as a dependency to non-web-layer objects (where it will be set for all #Autowired references). EmployeeInfoCache itself has no web-layer dependencies (e.g. HttpServletRequest).
Can this be done? I thought about writing a spring interceptor which does the following but I don't know what to do to put an object in the spring context such that it will be used to resolve all #Autowired dependencies.
e.g.
public class MyInterceptor extends HandlerInterceptorAdapter
{
public boolean preHandle(
HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception
{
- use info from HttpServletRequest to make calls to external app
- create EmployeeInfoCache object w/ this info
- add EmployeeInfoCache to spring application context where it will be used for resolution of #Autowired
}
}
And the remaining code:
// Assume don't have 'Component' or a similar annotation?
public class EmployeeInfoCache
{
...
}
// REST controller that calls the business logic method
#Controller
MyController
{
#Autowired
private MyBusinessObjectInterface myBusinessObject;
#RequestMapping(...)
public #ResponseBody MyResult myMethod(#RequestBody MyObject myObject, HttpServletRequest request, HttpServletResponse response)
{
myBusinessObject.doIt();
}
}
// Non-web-layer code that uses EmployeeInfoCache
#Service(...)
MyBusinessObject implements MyBusinessObjectInterface
{
// I want the EmployeeInfoCache instance created in MyInterceptor to be autowired here
#Autowired
private EmployeeInfoCache employeeInfoCache;
#Override
public void doIt()
{
employeeInfoCache.getName();
}
}
It sounds like you want to use a factory pattern. Have a Spring bean which is the factory method that returns the Cache.
http://kh-yiu.blogspot.in/2013/04/spring-implementing-factory-pattern.html
Just out of curiosity, is it possible to directly control an EJB transaction from the Web Container?
To illustrate I made this simple example initiating a UserTransaction in the Web Container(using a Servlet), but the transaction is not bound to the EJB Container (in this case a BMT SFSB).
Why is it? Is there a way to do it?
Stateful Session Bean using BMT
#Stateful
#TransactionManagement(TransactionManagementType.BEAN)
public class CustomerBean implements CustomerBeanLocal{
#PersistenceContext(type=PersistenceContextType.EXTENDED)
private EntityManager em;
#Override
public Integer createCustomer(String name) {
Customer customer = new Customer();
customer.setId(1);
customer.setName(name);
em.persist(customer);
//em.flush();
return customer.getId();
}
}
UserTransaction is initiated in the Servlet, but the Session Bean doesn't persist
The Customer is not persisted to the database.
public class BMTServlet extends HttpServlet {
#EJB
private CustomerBeanLocal customerBean;
#Resource
private UserTransaction userTransaction;
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
userTransaction.begin();
customerBean.createCustomer("Tars");
userTransaction.commit();
} catch (Exception e) {
throw new ServletException(e);
}
}
}
If we uncomment the em.flush(); then we get the following exception:
javax.persistence.TransactionRequiredException: no transaction is in progress
org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:792)
org.jboss.ejb3.jpa.integration.JPA1EntityManagerDelegator.flush(JPA1EntityManagerDelegator.java:86)
bean.CustomerBean.createCustomer(CustomerBean.java:25)
BMT will not work in your scenario, as BMT bean will be handling transaction by itself and will not participate in the transaction started in the web module (the container transaction). To control transaction from servlet using UserTransaction, the bean must be CMT.
I'm trying to implement my HK2 binding in Jersey, in a servlet / tomcat context.
I do, in a servlet which extends org.glassfish.jersey.servlet.ServletContainer :
#Override
public void init(ServletConfig config) throws ServletException
{
super.init(config);
// BinderInjection extends org.glassfish.hk2.utilities.binding.AbstractBinder
getConfiguration().register(new BinderInjection());
}
... but I get :
java.lang.IllegalStateException: The resource configuration is not modifiable in this context.
at org.glassfish.jersey.server.ResourceConfig$ImmutableState.register(ResourceConfig.java:270)
at org.glassfish.jersey.server.ResourceConfig$ImmutableState.register(ResourceConfig.java:218)
at org.glassfish.jersey.server.ResourceConfig.register(ResourceConfig.java:448)
at A_Servlet.init(RestServlet.java:45)
at org.apache.catalina.core.StandardWrapper.initServlet(StandardWrapper.java:1190)
So how can I do my own HK2 binding ?
Why this question ? (edit)
It's for EntityManager and JPA in Jersey.
With Netbeans, if I generate an AbstractFacade it put
#PersistenceContext(unitName = "myunit")
private EntityManager em;
... and :
#Override
protected EntityManager getEntityManager()
{
return em;
}
But, when I call the service, em is null. So I suppose it's #PersistenceContext which doesn't work ?
If I use the solution Tutorial: Put JPA in your Web App (tomcat, EclipseLink) and provide Rest JSON output all work like a charm, but I don't like use static variable private static EntityManagerFactory emf; for entity manager.
Thanks.
Below is an example where I am binding a Spring injected jersey resource to the Jetty Webserver. ResourceConfig utility is provided by Jersey. Hope this example helps.
p.s. -- restService is a Spring injected dependency
ResourceConfig config = new ResourceConfig(CustomRestService.class);
config.register(new AbstractBinder() {
#Override
protected void configure() {
bind(restService).to(CustomRestService.class);
}
});
restService.start();
ServletHolder apiServlet = new ServletHolder(new ServletContainer(config));
ServletHolder apiServlet = new ServletHolder(new HttpServletDispatcher());
servletContainer.addServlet(apiServlet, "/api/v1*//*");
I would like to add web interface to my Java application, so that I can manipulate it's state using HTTP.
I have added to application context a Spring bean for some class that starts embedded Tomcat. This class of course has access to context that creates it. But I would like to store this context somehow in Tomcat class (org.apache.catalina.startup.Tomcat) so that later in can be retrieved in Servlets, so that I can do something like this:
public SomeClass extends extends HttpServlet {
#Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ApplicationContext appContext = getContextStoredEarlierInTomcatClass();
SomeBeanFromContext sbfc = appContext.getBean("sbfc", ApplicationContext.class);
sbfc.setSomeProperty(newValue);
}
}
Any idea how I could achieve it?
Thanks!
Classes including Servlets do not require an ApplicationContext to obtain references to String beans. This is done using dependency injection
#Controller
#RequestMapping("/mypage")
public class SomeClass {
#Autowired
private SomeBeanFromContext sbfc;
#RequestMapping(value = "/individualRequest", method = RequestMethod.GET)
public String doIndividualRequest(HttpServletRequest request) {
sbfc.setSomeProperty(newValue);
...
}
}
Spring MVC offers a complete method of injecting beans into target web controller classes using #Controller annotated classes.