I have an infrastructure based on EclipseLink + JPA Guice Persist
When I redeploy the application always I have caching problems with caching Entitys and I have to reboot the server (Oracle Weblogic 11g) .This problem is treated in a this post: https://bugs.eclipse.org/bugs/show_bug.cgi?id=326552 But, maybe is not a bug ¿?¿? ...
I managed to solve the problem as follows :
Originally I have centralized everything in a GuiceModule:
1.Create the module JPAPersist
2.Binding of a Initializer class thas invokes the persistenceService.start()
public class MyGuiceModule implements Module {
#Override
public void configure(final Binder binder) {
Properties props = _dbConnectionPropertiesForPool();
JpaPersistModule jpaModule = new JpaPersistModule(persistenceUnit);
jpaModule.properties(props);
binder.install(jpaModule);
binder.bind(JPAInitializer.class).asEagerSingleton();
}
public class JPAInitializer {
#Inject
public JPAInitializer(final PersistService service) {
service.start();
}
}
Everything works fine .... but as I said when redeploy remain cached instances
HOW DO I HAVE SOLVED?
I changed the method JPAInitializer
public static class JPAInitializer {
private static PersistService _persistenceService = null;
#Inject
public JPAInitializer(final PersistService service) {
_persistenceService = service;
_persistenceService.start();
}
public static void stop() {
_persistenceService.stop();
}
}
I created a method stop () that stops the service ..but WTF! I have been forced to save the the injected Persistence service in a static variable :((
From the guice / listener filter that is the entrypoint invoke the stop when the application is undeployed (onContextDestroyed)
public void contextDestroyed(ServletContextEvent servletContextEvent) {
JPAInitializer.stop();
}
Now, when i redeploy there is no cache issue or problem, and there is no need to restart the server
It works this way, but I do not know if it's all right to create a static instance PesistenceService., so i'm trying to find another way to invoke the stop.....
Any suggestion?
Found solution .
Create a inteface to handle Guice Persistence Service :
interface MyPersistenceServiceHandler {
public void start();
public void stop();
}
This will be used into the main DB Guice Module :
binder.bind(MyPersistenceServiceHandler .class)
.to(JPAPersistenceServiceControl.class)
.in(Singleton.class);
static class JPAPersistenceServiceControl
implements MyPersistenceServiceHandler {
private final PersistService _service;
#Inject
public JPAPersistenceServiceControl(final PersistService service) {
_service = service;
}
#Override
public void start() {
if (_service == null) throw new IllegalStateException("NO persistence service available!");
_service.start();
}
#Override
public void stop() {
if (_service == null) throw new IllegalStateException("NO persistence service available!");
_service.stop();
}
}
Get the instance in the RESTEndoint/Guice filter through Guice Injector.
jpaServiceHandler = _myGuiceInjector.getInstance(MyPersistenceServiceHandler .class);
Start the service on contextInitialized : jpaServiceHandler.start();
Stop the service on contextDeproyed : jpaServiceHandler.stop();
Related
I am trying to catch the event code at the time my springboot app is destroyed. I have the following bean:
#Configuration
public class DestroyListenerConfig {
#Bean
DemoListener demoListenerBean() {
return new DemoListener();
}
private static class DemoListener {
#EventListener
public void exitEvent(ExitCodeEvent event) {
System.out.println("Exit code: " + event.getExitCode());
}
}
}
The bean is registering properly, but when I kill the application, the exitEvent() method is not invoked ( the system out never displays, or when run in debug mode from IDE, it never enters the method).
Am I leaving something out? My impression was this is all that is needed. Thanks.
ExitCodeEvent is published from org.springframework.boot.SpringApplication#exit. So, you need to manually call SpringApplication.exit as below.
#Autowired
ApplicationContext applicationContext;
#GetMapping("/shutdown")
void shutdown() {
SpringApplication.exit(applicationContext, () -> 100);
}
If you want to listen to a bean destroy event then you can use #PreDestroy as following:
Note that if you have multiple beans created for this class, you will get multiple triggers.
//Put this into a #Component(or inside RestController, Controller, Service etc) class
#javax.annotation.PreDestroy
public void destroy() {
System.out.println("Triggered - #PreDestroy.");
}
If your app is a webapp and you want to listen to shutdown event (ideally the contextDestroyed event) you can register a MyServletContextListener to ServletListenerRegistrationBean:
#Bean
ServletListenerRegistrationBean<ServletContextListener> servletListener() {
ServletListenerRegistrationBean<ServletContextListener> srb = new ServletListenerRegistrationBean<>();
srb.setListener(new MyServletContextListener());
return srb;
}
class MyServletContextListener implements ServletContextListener {
#Override
public void contextDestroyed(ServletContextEvent event) {
System.out.println("Callback triggered - ContextListener.");
}
}
Ref:
https://github.com/spring-projects/spring-boot/blob/master/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/SpringApplication.java#L1332
I have a resource endpoint that injects a #PathParam into constructor, i.e., different instance per #PathParam value. It all works fine in Jetty. But now I'm trying to write unit tests using Jersey Test Framework, and it seems that the test framework only supports one registered endpoint per type.
So if I do something like this:
#Path("/users")
public class MyResource {
public MyResource(#PathParam("userId") int userId) {
}
#Path("{userId}")
public String get() {
}
}
public class MyTest extends JerseyTestNg.ContainerPerClassTest {
#Override
protected Application configure() {
return new ResourceConfig()
.register(new MyResource(1))
.register(new MyResource(2));
}
#Test
public void test2() {
target("/users/1").request().get();
}
#Test
public void test2() {
target("/users/2").request().get();
}
}
I see that both test1 and test2 are invoking the instance of MyResource(1). Is this expected? Is there a solution to invoke the correct instance?
You should register the resource as a class. Jersey will create it for you. And handle all the injections.
"The example I posted is dumbed down. In reality, my resource constructor has another injected object that I need to mock. So how would I specify a mocked object parameter for the constructor?"
You can do something like
#Mock
private Service service;
#Override
public ResourceConfig configure() {
MockitoAnnotations.initMocks(this);
return new ResourceConfig()
.register(MyResource.class)
.register(new AbstractBinder() {
#Override
protected configure() {
bind(service).to(Service.class);
}
});
}
#Test
public void test() {
when(service.getSomething()).thenReturn("Something");
// test
}
Assuming you are already using the built in HK2 DI, and have an #Inject annotation on the constructor of your resource class, this should work. In the AbstractBinder we are making the mock object injectable. So now Jersey can inject it into your resource.
See Also:
Jersey - How to mock service
How can I run some code inside a Spring Container after all beans has been loaded? I know I can use #PostConstruct for a single bean, but I would like to run that piece of code after all PostConstructs are called.
Is is possibile?
---UPDATE---
I tried to follow the ApplicationListener way, this is the implementation:
#Component
public class PostContructListener implements ApplicationListener<ContextRefreshedEvent> {
private static Logger log = LoggerFactory.getLogger(PostContructListener.class);
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
Collection<Initializable> inits= contextRefreshedEvent.getApplicationContext().getBeansOfType(Initializable.class).values();
for (Initializable initializable : inits) {
try{
log.debug("Initialization {} ",initializable.getClass().getSimpleName());
initializable.init();
}catch(Exception e){
log.error("Error initializing {} ",initializable.getClass().getSimpleName(),e);
}
}
}
}
Applying "Initializable" interface to all services I got what I needed, how every this way I broke all autowires, I cannot understand why but seems to be connected to the new "Initializable" interface:
java.lang.IllegalArgumentException: Can not set com.service.MyService field com.controller.RestMultiController.myService to com.sun.proxy.$Proxy41
I think you need this.
public class SpringListener implements ApplicationListener<ContextRefreshedEvent>{
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent ) {
// do things here
}
}
http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#context-functionality-events
I am using this Spring AOP code in my Spring Boot starter project in STS. After debugging this for some time I don't see any problem with the AspectJ syntax. The Maven dependencies are generated by STS for a AOP starter project. Is there a glaring omission in this code like an annotation ? The other problem could be with the AOP starter project or with the way I try to test the code in a #PostConstruct method.
I installed AJDT but it appears STS should show AspectJ markers in the IDE on its own. Right ? I don't see the markers. What other AspectJ debugging options are included in STS ? -Xlint is what I used in Eclipse/AJDT.
StateHandler.java
public class StateHandler<EVENTTYPE extends EventType> {
private State<EVENTTYPE> state;
private Event<EVENTTYPE> event;
public StateHandler(State<EVENTTYPE> state, Event<EVENTTYPE> event) {
this.state = state;
this.event = event;
}
public void handle( Event<EVENTTYPE> event ){
state = state.handle( event );
}
public State<EVENTTYPE> getState() {
return state;
}
}
DeviceLogger .java
#Aspect
#Component
public class DeviceLogger {
private static Logger logger = Logger.getLogger("Device");
#Around("execution(* com.devicemachine.StateHandler.*(..))")
public void log() {
logger.info( "Logger" );
}
}
LoggerApplication.java
#SpringBootApplication
public class LoggerApplication {
private static Logger logger = Logger.getLogger("Device");
public static void main(String[] args) {
SpringApplication.run(LoggerApplication.class, args);
}
#PostConstruct
public void log(){
DeviceState s = DeviceState.BLOCKED;
StateHandler<DeviceEvent> sh = new StateHandler<DeviceEvent>( s,
Event.block(DeviceEvent.BLOCKED, "AuditMessage") );
sh.handle(Event.block(DeviceEvent.UNBLOCKED, "AuditMessage"));
}
}
There are 3 obvious things wrong and 1 not so obvious wrong.
Your aspect is wrong and breaks proper method execution. When using an around aspect you must always return Object and use a ProceedingJoinPoint and call proceed() on that.
You are creating new instances of classes yourself, Spring, by default, uses proxy based AOP and will only proxy beans it knows.
In a #PostConstruct method it might be that proxies aren't created yet and that nothing is being intercepted
You need to use class based proxies for that to be enabled add spring.aop.proxy-target-class=true to your application.properties. By default JDK Dynamic Proxies are used which are interface based.
Fix Aspect
Your current aspect doesn't use a ProceedingJoinPoint and as such never does the actual method call. Next to that if you now would have a method that returns a value it would all of a sudden return null. As you aren't calling proceed on the ProceedingJoinPoint.
#Around("execution(* com.devicemachine.StateHandler.*(..))")
public Object log(ProceedingJoinPoint pjp) throws Throwable {
logger.info( "Logger" );
return pjp.proceed();
}
Create a bean to fix proxying and #PostConstruct
#SpringBootApplication
public class LoggerApplication {
private static Logger logger = Logger.getLogger("Device");
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(LoggerApplication.class, args);
StateHandler<DeviceEvent> sh = context.getBean(StateHandler<DeviceEvent>.class);
sh.handle(Event.block(DeviceEvent.UNBLOCKED, "AuditMessage"));
}
#Bean
public StateHandler<DeviceEvent> auditMessageStateHandler() {
return new StateHandler<DeviceEvent>(DeviceState.BLOCKED, Event.block(DeviceEvent.BLOCKED, "AuditMessage") );
}
}
Add property to enable class proxies
In your application.properties in src\main\resources add the following property with a value of true
spring.aop.proxy-target-class=true
i am working on OSGI bundle. I have implemented a BundleActivator and here is my code.
public class Activator implements BundleActivator {
private static final String CONFIG_PID = "ConfigApp";
private ServiceRegistration serviceReg;
public VfsDAO app;
#Override
public void start(BundleContext context) throws Exception {
System.out.println("Hello................ bundle started");
Hashtable<String, Object> properties = new Hashtable<String, Object>();
properties.put(Constants.SERVICE_PID, CONFIG_PID);
serviceReg = context.registerService(ManagedService.class.getName(), new ConfigUpdater() , properties);
}
#Override
public void stop(BundleContext context) throws Exception {
serviceReg.unregister();
}
/**
* Updates the configuration in the application. Of course your class can also directly implement ManagedService but this
* way you can work with pojos
*/
private final class ConfigUpdater implements ManagedService {
#SuppressWarnings("rawtypes")
#Override
public void updated(Dictionary config) throws ConfigurationException {
if (config == null) {
return;
}
if (app == null) {
app = new VfsDAO();
}
app.setAllowed((String)config.get("title"));
System.out.println("FROM................ bundle ACTIVATOR");
app.refresh();
}
}
}
Now if i make a object of VfsDAO() in any other class setAllowed is not called and so the String allowed is not initialized.How can i get the value when i make a new VfsDAO() object in any other class? or how can i call (String)config.get("title") in VfsDAO class to that when i make a new object of VfsDAO() in any other class string allowed is initiled.
For your VfsDAO to be notified by OSGi on a configuration change it has to be managed in some way by the OSGi runtime. The code you have above accomplishes this by acting as an OSGi managed service wrapper around your VfsDAO. If you're using new VfsDAO() in some places in your code and you aren't wrapping that lifecycle management as you are in the example above (or have some other lifecycle tie-in to OSGi) there will be no way for OSGi to know that your code created a VfsDAO.
If you need multiple VfsDAO instances perhaps configured with different allowed attributes, I'd recommend using the OSGi ManagedServiceFactory. Using the managed service factory you would instead create multiple VfsDAO instances each with their own PID and configuration by way of the ConfigAdmin.