Syntax error when adding Runnable in static initializer with javassist - javassist

I want to use javassist (version 3.19-GA) to generate bytecode of a static initializer of a class that starts a thread. For some reason I cannot understand javassist expects a ";" somewhere even though I believe the code I provide is syntactically correct. Does someone see more than I do? Here is the code. What is the problem?
ClassPool pool = ClassPool.getDefault();
final CtClass clazz = pool.get(somename);
clazz.makeClassInitializer().insertAfter(
"try{Runnable r=new Runnable () {public void run () { System.out.println (\"hello!!!!\"); }}; " +
"new Thread(r).start(); } catch(Exception e){}");
I'm getting the following exception:
javassist.CannotCompileException: [source error] ; is missing
at javassist.CtBehavior.insertAfter(CtBehavior.java:877)
at javassist.CtBehavior.insertAfter(CtBehavior.java:792)
at my.code(myclass.java:111)
Thanks for any hint.

Most probably javassist compiler does not support anonymous inner classes like new Runnable () {...}
You have to create new class, inherit it from Runnable, implement method run and create object of this class in your constructor.
package hello;
import javassist.*;
class Test{
}
class Main {
public static void main(String[] args) throws Exception {
ClassPool pool = ClassPool.getDefault();
final CtClass clazz = pool.get(Test.class.getCanonicalName());
CtClass runnable = pool.makeClass("my.custom.RunnableImpl");
runnable.addInterface(pool.get("java.lang.Runnable"));
CtMethod method = CtNewMethod.make("public void run() { System.out.println(\"hello!!!!\"); }", runnable);
runnable.addMethod(method);
// load class
runnable.toClass();
clazz.setName("newTest");
CtConstructor ctConstructor = clazz.makeClassInitializer();
ctConstructor.insertAfter("try{ new Thread( new my.custom.RunnableImpl() ).start(); } catch(Exception e){}");
Class aClass = clazz.toClass();
// call initializer
Class.forName(aClass.getCanonicalName());
Thread.sleep(1000);
}
}

From the official documentation of void insertAfter(String src):
Parameters:
src - the source code representing the inserted bytecode. It must be a
single statement or block.
In your src String parameter, you don't provide a single statement or a block.
A block is "{}".
Try insertBefore(String src) method with global enclosing "{}":
ClassPool pool = ClassPool.getDefault();
final CtClass clazz = pool.get(somename);
clazz.makeClassInitializer().insertBefore(
"{try{Runnable r = new Runnable () {public void run () { System.out.println (\"hello!!!!\"); }}; " +
"new Thread(r).start(); } catch(Exception e){}}");

Related

JUnit 5 Parameterized test #ArgumentsSource parameters not loading

I have created below JUnit5 parameterized test with ArgumentsSource for loading arguments for the test:
public class DemoModelValidationTest {
public ParamsProvider paramsProvider;
public DemoModelValidationTest () {
try {
paramsProvider = new ParamsProvider();
}
catch (Exception iaex) {
}
}
#ParameterizedTest
#ArgumentsSource(ParamsProvider.class)
void testAllConfigurations(int configIndex, String a) throws Exception {
paramsProvider.executeSimulation(configIndex);
}
}
and the ParamsProvider class looks like below:
public class ParamsProvider implements ArgumentsProvider {
public static final String modelPath = System.getProperty("user.dir") + File.separator + "demoModels";
YAMLDeserializer deserializedYAML;
MetaModelToValidationModel converter;
ValidationRunner runner;
List<Configuration> configurationList;
List<Arguments> listOfArguments;
public ParamsProvider() throws Exception {
configurationList = new ArrayList<>();
listOfArguments = new LinkedList<>();
deserializedYAML = new YAMLDeserializer(modelPath);
deserializedYAML.load();
converter = new MetaModelToValidationModel(deserializedYAML);
runner = converter.convert();
configurationList = runner.getConfigurations();
for (int i = 0; i < configurationList.size(); i++) {
listOfArguments.add(Arguments.of(i, configurationList.get(i).getName()));
}
}
public void executeSimulation(int configListIndex) throws Exception {
final Configuration config = runner.getConfigurations().get(configListIndex);
runner.run(config);
runner.getReporter().consolePrintReport();
}
#Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return listOfArguments.stream().map(Arguments::of);
// return Stream.of(Arguments.of(0, "Actuator Power"), Arguments.of(1, "Error Logging"));
}}
In the provideArguments() method, the commented out code is working fine, but the first line of code
listOfArguments.stream().map(Arguments::of)
is returning the following error:
org.junit.platform.commons.PreconditionViolationException: Configuration error: You must configure at least one set of arguments for this #ParameterizedTest
I am not sure whether I am having a casting problem for the stream in provideArguments() method, but I guess it somehow cannot map the elements of listOfArguments to the stream, which can finally take the form like below:
Stream.of(Arguments.of(0, "Actuator Power"), Arguments.of(1, "Error Logging"))
Am I missing a proper stream mapping of listOfArguments?
provideArguments(…) is called before your test is invoked.
Your ParamsProvider class is instantiated by JUnit. Whatever you’re doing in desiralizeAndCreateValidationRunnerInstance should be done in the ParamsProvider constructor.
Also you’re already wrapping the values fro deserialised configurations to Arguments and you’re double wrapping them in providesArguments.
Do this:
#Override
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
return listOfArguments.stream();
}}

ByteBuddy: AbstractMethodError when set interceptor

The following is my learning code, when I executing the code, an exception happened:
Exception in thread "main"
java.lang.AbstractMethodError: org.learning.UserRepository$ByteBuddy$etz0xUhc.$$_pharos_set_interceptor(Lorg/learning/Interceptor;)V**
ByteBuddy version: 1.10.14
final TypeCache.SimpleKey cacheKey = getCacheKey(learningClazz, interceptor.getClass());
Class proxyClass = load(learningClazz, proxyCache, cacheKey, byteBuddy ->
byteBuddy
.subclass(learningClazz)
.defineField(ProxyConfiguration.INTERCEPTOR_FIELD_NAME, Interceptor.class, Visibility.PRIVATE)
.method(not(isDeclaredBy(Object.class)))
.intercept(MethodDelegation.to(ProxyConfiguration.InterceptorDispatcher.class))
.implement(ProxyConfiguration.class)
.intercept(FieldAccessor.ofField(ProxyConfiguration.INTERCEPTOR_FIELD_NAME)
.withAssigner(Assigner.DEFAULT, Assigner.Typing.DYNAMIC)));
final ProxyConfiguration proxy = (ProxyConfiguration) proxyClass.getDeclaredConstructor().newInstance();
proxy.$$_pharos_set_interceptor(interceptor);
return (T) proxy;
public interface ProxyConfiguration {
String INTERCEPTOR_FIELD_NAME = "$$_pharos_interceptor";
void $$_pharos_set_interceptor(Interceptor interceptor);
class InterceptorDispatcher {
#RuntimeType
public static Object intercept(
#This final Object instance,
#Origin final Method method,
#AllArguments final Object[] arguments,
#StubValue final Object stubValue,
#FieldValue(INTERCEPTOR_FIELD_NAME) Interceptor interceptor,
#SuperMethod Method superMethod
) throws Throwable
{
if (interceptor == null) {
return stubValue;
}
else {
return interceptor.intercept(instance, method, arguments, superMethod);
}
}
}
}
Package-private methods are overriden but the JVM will not dispatch them dynamically if the subclass is loaded on a different class loader. If you declare the method public, the problem should be solved. Alternatively, inject the class into the target class loader.

Instrumenting constructors in Javassist using insertAfter

I am trying to instrument constructors to print out something when they are called. I do this by using insertAfter. I also wanted to print out the object reference. I tried using $_ but that was just set to 0. Is printing out the object reference of the newly newed object possible?
I ended finding a solution to what i wanted. It consisted of using an ExprEditor like this:
public CtClass instrumentMethods() throws CannotCompileException, IOException {
ClassPool cp = ClassPool.getDefault();
cp.insertClassPath(new LoaderClassPath(loader));
CtClass ctKlazz = cp.makeClass(instream);
CtMethod[] methods = ctKlazz.getMethods();
for (CtMethod method : methods) {
final CtBehavior method = methods[ind];
method.instrument(
new ExprEditor() {
// Instrument new expressions:
public void edit(NewExpr expr) throws CannotCompileException {
final int allocSiteId = getAllocSiteId(className, expr.indexOfBytecode());
expr.replace( "{ $_ = $proceed($$); someCodeHere($_); }");
}
}
);
method.insertBefore("{ someInstrumentationAtStart(); }");
}
return ctKlazz;
}

How to generalize a JMockit test using Spring autowiring

So I would like to use a generic test for a few different Dao methods. Inside the Dao, I implemented the save functionality to be Entity independent, so I figured it would be best to make the tests Entity independent as well. Currently I have the following for one of my jmockit tests that is autowired with spring.
#Injectable
public EntityManager em;
#Tested
SyncClaimDao syncClaimDao = new SyncClaimDaoImpl();
#Before
public void setUp() {
Deencapsulation.setField(syncClaimDao, "em", em);
}
private void testSaveEntity (Class T) {
// Existing claim happy path
new Expectations() {
{
em.contains(any); result = true;
em.merge(any);
}
};
if (T.isInstance(SyncClaimEntity.class)) {
Assert.assertTrue(syncClaimDao.saveClaim(new SyncClaimEntity()));
} else if (...) {...}
}
#Test
public void testSaveClaim() {
testSaveEntity(SyncClaimEntity.class);
}
SyncClaimDaoImpl
#Override
public boolean saveClaim(SyncClaimEntity claim) {
return saveEntity(claim);
}
private boolean saveEntity(Object entity) {
boolean isPersisted = false;
try {
isPersisted = em.contains(entity);
if (isPersisted) {
em.merge(entity);
} else {
em.persist(entity);
em.flush();
isPersisted = true;
}
logger.debug("Persisting " + entity.getClass().getSimpleName() + ": " + entity.toString());
}
catch (NullPointerException ex) {
...
}
catch (IllegalArgumentException ex) {
...
}
return isPersisted;
}
When I run the tests I am seeing the following errors:
mockit.internal.MissingInvocation: Missing invocation of:
javax.persistence.EntityManager#contains(Object)
with arguments: any Object
on mock instance: javax.persistence.$Impl_EntityManager#44022631
at at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
... 4 more
Caused by: Missing invocation
at [redacted].dal.dao.SyncClaimDaoImplTest$1.<init>(SyncClaimDaoImplTest.java:48)
at [redacted].dal.dao.SyncClaimDaoImplTest.testSaveEntity(SyncClaimDaoImplTest.java:46)
at [redacted].dal.dao.SyncClaimDaoImplTest.testSaveClaim(SyncClaimDaoImplTest.java:67)
... 10 more
Now if I just move the Expectations block into the #Test method like so:
#Test
public void testSaveClaim() {
new Expectations() {
{
em.contains(any); result = true;
em.merge(any);
}
};
Assert.assertTrue(syncClaimDao.saveClaim(new SyncClaimEntity()));
I get a successful test run as should be. I'm thinking that the spring autowiring for the Test method is not properly scoping my Expectations. That's why I'm seeing the missing invocation errors.
Does anyone have any ideas on how to generalize my Expectations so I can create simpler tests for generalized methods?
I see the mistake now: T.isInstance(SyncClaimEntity.class). The Class#isInstance(Object) method is supposed to be called with an instance of the class, not with the class itself; so, it's always returning false because SyncClaimEntity.class is obviously not an instance of SyncClaimEntity.

AspectJ, Intertype Definition

I am receiving this error when I compile
The type XXX must implement the inherited abstract method
I have three files
A default implementation [com.SafeReaderIMPL.java]
public class SafeReaderIMPL implements ISafeReader {
private boolean successfulRead;
public SafeReaderIMPL() {
successfulRead = true;
}
protected void fail() {
successfulRead = false;
}
#Override
public boolean isSuccessfulRead() {
return successfulRead;
}
}
An interface file [com.ISafeReader.java]
public interface ISafeReader {
public boolean isSuccessfulRead();
}
An apsect (using annotations) [com.SafeReaderAspect.java]
#Aspect
public class SafeReaderAspect {
#DeclareParents(value = "com.BadReader", defaultImpl = SafeReaderIMPL.class)
public ISafeReader implementedInterface;
#AfterThrowing(pointcut = "execution(* *.*(..)) && this(m)", throwing = "e")
public void handleBadRead(JoinPoint joinPoint, ISafeReader m, Throwable e) {
((SafeReaderIMPL)m).fail();
}
}
And a Test Class [com.BadReader]
public class BadReader {
public void fail() throws Throwable {
throw new Throwable();
}
}
I compile the first three files in a separate jar using
ajc -source 1.8 -sourceroots . -outjar aspectLib.jar
I then compile the second file using the aspectLib like so
ajc -source 1.8 -sourceroots . -aspectpath ./aspectLib.jar -outjar common.jar
When I go to compile the second jar I get the error. I am using the latest stable version of AspectJ 1.8.3
BadReader.java:10 [error] The type BadReader must implement the
inherited abstract method ISafeReader.isSuccessfulRead() public class
BadReader {
^^^^^^^^
The problem is not two-step compilation as such, but the fact that #DeclareParents in #AspectJ syntax in not 100% compatible with declare parents in native syntax. Actually, #DeclareParents for introducing default interface implementations is superseded by #DeclareMixin (see this bug ticket), but the downside of the mixin approach is that you do not have a real A implements B scenario there, i.e. you cannot cast as you wish in your after-throwing advice, so this is also not a good option in your case.
So what do you do if you want to keep the two-step compilation approach? Just use native syntax:
Interface:
package com;
public interface ISafeReader {
boolean isSuccessfulRead();
}
Default implementation:
package com;
public class SafeReaderIMPL implements ISafeReader {
private boolean successfulRead;
public SafeReaderIMPL() { successfulRead = true; }
public void fail() { successfulRead = false; }
#Override public boolean isSuccessfulRead() { return successfulRead; }
}
ITD aspect:
package com;
public aspect SafeReaderAspect {
declare parents : com.BadReader implements SafeReaderIMPL;
after(ISafeReader safeReader) throwing : execution(* *(..)) && this(safeReader) {
System.out.println(thisJoinPoint + " - calling 'fail()' before rethrowing error");
((SafeReaderIMPL) safeReader).fail();
}
}
ITD target class with sample main method:
package com;
public class BadReader {
public void doSomething() {
throw new RuntimeException("my error");
}
public static void main(String[] args) {
BadReader badReader = new BadReader();
System.out.println("badReader.isSuccessfulRead() = " + badReader.isSuccessfulRead());
try { badReader.doSomething(); }
catch(Throwable t) { System.out.println(t); }
System.out.println("badReader.isSuccessfulRead() = " + badReader.isSuccessfulRead());
}
}
Now you can use the two-stage compilation approach.
Console output:
badReader.isSuccessfulRead() = true
execution(void com.BadReader.doSomething()) - calling 'fail()' before rethrowing error
java.lang.RuntimeException: my error
badReader.isSuccessfulRead() = false
The problem is due to the two-step compilation. During the second step, ajc needs the source code of SafeReaderIMPL to be able to weave BadReader, but it cannot find it into aspectLib.jar
In fact, if you try compiling in a single step (I did), it compiles and runs.
Unfortunately I don't know a way to fix this without providing the source code during the second compile step, which I suppose would render the whole two-step approach a bit pointless.