why is my custom annotation on interface not available through reflection - java

I am using Spring Data to access a Mongo database in a Spring Boot application. I'm doing this by extending MongoRepository.
#CrudPublishingRepository
public interface ProfileRepository extends MongoRepository<Profile, String> {
Optional<Profile> findByUserName(String userName);
#Query("{'products.contracts.contractId': ?0}")
List<Profile> findByContractId(String contractId);
}
For some of my repositories (including the ProfileRepository above) I need to perform some actions every time a save or delete action is performed, which is why I created the #CrudPublishingRepository annotation.
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
#Documented
public #interface CrudPublishingRepository {}
I have used Spring AOP to intercept all save and delete methods on Spring's CrudRepository, with the following advices:
#AfterReturning("execution(public * delete(..)) && this(org.springframework.data.repository.CrudRepository)")
public void onDeleteExecuted(JoinPoint pjp) {
onDelete(pjp);
}
#AfterReturning("execution(public * deleteById(..)) && this(org.springframework.data.repository.CrudRepository)")
public void onDeleteByIdExecuted(JoinPoint pjp) {
onDeleteById(pjp);
}
#AfterReturning("execution(public * deleteAll(..)) && this(org.springframework.data.repository.CrudRepository)")
public void onDeleteAllExecuted(JoinPoint pjp) {
onDeleteAll(pjp);
}
#Around(value = "execution(public * save(..)) && this(org.springframework.data.repository.CrudRepository)")
public Object onSaveExecuted(ProceedingJoinPoint pjp) throws Throwable {
return onSave(pjp);
}
the onSave method looks like this:
private Object onSave(ProceedingJoinPoint pjp) throws Throwable {
System.out.println(getTopicName(pjp));
try {
Object result = pjp.proceed();
return result;
} catch (Throwable t) {
throw t;
}
}
And at last, the getTopicName(JoinPoint pjp) function, which is where the problem is:
private Optional<String> getTopicName(JoinPoint pjp) {
Class clazz = pjp.getTarget().getClass();
while (clazz != null) {
for (Class i : pjp.getTarget().getClass().getInterfaces()) {
if (i.getAnnotation(CrudPublishingRepository.class) != null) {
return Optional.of("found it!");
}
}
clazz = clazz.getSuperclass();
}
return Optional.empty();
}
The implementation obviously isn't finished yet, but I would expect it to return Optional.of("found it!"), but it doesn't.
When I debug, I can see that ProfileRepository is one of the interfaces, as expected, but getAnnotations() returns an empty array.
Can anyone offer me a solution or an explanation as to why this is not working?

Related

How to get parameter of custom annotation by aspect?

In my aspect method, i need get value of name (param of custom annotation) name = "unit test"
Method call by user:
#Service
#RequiredArgsConstructor
#Slf4j
public class Task {
#CronLogger(name = "unit test")
public void testCronLogger(String param) {
log.info("testCronLogger ...");
}
}
custom annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface CronLogger {
public String name() default "";
}
Aspect method:
#Aspect
#Component
#EnableAspectJAutoProxy
public class CronLoggerAspect {
private static final Logger log = LoggerFactory.getLogger(CronLoggerAspect.class);
#Around("#annotation(CronLogger)")
public Object trace(ProceedingJoinPoint joinPoint) throws Throwable {
Object[] tab = joinPoint.getArgs();
for (Object object : tab) {
log.debug("CronLogger: {}", object);
}
return joinPoint.proceed();
}
}
Console:
CronLogger: test
testCronLogger ...
How about this (untested, I simply modified your code)?
#Around("#annotation(cronLogger)")
public Object trace(ProceedingJoinPoint joinPoint, CronLogger cronLogger) throws Throwable {
log.debug("CronLogger: {}", cronLogger.name());
return joinPoint.proceed();
}
Please be careful with upper- and lower-case characters. One is an annotation class name, the other a method parameter name.
need get Method and get Annotation of this method.
#Around("#annotation(CronLogger)")
public Object trace(ProceedingJoinPoint joinPoint) throws Throwable {
String name = MethodSignature.class.cast(joinPoint.getSignature()).getMethod().getAnnotation(CronLogger.class)
.name();
log.debug("CronLogger: {}", name);
return joinPoint.proceed();
}

How to code spring custom annotation which works like ibatis Select annotation?

I am trying to make a custom annotation like ibatis #Select.
Anyway, In conclusion, the goal is
append some data into the parameter which the method has custom annotation
First take a look end point - ArtistNodeRepository.java
#Repository
public interface ArtistNodeRepository {
#CreateNode(tid = "artist")
public Node create(Map data) throws Exception;
}
What want to do with CreateNode annotation is put data.put("type", "artist") into parameter Map.
Here is the Annotation - CreateNode.java
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#Documented
public #interface CreateNode {
String[] values() default "";
String tid();
}
To controller annotation, I prepared this BeanPostProcessor - NodeAnnotationProcessor.java
#Component
public class NodeAnnotationProcessor implements BeanPostProcessor {
private ConfigurableListableBeanFactory configurableListableBeanFactory;
#Autowired
public NodeAnnotationProcessor(ConfigurableListableBeanFactory configurableListableBeanFactory) {
super();
this.configurableListableBeanFactory = configurableListableBeanFactory;
}
#Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
this.scanNodeAnnotation(bean, beanName);
return bean;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// this.scanNodeAnnotation(bean, beanName);
return bean;
}
protected void scanNodeAnnotation(Object bean, String beanName){
this.configureMethodAction(bean);
}
private void configureMethodAction(Object bean){
Class<?> managedBeanClass = bean.getClass();
ReflectionUtils.MethodCallback methodCallback = new NodeMethodCallback(configurableListableBeanFactory, bean);
ReflectionUtils.doWithMethods(managedBeanClass, methodCallback);
}
}
I am not clear where to put MethodCallback to postProcessBeforeInitialization or postProcessAfterInitialization. In my thought, it would be in after since I am trying to manipulate parameter of the method
Finally, this is the MethodCallback - NodeMethodCallback.java
public class NodeMethodCallback implements ReflectionUtils.MethodCallback {
private Logger logger = LoggerFactory.getLogger(NodeMethodCallback.class);
private ConfigurableListableBeanFactory beanFactory;
private Object bean;
private static int AUTOWIRE_MODE = AutowireCapableBeanFactory.AUTOWIRE_BY_NAME;
public NodeMethodCallback(ConfigurableListableBeanFactory beanFactory, Object bean) {
this.beanFactory = beanFactory;
this.bean = bean;
}
#Override
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
logger.info("doWith method info :: " + String.valueOf(bean) + "." + bean.getClass().getName());
/*
What I expected is Printing ArtistNodeRepository Class with create Method
But It prints something like ...
SessionFlashMapManager
DefaultRequestToViewNameTranslator
...
*/
try {
logger.info("When I call you :: " + method.getName()); // I expect method which contains #CreateNode annotation, but it is not ...
Annotation[] methodAnnotations = method.getDeclaredAnnotations();
boolean isTarget = false;
String tid = "";
for(Annotation anno : methodAnnotations) {
logger.info("annotation Class :: " + anno.getClass().getName());
if(isTarget) break;
if(anno instanceof CreateNode) {
logger.info("CreateNode annotation found");
CreateNode createNode = method.getDeclaredAnnotation(CreateNode.class);
tid = createNode.tid();
isTarget = true;
}
}
if(!isTarget) return;
ReflectionUtils.makeAccessible(method);
/*
Do Somthing with Parameter ...
Do Somthing with Parameter ...
Do Somthing with Parameter ...
Do Somthing with Parameter ...
Do Somthing with Parameter ...
*/
} catch (Exception e ){
logger.error("ERROR", e);
}
}
}
The problem is ... in doWith I could not find ArtistNodeRepository instance.
What should I do with MethodCallback and BeanPostProcessor to achieve the goal?
Good sample codes would be nice as well as good answers.
i think you misunderstood the useage of ReflectionUtils.doWithMethods. it means iterate the class method if match callback. rather than when invoke method callback.
public static void doWithMethods(Class<?> clazz, MethodCallback mc, MethodFilter mf) {
// Keep backing up the inheritance hierarchy.
Method[] methods = getDeclaredMethods(clazz);
for (Method method : methods) {
if (mf != null && !mf.matches(method)) {
continue;
}
try {
mc.doWith(method);
}
catch (IllegalAccessException ex) {
throw new IllegalStateException("Not allowed to access method '" + method.getName() + "': " + ex);
}
}
if (clazz.getSuperclass() != null) {
doWithMethods(clazz.getSuperclass(), mc, mf);
}
else if (clazz.isInterface()) {
for (Class<?> superIfc : clazz.getInterfaces()) {
doWithMethods(superIfc, mc, mf);
}
}
}
i think you can use aspect. like this.
#Around("execution(public * org.springframework.data.jpa.repository.JpaRepository+.*(..))")
// #Around("#annotation(Repository)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
CreateNode createNode = method.getAnnotation(CreateNode.class);
if(createNode != null) {
Object[] args = joinPoint.getArgs();
// do your business
}
return joinPoint.proceed();
}
hope can help you

Spring AOP intercept wrong generic method

I am trying to intercept a method but I think that Spring AOP give me the wrong jointpoint. See the example below:
public interface IMyDummyComponent <K>{
public String getValue(final K key);
}
I have a class that implements this interfacce.
public class MyDummyComponent implements IMyDummyComponent<String>{
#Override
#Logging
public String getValue(String key) {
LOG.info("begin getValue()");
return "value";
}
}
Now here is my Spring AOP aspect:
#Aspect
public class LogginAspect {
private Logger LOG = LoggerFactory.getLogger(LogginAspect.class);
#Pointcut("#annotation(com.package.annotation.Logging)")
public void loggingAnnotationPointcut() {
}
#Around("loggingAnnotationPointcut()")
public Object methodsAnnotatedWithLogging(final ProceedingJoinPoint joinPoint) throws Throwable {
LOG.info("begin methodsAnnotatedWithLogging()");
Object result = null;
try {
LOG.debug("begin interpected method=" + joinPoint.getSignature());
result = joinPoint.proceed();
} finally {
LOG.debug("end interpected method=" + joinPoint.getSignature());
}
return result;
}
}
The problem is that the joinPoint.getSignature() return "public String getValue(Object key)" instead of "public String getValue(String key)". Is this a spring AOP bug ? I need to know the real signature of my joinpoint (getValue(String key)). Is there a form to get this using aspect? If you use reflection you can get this method by in my point of view this annotacion should get this. Thanks.

Junit test cases for Rest APIs connecting to DB

I have a JAVA rest service that interacts with the database , does some manipulation and returns the data.
I am trying to write test cases for these APIs.
I am trying to use the below link for reference to implement this.
http://www.developer.com/java/other/article.php/10936_3882311_2/Mockito-Java-Unit-Testing-with-Mock-Objects.htm
Here, calls made to the database are suppressed and the dto is mocked with made up values.
Is there an alternate method where we actually get to run the queries w/o talking to the db , (an in-memory db may be? )
Any code sample or reference would be of great help.
For a pure HashMap solution, something like this would work though then you would loose access to SQL query functionality (unless you mock the query too).
public class MockDatabase<T> {
protected Map<Serializable, T> fakeDatabase = Maps.newHashMap();
private final CustomRepository<T,Serializable> repository;
private Validator validator;
public void setValidator(Validator validator) {
this.validator = validator;
}
public static <T extends CustomRepository> T mock(Class<T> classToMock, Validator validator) {
T repository = Mockito.mock(classToMock);
MockDatabase md = new MockDatabase<T>(repository, validator);
return repository;
}
public <ID extends Serializable> MockDatabase(CustomRepository<T, ID> repository, Validator validator){
this.repository = (CustomRepository<T, Serializable>) repository;
this.validator = validator;
reset(repository);
doAnswer(new Answer<Object>() {
#Override
public Object answer(InvocationOnMock invocation) throws Throwable {
fakeDatabase.clear();
return null;
}
}).when(repository).deleteAll();
when(repository.save((T) anyObject())).thenAnswer(new Answer<T>() {
#Override
public T answer(InvocationOnMock invocation) throws Throwable {
return saveOrSaveAndFlush(invocation);
}
});
when(repository.getReference((ID)anyObject())).thenAnswer(new Answer<T>() {
#Override
public T answer(InvocationOnMock invocation) throws Throwable {
return fakeDatabase.get(invocation.getArguments()[0]);
}
});
when(repository.findOne((ID)anyObject())).thenAnswer(new Answer<T>() {
#Override
public T answer(InvocationOnMock invocation) throws Throwable {
return fakeDatabase.get(invocation.getArguments()[0]);
}
});
doAnswer(new Answer<T>() {
#Override
public T answer(InvocationOnMock invocation) throws Throwable {
return fakeDatabase.remove(ReflectionTestUtils.invokeGetterMethod(invocation.getArguments()[0], "getId"));
}
}).when(repository).delete((T)anyObject());
doAnswer(new Answer<ID>() {
#Override
public ID answer(InvocationOnMock invocation) throws Throwable {
fakeDatabase.remove(((ID)invocation.getArguments()[0]));
return null;
}
}).when(repository).delete((ID)anyObject());
when(repository.saveAndFlush((T) anyObject())).thenAnswer(new Answer<T>() {
#Override
public T answer(InvocationOnMock invocation) throws Throwable {
return saveOrSaveAndFlush(invocation);
}
});
when(repository.exists((ID)anyObject())).thenAnswer(new Answer<Boolean>() {
#Override
public Boolean answer(InvocationOnMock invocation) throws Throwable {
return fakeDatabase.containsKey(invocation.getArguments()[0]);
}
});
when(repository.merge(anyObject())).thenAnswer(new Answer<T>() {
#Override
public T answer(InvocationOnMock invocation) throws Throwable {
return (T) invocation.getArguments()[0];
}
});
when(repository.findAll()).thenAnswer(new Answer<List<T>>() {
#Override
public List<T> answer(InvocationOnMock invocation) throws Throwable {
return Lists.newLinkedList(fakeDatabase.values());
}
});
customMethods();
}
private T saveOrSaveAndFlush(InvocationOnMock invocation) throws NoSuchMethodException {
Object[] args = invocation.getArguments();
Serializable id = (Serializable) ReflectionTestUtils.getField(args[0], "id");
if (id == null) {
Class<?> returnType = args[0].getClass().getMethod("getId").getReturnType();
if (returnType.equals(Long.class)) {
id = (Long) new Random().nextLong();
} else if (returnType.equals(Integer.class)) {
id = (Integer) new Random().nextInt();
}
ReflectionTestUtils.setField(args[0], "id", id);
}
Set<ConstraintViolation<T>> validations = validator.validate((T)args[0]);
if (!validations.isEmpty()){
throw new IllegalStateException("Object failed validations (it would also fail on a db): "+validations);
}
for (Method method: args[0].getClass().getDeclaredMethods()){
if (method.isAnnotationPresent(Basic.class)){
Annotation a = method.getAnnotation(Basic.class);
if (!(boolean) AnnotationUtils.getValue(method.getAnnotation(Basic.class), "optional")){
if (ReflectionTestUtils.invokeGetterMethod(args[0], method.getName()) == null){
throw new IllegalStateException(args[0].getClass().getSimpleName()+"."+method.getName() + " returned null, but marked with #Basic(optional=false) - it would also fail on a db: "+validations);
}
}
}
}
fakeDatabase.put(id, (T) args[0]);
return (T) args[0];
}
public void customMethods() {
// override here if you want
}
}
If you had #Entity annotated POJOs, then with say hibernate library you can ask it to export to HSQLDB script and then use that. Eg you export via:
Configuration configuration = new Configuration();
try {
classes().forEach(cl -> {
configuration.addAnnotatedClass(cl);
});
configuration.setProperty("hibernate.dialect", HSQLCustomDialect.class.getName());
SchemaExport schemaExport = new SchemaExport(configuration);
schemaExport.setOutputFile("someFileName.sql");
schemaExport.setFormat(false);
schemaExport.setDelimiter(";");
schemaExport.execute(true, false, false, true);
and thereafter you would use spring to insert that SQL script for you:
#ActiveProfiles("test")
#SqlConfig(transactionMode = SqlConfig.TransactionMode.ISOLATED)
#SqlGroup({
#Sql(statements = "DROP SCHEMA PUBLIC CASCADE"),
#Sql(scripts = "classpath:yourGeneratedSQL.sql"),
})
public class DAOIntegrationTest {
HSQLDB is one in memory db i'm familiar with. The examples showed here against HSQLDB used with hibernate and JPA. http://uaihebert.com/tdd-with-hsqldb-jpa-and-hibernate/.
However i think it'll be useful to ask why you would prefer connecting to an in memory db than mocking the db in your situation?
It boils down to what scope of testing unit/integration you are trying to achieve.
What are you trying to test the manupilation logic in the rest layer? Where mocking is sufficient.
Are you trying to test how the rest handles the data access behavior, such as db error handling etc, than in memory might be slightly better.
Is the thing you are testing dependent on data setup/ testing data setup , in which case in memory db might be closer, since you can use the same/similar sql creation to test in inmemory db.

Java - Execute a class method with a specify annotation

I have a android application, but it is not relevant.
I have a class called "Front controller" which will receive some message
through it's constructor. The message, for brievity, could be an integer.
I want somewhere else to create a new controller which will execute
a method based on the integer defined above
public class OtherController {
#MessageId("100")
public void doSomething(){
//execute this code
}
#MessageId("101")
public void doSomethingElse(){
//code
}
}
The front controller could be something like this:
public class FrontController {
private int id;
public FrontController(int id){
this.id=id;
executeProperControllerMethodBasedOnId();
}
public void executeProperControllerMethodBasedOnId(){
//code here
}
public int getId(){
return id;
}
}
So, if the Front Controller will receive the integer 100, it
will execute the method annotated with #MessageId(100). The
front controller don't know exactly the class where this method
is.
The problem which I found is that I need to register somehow
each controller class. I Spring I had #Component or #Controller
for autoloading. After each controllers are register, I need to
call the properly annotated method.
How to achieve this task? In Spring MVC, I had this system
implemented, used to match the HTTP routes. How could I implement
this in a plain java project?
Any suggestions?
Thanks to Google Reflections (hope you can integrate this in your android project.)
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections-maven</artifactId>
<version>0.9.8</version>
</dependency>
For optimisation I've added the requirement to also annotate the class with MessageType annotation and the classes should be in the same package (org.conffusion in my example):
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface MessageType {
}
The OtherController looks like:
#MessageType
public class OtherController {
#MessageId(id=101)
public void method1()
{
System.out.println("executing method1");
}
#MessageId(id=102)
public void method2()
{
System.out.println("executing method2");
}
}
The implementation will look like:
public void executeProperControllerMethodBasedOnId() {
Set<Class<?>> classes = new org.reflections.Reflections("org.conffusion")
.getTypesAnnotatedWith(MessageType.class);
System.out.println("found classes " + classes.size());
for (Class<?> c : classes) {
for (Method m : c.getMethods()) {
try {
if (m.isAnnotationPresent(MessageId.class)) {
MessageId mid = m.getAnnotation(MessageId.class);
Object o = c.newInstance();
if (mid.id() == id)
m.invoke(o);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
Maybe you can optimise and build a static hashmap containing already scanned message ids.
You need to implement some of the work by yourself using reflection, I would recommend to prepare message handlers on initial phase in regards to performance. Also you possibly want to think about Singleton/Per Request controllers. Some of the ways to implement the solution:
interface MessageProcessor {
void execute() throws Exception;
}
/* Holds single instance and method to invoke */
class SingletonProcessor implements MessageProcessor {
private final Object instance;
private final Method method;
SingletonProcessor(Object instance, Method method) {
this.instance = instance;
this.method = method;
}
public void execute() throws Exception {
method.invoke(instance);
}
}
/* Create instance and invoke the method on execute */
class PerRequestProcessor implements MessageProcessor {
private final Class clazz;
private final Method method;
PerRequestProcessor(Class clazz, Method method) {
this.clazz = clazz;
this.method = method;
}
public void execute() throws Exception {
Object instance = clazz.newInstance();
method.invoke(instance);
}
}
/* Dummy controllers */
class PerRequestController {
#MessageId(1)
public void handleMessage1(){System.out.println(this + " - Message1");}
}
class SingletonController {
#MessageId(2)
public void handleMessage2(){System.out.println(this + " - Message2");}
}
class FrontController {
private static final Map<Integer, MessageProcessor> processors = new HashMap<Integer, MessageProcessor>();
static {
try {
// register your controllers
// also you can scan for annotated controllers as suggested by Conffusion
registerPerRequestController(PerRequestController.class);
registerSingletonController(SingletonController.class);
} catch (Exception e) {
throw new ExceptionInInitializerError();
}
}
private static void registerPerRequestController(Class aClass) {
for (Method m : aClass.getMethods()) {
if (m.isAnnotationPresent(MessageId.class)) {
MessageId mid = m.getAnnotation(MessageId.class);
processors.put(mid.value(), new PerRequestProcessor(aClass, m));
}
}
}
private static void registerSingletonController(Class aClass) throws Exception {
for (Method m : aClass.getMethods()) {
if (m.isAnnotationPresent(MessageId.class)) {
MessageId mid = m.getAnnotation(MessageId.class);
Object instance = aClass.newInstance();
processors.put(mid.value(), new SingletonProcessor(instance, m));
}
}
}
/* To process the message you just need to look up processor and execute */
public void processMessage(int id) throws Exception {
if (processors.containsKey(id)) {
processors.get(id).execute();
} else {
System.err.print("Processor not found for message " + id);
}
}
}

Categories