java custom annotation building and running command line - java

I want to create an api using my own custom annotation that the hidden code should be triggered.
I have created my annotation and have created the processor as well.
But now the problem is, I don't know how to build it.
Let me explain in better way:
Its a console applicatioyn, I have to print a text once a method is called.
So, I have created an annotation #PrintText and also created a PrintTextProcessor.
But when I try to compile it, it doesn't show the required output.
I am annotating a method.
But it looks annotation doesn't work.
Am I missing anything.
Following is my code
Annotation Class:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface PrintText{
}
Annotation Processor Class:
#SupportedAnnotationTypes("com.example.PrintText")
public class PrintTextProcessor extends AbstractProcessor {
#Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(PrintText.class);
for(Element e : elements){
if(!e.getClass().equals(ParticularType.class)){
processingEnv.getMessager().printMessage(Kind.ERROR,
"#PrintText annotated fields must be of type ParticularType");
}
}
return true;
}
}
Now my main class comes:
public class Main{
#PrintMe
public void testMethod(){
System.out.println("In test method");
}
public static void main(String s[]){
new Main().testMethod();
}
}
Now when I try to compile this program and run it, it only prints the following text:
In test method
I used following commands
javac Main.java
java Main
Did I miss something?
I have been gone through several posts on the internet and found that there is apt tool.
But I don't know how to build and run it via command line.
I am using java6.
Thanks in advance.

Here is very good example https://github.com/provegard/aptdemo
but basically you have to create package META-INF/services
and put javax.annotation.processing.Processor file with classpath to your processor (in other words register your processor), then build your app let's say with mvn to get jar file (mvn package, see example) and then compile with javac (javac -cp /path/to/aptdemo-1.0-SNAPSHOT.jar SomeTestClass.java)

Related

Run methods annotated with #Category from IntelliJ IDEA

I have a test class with multiple test methods that I would like to group by some criteria. For this purpose, using JUnit's #Category annotation on a method level seemed like a fine solution:
public class TestClass {
#Test
#Category(AssignmentServiceCategory.class)
public void testMethod1() {}
#Test
#Category(OtherCategory.class)
public void testMethod2() {}
}
I would like to create different run configurations in IntelliJ IDEA for those separate categories so that only the test methods annotated with certain category are executed. My configuration looks like this:
However, when I run this, all of the tests from the class where the method is declared are run, instead of only the ones annotated with specified category. Is my configuration incorrect, or does IDEA allow only class-level #Category annotations?
Versions:
IntelliJ IDEA 2018.1 (181.4203.550)
JRE: 1.8.0_152-release-1136-b20 amd64
JUnit 4.12
UPDATED
I tried to reproduce the issue and could not.
Here's my Test Class
package com.mytests.category;
import org.junit.Test;
import org.junit.experimental.categories.Category;
public class MyTest {
#Test
#Category(PerformanceTests.class)
public void testMethod1() {
System.out.println("method1");
}
#Test
#Category(RegressionTests.class)
public void testMethod2() {
System.out.println("method2");
}
}
Make sure you have the necessary interfaces. In JUnit, you need to create marker interfaces to represent the categories:
package com.mytests.category;
public interface RegressionTests {}
and
package com.mytests.category;
public interface PerformanceTests {}
Then in IntelliJ, I ran the tests once and it creates a configuration for me automatically. Then I edit the configuration
The results were as expected:
Only testMethod1 was executed.
OLDER ANSWER
Or from IntelliJ's Doc (https://www.jetbrains.com/help/idea/run-debug-configuration-junit.html)
Category Select this option if you only want to run test classes and
test methods that are annotated either with the category given with
the #IncludeCategory annotation, or a subtype of this category. Fill in the following fields:
Category Specify the desired category. Type category name, or click
browseButton and select the desired category in the dialog that opens.
Or You could create a TestSuite and specify (in there) which categories the suite is to include.
Something like
package org.mytests.category;
import org.junit.experimental.categories.Categories;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;
#RunWith(Categories.class)
#Categories.IncludeCategory(RegressionTests.class)
#Suite.SuiteClasses({ClassA.class, ClassB.class, ClassC.class})
public class RegressionTestSuite {
}
I have (had) the same problem with Intellij 2020.1.2
There seems to be a bug related to what setting you select for search for tests.
If i choose in single module all the test are executed, even the ones annotated with other Categories.
If I choose any of the other options (in whole project, across module dependencies) it simply works as expected..

Bring Outside Java File Structure Into Java Program

I would like to be able to add a file with the java structure and extension into my program to an arraylist via outside the actual program directory/jar.
Ex,
Test.java, located at C:\Users\user\Desktop\Test.java (Outside the jar)
public class Test extends Object {
public Test() {}
public void someMethod() {}
}
MyProgram.java
import java.util.ArrayList;
public class MyProgram extends Object {
public MyProgram() {}
public void readIn() {
ArrayList<Object> list = new ArrayList<Object>();
list.add(Test.java);
}
}
Obviously a lot more will have to be done but hopefully you understand the point.
Read In Test.java -> Convert it somehow so it's added to the arraylist due to it's extension. So if the extension was Family instead of Object, the arraylist would be ArrayList instead and Test extends Family.
Edit
As stated by a comment, this is an approach to a plugin mechanism.
The answer to this question stems from Seelenvirtuose's suggestion along with crick_007. To access classes outside the class path, simply create a ClassLoader and load the class in.
You must also use an interface to interact between the two classes, also knowing what methods are provided. Lastly, packaging must also be the same or else you'll get errors such as
PACKAGENAME.CLASS cannot be cast to PACKAGENAME.CLASS even if the class has the same name as in your program (A test I tried)

How to implement something similar to the #Override java annotation?

With this jdk code in ../java/lang/Override.java,
package java.lang;
import java.lang.annotation.*;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.SOURCE)
public #interface Override {
}
having just annotation declaration, java compiler is intelligent enough to detect error(compile time):
The method toString123() of type Example must override or implement a supertype method
in the below problem code.
package annotationtype;
public class Example {
#Override public String toString() {
return "Override the toString() of the superclass";
}
#Override public String toString123() {
return "Override the toString123() of the superclass";
}
public static void main(String[] args) {
}
}
Annotation declaration for Override just gets compiled to,
interface java.lang.Override extends java.lang.annotation.Annotation{
}
which is nothing more than an interface.
So,
How does interface java.lang.Override syntax help java compiler to detect above error at compile time?
The implementation that triggers the compile error doesn't lie in the annotation, it lies in the Java compiler.
If you want to write your own similar annotation processor, you would use the annotation processor API: http://docs.oracle.com/javase/7/docs/api/javax/annotation/processing/Processor.html
which is nothing more than an interface.
So,
How does interface java.lang.Override syntax help java compiler to
detect above error at compile time?
That's right. Override is nothing more than an interface. The actual work is done by the java compiler. How the compiler does this is not specified.
Here are some links that explain how to work with an AnnotationProcessor to implement something similar to #Override :
Processor Java doc
Java annotation processing tool
Code generation using AnnotationProcessor
Annotation Processor, generating a compiler error
Source code analysis using Java 6 API
Playing with Java annotation processing

How to give warning message in own API?

Is that possible to give custom warning message in my own API like below? Is Resource leak:'ois' is never closed message related with Java API or JVM?
It's possible using a compiler API. You have to extend an AbstractProcessor and then make sure compiler knows about it.
Lets say we don't like programmers to swear in the source code. So, when someone defines a field with name "shit", we want to show a warning. Here is a simple implementation:
import java.util.List;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;
#SupportedSourceVersion(SourceVersion.RELEASE_7)
#SupportedAnnotationTypes("*")
public class Test extends AbstractProcessor {
public int shit;
public int foo;
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
Set<? extends Element> rootElements = roundEnv.getRootElements();
for (Element element : rootElements) {
if (element.getKind() == ElementKind.CLASS) {
List<? extends Element> classElements = element.getEnclosedElements();
for (Element classElement : classElements) {
if (classElement.getKind() == ElementKind.FIELD) {
if (classElement.getSimpleName().contentEquals("shit")) {
processingEnv.getMessager().printMessage(
Kind.WARNING,
"How dare you to swear in the source code?!",
classElement
);
}
}
}
}
}
return false;
}
public static void main(String[] args) {
//
}
}
Now, we want to apply such a processor just for this very class, because there is an ugly bad-called field too.
Using a command line:
javac Test.java
javac -processor Test Test.java
We need to firstly build a processor and then apply it while compiling (in this case to the same file).
And this is the output we get:
Test.java:17: warning: How dare you to swear in the source code?!
public int shit;
^
1 warning
To have the same warning in Eclipse or any other IDE, it's necessary to change compiler settings so it uses this custom processor.
Update: In the comments, kapep sent a link on how to set a custom processor in Eclipse: http://kerebus.com/2011/02/using-java-6-processors-in-eclipse/
Just for the record: Exactly the same warning may be achieved by implementing interface Closeable:
import java.io.Closeable;
import java.io.IOException;
public class Test implements Closeable {
#Override
public void close() throws IOException {
// TODO Auto-generated method stub
}
public static void main(String[] args) {
new Test();
}
}
And you see the same warning:
You can create warnings, notes, errors and other diagnostic messages like this using an annotation processor. It's a compiler plugin api integrated in the JDK. It lets you analyse the outline structure of source code. Despite the name you don't really need to handle any annotation when processing code. Messages are created using the Messager class. If you provide an element, it will be marked and the message will be shown next to it in the source code editor.
You won't be able to show message on elements inside methods or expressions though, only on declarations like types, properties, methods or parameters. It's possible to additionally parse the method body and generate messages based on the content using other tools, but as far as I know you can't show the message on the actual local element then. You could still show the message on the enclosing method or don't specify any element at all and show it in the IDE's log.
The IDE also needs to support this. I know that Eclipse and NetBeans do support messages generated by annotation processors, but there are probably other modern IDE that do so as well. If you need more features like messages on elements inside method bodies or the quick fix feature as shown in the example, I guess you need to create a plugin for the IDE.
I would believe that it is related to the eclipse ide, you could possibly write a plugin which displays warnings like that.
For example, when you use a method which has the annotation '#Deprecated' the ide automatically tells the programmer that the method is deprecated.

Java compiler api error

I'm trying to compile in-memory a class that implements an interface.
I have an interface named CacheRule (in com/vpfw/proxy/logicRules/CacheRule.class).
I have a class named CacheRuleBean that I compile in-memory.
If this class does not implement CacheRule, compilations works. But if this class implements CacheRule, then the error is:
java.lang.NoClassDefFoundError: com/vpfw/proxy/logicRules/CacheRule (wrong name: com/vpfw/proxy/logicRules/CacheRuleBean)
Curiously, if I perform this compilation inside Eclipse, works.
But when I execute it from Tomcat, I get the previous error.
This is the code for the CacheRule interface:
package com.vpfw.proxy.logicRules;
public interface CacheRule
{
void executeRule();
}
This is the code for CacheRuleBean:
package com.vpfw.proxy.logicRules;
import com.vpfw.proxy.logicRules.CacheRule;
public class CacheRuleBean implements CacheRule
{
public void executeRule() {}
}
And the call to compile is:
String[] compilationOptions = { "-cp", classDir };
return (new CompilerService().compile("com.vpfw.proxy.logicRules.CacheRuleBean",
source, compilationOptions));
Where
classDir is the directory /home/app/WEB-INF/classes that contains the com folder of this project (classPath is correct, If I add another classes of this project as imports in CacheRuleBean, compile ok).
The name of the class I use is com.vpfw.proxy.logicRules.CacheRuleBean.
source is the source code of CacheRuleBean.
CompilerService is my implementation of compiler API, which works perfectly with all classes except those that implement an interface.
What can I be doing wrong?

Categories

Resources