Problem migrating from AspectJ to Byte Buddy Plugin - java

I am attempting to migrate an AspectJ project to a Byte Buddy plugin and having some difficulty. I want to do compile-time byte code modifications.
The exception I am getting is :
[ERROR] Failed to execute goal net.bytebuddy:byte-buddy-maven-plugin:1.11.0:transform (default) on project timing-example: Failed to transform class files in /tmp/timing-example/target/classes: protected void com.walterjwhite.examples.timing.TimingExampleCommandLineHandler.doRun(java.lang.String[]) does not define an index 1 -> [Help 1]
Plugin:
NOTE:
package com.walterjwhite.timing.plugin;
import static net.bytebuddy.matcher.ElementMatchers.*;
import com.walterjwhite.timing.annotation.Timing;
import java.io.IOException;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.build.HashCodeAndEqualsPlugin;
import net.bytebuddy.build.Plugin;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.ClassFileLocator;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.matcher.ElementMatchers;
#HashCodeAndEqualsPlugin.Enhance
public class TimingPlugin extends Plugin.ForElementMatcher implements Plugin.Factory {
public TimeoutPlugin() {
super(declaresMethod(isAnnotatedWith(Timing.class)));
}
#Override
public DynamicType.Builder<?> apply(
DynamicType.Builder<?> builder,
TypeDescription typeDescription,
ClassFileLocator classFileLocator) {
System.out.println("Timing: start");
for (MethodDescription.InDefinedShape methodDescription :
typeDescription
.getDeclaredMethods()
.filter(
not(isBridge()).<MethodDescription>and(isAnnotatedWith(Timing.class)))) {
System.out.println("Timing: " + methodDescription);
if (methodDescription.isAbstract()) {
throw new IllegalStateException(
"Cannot implement timing on an abstract method: " + methodDescription);
}
builder = builder.visit(Advice.to(TimingAdvice.class).on(is(methodDescription)));
}
System.out.println("Timing: end");
return builder;
}
Advice:
NOTE: I would like to wrap the original method invocation in a try-catch-finally so I can time it. I'm not sure I can do that with advice. In any case, that is further down the road, I want to see that I can write a plugin and have my code instrumented.
package com.walterjwhite.timing.plugin;
import java.lang.reflect.Method;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
public class TimingAdvice {
#RuntimeType
#Advice.OnMethodEnter
public static void onEnter(#Advice.This Object intercepted, #Origin Method method, #RuntimeType #AllArguments Object[] arguments)
throws Throwable {
System.out.println(System.currentTimeNanos());
}
}
Method being advised:
#Timing
#Override
protected void doRun(String... arguments) {
int i = 0;
while (true) {
try {
System.out.println("i:" + i++);
Thread.sleep(50);
} catch (InterruptedException e) {
System.out.println("Exiting as instructed to do so.");
System.exit(1);
}
}
}
excerpt from pom.xml:
<plugin>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-maven-plugin</artifactId>
<version>1.11.0</version>
<executions>
<execution>
<goals>
<goal>transform</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.walterjwhite.aspects.timing</groupId>
<artifactId>plugin</artifactId>
<version>0.0.1</version>
</dependency>
</dependencies>
</plugin>
Lastly, the plugin project has the file in place because the byte buddy plugin does pick it up. But, whenever it attempts to transform the class files, it fails. So, my configuration isn't quite right.
EDIT #2:
The pom is partially correct, the other issue I ran into was:
NoClassDefFoundError
This was due to the fact that I needed the dependency also listed as a dependency for the project and not just the plugin. Ie:
<plugin>
<groupId>net.bytebuddy</groupId>
<artifactId>byte-buddy-maven-plugin</artifactId>
<version>1.11.0</version>
<executions>
<execution>
<goals>
<goal>transform</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>com.walterjwhite.aspects.timing</groupId>
<artifactId>plugin</artifactId>
<version>0.0.1</version>
</dependency>
</dependencies>
</plugin>
</plugins
</build>
<dependencies>
<dependency>
<groupId>com.walterjwhite.aspects.timing</groupId>
<artifactId>plugin</artifactId>
<version>0.0.1</version>
</dependency>
</dependencies>
...

You are blending annotations from the MethodDelegation API with the Advice API. The annotations are very similar as they intend to support the same approach but an unfortunate side effect is that they get confused. Instead of importing
import net.bytebuddy.implementation.bind.annotation.AllArguments;
import net.bytebuddy.implementation.bind.annotation.Origin;
import net.bytebuddy.implementation.bind.annotation.RuntimeType;
you need to use annotations declared within the Advice class with the same name. Ideally, you prefix all annotations with Advice:
#Advice.OnMethodEnter
public static void onEnter(#Advice.This Object intercepted, #Advice.Origin Method method, #Advice.AllArguments Object[] arguments)
throws Throwable {
System.out.println(System.currentTimeNanos());
}
Note that the #RuntimeType has no equivalent in the Advice API. It is not normally needed.

Related

annotation to generate source code for same file in java

I am trying to define a simple annotation and use it during compile time only to add a simple method to the consumed source file/class file but nothing is getting added.
I am using maven and java 1.8.
Below is the code and not sure where it is going wrong
Annotation class definition:
package com.test.annotations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.SOURCE)
public #interface AddMethod {
}
Processor for the annotation
package com.test.annotations;
import com.google.auto.service.AutoService;
import javax.annotation.processing.*;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementFilter;
import javax.tools.JavaFileObject;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;
import java.util.stream.Collectors;
#SupportedAnnotationTypes("com.test.annotations.AddMethod")
#SupportedSourceVersion(SourceVersion.RELEASE_8)
#AutoService(Processor.class)
public class AddMethodProcessor extends AbstractProcessor {
#Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
// get all the classes annotated with ToJsonString class
Collection<? extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(AddMethod.class);
annotatedElements.forEach(e -> {
// some code to get the class name
try {
writeBuilderFile(qualifiedClassName);
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
});
return true;
}
private void writeBuilderFile(String qualifiedClassName) throws IOException {
String packageName = null;
int lastDot = qualifiedClassName.lastIndexOf('.');
if (lastDot > 0) {
packageName = qualifiedClassName.substring(0, lastDot);
}
String simpleClassName = qualifiedClassName.substring(lastDot + 1);
JavaFileObject generatedSourceFile = processingEnv.getFiler()
.createSourceFile(qualifiedClassName);
try (PrintWriter out = new PrintWriter(generatedSourceFile.openWriter())) {
if (packageName != null) {
out.print("package ");
out.print(packageName);
out.println(";");
out.println();
}
out.print("public class ");
out.print(simpleClassName);
out.println(" {");
out.println();
out.print(" public ");
out.print("String");
out.println(" testMethod() {");
out.println(" return \"this is a test method\" ");
out.println(" }");
out.println();
out.println("}");
}
}
}
After this, I am running mvn install to install the jar in my .m2 repository so that the consumer can use it.
consumer project:
pom.xml
<dependencies>
<dependency>
<groupId>com.test</groupId>
<artifactId>add-method</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
<forceJavacCompilerUse>true</forceJavacCompilerUse>
<generatedSourcesDirectory>${project.build.directory}
/generated-sources/</generatedSourcesDirectory>
<annotationProcessors>
<annotationProcessor>
com.test.annotations.AddMethodProcessor
</annotationProcessor>
</annotationProcessors>
</configuration>
</plugin>
</plugins>
</build>
</project>
Test class using the annotation
package org.example;
import com.test.annotations.AddMethod;
#AddMethod
public class Test {
private String x;
}
On running build in intellij, I could not see the source file or class file having the auto generated method.

maven test fails with nullpointer but junit 5 ok

I'm having a strange problem with maven test with Junit 5.
I'd created the test suites with junit tools for each method, every test starts like this.
private Class2Test createTestSubject() {
return new Class2Test();
}
public void test1() throws Exception {
Class2Test testSubject;
String result;
// default test
testSubject = createTestSubject();
result = testSubject.getData();
//testing, assert
}
The line
result = testSubject.getData();
returns a NullPointerException
When I execute the same test via eclipse finish ok. The surefire plugin are defined
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.0.0-M5</version>
<dependencies>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<version>5.7.0</version>
</dependency>
</dependencies>
<configuration>
<systemPropertyVariables>
<entorno>${project.basedir}\resources\maqueta.properties</entorno>
<hibernate>${project.basedir}\resources\hibernate.cfg.xml</hibernate>
</systemPropertyVariables>
<parallel>classes</parallel>
<threadCount>10</threadCount>
</configuration>
</plugin>
I'd tried to change the declaration of the object to dogde the nullpointer but it fails.
Class2Test() is the default constructor, doesn't requiere parameters or read files.
package test.com.my.myself;
import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import javax.annotation.Generated;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.junit.tools.configuration.base.MethodRef;
import com.my.myself.Class2Test;
import com.google.gson.Gson;
import junitparams.JUnitParamsRunner;
#Generated(value = "org.junit-tools-1.1.0")
#RunWith(JUnitParamsRunner.class)
public class Tester1{
private Class2Test createTestSubject() {
return new Class2Test();
}
#DisplayName("TEST1")
#MethodRef(name = "test1", signature = "()QString;")
#Test
public void test1() throws Exception {
Class2Test testSubject;
String result;
// default test
testSubject = createTestSubject();
result = testSubject.test1();
}
}
and the class to test
public class Class2Test{
private Connection conn = new Connector();
private static final Logger logger = Logger.getLogger(Class2Test.class);
public String test1() {
PrepareStatement pstm = conn.prepareStatement("select 1 from dual");
ResultSet rs = pstm.executeQuery();
...
return 1;
}
}
There was a problem with pom.xml the resources folder was wrong setted. :(

I doing an assigment which require me to create a telegram bot bot

package org.example;
import org.telegram.telegrambots.meta.TelegramBotsApi;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
import org.telegram.telegrambots.updatesreceivers.DefaultBotSession;
public class App {
public static void main( String[] args ) {
try {
TelegramBotsApi botsApi = new TelegramBotsApi(DefaultBotSession.class);
botsApi.registerBot(new s261251Bot());
} catch (TelegramApiException e) {
e.printStackTrace();
}
}
}
package org.example;
import org.telegram.telegrambots.bots.TelegramLongPollingBot;
import org.telegram.telegrambots.meta.api.methods.send.SendMessage;
import org.telegram.telegrambots.meta.api.objects.Update;
import org.telegram.telegrambots.meta.exceptions.TelegramApiException;
public class s261251Bot extends TelegramLongPollingBot {
#Override
public String getBotUsername() {
return "username";
}
#Override
public String getBotToken() {
return "..............";
}
#Override
public void onUpdateReceived(Update update) {
SendMessage message = new SendMessage();
message.setChatId(String.valueOf((update.getMessage().getChatId())));
message.setText("hello " + update.getMessage().getFrom().getFirstName() +" " + "\nInsert your matric number." + update.getMessage().getText());
try {
execute(message);
}catch (TelegramApiException e){
e.printStackTrace();
}
}
}
I put the right username and token but in here I replace it with other
I keep getting this error:
C:\Users\safwa\Documents\IntelliJ\assignment-2-safwan0908\src\main\java\my\assignment2\src\main\java\org\example\s261251Bot.java:9:8
java: cannot access java.util.concurrent.CompletableFuture class
file for java.util.concurrent.CompletableFuture not found
I had the same problem.
Make sure you use the correct or the same java version in your project and frameworks.
For more details read this article
In my case, I updated my maven version, using these properties (as described in the article):
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
or
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>LATEST</version>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>

Eclipse cannot find main class of maven project

Before you flag this as a duplicate, please note that I have read several other similar questions, but none of them fixed the issue.
I have an Eclipse project using maven. It uses Java 15, with Javafx 13.
When I try to run the app, it has the error message:
Error: Could not find or load main class app.cleancode.Start
I have tried:
refreshing the project,
rebuilding from the maven project,
and even deleting all of the eclipse files and reimporting the project.
None of them made any difference.
I can run any other project in Eclipse, but this one is just not working.
I am doing this on Windows 10 home.
My pom file:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>app.cleancode</groupId>
<artifactId>javafx-app</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>15</java.version>
<javafx.version>13</javafx.version>
</properties>
<dependencies>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-swing</artifactId>
<version>${javafx.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<release>${java.version}</release>
<source>${java.version}</source>
<target>${java.version}</target>
<compilerArgs>--enable-preview</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.openjfx</groupId>
<artifactId>javafx-maven-plugin</artifactId>
<version>0.0.3</version>
<configuration>
<mainClass>app.cleancode.Start</mainClass>
</configuration>
</plugin>
</plugins>
</build>
</project>
app.cleancode.Start:
package app.cleancode;
import app.cleancode.game.GameListener;
import app.cleancode.game.GameLoop;
import app.cleancode.game.GameObject;
import app.cleancode.game.PhysicalLaw;
import app.cleancode.game.physics.Gravity;
import app.cleancode.game.physics.Movement;
import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.input.KeyCombination;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class Start extends Application {
private static GameListener[] gameListeners = new GameListener[] {
};
#SuppressWarnings("unchecked")
private static GameObject<Node> [] gameObjects = new GameObject[] {
};
private static PhysicalLaw[] laws = new PhysicalLaw[] {
new Movement(),
new Gravity()
};
public static void main(String[] args) {
launch(args);
}
private Pane nodes = new Pane();
private Pane gamePane = new Pane();
#Override
public void start(Stage primaryStage) throws Exception {
Scene scene = new Scene(gamePane);
scene.getStylesheets().add("/app/cleancode/app.css");
nodes.getChildren().add(new Text("Loading"));
primaryStage.setTitle("Game");
primaryStage.setFullScreen(true);
primaryStage.setFullScreenExitKeyCombination(KeyCombination.NO_MATCH);
primaryStage.setScene(new Scene(nodes));
primaryStage.show();
for(GameListener listener : gameListeners) {
for(String gameObjectName : listener.getGameObjects()) {
for(GameObject<Node> gameObject : gameObjects) {
gameObject.addNode = this::addNode;
if(gameObject.getName().equalsIgnoreCase(gameObjectName)) {
try {
var gameObjectField = listener.getClass().getDeclaredField(gameObjectName);
gameObjectField.set(listener, gameObject);
break;
}catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
listener.startup();
}
scene.setFill(Color.BLACK);
primaryStage.setScene(scene);
primaryStage.setFullScreen(true);
GameLoop loop = new GameLoop(this::tick);
loop.start();
}
public void tick() {
for(GameListener gameListener : gameListeners) {
gameListener.update();
}
for(PhysicalLaw law : laws) {
for(GameObject<Node> gameObject : gameObjects) {
law.handle(gameObject);
}
}
}
public void addNode(Node node) {
gamePane.getChildren().add(node);
}
}
** module-info.java **:
module app.cleancode.javafx-app {
exports app.cleancode.axis;
exports app.cleancode.animation;
exports app.cleancode;
exports app.cleancode.sound;
exports app.cleancode.game.physics;
exports app.cleancode.bounds;
exports app.cleancode.game.snake;
exports app.cleancode.map;
exports app.cleancode.sprite;
exports app.cleancode.game;
requires java.desktop;
requires javafx.base;
requires javafx.graphics;
requires javafx.media;
requires javafx.swing;
}
Another piece of information I just found out is that if i create a new Start.java with just a main method, it works fine. Not sure why.
To answer my own question, the easiest solution is to create a new main class that calls the main of the original start.
First, rename Start.java to Starter.java (or whatever you want to call it)
finally, create a new Start.java in the place of the old one:
Start.java:
package app.cleancode;
public class Start {
public static void main(String[] args) {
Starter.main(args);
}
}
Then you can just run the new Start.java.

JUnit test case failure with JMockit java.lang.IllegalStateException: Invalid context for the recording of expectations

#Test
public void testGetOnlyNewPartitions() throws Exception {
setUp();
new Expectations(){
HiveUtil hiveUtil;
{
HiveUtil.getInstance(); returns(hiveUtil);
hiveUtil.getAllpartitions(oldTable); returns(oldPartitions);
hiveUtil.getAllpartitions(newTable); returns(newPartitions);
}
};
PartitionFilter partitionFilter = new PartitionFilter(oldTable, newTable, HiveUtil.getInstance());
}
I am unit testing the class PartitionFilter, which uses a singleton class HiveUtil.
My test case is failing with the error "java.lang.IllegalStateException: Invalid context for the recording of expectations" while running. Any explanation on why this is happening?
This is the relevant part of my pom.xml:
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.13</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
I have tried to put the jmockit dependency before the junit dependency in the pom. That didn't work.
Some more research suggested that I was not using the #RunWith(JMockit.class) annotation at the beginning of the class.
However, when I tried to use it, I was presented with the error "Class cannot be resolved to a type". I made all the relevant imports.
import static org.junit.Assert.assertEquals;
import org.junit.Test;
import org.junit.runner.*;
import mockit.*;
import mockit.integration.junit4.*;
import junit.framework.TestCase;
What am I doing wrong?
Recent versions of JMockit (since version 1.7) require the use of a mocking annotation to introduce a mocked type/instance. Also, local mock fields are no longer supported. So, the test should be written as follows:
#Test
public void getOnlyNewPartitions(#Mocked final HiveUtil hiveUtil) throws Exception {
setUp();
new Expectations() {{
hiveUtil.getAllpartitions(oldTable); result = oldPartitions;
hiveUtil.getAllpartitions(newTable); result = newPartitions;
}};
PartitionFilter partitionFilter =
new PartitionFilter(oldTable, newTable, HiveUtil.getInstance());
}

Categories

Resources