I have some ear application which I need to rewrite to spring. War contains one class which run EJB:
/**
* The ear initialization.
*/
public final class Startup extends HttpServlet {
private static final long serialVersionUID = 6465240550145652729L;
#EJB(name="MyStartupBean")
private MyBeanLocal bean;
#Override
public void init(final ServletConfig servletConfiguration) throws ServletException {
bean.start();
}
#Override
public void destroy() {
bean.stop();
}
}
EJB contains some quart scheduler which run job every 30s
I really try to find some example of ear spring application with EJB but with no succes. How should I rewrite it into spring ?
Spring supports #EJB (not widely known but it does). So basically you can simply port your class to spring by removing the extends HttpServlet, add a #Component annotation, simplify the init method and add #PostConstruct and add #PreDestroy to the destroy method.
#Component
public final class Startup {
private static final long serialVersionUID = 6465240550145652729L;
#EJB(name="MyStartupBean")
private MyBeanLocal bean;
#PostConstruct
public void init() {
bean.start();
}
#PreDestroy
public void destroy() {
bean.stop();
}
}
Something like that would be the result. Now either declare this bean in xml
<bean class="Startup" />
Or use component scanning to detect/pickup this bean.
But as mentioned I would probably ditch the EJB altogether and use spring to bootstrap Quartz instead.
Related
I'm new to Spring so I'm just trying to understand how it works. I've developed a simple servletprojectusing spring to manage hibernate framework.
I have a service
#Service("service")
#Transactional
public class CdServiceImpl {
#Autowired
private HibernateUtility hibernateutility;
public int saveCd(CD cd) {
return hibernateutility.saveCd(cd);
}
public List getCd(String searchedCd) {
return hibernateutility.getCd(searchedCd);
}
public List getAllCd() {
return hibernateutility.getAllCd();
}
public void deleteCd(int id) {
hibernateutility.deleteCd(id);
}
public User getUser(String username, String password) {
return hibernateutility.getUser(username, password);
}
}
And then I use it in the servlet
context.scan("it.project");
context.refresh();
CdServiceImpl service = (CdServiceImpl) context.getBean("service");
context.register(ApplicationContextConfig.class);
context.refresh();
1) It works but I have two question. It is the right way to work?
2) I've tried to set a field in the servlet like:
#Autowired
private CdServiceImpl service
and then I remove the context.scan ecc part and it gave me nullpointerexception. Why?
Doing that I also defined a new bean
#Bean
public CdServiceImpl getCdServiceImpl() {
return new CdServiceImpl();
}
Why it doesn't work? I know that maybe this is a noob question but I'm tryingto figure out how spring works
Basically as soon as you start doing things like new **ApplicationContext you need to scratch your head, take 2 steps away from the keyboard and think if you really want to do this. In 99% of the cases this isn't what you want or at least should do.
Instead have the ContextLoaderListener load your configuration. Assuming you don't have a web.xml use the the AbstractContextLoaderInitializer base class.
public ApplicationInitializer extends AbstractContextLoaderInitializer {
protected WebApplicationContext createApplicationContext() {
return new AnnotationConfigWebApplicationContext(ApplicationContextConfig.class);
}
}
Note: As this is for bootstrapping your application you need to create the the context. You could also use AbstractAnnotationConfigDispatcherServletInitializer which eliminates this need, but also creates a DispatcherServlet which you don't need.
Now that your configuration is automatically loaded you can use the WebApplicationContextUtils to get the context to do the lookup. Do this in the init method of your servlet.
public class YourServlet extends GenericServlet {
private CdServiceImpl service;
public void init() throws ServletException {
ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
service = ctx.getBean(CdServiceImpl.class);
}
}
Now you initialize the bean once and don't need to handle anything anymore. You could also use #Autowired and manuallly have it injected.
public class YourServlet extends GenericServlet {
#Autowired
private CdServiceImpl service;
public void init() throws ServletException {
ApplicationContext ctx = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
ctx.getAutowireCapableBeanFactory().autowireBean(this);
}
}
I am in the process of refactoring an old module, by adding CDI.
I end with
public interface ApiFactory {
...
}
public class ApiFactorySp
implements ApiFactory {
#Inject
UrlProducer urlProducer; // <-- Does not get injected
...
}
and
public interface UrlProducer {
public String getUrl();
}
#Alternative
public class UrlProducerTest
implements UrlProducer {
#Override
public String getUrl() {
return "https://myTestEnv.mydomain/myWebApp";
}
}
For testing, I create a beans.xml file in META-INF:
<beans
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
<alternatives>
<class>myOrg.myProject.myPackage.UrlProducerTest</class>
</alternatives>
</beans>
To test it, I am doing like shown in this blog
public class WeldContext {
public static final WeldContext INSTANCE = new WeldContext();
private final Weld weld;
private final WeldContainer container;
private WeldContext() {
this.weld = new Weld();
this.container = weld.initialize();
Runtime.getRuntime().addShutdownHook(new Thread() {
#Override
public void run() {
weld.shutdown();
}
});
}
public <T> T getBean(Class<T> type) {
return container.instance().select(type).get();
}
}
and
public class WeldJUnit4Runner extends BlockJUnit4ClassRunner {
public WeldJUnit4Runner(Class<Object> clazz) throws InitializationError {
super(clazz);
}
#Override
protected Object createTest() {
final Class<?> test = getTestClass().getJavaClass();
return WeldContext.INSTANCE.getBean(test);
}
}
Now, when I try to test the logic, I do
#RunWith(WeldJUnit4Runner.class)
public class MyTest {
#Inject
UrlProducer urlProducer;
#Inject
ApiFactory apiFactory;
#Test
public void test() {
apiFactory.doSomethingThatRequiresUrlProducer();
}
}
When I run this, both of the test attributes are inject, but I get NPE because the urlProducer attribute inside of the apiFactory instance has not been assigned a value.
Why is Weld not recognizing the #Inject attribute inside ApiFactory?
JDK 7, Weld 2.2.10, Junit 4.12
UPDATE: After posting the question, started trying with a simpler, brand new project (with just two interfaces and three classes). Using Weld "standalone" did not solve the issue, using CDI-Unit did solve it.
Then I modified my original project to use CDI-Unit, but it did not improve anything. After that I change the injection of UrlProducerTest in ApiFactory from field to constructor (i.e., defining the #Inject ApiFactory(UrlProducer urlProducer) constructor) solved it. I still have not tried this solution with "standalone" Weld (that is for tomorrow), but nonetheless I am still interested in know why field injection is not working.
If UrlProducerTest is an alternative and you want to inject this bean this class should be added to beans.xml into <alternatives> tag.
EDIT:
I believe if some bean can't be injected you get exception with 'unsatisfied/ambiguous dependencies' message. Null could be injected if you used CDI producer method that returned null but this is not your scenario.
So if there are no errors in console I have two assumptions:
Injection doesn't work at all and you get NPE because apiFactory is null
You use urlProducer before injection. For example, from constructor or initialization block (apiFactory.doSomethingThatRequiresUrlProducer() is not provided). So move this logic to some method and annotate it by #PostConstruct
Because ApiFactorySp isn't a CDI bean. You need to annotate the class with #Named to identify the class as a CDI bean for CDI to perform dependency injection.
What could be the possible usage of having a reference of applicationContext within a bean ?
It used to be useful to have a handle to an ApplicationContext or ConfigurableApplicationContext when writing infrastructure code. For example, if you wanted to write a service locator that should find all beans implementing an interface, you could
#Autowired
private ApplicationContext applicationContext;
#PostConstruct
public void addFooServices() {
for (... : this.applicationContext.getBeansOfType(FooService.class)) {
// do something with each
}
These kinds of uses are diminishing, since you can now (3.2, if not earlier) just inject
#Autowired
private List<FooService> fooServices;
But it can still be useful to have your hands on the container at times, e.g. to trigger a container refresh from JMX, or some other hackery.
Will tell you one of our usage experience. We used the application context to get the collection of beans implementing a particular interface:
public class MyBean implements ApplicationListener<ContextRefreshedEvent> {
#Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContext context = event.getApplicationContext();
Collection<IMyInterface> implementors = context.getBeansOfType(IMyInterface.class).values();
}
}
We are migrating some of our data services from Jersey 1.x using jersey-spring to Jersey 2.x using jersey-spring3.
We have a few test classes that inherit from JerseyTest. Some of these classes use custom applicationContext.xml files that are not specified in the web.xml file.
In Jersey 1.x the test classes that extended JerseyTest could call the super constructor with a WebappDescriptor.Builder to which a context parameter could be passed to set or override the application context path.
E.g.
public MyTestClassThatExtendsJerseyTest()
{
super(new WebAppDescriptor.Builder("com.helloworld")
.contextParam( "contextConfigLocation", "classpath:helloContext.xml")
.servletClass(SpringServlet.class)
.contextListenerClass(ContextLoaderListener.class)
.requestListenerClass(RequestContextListener.class).build());
}
How can the same be achieved with Jersey 2.x?
I have combed through the API docs, user guides and some of the sources but was unable to find an answer.
This didn't work for me as I was not using the .xml style configuration, I was using #Configuration annotations. So I had to directly provide the application context to the ResourceConfig class.
I defined the configure method in my JerseyTest like so:
#Override
protected Application configure() {
ResourceConfig rc = new ResourceConfig();
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
rc.property("contextConfig", ctx);
}
where SpringConfig.class is my class with the #Configuration annotation and
importing org.springframework.context.annotation.AnnotationConfigApplicationContext
Lets assume your Application looks like:
#ApplicationPath("/")
public class MyApplication extends ResourceConfig {
/**
* Register JAX-RS application components.
*/
public MyApplication () {
// Register RequestContextFilter from Spring integration module.
register(RequestContextFilter.class);
// Register JAX-RS root resource.
register(JerseySpringResource.class);
}
}
Your JAX-RS root resource like:
#Path("spring-hello")
public class JerseySpringResource {
#Autowired
private GreetingService greetingService;
#Inject
private DateTimeService timeService;
#GET
#Produces(MediaType.TEXT_PLAIN)
public String getHello() {
return String.format("%s: %s", timeService.getDateTime(), greetingService.greet("World"));
}
}
And you have spring descriptor named helloContext.xml available directly from your class-path. Now you want to test your getHello resource method using Jersey Test Framework. You can write your test like:
public class JerseySpringResourceTest extends JerseyTest {
#Override
protected Application configure() {
// Enable logging.
enable(TestProperties.LOG_TRAFFIC);
enable(TestProperties.DUMP_ENTITY);
// Create an instance of MyApplication ...
return new MyApplication()
// ... and pass "contextConfigLocation" property to Spring integration.
.property("contextConfigLocation", "classpath:helloContext.xml");
}
#Test
public void testJerseyResource() {
// Make a better test method than simply outputting the result.
System.out.println(target("spring-hello").request().get(String.class));
}
}
I want to create a timer EJB3 when a stateless bean is created in the pool.
But if I use #PostConstruct I get the exception:
java.lang.IllegalStateException: [EJB:010193]Illegal call to EJBContext method. The bean is in "null" state. It cannot perform 'getting the Timer Service' action(s). Refer to the EJB specification for more details.
If container calls #PostConstruct, the bean ins't null. So, why I get this exception?
CLASS
#Stateless
public class TestBean implements TestLocal {
#Resource
TimerService timerService;
#PostConstruct
public void startTimer() {
if (timerService.getTimers().size() == 0) {
timerService.createTimer(1 * 1000, 1 * 1000, null);
}
}
#Override
public void test() {
}
}
INTERFACE
#Local
public interface TesteLocal {
void test();
}
SERVLET
public class TestServlet extends HttpServlet {
#EJB
private TestLocal test;
protected void doGet(....) throws .... {
test.test();
}
}
DETAILS
I'm using weblogic server 11g.
You can NOT use a #PostConstruct to create a timer in a stateless bean EJB 3. See this blog How to use EJB 3 timer in a weblogic 10 cluster environment for the explanation. Even the blog was talking about weblogic, but the explanation should apply to other app servers also.
Container will not allow for timerService in method annotated with #PostConstruct of Stateless Session Bean. If you want to use timerService in method annotated with #PostConstruct the go for Singleton session bean(#Singleton).
I'm not 100% sure but I think that the bean class must implement javax.ejb.TimedObject or have a method annotated with #Timeout to use EJB timers. Example:
#Stateless
public class TestBean implements TestLocal {
#Resource
TimerService timerService;
#PostConstruct
public void startTimer() {
if (timerService.getTimers().size() == 0) {
timerService.createTimer(1 * 1000, 1 * 1000, null);
}
}
#Timeout
#TransactionAttribute(value=REQUIRES_NEW)
public void timeoutCallback(Timer timer) {
...
}
}
Does WebLogic still complain with the above code?
PS: In any case, the error you currently get is very poorly reported, you should probably open a case.