I want to use local variable annotations to do better AOP. One idea is to implement the Future<T> concept with a proxy using an annotation.
#NonBlocking ExpensiveObject exp = new ExpensiveObject();
//returns immediately, but has threaded out instantiation of the ExpensiveObject.
exp.doStuff();
//okay, now it blocks until it's finished instantiating and then executes #doStuff
Can I sick AspectJ on this somehow and get what I want done with local variable annotations? I know other threads have indicated that Java doesn't really support them but it would be magical. I really don't want to pass around a Future and break encapsulation.
You can not do this with a proxy, but real aspectj bytecode weaving will get you there if you annotate the type instead of the local variable. (I don't think local variable access is supported as a pointcut). Anyway, here's some code.
An annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.TYPE)
public #interface Later {}
A class marked with this annotation:
package com.dummy.aspectj;
#Later
public class HeavyObject{
public HeavyObject(){
System.out.println("Boy, I am heavy");
}
}
A main class:
package com.dummy.aspectj;
public class HeavyLifter{
public static void main(final String[] args){
final HeavyObject fatman = new HeavyObject();
System.out.println("Finished with main");
}
}
and an aspect:
package com.dummy.aspectj;
public aspect LaterAspect{
pointcut laterInstantiation() :
execution(#Later *.new(..)) ;
void around() : laterInstantiation() {
new Thread(new Runnable(){
#Override
public void run(){
System.out.println("Wait... this is too heavy");
try{
Thread.sleep(2000);
} catch(final InterruptedException e){
throw new IllegalStateException(e);
}
System.out.println("OK, now I am up to the task");
proceed();
}
}).start();
}
}
Here is the output from HeavyLifter when you run it as an AspectJ/Java Application from eclipse:
Finished with main
Wait... this is too heavy
OK, now I am up to the task
Boy, I am heavy
Related
I'm trying to measure the time for certains method of some specific classes. I'm using ByteBuddy and I created the following interceptor class:
public class TimingInterceptor {
#RuntimeType
public static Object intercept(#Origin Method method,
#SuperCall Callable<?> callable) {
long start = System.currentTimeMillis();
try {
return callable.call();
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(method + " took " + (System.currentTimeMillis() - start));
}
return null;
}
}
And this is the Java Agent Class I'm using:
public class TimerAgent {
public static void premain(String arguments,
Instrumentation instrumentation) {
new AgentBuilder.Default()
.type(ElementMatchers.nameStartsWith("BP")) // This is because I used the prefix BP to name methods I need to measure
.transform((builder, type, classLoader, module) ->
builder.method(ElementMatchers.any())
.intercept(MethodDelegation.to(TimingInterceptor.class))
).installOn(instrumentation);
}
}
I ran a test application including methods named with "BP" as prefix, and the message in the class TimingInterceptor indicating the method duration is not shown.
Some ideas guys?
Example full code:
Agent: https://github.com/rauldevilla/byte-buddy-agent
Test application: https://github.com/rauldevilla/byte-buddy-lab
To debug issues, it is always a good idea to register an AgentBuilder.Listener with your agent. The instrumentation API in the JVM suppresses all exceptions that are thrown during an instrumentation and the listener allows you to catch those exceptions and to print them for example.
For such "around" instrumentation, I generally recommend using Advice as it does not make assumptions on class loader hierarchies which you need to address explicitly when using delegation.
I have create a simple plugin system that allows others upload their plugin's jar, and the plugin system will load it and execute some code in it.
the plugin system will get a subclass of Function<Input, Output> to execute the loaded plugin logic, but I do not want that Function to create new Thread or do some danger action like System.exit. how can I forbid this action?
I have found the AccessController or SecurityManager in Java, how to use it to implement my intent.
Like you said, you can add a security Manager. Something like below: You can put your code in try catch block and catch your custom security exception thrown. This code below runs in loop and keeps on calling System.exit(1);
import java.security.Permission;
public class TestPreventSystemExit {
public static void main(String[] args) {
forbidSystemExitCall();
while (true) {
try {
System.exit(1);
} catch (Exception ex) {
}
}
}
private static class PreventExitException extends SecurityException {
}
private static void forbidSystemExitCall() {
final SecurityManager securityManager = new SecurityManager() {
public void checkPermission(Permission permission) {
if (permission.getName().indexOf("exitVM") >= 0) {
System.out.println("Why you did this to me? :)");
throw new PreventExitException();
}
}
};
System.setSecurityManager(securityManager);
}
}
For System.exit() - see the other answer.
For preventing the starting of threads: possible, but requires to extend the SecurityManager class - see here.
AccessController is more about how a client would write code that is potentially checked. It is not something that you, as the "owner" of the JVM can make usage of (see here). So it doesn't help with your problem.
I am new with AspectJ annotation for Java, and I am wondering if it is possible to put pointcut on a cross thread invocation.
Here is the code:
public class App {
public static void main( String[] args ) {
new Connector().getStart("testtest");
}
}
public class Connector {
public void getStart(String s1) {
Handler h = new Handler(s1);
h.start();
}
}
public class Handler extends Thread {
String s1;
public Handler(String s1) {
this.s1 = s1;
}
public void run() {
new Plain().getValue(s1);
}
}
public class Plain {
public void getValue(String s1) {
System.out.println("Plain getValue: " + s1);
}
}
I would like to have a pointcut that only triggers when Plain.getValue() is called by Connector.getStart().
Is it possible? Thanks.
You are making a mistake believing that Plain.getValue(..) is called by Connector.getStart(..) because in a multi-threaded environment it is not. Let me prove it with a little tweak to the getValue(..) method, printing a stack trace:
package de.scrum_master.app;
public class Plain {
public void getValue(String s1) {
System.out.println("Plain getValue: " + s1);
new Exception().printStackTrace(System.out);
}
}
By the way, I have moved all your classes to package de.scrum_master.app because using the default package is discouraged in Java and also AspectJ does not like it when trying to match pointcuts.
Console log (multi-threaded):
Plain getValue: testtest
java.lang.Exception
at de.scrum_master.app.Plain.getValue(Plain.java:4)
at de.scrum_master.app.Handler.run(Handler.java:9)
See? There is no trace of Connector.getStart(..) in the log. If we also tweak getStart(..) so as to call the thread's run() method directly (i.e. not starting a new thread but executing in the same thread) instead of start(), the situation changes:
package de.scrum_master.app;
public class Connector {
public void getStart(String s1) {
Handler h = new Handler(s1);
h.run();
}
}
Console log (single-threaded):
Plain getValue: testtest
java.lang.Exception
at de.scrum_master.app.Plain.getValue(Plain.java:4)
at de.scrum_master.app.Handler.run(Handler.java:9)
at de.scrum_master.app.Connector.getStart(Connector.java:4)
at de.scrum_master.app.App.main(App.java:3)
In this situation we could use AspectJ's dynamic cflow() (control flow) pointcut like this:
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 SingleThreadAspect {
#Before("execution(* de.scrum_master.app.Plain.getValue(..)) && cflow(execution(* de.scrum_master.app.Connector.getStart(..)))")
public void interceptControlFlow(JoinPoint thisJoinPoint) {
System.out.println(thisJoinPoint);
}
}
The advice would be triggered just as you wish. But for the reason explained at the beginning of my answer cflow() does not (and cannot) work across threads because there is no such thing as a direct control flow across threads. Each thread's control flow starts with its run() method, no earlier. That is the whole concept of multi-threading.
So if you really want to emulate something like a cross-thread control flow for whatever doubtful reason, you need to do some manual bookkeeping.
But first let us revert the tweaked h.run() back to the original h.start() so as to reinstate the multi-threading situation. Let us also remove the printStackTrace(..) line from Plain.getStart(..).
Solution:
Disclaimer: I do not like annotation-style #AspectJ syntax, so I am switching over to native syntax. It is much more expressive and we can achieve what we want more easily in terms of ITD (inter-type definition) because
in native syntax we can just declare an additional instance member variable for a given class while
in #AspectJ syntax we would have to declare the target class to implement an interface with a default implementation which in turn would carry the member variable for our manual bookkeeping.
Let us modify App so as to also start a Handler thread directly. This is our negative test case because we do not want to trigger our advice there as the thread is started outside of Plain.getValue(..):
package de.scrum_master.app;
public class App {
public static void main(String[] args) throws InterruptedException {
// The aspect should ignore this thread
new Handler("foo").start();
// Wait a little while so as not to mess up log output
Thread.sleep(250);
new Connector().getStart("testtest");
}
}
Console log without aspect:
Plain getValue: foo
Plain getValue: testtest
Aspect:
package de.scrum_master.aspect;
import de.scrum_master.app.*;
public aspect CrossThreadAspect {
// Declare a new instance member for our bookkeeping
private boolean Handler.cflowConnectorGetStart = false;
// If handler thread is started from Connector.getStart(..), set a mark
before(Handler handler) :
call(void Handler.start()) &&
cflow(execution(* Connector.getStart(..))) &&
target(handler)
{
System.out.println(thisJoinPoint + "\n doing bookkeeping");
handler.cflowConnectorGetStart = true;
}
// If current thread is a marked Handler, log it
before() :
execution(* Plain.getValue(..)) &&
if(Thread.currentThread() instanceof Handler) &&
if(((Handler) Thread.currentThread()).cflowConnectorGetStart)
{
System.out.println(thisJoinPoint + "\n triggered from parent thread via Connector.getStart(..)");
}
}
Console log with aspect:
As you can see, the Handler thread started from App.main(..) is ignored by the aspect as expected. The Handler started from Connector.getStart(..) triggers the aspect.
Plain getValue: foo
call(void de.scrum_master.app.Handler.start())
doing bookkeeping
execution(void de.scrum_master.app.Plain.getValue(String))
triggered from parent thread via Connector.getStart(..)
Plain getValue: testtest
GlobalVariables class holds different variables which are used across my framework one of them is WebDriver instance:
public class GlobalVariables
{
public static WebDriver driver;
//Some other static global variables required across my framework
public GlobalVariables(String propertiesFile)
{
initializeVariables(propertiesFile);
}
public void initializeVariables(String propertiesFile)
{
GlobalInitializer obj=new GlobalInitializer();
obj.initialize(String propertiesFile);
}
}
GlobalInitializer contains methods to initialize all the GlobalVariables:
public class GlobalInitializer extends GlobalVariables
{
public void initialize(String propertiesFile)
{
//Some logic to read properties file and based on the properties set in it, call other initialization methods to set the global variables.
}
public void initializeDriverInstance(String Browser)
{
driver=new FireFoxDriver();
}
//Some other methods to initialize other global variables.
}
I have many GetElement classes which use the driver instance to get UI control elements E.g:
public class GetLabelElement extends GlobaleVariables
{
public static WebElement getLabel(String someID)
{
return driver.findElement(By.id(someId));
}
//Similar methods to get other types of label elements.
}
public class GetTextBoxElement extends GlobaleVariables
{
public static WebElement getTextBox(String someXpath)
{
return driver.findElement(By.xpath(someXpath));
}
//Similar methods to get other types of text box elements.
}
I have other classes which perform some actions on the UI Control(This classes too use the global variables) E.g:
public class GetLabelProperties extends GlobalVariables
{
public static String getLabelText(WebElement element)
{
return element.getText();
}
}
public class PerformAction extends GlobalVariables
{
public static void setText(String textBoxName,String someText)
{
driver.findElement(someLocator(textBoxName)).setText("someText");
}
//Some other methods which may or may not use the global variables to perform some action
}
My test class in testng looks like this:
public class TestClass
{
GlobalVariables globalObj=new GlobalVariables(String propertiesFile);
#Test(priority=0)
{
GlobalVariables.driver.get(someURL);
//Some assertion.
}
#Test(priority=1)
{
WebElement element=GetLabelElement.getLabel(someID);
String labelName=GetLabelProperties.getLabelText(element);
//Some assertion.
}
#Test(priority=2)
{
WebElement element=GetTextBoxElement.getTextBox(someXpath);
PerformAction.setText(element.getText(),someText);
//Some assertion.
}
}
I have similar multiple test classes based on scenarios.
Now this tests are running fine if i am running them individually. But when i try to run them in parallel, then this tests are failing in some weird fashion. On analyzing i found out that its the static global variables which are getting initialized by each tests thus leaving the other tests to fail. Now how should i go about achieving my objective to run multiple tests parallely with minimal changes in my framework design? i have tried searching for options, and i have come across some option i.e 1) use of synchronized. 2) Create ThreadLocal instance(Note : I have tried this solution but still same issue. tests are mixing up with each other resulting in failure. I had marked the WebDriver instance as ThreadLocal and overriden the initialValue method of ThreadLocal to initialize the driver instance. Still i am not sure whether i had implemented it correctly or not.). Now i am not sure how best to implement any one of this solution in the given scenario. Any help is appreciated. TIA!
I have found out the solution : Use of ThreadLocal is the best solution to run tests in a huge multithreaded environment.
Code snippet to use WebDriver in multithreaded environment:
public static ThreadLocal<WebDriver> driver;
driver=new ThreadLocal<WebDriver>()
{
#Override
protected WebDriver initialValue()
{
return new FirefoxDriver(); //You can use other driver based on your requirement.
}
};
Now every time a test thread is created a new browser will open. ThreadLocal will make sure that there's only one copy of static webdriver instance per thread. [NOTE: Make sure your other global variables are too ThreadLocals. In my case they were not thats why i was running into test goof up issue]. Some extra knowledge which i would like to share so that others may find it informative. In ThreadLocal whenever the ThreadLocal.get() method is called you have to make sure that there is a provision to initialize the thread local as shown above in initialValue() method or you may run into null pointer exception. Thanks everyone.
If you are going to run non-parallel, then using a static webdriver member (or a instance shared between test classes by passing by reference) is fine because it is a good way to not have to close the webdriver instance between test classes. If you want to run parallel though, you need to have one instance of webdriver for EACH thread and so in that case using a static member is the WRONG way to go. Instead you need to create or pass a webdriver instance when the test case class is invoked.
Also, you are breaking a test into separate tests for each step of the test. That is very unusual and I do not see the reason why you are doing that. You could really simplify your tests by keeping all the test steps within one singe test case like people usually do.
You are getting this because of how JVM handles static members and methods.
You can't have a static webdriver object if you are going to run in parallel.
Source: The automated regression system i implemented where I work - we had this issue.
you can try something like this
public class DriverManager {
private static final ThreadLocal<WebDriver> threadLocal = new ThreadLocal<WebDriver>();
public static WebDriver getDriver() {
return threadLocal.get();
}
public static void setDriver(WebDriver driver) {
threadLocal.set(driver);
}
public static void closeDriver() {
if (getDriver() != null) {
try {
getDriver().close();
} catch (Exception e) {
e.printStackTrace();
}
try {
getDriver().quit();
} catch (Exception e) {
e.printStackTrace();
}
}
threadLocal.remove();
}
}
I have a lot of methods for logging, like logSomeAction, logAnotherAction etc.
Now I want all these methods make a small pause after printing messages (Thread.sleep).
If I do it manually, I would do something like this:
//before:
public static void logSomeAction () {
System.out.println (msg(SOME_ACTION));
}
//after:
public static void logSomeAction () {
System.out.println (msg(SOME_ACTION));
try {
Thread.sleep (2000);
} catch (InterruptedException ignored) { }
}
I remember that Java has proxy classes and some other magic-making tools. Is there any way avoid copy-n-pasting N sleep-blocks to N logging methods?
You could use Aspects to add extra "orthogonal" functionality to your methods.
If that sounds too esoteric, a simpler, down-to-earth solution would be to add the sleep in a separate method, then call that method in each of your logging methods. The first time you do this, you need to touch each method, but the next time if you want to modify the extra behaviour or add something else, you can do it in one single place.
It looks like you want to use Aspect Oriented Programming. You could use Spring for AOP, or AspectJ.
The OP mentions in a comment that the preferred solution is to use plain java proxies. The current code is implemented as static methods - for java proxies to be of any use, the logger class will need to be reworked as an interface. Something like this:
public interface SomeActionLogger
{
void logSomeAction();
void logSomeOtherAction();
// etc..
}
You then create your concrete implementation
public class SystemOutActionLogger implements SomeActionLogger
{
public void logSomeAction () {
System.out.println (msg(SOME_ACTION));
}
}
You can then have Java proxies wrap the SomeActionLogger interface
class DelayAfterInvocationHandler implements InvocationHandler
{
private Object delegate;
private int duration;
DelayAfterInvocationHandler(Object delegate, int duration)
{
this.delegate = delegate;
this.duration = duration;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
Object returnValue = method.invoke(delegate, args);
Thread.sleep(duration);
// you may want to catch InterruptedEception
return returnValue;
}
}
To hide some of the not-so-pretty proxy code, you can then have a method that wraps your logger to create the delay, e.g.
public ActionLogger addDelay(SomeActionLogger logger, int delay)
{
return (ActionLogger)Proxy.newProxyInstance(
impl.getClass().getClassLoader(),
new Class[] { SomeActionLogger.class },
new DelayAfterInvocationHandler(logger, delay));
}
So you then write
SomeActionLogger log = addDelay(new SystemOutActionLogger(), 2000);
Note that the DelayInvocationHandler is orthogonal to the logging interface - it can be used to add delay to any interface. You might then create a generic wrapping method like this:
public <T> T addDelay(T delegate, int delay, Class<T> interfaceType)
{
return (T)Proxy.newProxyInstance(
delegate.getClass().getClassLoader(),
new Class[] { type },
new DelayAfterInvocationHandler(delegate, delay));
}
Make a utility class that has a static SleepFor method which includes your try ... catch block and call that from every method you want a sleep in?
Replace all the System.out.println(msg(SOME_ACTION)); with printAndWait(SOME_ACTION);
You should be able to do that with find and replace.
Then create a method
public static void printAndWait(Object someAction) {
System.out.println (msg(someAction));
try {
Thread.sleep (2000);
} catch (InterruptedException ignored) {
Thread.currentThread.interrupt();
}
}
That way the code appears once and you can change it easily in one place.
Replace all of your logSomeAction() methods with a single logAction(Action a) method. This way, when you add more actions in the future, you will not be repeating your code for handling the action log and the thread sleep.