For some reason I can't work out yet, my agent doesn't intercept java LinkageError instances.
Agent code:
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.implementation.SuperMethodCall;
import net.bytebuddy.matcher.ElementMatchers;
import java.lang.instrument.Instrumentation;
public class MyAgent {
public static void premain(String arguments, Instrumentation instrumentation) {
new AgentBuilder.Default()
.type(ElementMatchers.isSubTypeOf(LinkageError.class))
.transform((builder, type, classLoader, module) ->
builder.constructor(ElementMatchers.isDefaultConstructor())
.intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.to(MyInterceptor.class)))
).installOn(instrumentation);
}
}
Interceptor code:
public class MyInterceptor {
#RuntimeType
public static void intercept(#Origin Constructor<?> constructor) throws Exception {
System.out.println("Intercepted: " + constructor.getName());
}
}
Test code:
public static void main(String[] args) {
new NoClassDefFoundError("should be intercepted!!!").toString();
new Foo("oh").toString();
}
What is puzzling is that replacing ElementMatchers.isSubTypeOf(LinkageError.class) with ElementMatchers.nameContains("Foo") gives the expected result and Foo constructor is intercepted.
The NoClassDefFoundError is loaded by the bootstrap loader. It willnot be able to see your interceptor class which is why it is never triggered.
Try using the Advice class (as a visitor) to add bytecode to matched classes which should resolve this problem.
Related
I'm a newbie to AspectJ, and trying to understand joinpoint model
Now i have class like this
public class Account {
private static Map<String, PaymentMethod> supportedPayments = new HashMap<>();
static {
supportedPayments.add("COD", new CodPaymentMethod());
supportedPayments.add("ATM", new ATMPaymentMethod());
}
}
as i read from AspectJ In Action, there is a way to define pointcut when class is intialization, but i could not find syntax.
Anyone help me?
This one does not work:
#Pointcut("initialization(com.jas.aop.bean.Payment"))
it say
ERROR] Syntax error on token "initialization(com.jas.aop.bean.Payment)", "name pattern" expected
/Users/admin/eclipse-workspace/aop/src/main/java/com/jas/aop/aspect/ClassInitializationAspect.java:9
#Pointcut("initialization(com.jas.aop.bean.Payment)")
Your pointcut has several problems:
There is a missing closing parenthesis ).
initialization intercepts constructors (i.e. object initialisation) rather than static class initialisation, which is not what you want and also would require a constructor pattern, not a class name pattern.
If your aspect does not happen to be in the exact same package as the target class, you must use a fully qualified class name such as my.package.Account in order to make the pointcut match.
By the way, your code snippets are just pseudo code because a hash map does not have an add method, rather a put method. The sample class does not even compile. Don't just invent code when posting questions here. Make life easier for the people trying to help you.
Now here is an MCVE, something I always suggest you to specify in your question in order to help people reproduce your situation. I did it for you this time, this was your free shot. Next time, please do it yourself.
Dependency classes used by the main class:
package de.scrum_master.app;
public interface PaymentMethod {}
package de.scrum_master.app;
public class ATMPaymentMethod implements PaymentMethod {}
package de.scrum_master.app;
public class CodPaymentMethod implements PaymentMethod {}
Target class with driver application:
package de.scrum_master.app;
import java.util.HashMap;
import java.util.Map;
public class Account {
private static Map<String, PaymentMethod> supportedPayments = new HashMap<>();
static {
System.out.println("Static initialiser block");
supportedPayments.put("COD", new CodPaymentMethod());
supportedPayments.put("ATM", new ATMPaymentMethod());
}
public Account() {
System.out.println("Creating account");
}
public void doSomething() {
System.out.println("Doing something");
}
public static void main(String[] args) {
new Account().doSomething();
}
}
Aspect:
The aspect shows both initialization and staticinitialization in order to show the difference in both syntax and functionality. You can find all of this explained with examples in the AspectJ manual, which I warmly recommend you to read.
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class AccountAspect {
#Before("initialization(de.scrum_master.app.Account.new(..))")
public void interceptObjectInitialisation(JoinPoint joinPoint) {
System.out.println(joinPoint);
}
#Before("staticinitialization(de.scrum_master.app.Account)")
public void interceptClassInitialisation(JoinPoint joinPoint) {
System.out.println(joinPoint);
}
}
Console log:
staticinitialization(de.scrum_master.app.Account.<clinit>)
Static initialiser block
initialization(de.scrum_master.app.Account())
Creating account
Doing something
When trying to use #AutoWire feature with one of StandAlone Application unable to do so instead getting Null Pointer Exception. Please highlight my mistakes if any. Your help is appreciated.
Spring Ver 5.1.5.RELEASE and we're not using any xml config file to tell spring there are annotated classes to look into instead using #ComponentScan or #EnableAutoConfiguration at the top of AppConfig and boost strap the Context from main() class as a first line. But Autowiring works perfectly with internal bean/java classes of jdk(Environment) but not with custom POJO classes. If we're trying to get through getBean method then it works. But I'm trying to avoid creating context everywhere and using getBean() Please Refer below and help me only with your valuable guidelines.
public class ContextMaster {
private static AnnotationConfigApplicationContext appContext;
public static AnnotationConfigApplicationContext getApplicationContext() {
if (appContext == null) {
appContext = new AnnotationConfigApplicationContext(ContextConfig.class);
//appContext = new AnnotationConfigApplicationContext("com.xx.xx.xxx","xx.xxx.xxxx.xxx.datamanager");
logger.debug("Context Invoked !!");
}
return appContext;
}
}
#Configuration
#EnableAutoConfiguration
#PropertySource("classpath:db.properties")
#EnableTransactionManagement
#ComponentScans(value = {
#ComponentScan(basePackages = "xxxxx.datamanager"),
#ComponentScan(basePackages = "com.xx.xx.xxx"),
#ComponentScan(basePackages = "com.xx.xx.xxx.utils")})
public class AppConfig {
#Autowired
private Environment env;
#Bean
public DataSource getDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(env.getProperty("db.driver"));
dataSource.setUrl(env.getProperty("db.url"));
return dataSource;
}
#Bean
public LocalSessionFactoryBean getSessionFactory() {
LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
//LocalSessionFactoryBean sessionFactoryBean = new AnnotationSessionFactoryBean();
factoryBean.setDataSource(getDataSource());
Properties props=new Properties();
props.put("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
props.put("hibernate.hbm2ddl.auto", env.getProperty("hibernate.hbm2ddl.auto"));
props.put("hibernate.cache.region.factory_class", env.getProperty("hibernate.cache.region.factory_class"));
factoryBean.setHibernateProperties(props);
factoryBean.setAnnotatedClasses(xx.class, xxxx.class, xxxx.class, xxx.class);
return factoryBean;
}
#Bean
public HibernateTransactionManager getTransactionManager() {
return transactionManager;
}
}
// Here is NPE thrown when tried with auto-configured bean
#Component
public class Good extends Good11 {
#Autowired
private RxxxDyyyyHelper rdh;
//RxxxDyyyyHelper rdh =
ContextHelper.getApplicationContext().getBean(RxxxDyyyyHelper .class);
rdh.setProperty(); // NPE here
rdh.getProperty(); // NPE
}
// Here we're trying to initiate the LosUtils class
public class LosUtils {
public static void main(String args[]) throws Exception {
AnnotationConfigApplicationContext applicationContext = `ContextHelper.getApplicationContext();`
}
It seems like you didn't put the full code here, because your Good class won't compile this way..
I have tried with almost all jar files with extentreport from 2.41.2 to
3.13.0 but whenever I try to write the command: extent.loadConfig(new
File(System.getProperty("user.dir")+"//ReportsConfig.xml")); it throws error on multiple lines but for instance i have put up one example
showing as "The method loadConfig(File) is undefined for the type
ExtentReports".
My code for ExtentReport Class is `enter code here`:
package TestNG_package;
import java.io.File;
import java.util.Date;
import com.aventstack.extentreports.ExtentReports;
import com.aventstack.extentreports.reporter.AbstractReporter;
import com.aventstack.extentreports.reporter.ExtentHtmlReporter;
import com.aventstack.extentreports.reporter.configuration.ChartLocation;
import com.aventstack.extentreports.reporter.configuration.Theme;
public class ExtentManager
{
private static ExtentReports extent;
public static String screenshotFolderPath;
static ExtentHtmlReporter htmlReporter;
public static ExtentReports getInstance()
{
if (extent == null)
{
extent = new
ExtentReport("E:\\Selenium\\Workspace\\New_Test\\test-output\\report.html");
extent.loadConfig(new
File(System.getProperty("user.dir")+"//ReportsConfig.xml"));
extent.addSystemInfo("Selenium ver" ,
"3.5.1").addSystemInfo("Environ" , "PROD");
}
return extent;
}
}
My next part of code is to invoke ExtentReport in other class called
loginTest
public class LoginTest()
{
#Test
public void doLogin()
{
ExtentReport rep = ExtentManager.getInstance();
ExtentTest Test = rep.startTest("UATRMS start");
Test.log(LogStatus.Info,"Starting UATRMS Test");
rep.endTest(test);
rep.flush();
}
}
The correct method is
reporter.loadXMLConfig("extent-config.xml");
The method you are using is for instances where you have a properties file. See the docs for more info. This method is used by the reporter, not the core API. Reporters can be configured using these configuration items.
#Before(value="#annotation(com.aspect.Loggable)",argNames="taskId")
public void logEmail(JoinPoint joinPoint) {
System.out.println("#Before is running!");
System.out.println("hijacked : " + joinPoint.getSignature().getName());
System.out.println("******");
}
i have a pointCut on method sendEmail() with custom annotation.
This method sendEmail() is called from differnt location in our application .
Like we call sendEmail from paymentApproved () method of paymentManager when payment is approved.
We call sendEmail from taskComplete() method of taskManger when task is completed.
i have to find out the event for which sendEmail is triggered.
I applied custom annotation #EVENT("PAYMENT") on paymentApproved () of paymentManager and #EVENT("TASK") on taskComplete() method of taskManger.
How can i get the value of #EVENT in logEmail(JoinPoint joinPoint) aspect.
Scaffolding:
Sorry, I do not like all-caps class names and I also used my own package names as an example because my template already generates them.
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface Loggable {}
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface Event {
String value();
}
Driver application:
This is pure Java because I am not a Spring user. Just imagine it is one or more #Components.
Please also note that in one case sendEmail() is called from a method not annotated by #Event. This should not trigger the aspect, only the two calls from the annotated methods.
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
Application application = new Application();
application.doSomething();
application.paymentApproved();
application.taskComplete();
}
public void doSomething() {
sendEmail();
}
#Event("paymentApproved")
public void paymentApproved() {
sendEmail();
}
#Event("taskComplete")
public void taskComplete() {
sendEmail();
}
#Loggable
public void sendEmail() {}
}
Aspect:
Your pointcut wants to express: Catch methods annotated with #Loggable within the control flow of methods annotated by #Event. Control flow can be expressed by cflow() or cflowbelow() pointcuts.
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import de.scrum_master.app.Event;
#Aspect
public class LogAspect {
#Before(
"#annotation(de.scrum_master.app.Loggable) &&" +
"execution(* *(..)) &&" + // only needed for AspectJ, not for Spring AOP
"cflow(#annotation(event))"
)
public void logEmail(JoinPoint thisJoinPoint, Event event) {
System.out.println(thisJoinPoint + " -> " + event);
}
}
Console log:
execution(void de.scrum_master.app.Application.sendEmail()) -> #de.scrum_master.app.Event(value=paymentApproved)
execution(void de.scrum_master.app.Application.sendEmail()) -> #de.scrum_master.app.Event(value=taskComplete)
Update: If you were using full AspectJ (e.g. via load-time weaving) instead of Spring AOP, you just could have used a call() pointcut and from there get the enclosing joinpoint's static information. Then the #Event annotation would not have been necessary. But Spring AOP is just "AOP lite" and does not support call().
You can access to the annotation receiving it as a parameter. Something like this:
#Before(value="#annotation(EVENT)",argNames="taskId")
public void logEmail(JoinPoint joinPoint, Event event) {
// do what you need with event. For example, if the field is called value you can do this:
if ("PAYMENT".equals(event.value())) {
// do sth
}
System.out.println("#Before is running!");
System.out.println("hijacked : " + joinPoint.getSignature().getName());
System.out.println("******");
}
New poster here, hope I don't brake any rules :)
I am using PrivateModule in google-guice in order to have multiple DataSource's for the same environment. But I am having a hard time getting MethodInterceptor's to work inside the private modules.
Below is a simple test case that explains the "problem".
A simple service class would be:
interface Service {
String go();
}
class ServiceImpl implements Service {
#Override #Transactional
public String go() {
return "Test Case...";
}
}
The MyModule class would be:
class MyModule extends AbstractModule {
#Override
protected void configure() {
install(new PrivateModule() {
#Override
protected void configure() {
bind(Service.class).to(ServiceImpl.class);
bindInterceptor(
Matchers.any(),
Matchers.annotatedWith(Transactional.class),
new MethodInterceptor() {
#Override
public Object invoke(MethodInvocation i)
throws Throwable {
System.out.println("Intercepting: "
+ i.getMethod().getName());
return i.proceed();
}
});
expose(Service.class);
}
});
}
}
And the final test case:
public class TestCase {
#Inject Service service;
public TestCase() {
Guice.createInjector(new MyModule()).injectMembers(this);
}
public String go() {
return service.go();
}
public static void main(String[] args) {
TestCase t = new TestCase();
System.out.println(t.go());
}
}
You would expect the output to be:
Intercepting: go
Test Case...
But it doesn't happen, the interceptor is not used, ant only Test Case... is output.
If I bind/expose the ServiceImpl instead of the interface then it works.
Thanks in advance,
Regards,
LL
Well... I figured it out shortly after I posted the question :)
The problem is that you also need to expose() the ServiceImpl class.
So the bind/expose would be.
bind(ServiceImpl.class); // ServiceImpl annotated with #Singleton
bind(Service.class).to(ServiceImpl.class);
expose(ServiceImpl.class);
expose(Service.class);
Regards,
LL
You need to explicitly bind ServiceImpl in the private module. The problem with your existing code is that it inherits the binding for ServiceImpl from the parent module. From the PrivateModule docs,
Private modules are implemented using parent injectors. When it can satisfy their dependencies, just-in-time bindings will be created in the root environment. Such bindings are shared among all environments in the tree.
Adding this line should fix the problem:
bind(ServiceImpl.class);