I'm trying to get a POJO starting on startup within my Weld/Seam3 application but not having much luck. I've tried the following but none of them have worked:
#Singleton
public class StartupJobs {
#Inject
private Logger log;
public void onStartup(#Observes #Initialized ServletContextEvent event) {
log.info("Starting startup jobs");
}
public void onStartupTwo(#Observes #Initialized WebApplication webApplication) {
log.info("Starting startup jobs");
}
}
-
// Guessing this way is no good as I can't use the javax.ejb.Startup annotation here
#ApplicationScoped
public class StartupJobs {
#Inject
private Logger log;
#PostConstruct
public void onStartup() {
log.info("Starting startup jobs");
}
}
But neither of those ways worked. My log message was never raised. As this application is run on Tomcat6 and I've had to add the "org.jboss.weld.environment.servlet.Listener" listener to my web.xml, I'm wondering if there's something that class raises that I could observe. I didn't notice anything in particular though.
Any clue what else I could try?
Found out my issue was configuration. I hadn't seen I needed some extra configuration due to being on Tomcat 6: http://docs.jboss.org/seam/3/servlet/latest/reference/en-US/html/servlet-installation.html#installation.pre-servlet-3
A quick note on the documentation on that page as it stands as I write this, the class for the "Catch Exception Filter" should be "org.jboss.seam.servlet.exception.CatchExceptionFilter". The documentation is missing out the "exception". It seems to have been fixed in the Seam Servlet code so I imagine this bug will be fixed next time the documentation is released.
Related
I have a custom API that I am working on that has to go through a variety of steps to authenticate and set itself up. That setup being oAuth, HTTP Request, and Websockets. I have the setup done fine and all that working, but what I am having trouble after.
When I implement and initialize my API in the java program the methods like api.methodHandler.sendMessage("Registering Events"); are running before the API finishes setting itself up.
I have tried using wait, sleeps, etc.
api.setupAPI(clientid, clientSecret, cid, uid, null);
api.methodHandler.sendMessage("Registering Events");
api.eventManager.registerEvent(new EventHandler());
No errors, but the methods are just running before they should.
There is a good site that explains your options for solving this problem: Running Setup Data on Startup in Spring. But to give you few options: You can annotate your method
#EventListener(ApplicationReadyEvent.class)
#Order(20)
public void runMyLogicAfterAppIsInitialized() {
...
}
The #Order annotation is not necessary but if you have several methods annotated with #EventListener you can control their execution order
Or a bit older way is to implement ApplicationListener interface
#Component
public class StartupApplicationListenerExample implements
ApplicationListener<ContextRefreshedEvent> {
private static final Logger LOG
= Logger.getLogger(StartupApplicationListenerExample.class);
public static int counter;
#Override public void onApplicationEvent(ContextRefreshedEvent event) {
LOG.info("Increment counter");
counter++;
}
}
I have an application that uses Jersey/JAX-RS for web services (annotations, etc) and Guice to inject service implementations. I don't really like the way Guice works with servlets directly, I prefer the Jersey way, so I had to do a bit of fussing to get the service injections to work since Guice wouldn't be creating my servlet classes, and I didn't want to deal with the HK2-Guice bridge. I did this by creating a listener class (called Configuration) that sets up the injectors in static fields upon application startup and then manually effecting the injections in each servlet class by creating a parent class that all my servlets extend with a constructor that contains the following:
public MasterServlet() {
// in order for the Guice #Inject annotation to work, we have to create a constructor
// like this and call injectMembers(this) on all our injectors in it
Configuration.getMyServiceInjector().injectMembers(this);
Configuration.getDriverInjector().injectMembers(this);
}
I know it's kind of hacky, but this works just fine in my servlets. I can use the Guice #Inject annotations on my services and switch between named implementations and so on. The problem comes when I go to set up my unit tests. I'm using JerseyTest to do my tests, but running a test against my servlets results in a 500 error with Guice saying the following:
com.google.inject.ConfigurationException: Guice configuration errors:
1) No implementation for com.mycompany.MyService was bound.
while locating com.mycompany.MyService
for field at com.mycompany.servlet.TestGetServlet.service(TestGetServlet.java:21)
while locating com.mycompany.servlet.TestGetServlet
The test looks like this:
public class TestServletTest extends JerseyTest {
#Test
public void testServletFunctional() {
final String response = target("/testget").request().get(String.class);
assertEquals("get servlet functional", response);
}
#Before
public void setup() {
Configuration configuration = new Configuration();
configuration.contextInitialized(null);
}
#Override
protected Application configure() {
return new ResourceConfig(TestGetServlet.class);
}
}
You'll notice in the setup method I am manually creating my Configuration class since I can't rely on the test container (Grizzly) to create it (I get NullPointerExceptions without those two lines). More about this below.
And here's the servlet being tested:
#Path("/testget")
public class TestGetServlet extends MasterServlet {
#Inject
MyService service;
#GET
#Produces({"text/plain", MediaType.TEXT_PLAIN})
public String testGet() {
//service = Configuration.getServiceInjector().getInstance(MyService.class);
return "get servlet functional";
}
}
Notice the commented line in the testGet() method? If I do that instead and remove the #Inject annotation above, everything works fine, which indicates that Grizzly is not creating my servlets the way I expect.
I think what's happening is that Grizzly doesn't know about Guice. Everything seems to suggest that Grizzly isn't seeing the Configuration class, despite the fact that by putting it in my test's #Before method it seems to be at least available to the classes that use it (see: the commented line in the TestGetServlet class). I just don't know how to fix it.
I'm still trying to figure this out but in the meantime I switched from Guice to HK2, which took a bit of doing but I figured this might be helpful for anyone who runs into this problem in the future.
I consider this an answer because truthfully my attempt to bypass the Guice-HK2 bridge but still use Guice with Jersey might not have been the best idea.
Switching from Guice to HK2 takes a bit of doing and there's no comprehensive guide out there with all the answers. The dependencies are really fussy, for example. If you try to use Jersey 2.27 you may run into the famous
java.lang.IllegalStateException: InjectionManagerFactory not found
error. Jersey 2.27 is not backwards compatible with previous versions due to HK2 itself. I am still working on getting that all to work, but in the meantime I had to downgrade all my Jersey dependencies to 2.26-b06 to get HK2 working properly.
Jersey thankfully already implements a bunch of HK2 boilerplate, so all you need to get injection working is proper use of #Contract, #Service (see HK2 docs for those), and then two new classes that look like this:
public class MyHK2Binder extends AbstractBinder {
#Override
protected void configure() {
// my service here is a singleton, yours might not be, so just omit the call to in()
// also, the order here is switched from Guice! very subtle!
bind(MyServiceImpl.class).to(MyService.class).in(Singleton.class);
}
}
And this:
public class MyResourceConfig extends ResourceConfig {
public MyResourceConfig() {
register(new MyHK2Binder());
packages(true, "com.mycompany");
}
}
Simple enough, but this only works for the application itself. The test container knows nothing about it, so you have to redo the Binder and ResourceConfig yourself in your test class, like this:
public class TestServletTest extends JerseyTest {
#Test
public void testServletFunctional() {
final String response = target("/testget").request().get(String.class);
assertEquals("get servlet functional", response);
}
#Before
public void setup() {
}
#Override
protected Application configure() {
return new TestServletBinder(TestGetServlet.class);
}
public class TestServletBinder extends ResourceConfig {
public TestServletBinder(Class registeree) {
super(registeree);
register(new MyHK2Binder());
packages(true, "com.mycompany");
}
}
}
Having to do this is actually fine because you can switch out the Binder for a test binder instead, in which you've bound your service to a mocked service instead or something. I haven't done that here but that's easy enough to do: replace new MyHK2Binder() in the call to register() with one that does a binding like this instead:
bind(MyTestServiceImpl.class).to(MyService.class).in(Singleton.class);
And voila. Very nice. Obviously you could achieve a similar result with Named bindings, but this works great and might even be simpler and more clear.
Hope this helps someone save the hours I spent screwing around to get this working.
my EventListener annotation don't receive any Spring Event. Here is my code:
#Component
public class ProxyConfig {
public ProxyConfig() {
System.out.println("I can see this in the console");
}
#EventListener
public void onApplicationEvent(ApplicationEnvironmentPreparedEvent event) {
System.out.println("WON'T WORK :-("); // FIXME
}
#EventListener
public void test(ApplicationStartedEvent event) {
System.out.println("WON'T WORK :-("); // FIXME
}
}
And here is my Application class:
#SpringBootApplication
public class Application {
public static void main(String[] args) throws Exception {
SpringApplication.run(MyApp.class, args);
}
}
According to https://spring.io/blog/2015/02/11/better-application-events-in-spring-framework-4-2 and https://solidsoft.wordpress.com/2015/09/29/annotation-driven-event-listeners-in-spring-4-2/ it must be working, but it still not print my "WON'T WORK :-(" String :(
Any idea?
Thanks!
The two events that you are listening for are both published very early in an application's lifecycle.
ApplicationStartedEvent is sent "as early as conceivably possible as soon as a SpringApplication has been started - before the Environment or ApplicationContext is available, but after the ApplicationListeners have been registered".
ApplicationEnvironmentPreparedEvent is published "when a SpringApplication is starting up and the Environment is first available for inspection and modification."
In both cases, the event is published too early for a listener to be found via annotations and the application context. As you've observed you can use spring.factories to register your listener. Alternatively, you can use the setter method on SpringApplication.
This is an old question, but anyway, to anyone having the same issue...
I've just wasted a few hours with the exact same problem, I've searched the internet and did countless tests to no avail, I've tried annotating with #EventListener(ApplicationReadyEvent.class) and #PostConstruct, nothing worked.
My philosophy is that when something simple doesn't work and the internet doesn't help, it means you messed up something. So I started reviewing my code and I see you've made the same mistake as me.
You've copied the example from somewhere else or changed the original configuration class from MyApp to Application, and you've forgotten to change the line:
SpringApplication.run(MyApp.class, args);
to
SpringApplication.run(Application.class, args);
The first line will do nothing, unless MyApp also has #SpringBootApplication or other configuration annotations, the second one will find the #SpringBootApplication annotation and will properly start the Spring Boot application.
By the way, both #EventListener(ApplicationReadyEvent.class) and #PostConstruct work just fine, tested on Java 17.
Here is my abstract class which starts Jersey with given Spring context:
public abstract class AbstractJerseyTest extends JerseyTest {
public void setUp() throws Exception {
super.setUp();
}
#AfterClass
public void destroy() throws Exception {
tearDown();
}
#Override
protected URI getBaseUri() {
return URI.create("http://localhost:9993");
}
#Override
protected Application configure() {
RestApplication application = new RestApplication();
Map<String, Object> properties = new HashMap<String, Object>();
properties.put(ServerProperties.BV_SEND_ERROR_IN_RESPONSE, true);
properties.put("contextConfigLocation", "classpath:spring-context-test.xml");
application.setProperties(properties);
application.register(this);
return application;
}
}
So, the problem is that I need to access Spring bean from my test to populate database with some data.
Jersey version is 2.6
Also I found a similar question here
But it's related to Jersey 1.x so it doesn't work for Jersey 2.x
Could anyone point me in the right direction?
Solution was really simple.
I added:
#Autowired
private Repository repository;
to the AbstractJerseyTest and this field was automatically autowired during test startup. I don't know details about how it works, but it seems that when I register instance of the test in REST application
application.register(this);
it automatically autowires all beans in the test.
Normally in your case, I'd just say work with mocks, but there are cases where you may need to expose the services in the test class.
To do this without any "ugly hacks", you will need to get a handle on the ServiceLocator (which is analogous to Spring's ApplicationContext). When the Jersey app boots up, all the Spring services from the ApplicationContext are put into the ServiceLocator through HK2's Spring bridge.
The problem is JerseyTest does not expose the ServiceLocator in any way. The only way I can think of to get a hold of it, is to create your own TestContainerFactory, and create the ApplicationHandler, which exposes the ServiceLocator.
Trying to implement your own TestContainerFactory is not a walk in the park, if you don't know what you're doing. The easiest thing to do is just look at the source code for Jersey's InMemoryTestContainerFactory. If you look at the constructor for the inner class InMemoryTestContainer, you will see it creating the ApplicationHandler. This is how you can expose the ServiceLocator, through the appHandler.getServiceLocator().
So if you copied that class, and exposed the ServiceLocator, you could create your JerseyTest extension, and call the ServiceLocator.inject(Object) method to inject the test class.
public abstract class AbstractServiceLocatorAwareJerseyTest extends JerseyTest {
private final ServiceLocatorAwareInMemoryTestContainerFactory factory
= new ServiceLocatorAwareInMemoryTestContainerFactory();
private ServiceLocator locator;
#Override
public TestContainerFactory getTestContainerFactory() {
return factory;
}
#Before
#Override
public void setUp() throws Exception {
super.setUp();
this.locator = factory.getServiceLocator();
if (injectTestClass()) {
this.locator.inject(this);
}
}
public boolean injectTestClass() {
return true;
}
public ServiceLocator getServiceLocator() {
return locator;
}
}
And if for any reason you needed it, the ServiceLocator also has the ApplicationContext, which you could also expose to your test class if needed.
I put together a GitHub project, with a complete implementation, with tests if you want to take a look at it.
UPDATE
Though the OP's answer to this question works, I believe the fact that it works, is a bug. I originally deleted this answer, after the OP posted their answer, but after some testing, I believe that solution is a bug, so I've undeleted this post for anyone who doesn't like the warning1 you get when you use that solution
1. "WARNING: A provider SimpleTest registered in SERVER runtime does not implement any provider interfaces applicable in the SERVER runtime. Due to constraint configuration problems the provider SimpleTest will be ignored."
These jars are both new released and have the latest solutions for Java EE applications. But I have a problem on specifiying hibernate listeners in hibernate.cfg.xml.
Before spring 3.1.0, LocalSessionFactroyBean was holding an attribute that keeps eventlisteners. But with 3.1.0.release there is no eventlisteners map. Now I fail keeping the track of modal objects on saveorupdate, postload etc. because they are not configured by Spring. Do you have an idea to solve this issue?
I had the same frustrating problem. Hibernate 4 appears to have fundamentally changed the way you register for events and the Spring group has not yet caught up. Here's my annotation-based solution using an init method to register a listener:
#Component
public class HibernateEventWiring {
#Autowired
private SessionFactory sessionFactory;
#Autowired
private SomeHibernateListener listener;
#PostConstruct
public void registerListeners() {
EventListenerRegistry registry = ((SessionFactoryImpl) sessionFactory).getServiceRegistry().getService(
EventListenerRegistry.class);
registry.getEventListenerGroup(EventType.POST_COMMIT_INSERT).appendListener(listener);
registry.getEventListenerGroup(EventType.POST_COMMIT_UPDATE).appendListener(listener);
}
}
An interceptor would be another fine approach, but support for interceptors was mistakenly dropped: https://jira.springsource.org/browse/SPR-8940
The new approach is to use an Integrator to register the event listeners. Hibernate will now use service discovery for registering event listeners and here is how I got it to work using a maven archetype-webapp
create a file under META-INF/services (which should reside under your resources directory) called org.hibernate.integrator.spi.Integrator with all classes that implement the hibernate spi Interface, one line each. Short example below:
...
META-INF/services/org.hibernate.integrator.spi.Integrator
com.example.CustomIntegrator
com.example.CustomIntegrator
package com.example;
import ...;
public void CustomIntegrator implements Integrator {
static final Logger logger = LoggerFactory.getLogger(CustomIntegrator.class);
#Override
public void integrate(Configuration configuration, SessionFactoryImplementor implementor, SessionFactoryServiceRegistry registry) {
final EventListenerRegistry eventRegistry = registry.getService(EventListenerRegistry.class);
logger.info("Registering event listeners");
// you can add duplication strategory for duplicate registrations
...
// prepend to register before or append to register after
// this example will register a persist event listener
eventRegistry.prependListeners(EventType.PERSIST, myListener);
...
}
...
}
com.example.MyListener
package com.example;
import ...
public class MyListener implements PersistEventListener {
static final Logger logger = LoggerFactory.getLogger(MyListener.class);
public void onPersist(PersistEvent event) throws HibernateException {
logger.debug("Entering MyListener");
if(event.getObject() instanceof MyPersistableEntity) {
MyPersistableEntity entity = (MyPersistableEntity) event.getObject();
// do something with entity
...
}
}
...
}
Any entity that needs to have this event registered to it must implement MyPersistableEntity (not shown here)
You might wanna check the Hibernate Ticket [1]: https://hibernate.onjira.com/browse/HHH-6945
The migration guide says:
hibernate.cfg.xml no longer supported as means of specifying
listeners. New approach invloves using an
org.hibernate.integrator.spi.Integrator which works based on "service
discovery".
And you can get the complete instructions # http://in.relation.to/Bloggers/EventListenerRegistration
The links in the ticket have some issue, use the following:
https://github.com/hibernate/hibernate-orm/blob/master/hibernate-envers/src/main/java/org/hibernate/envers/event/EnversIntegrator.java
https://github.com/hibernate/hibernate-orm/blob/master/hibernate-envers/src/main/resources/META-INF/services/org.hibernate.integrator.spi.Integrator
Hope this helps someone looking answers for this problem.
When appending custom Event listeners via the EventListenerRegistry class as mentioned in above responses one needs to make sure the default event listeners are removed. Otherwise if in your application there is onetomany mapping then "Hibernate exception: shared references to the collection: xyz" exception will be thrown