Spring Boot + Liquibase - custom LockService class - java

I'm trying to mplement custom LockService class like it said in this answer: https://stackoverflow.com/a/15567073/5182320
package liquibase.ext;
import liquibase.exception.DatabaseException;
import liquibase.lockservice.StandardLockService;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;
#Slf4j
#Configuration
public class TimeoutLockService extends StandardLockService {
#SneakyThrows
#Override
public void waitForLock() {
forceReleaseLock();
}
#Override
public int getPriority() {
return super.getPriority() + 1;
}
#Override
public void init() throws DatabaseException {
super.init();
log.info("Init called");
}
}
Placed the class in the package liquibase.ext
But when I'm running my application it's ignoring this class and still trying to acquire the lock.

I was trying to do something similar and I had a similar issue, where my changes were not pickup, even though it was in package liquibase.ext . I am using liquibase 4.3.5 and the following document helped me.
Starting with 4.0, we switched to the standard java.util.ServiceLoader system to find extension classes.
https://docs.liquibase.com/tools-integrations/extensions/extension-upgrade-guides/lb-4.0-upgrade-guide.html
I had to crate the liquibase.lockservice.LockService file in META-INF/services with my implementation and solve the issue.

Related

How to activate #Aspect in AEM?

I working with java code in AEM.
We use maven and in pom.xml is exist all needed dependencies and aspectj-maven-plugin
I prepared aspect with pointcut:
package org.xxx.aop.aspects;
import javax.jcr.Node;
import org.apache.sling.api.SlingHttpServletRequest;
import org.aspectj.lang.*;
import org.aspectj.lang.annotation.*;
import org.slf4j.*;
#Aspect
public class NodeAspect {
private final Logger LOG = LoggerFactory.getLogger(NodeAspect.class);
#Pointcut("execution(public * javax.jcr.Node.*(..)")
public void jcrNodeAccess() {}
#Around("jcrNodeAccess()")
public Object jcrNodeMethodCall(ProceedingJoinPoint thisJoinPoint) throws Throwable {
LOG.info("--- LOG NodeAspect ---");
Object result = thisJoinPoint.proceed();
// some code
// ..
return result;
}
But when I installed my package in AEM, my aspect does not work.
Maybe my aspect I will need to activate?
Maybe this problem related with behavior of OSGI (felix) in AEM?
Please provide to me ideas how to fix it

No qualifying bean of type 'concert.PerformanceImp' available

I am still a beginner in Spring Framework so I tried to code a program about "introduction" in Spring AOP but I am facing an error while compiling. Please find below the classes in the package concert:
PerformanceImp.java
package concert;
import org.springframework.stereotype.Component;
#Component
public class PerformanceImp implements Performance {
public void perform() {
System.out.println("This is the performance function");
}
}
Performance.java
package concert;
public interface Performance {
public void perform();
}
Encoreable.java
package concert;
public interface Encoreable {
void performEncore();
}
DefaultEncoreable.java
package concert;
import org.springframework.stereotype.Component;
#Component
public class DefaultEncoreable implements Encoreable {
public void performEncore() {
System.out.println("This is the performEncore function");
}
}
EncoreableIntroducer.java
package concert;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclareParents;
import org.springframework.stereotype.Component;
#Component
#Aspect
public class EncoreableIntroducer {
#DeclareParents(value="concert.Performance+",
defaultImpl=DefaultEncoreable.class)
public static Encoreable encoreable;
}
ConcertConfig.java
package concert;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
import org.springframework.stereotype.Component;
#Configuration
#EnableAspectJAutoProxy
#ComponentScan("concert")
public class ConcertConfig {
}
And the main class:
Main.java
package concert;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(ConcertConfig.class);
PerformanceImp pi = (PerformanceImp) context.getBean(PerformanceImp.class);
((Encoreable) pi).performEncore();
pi.perform();
}
}
I am getting the error:
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'concert.PerformanceImp' available
Any help please ?
You cannot access the implementation (PerformanceImp) by default, because you enabled AOP, which sets to target interfaces instead of implementation. If you would remove EnableAspectJAutoProxy, you would see the code would work fine.
To understand a bit more about how AOP targeting works, take a look at this Spring Documentation
Spring AOP can also use CGLIB proxies. This is necessary to proxy
classes rather than interfaces. CGLIB is used by default if a business
object does not implement an interface. As it is good practice to
program to interfaces rather than classes; business classes normally
will implement one or more business interfaces. It is possible to
force the use of CGLIB, in those (hopefully rare) cases where you need
to advise a method that is not declared on an interface, or where you
need to pass a proxied object to a method as a concrete type.
So you have two options:
Take the interface when trying to get the bean from the ApplicationContext.
Enable AOP to target concrete classes instead.
To do this point #2, modify your annotation as follows:
#EnableAspectJAutoProxy(proxyTargetClass = true)
Try:
Performance pi = context.getBean("performanceImp", Performance.class);
instead of:
PerformanceImp pi = (PerformanceImp) context.getBean(PerformanceImp.class);

Proxy for abstract class without changing the usage

I have an abstract class (database mapping) implementing an interface where default implementations are injected at runtime (this is part of another library and cannot be changed).
I want to override one of the default implementation via a proxy (as that seems like the way to override this).
public abstract class Table1 implements Storable<Table1>
{
#Sequence("ID_SEQUENCE")
#Alias("ID")
public abstract String getID();
public abstract void setID(String ID);
#Alias("NAME")
public abstract String getAvailabilityZone();
public abstract void setAvailabilityZone(String value);
}
public interface Storable<S extends Storable<S>> {
//a bunch of method definition.
boolean tryLoad() throws Exception;
}
Let's say I want to override tryLoad() method to do my own things instead of what the generated code provides. Given the nature of the library, it is not something I can achieve by simple #Override.
The simple way this is currently used is as following:
public void method() {
Table1 t = Repository.storageFor(Table1.class).prepare();
t.setName( "temp" );
if (!t.tryLoad())
t.tryInsert();
}
I want to proxy tryLoad() without making changes in all the methods across the whole codebase - that would be to get proxied instance instead of actual one and perform the operation on that.
Is there any recommended way to achieve this?
Thanks!
I woke up last night and felt bored, so despite your lack of feedback I created a little Carbonado showcase project and shared it on GitHub. I made three commits:
Initial commit with Maven project already prepared for AspectJ and a JUnit test for me to find out how Carbonado actually works, because I had never used it before.
Add failing unit test for behaviour of tryLoad() expected to be provided by aspect.
Add aspect to make unit test pass. Aspect hooks into tryLoad() and auto-creates non-existent record. I do not know if I guessed right what you actually wanted to achieve, but if it was a different thing, just change the aspect implementation.
Sample code
Carbonado storable:
package de.scrum_master.app;
import com.amazon.carbonado.Nullable;
import com.amazon.carbonado.PrimaryKey;
import com.amazon.carbonado.Storable;
#PrimaryKey("ID")
public interface StoredMessage extends Storable<StoredMessage> {
long getID();
void setID(long id);
#Nullable String getMessage();
void setMessage(String message);
}
Aspect:
package de.scrum_master.aspect;
import com.amazon.carbonado.Storable;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
#Aspect
public class CarbonadoAspect {
#Around("call(boolean tryLoad()) && target(storable)")
public boolean tryInsertIfNotFound(ProceedingJoinPoint thisJoinPoint, Storable storable) throws Throwable {
System.out.println(thisJoinPoint);
if ((boolean) thisJoinPoint.proceed())
return true;
System.out.println("Not found: " + storable + " -> inserting");
return storable.tryInsert();
}
}
JUnit test:
package de.scrum_master.app;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.amazon.carbonado.FetchException;
import com.amazon.carbonado.PersistException;
import com.amazon.carbonado.Repository;
import com.amazon.carbonado.RepositoryException;
import com.amazon.carbonado.Storage;
import com.amazon.carbonado.SupportException;
import com.amazon.carbonado.repo.map.MapRepositoryBuilder;
import de.scrum_master.app.StoredMessage;
public class CarbonadoTest {
private Repository repo;
private Storage<StoredMessage> storage;
StoredMessage message;
#Before
public void setUp() throws Exception {
repo = MapRepositoryBuilder.newRepository();
storage = repo.storageFor(StoredMessage.class);
message = storage.prepare();
}
#After
public void tearDown() throws Exception {
repo.close();
repo = null;
storage = null;
message = null;
}
// (...)
#Test
public void aspectCreatesNonExistentRecord() throws SupportException, RepositoryException {
message.setID(1);
// Without the aspect this would be false
assertTrue(message.tryLoad());
assertEquals(message.getID(), 1);
assertEquals(message.getMessage(), null);
}
}
Enjoy!

Custom Error Page in PlayFramework2.4.x

I did these and still can't see my custom page, please help,
Added MyGlobal.java extends GlobalSettings
Added onHandlerNotFound to override:
#Override
public F.Promise<Result> onHandlerNotFound(RequestHeader request) {
Logger.error("onHandlerNotFound!");
return Promise.<Result> pure(Results.internalServerError(views.html.page404.render()));
}
Added application.global = "MyGlobal" in application.conf
But nothing happends, play2.4.6 still runs out the default page from devNotFound.scala.html in playframework source folder of framework/play/src/main/scala/views/defaultpages/
Please help.
The way to do it in Play 2.4.x is to use an HttpErrorHandler as documented here:
https://www.playframework.com/documentation/2.4.x/JavaErrorHandling
You can just extends the DefaultHttpErrorHandler and add return your custom pages:
https://www.playframework.com/documentation/2.4.x/JavaErrorHandling#Extending-the-default-error-handler
https://www.playframework.com/documentation/2.4.x/api/java/play/http/DefaultHttpErrorHandler.html
Here is a full example, considering that you are trying to override the default 404 page:
package com.acme.controllers.handlers;
import play.*;
import play.api.OptionalSourceMapper;
import play.api.UsefulException;
import play.api.routing.Router;
import play.http.DefaultHttpErrorHandler;
import play.libs.F.*;
import play.mvc.Http.*;
import play.mvc.*;
import javax.inject.*;
public class ErrorHandler extends DefaultHttpErrorHandler {
#Inject
public ErrorHandler(Configuration configuration, Environment environment,
OptionalSourceMapper sourceMapper, Provider<Router> routes) {
super(configuration, environment, sourceMapper, routes);
}
#Override
protected Promise<Result> onNotFound(RequestHeader request, java.lang.String message) {
Logger.error("onHandlerNotFound!");
return Promise.pure(Results.internalServerError(views.html.page404.render()));
}
}
And then, you need to configure in your conf/application.conf like this:
play.http.errorHandler = "com.acme.controllers.handlers.ErrorHandler"
public class Global extends GlobalSettings {
#Override
public Promise<SimpleResult> onHandlerNotFound(RequestHeader request) {
return Promise.<SimpleResult>pure(internalServerError(views.html.page404.render()));
}
}
#Override
protected F.Promise<Result> onNotFound(RequestHeader request, String message) {
Logger.debug("onNotFound: " + message);
return Promise.<Result> pure(Results
.ok(views.html.admin.page404.render(request.method(), request.uri())));
}
Thanks for help, it really does work in dev env..
I already had the MyErrorHandler.java but not overriding the onNotFound function, because i was almost lost in the extending GlobalSettings's onHandlerNotFound.

Markers for advised methods in eclipse

In a project we use annotions to define aspects.
Unfortunately I can't get eclipse to show a marker next to the advised methods.
In another project we use XML to define the aspects and eclipse shows markers.
Best I post some code to clarify:
First a bean to be advised:
package aop.test;
import org.springframework.stereotype.Service;
#Service
public class Worker {
public void work() {}
}
Then the aspect:
package aop.test;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Service;
#Aspect
#Service
public class WorkerLogger {
#Before("execution(void aop.test.Worker.work())")
public void log() {
System.out.println("working...");
}
}
And finally a main method to prepare the ApplicationContext, get the bean and run the advised method:
package aop.test;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
public static void main(String[] args) {
AnnotationConfigApplicationContext context;
context = new AnnotationConfigApplicationContext();
context.register(AnnotationAwareAspectJAutoProxyCreator.class);
context.scan("aop.test");
context.refresh();
context.getBean(Worker.class).work();
}
}
I tried this in eclipse using the STS plugin and the STS itself. I never get a red arrow next to work() indicating it is advised.
What am I missing?
Have you installed the STS plugin on your Eclipse? It should be available on the Help > Eclipse Marketplace

Categories