SonarQube: Create custom rules to get custom annotations in a class - java

I'm trying to create a custom SonarQube rule which will detect the usage of a specific custom Java Annotation. Here is the code I found which prints a list of all annotations used in a class.
public class SampleAnnotationCheck extends IssuableSubscriptionVisitor {
#Override
public List<Tree.Kind> nodesToVisit() {
return ImmutableList.of(Tree.Kind.METHOD);
}
#Override
public void visitNode(Tree tree) {
MethodTree methodTree = (MethodTree) tree;
for (AnnotationInstance ai : ((JavaSymbol.MethodJavaSymbol) methodTree.symbol()).metadata().annotations()) {
System.out.println(ai.symbol().name());
}
}
}
Sample Java File:
#GET
#Path(values)
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
method1(...) {...}
#CustomAnnotation(values)
#POST
#Path(values)
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
method2(...) {...}
#PATCH
#Path(values)
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
method3(...) {...}
Expected Output:
GET
Path
Consumes
Produces
CustomAnnotation
POST
Path
Consumes
Produces
PATCH
Path
Consumes
Produces
Actual Output:
GET
Path
Consumes
Produces
!unknownSymbol!
POST
Path
Consumes
Produces
!unknownSymbol!
Path
Consumes
Produces
I'm getting !unknownSymbol! instead of the Custom Annotations' actual names. One of the custom annotations is io.swagger.jaxrs.PATCH.
The other annotation is defined inside a separate package and imported by the sample class.
Do we have to register these custom annotations somewhere for the API to detect?
Please suggest what changes should be made so that we can detect and print the actual Custom Annotation's name.
Thanks in advance!

I assume you are following this guide and running check within your tests https://docs.sonarqube.org/display/PLUG/Writing+Custom+Java+Rules+101 . To provide dependencies to your tests you can either
put jars in target/test-jars directory (see how to do it with maven dependency plugin here https://github.com/SonarSource/sonar-custom-rules-examples/blob/master/java-custom-rules/pom.xml#L147)
in your test provide custom classpath using JavaCheckVerifier.verify(filename, check, classpath)

Related

where should I put RestController in spring boot project

I used idea IDE to developed a Java springboot project follow a tutorial.
However, in the tutorial, there's a function shown below. I do not know where to put this function in me spring boot project.
The function:
package com.helloworld.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created by fangxiao on 2017/3/24.
*/
#RestController
public class Controller {
#RequestMapping("/helloworld")
public String helloWorld() {
return "helloworld";
}
}
where should I put this function as for the below project structure?
project structure
Following your example, the Controller Class have a organization like this picture:
I also created another packages that are frequentelly used in a spring-boot project
Since your package is :
package com.helloworld.controller;
You can create folder structure inside java folder com -> helloworld -> controller and keep the code (Controller.java class) inside.
Create a package under src/main/java like com.abc.rest and create RestController class
:Here is the sample code and to access this api use url like : localhost:8080/test/headers
#RestController
#RequestMapping(value = "/test", produces = MediaType.APPLICATION_JSON_VALUE)
public class Rest {
#GetMapping("/headers")
public String listAllHeaders(#RequestHeader Map<String, String> headers) {
headers.forEach((key, value) -> {
System.out.println(String.format("Header '%s' = %s", key, value));
});
return "success";
}
}
For learning purpose is good to do it manually, but if you want to skip it, you can create the class shown above in the src/main/java and IntelliJ will dectect and offer to relocate it to the proper package.
Create your package com.project.controller under
D:\Bill\java\test\src\main\java
directory

WARNING: The (sub)resource method contains empty path annotation

I have configured rest path like "/v1/" and the endpoint configured in servlet like '/test/'.
Now I removed the "/v1" from the java class "Test".
org.glassfish.jersey.internal.Errors logErrors
WARNING: The following warnings have been detected: WARNING: The (sub)resource method test in com.abc.services.Test contains empty path annotation.
I got the above warning after make this change. How to handle this warning?
And I want this "/v1" removing changes for across 10 rest paths. So anyone help me to run without warnings?
The warning means you have a resource method annotated with #Path("/") or #Path(""). For instance
#Path("test")
public class Test {
#GET
#Path("/")
public String test(){}
}
Not sure why Jersey would give a warning, maybe just to make sure that's what you really want. The reason is that a resource method with #Path("/") is redundant, as it's already implied if you were just to do
#Path("test")
public class Test {
#GET
public String test(){}
}
without the #Path("/"). It works the same. So if you have these, remove them, and it should take away the warnings.

custom annotation on class and method

Is it possible that I put custom annotation on class and then it forceful the developer to put the annotation on method?
suppose I have three custom annotation
#Loggable
#MySQLLoggable
#CassandraLoggable
when I put #Loggble annotation on class , it forceful the developer to annotate its all methods either by #MySQLLoggable or #CassandraLoggable.
update me!
EDITED
#Loggable // Suppose I put this annotation on class
public class Service {
#MySQLLoggable //eclipse forceful the developer
//to put #MySQLLoggable or #CassandraLoggable on sayHello()
public String sayHello() {
return null;
}
}
use #interface
public #interface foo {
String str();
}
#foo(str = "str")
public class myClass{
}
edit
public #interface loggable {
#loggable1(method) #loggable2(method))
String foo();
}
Sorry if you want that IDE make developers use your custom annotations then you better read your IDE specification and possible customization. If you want that later on during execution your app will throw exceptions if methods are not annotated then you have to use reflection. Let's save you use Factory Pattern to create objects. So before create them you can check whether all methods are annotated and if not all then throw an exception. Again I believe you have to use reflection.. this is only one way!

Can I overcome colliding base #Path in Jax-RS?

I have a base collection of REST resources annotated to correspond to certain paths.
Psuedo code:
#Path("/collection")
class Stuff {
#Path("/{id}")
#GET
public String get(#PathParm("id") int id) {
return String.format("Item #%d". id);
}
}
Now, I need to add a sub resource to this collection without changing the class Stuff, but adding code like below doesn't work:
#Path("/collection/{id}")
class StuffPlugin {
#Path("/extra")
#GET
public String extra(#PathParm("id") int id) {
return String.format("Extra info about item #%s", id);
}
}
This used to work in RESTeasy 2.3 but now, upgrading to version 3.0.4 the latter seems to shadow the Stuff class when RESTeasy is looking for possible path matches and thus break the whole structure of my app.
How would this be accomplished in RESTeasy 3?
Thank you
PS
I am adding the provider classes programmatically like below and everything that doesn't have colliding base paths is working fine.
public class EntryPoint extends Application {
public EntryPoint() {}
#SuppressWarnings("serial")
#Override
public Set<Class<?>> getClasses() {
return new HashSet<Class<?>>() {
{
add(Stuff.class);
add(StuffPlugin.class);
}
}
}
}
RestEasy 3.x is based on JAX-RS 2.0 which is a new specification update in JAX-RS.
The developer who maintains the RestEasy has already written a blog post about weird path matching algorithm !
http://bill.burkecentral.com/2013/05/29/the-poor-jax-rs-request-dispatching-algorithm/
In your case it might have worked earlier as RestEasy was implementing the old spec which was not so strict about the implementation of the matching algorithm, which may not work now..
Better be you reflector the code and define the resources appropriately !

Conditional execution of a method using annotation

In java can I have conditional execution of a method using annotations?
I wish to have some system property set and based on that system property I wish to either execute or not execute a method (specifically ant script based JUnits) at runtime.
Please let me know if it's possible using the annotations.
I think that you can implement it in Java but I suggest you to take a look on Spring AOP - I believe that this is what you are looking for.
You can group tests by #Category and tell the running to include this category.
From http://alexruiz.developerblogs.com/?p=1711
public interface FastTests { /* category marker */ }
public interface SlowTests { /* category marker */ }
public class A {
#Category(SlowTests.class)
#Test public void a() {}
}
#Category(FastTests.class})
public class B {
#Test public void b() {}
}
#RunWith(Categories.class)
#IncludeCategory(SlowTests.class)
#ExcludeCategory(FastTests.class)
#SuiteClasses({ A.class, B.class })
public class SlowTestSuite {}
You can implement your own TestRunner or user AOP for this.
An annotation, in the Java computer programming language, is a form of syntactic metadata that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated.
Take a look at this
I'd write a simple custom test runner by extending BlockJUnit4ClassRunner. That runner would read a configuration from a system property or a configuration file to only run the defined tests. The simplest solution would be a blacklist to exclude selected methods, because the default behaviour of this (default!) runner is to run every test.
Then just say
#RunWith(MyTestRunner.class)
public void TestClass {
// ...
}
For the implementation, it could be sufficiant to just overwrite the getChildren() method:
#Overwrite
List<FrameworkMethod> getChildren() {
List<FrameworkMethod> fromParent = super.getChildren();
List<FrameworkMethod> filteredList = new ArrayList(filter(fromParent));
return filteredList;
}
where filter checks for every FrameworkMethod whether it should be executed or not according to the "blacklist", that has been created based on the property.

Categories