AspectJ, Intertype Definition - aop

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.

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();
}}

Customized parameter logging when using aspect oriented programing

All the examples I've seen that use aspect oriented programming for logging either log just class, method name and duration, and if they log parameters and return values they simply use ToString(). I need to have more control over what is logged. For example I want to skip passwords, or in some cases log all properties of an object but in other cases just the id property.
Any suggestions? I looked at AspectJ in Java and Unity interception in C# and could not find a solution.
You could try introducing parameter annotations to augment your parameters with some attributes. One of those attributes could signal to skip logging the parameter, another one could be used to specify a converter class for the string representation.
With the following annotations:
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Log {
}
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface SkipLogging {
}
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface ToStringWith {
Class<? extends Function<?, String>> value();
}
the aspect could look like this:
import java.lang.reflect.Parameter;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public aspect LoggingAspect {
private final static Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
pointcut loggableMethod(): execution(#Log * *..*.*(..));
before(): loggableMethod() {
MethodSignature signature = (MethodSignature) thisJoinPoint.getSignature();
Parameter[] parameters = signature.getMethod()
.getParameters();
String message = IntStream.range(0, parameters.length)
.filter(i -> this.isLoggable(parameters[i]))
.<String>mapToObj(i -> toString(parameters[i], thisJoinPoint.getArgs()[i]))
.collect(Collectors.joining(", ",
"method execution " + signature.getName() + "(", ")"));
Logger methodLogger = LoggerFactory.getLogger(
thisJoinPointStaticPart.getSignature().getDeclaringType());
methodLogger.debug(message);
}
private boolean isLoggable(Parameter parameter) {
return parameter.getAnnotation(SkipLogging.class) == null;
}
private String toString(Parameter parameter, Object value) {
ToStringWith toStringWith = parameter.getAnnotation(ToStringWith.class);
if (toStringWith != null) {
Class<? extends Function<?, String>> converterClass =
toStringWith.value();
try {
#SuppressWarnings("unchecked")
Function<Object, String> converter = (Function<Object, String>)
converterClass.newInstance();
String str = converter.apply(value);
return String.format("%s='%s'", parameter.getName(), str);
} catch (Exception e) {
logger.error("Couldn't instantiate toString converter for logging "
+ converterClass.getName(), e);
return String.format("%s=<error converting to string>",
parameter.getName());
}
} else {
return String.format("%s='%s'", parameter.getName(), String.valueOf(value));
}
}
}
Test code:
public static class SomethingToStringConverter implements Function<Something, String> {
#Override
public String apply(Something something) {
return "Something nice";
}
}
#Log
public void test(
#ToStringWith(SomethingToStringConverter.class) Something something,
String string,
#SkipLogging Class<?> cls,
Object object) {
}
public static void main(String[] args) {
// execution of this method should log the following message:
// method execution test(something='Something nice', string='some string', object='null')
test(new Something(), "some string", Object.class, null);
}
I used Java 8 Streams API in my answer for it's compactness, you could convert the code to normal Java code if you don't use Java 8 features or need better efficiency. It's just to give you an idea.

Unable to get basic JMockit test working

This is my first attempt at using jmockit. I have a gradle project (spring boot). Ensured that jmockit is the first in test classpath.
compile "net.bytebuddy:byte-buddy:1.4.5"
testCompile "org.jmockit:jmockit:1.8"
testCompile "org.springframework.boot:spring-boot-starter-test"
And then I want to fake a TranslatorFactory such that it returns a fake Translator.
TranslatorFactory class is something like
public class TranslatorFactory {
public ArgTranslator getTranslator(Class<?> aClass) {
return new ArgTranslator() {
#Override
public Object translate(String arg) {
return "Real translation " + arg;
}
};
}
}
And my test class is annotated with #RunWith(JMockit.class)
#Test
public void testTranslatorFactory() {
new MockUp<TranslatorFactory>() {
public ArgTranslator getTranslator(Class<?> baseClass) {
return new MockUp<ArgTranslator>() {
public Object translate(String arg) {
return "Fake translation " + arg;
}
}.getMockInstance();
}
};
assertEquals("Fake translation something", new TranslatorFactory().getTranslator(String
.class).translate("something"));
}
But I just dont seem to get this working. The test always fails. (Running in IntelliJ)
org.junit.ComparisonFailure:
Expected :Fake translation something
Actual :Real translation something
Any help/pointers will be greatly appreciated.

Gradle : how to use BuildConfig in an android-library with a flag that gets set in an app

My (gradle 1.10 and gradle plugin 0.8)-based android project consists of a big android-library that is a dependency for 3 different android-apps
In my library, I would love to be able to use a structure like this
if (BuildConfig.SOME_FLAG) {
callToBigLibraries()
}
as proguard would be able to reduce the size of the produced apk, based on the final value of SOME_FLAG
But I can't figure how to do it with gradle as :
* the BuildConfig produced by the library doesn't have the same package name than the app
* I have to import the BuildConfig with the library package in the library
* The apk of an apps includes the BuildConfig with the package of the app but not the one with the package of the library.
I tried without success to play with BuildTypes and stuff like
release {
// packageNameSuffix "library"
buildConfigField "boolean", "SOME_FLAG", "true"
}
debug {
//packageNameSuffix "library"
buildConfigField "boolean", "SOME_FLAG", "true"
}
What is the right way to builds a shared BuildConfig for my library and my apps whose flags will be overridden at build in the apps?
As a workaround, you can use this method, which uses reflection to get the field value from the app (not the library):
/**
* Gets a field from the project's BuildConfig. This is useful when, for example, flavors
* are used at the project level to set custom fields.
* #param context Used to find the correct file
* #param fieldName The name of the field-to-access
* #return The value of the field, or {#code null} if the field is not found.
*/
public static Object getBuildConfigValue(Context context, String fieldName) {
try {
Class<?> clazz = Class.forName(context.getPackageName() + ".BuildConfig");
Field field = clazz.getField(fieldName);
return field.get(null);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
To get the DEBUG field, for example, just call this from your Activity:
boolean debug = (Boolean) getBuildConfigValue(this, "DEBUG");
I have also shared this solution on the AOSP Issue Tracker.
Update: With newer versions of the Android Gradle plugin publishNonDefault is deprecated and has no effect anymore. All variants are now published.
The following solution/workaround works for me. It was posted by some guy in the google issue tracker:
Try setting publishNonDefault to true in the library project:
android {
...
publishNonDefault true
...
}
And add the following dependencies to the app project that is using the library:
dependencies {
releaseCompile project(path: ':library', configuration: 'release')
debugCompile project(path: ':library', configuration: 'debug')
}
This way, the project that uses the library includes the correct build type of the library.
You can't do what you want, because BuildConfig.SOME_FLAG isn't going to get propagated properly to your library; build types themselves aren't propagated to libraries -- they're always built as RELEASE. This is bug https://code.google.com/p/android/issues/detail?id=52962
To work around it: if you have control over all of the library modules, you could make sure that all the code touched by callToBigLibraries() is in classes and packages that you can cleave off cleanly with ProGuard, then use reflection so that you can access them if they exist and degrade gracefully if they don't. You're essentially doing the same thing, but you're making the check at runtime instead of compile time, and it's a little harder.
Let me know if you're having trouble figuring out how to do this; I could provide a sample if you need it.
I use a static BuildConfigHelper class in both the app and the library, so that I can have the packages BuildConfig set as final static variables in my library.
In the application, place a class like this:
package com.yourbase;
import com.your.application.BuildConfig;
public final class BuildConfigHelper {
public static final boolean DEBUG = BuildConfig.DEBUG;
public static final String APPLICATION_ID = BuildConfig.APPLICATION_ID;
public static final String BUILD_TYPE = BuildConfig.BUILD_TYPE;
public static final String FLAVOR = BuildConfig.FLAVOR;
public static final int VERSION_CODE = BuildConfig.VERSION_CODE;
public static final String VERSION_NAME = BuildConfig.VERSION_NAME;
}
And in the library:
package com.your.library;
import android.support.annotation.Nullable;
import java.lang.reflect.Field;
public class BuildConfigHelper {
private static final String BUILD_CONFIG = "com.yourbase.BuildConfigHelper";
public static final boolean DEBUG = getDebug();
public static final String APPLICATION_ID = (String) getBuildConfigValue("APPLICATION_ID");
public static final String BUILD_TYPE = (String) getBuildConfigValue("BUILD_TYPE");
public static final String FLAVOR = (String) getBuildConfigValue("FLAVOR");
public static final int VERSION_CODE = getVersionCode();
public static final String VERSION_NAME = (String) getBuildConfigValue("VERSION_NAME");
private static boolean getDebug() {
Object o = getBuildConfigValue("DEBUG");
if (o != null && o instanceof Boolean) {
return (Boolean) o;
} else {
return false;
}
}
private static int getVersionCode() {
Object o = getBuildConfigValue("VERSION_CODE");
if (o != null && o instanceof Integer) {
return (Integer) o;
} else {
return Integer.MIN_VALUE;
}
}
#Nullable
private static Object getBuildConfigValue(String fieldName) {
try {
Class c = Class.forName(BUILD_CONFIG);
Field f = c.getDeclaredField(fieldName);
f.setAccessible(true);
return f.get(null);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
Then, anywhere in your library where you want to check BuildConfig.DEBUG, you can check BuildConfigHelper.DEBUG and access it from anywhere without a context, and the same for the other properties. I did it this way so that the library will work with all my applications, without needing to pass a context in or set the package name some other way, and the application class only needs the import line changed to suit when adding it into a new application
Edit: I'd just like to reiterate, that this is the easiest (and only one listed here) way to get the values to be assigned to final static variables in the library from all of your applications without needing a context or hard coding the package name somewhere, which is almost as good as having the values in the default library BuildConfig anyway, for the minimal upkeep of changing that import line in each application.
For the case where the applicationId is not the same as the package (i.e. multiple applicationIds per project) AND you want to access from a library project:
Use Gradle to store the base package in resources.
In main/AndroidManifest.xml:
android {
applicationId "com.company.myappbase"
// note: using ${applicationId} here will be exactly as above
// and so NOT necessarily the applicationId of the generated APK
resValue "string", "build_config_package", "${applicationId}"
}
In Java:
public static boolean getDebug(Context context) {
Object obj = getBuildConfigValue("DEBUG", context);
if (obj instanceof Boolean) {
return (Boolean) o;
} else {
return false;
}
}
private static Object getBuildConfigValue(String fieldName, Context context) {
int resId = context.getResources().getIdentifier("build_config_package", "string", context.getPackageName());
// try/catch blah blah
Class<?> clazz = Class.forName(context.getString(resId) + ".BuildConfig");
Field field = clazz.getField(fieldName);
return field.get(null);
}
use both
my build.gradle
// ...
productFlavors {
internal {
// applicationId "com.elevensein.sein.internal"
applicationIdSuffix ".internal"
resValue "string", "build_config_package", "com.elevensein.sein"
}
production {
applicationId "com.elevensein.sein"
}
}
I want to call like below
Boolean isDebug = (Boolean) BuildConfigUtils.getBuildConfigValue(context, "DEBUG");
BuildConfigUtils.java
public class BuildConfigUtils
{
public static Object getBuildConfigValue (Context context, String fieldName)
{
Class<?> buildConfigClass = resolveBuildConfigClass(context);
return getStaticFieldValue(buildConfigClass, fieldName);
}
public static Class<?> resolveBuildConfigClass (Context context)
{
int resId = context.getResources().getIdentifier("build_config_package",
"string",
context.getPackageName());
if (resId != 0)
{
// defined in build.gradle
return loadClass(context.getString(resId) + ".BuildConfig");
}
// not defined in build.gradle
// try packageName + ".BuildConfig"
return loadClass(context.getPackageName() + ".BuildConfig");
}
private static Class<?> loadClass (String className)
{
Log.i("BuildConfigUtils", "try class load : " + className);
try {
return Class.forName(className);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return null;
}
private static Object getStaticFieldValue (Class<?> clazz, String fieldName)
{
try { return clazz.getField(fieldName).get(null); }
catch (NoSuchFieldException e) { e.printStackTrace(); }
catch (IllegalAccessException e) { e.printStackTrace(); }
return null;
}
}
For me this is the ONLY ONE AND ACCEPTABLE* SOLUTION TO determine the ANDROID APPLICATION BuildConfig.class:
// base entry point
// abstract application
// which defines the method to obtain the desired class
// the definition of the application is contained in the library
// that wants to access the method or in a superior library package
public abstract class BasApp extends android.app.Application {
/*
* GET BUILD CONFIG CLASS
*/
protected Class<?> getAppBuildConfigClass();
// HELPER METHOD TO CAST CONTEXT TO BASE APP
public static BaseApp getAs(android.content.Context context) {
BaseApp as = getAs(context, BaseApp.class);
return as;
}
// HELPER METHOD TO CAST CONTEXT TO SPECIFIC BASEpp INHERITED CLASS TYPE
public static <I extends BaseApp> I getAs(android.content.Context context, Class<I> forCLass) {
android.content.Context applicationContext = context != null ?context.getApplicationContext() : null;
return applicationContext != null && forCLass != null && forCLass.isAssignableFrom(applicationContext.getClass())
? (I) applicationContext
: null;
}
// STATIC HELPER TO GET BUILD CONFIG CLASS
public static Class<?> getAppBuildConfigClass(android.content.Context context) {
BaseApp as = getAs(context);
Class buildConfigClass = as != null
? as.getAppBuildConfigClass()
: null;
return buildConfigClass;
}
}
// FINAL APP WITH IMPLEMENTATION
// POINTING TO DESIRED CLASS
public class MyApp extends BaseApp {
#Override
protected Class<?> getAppBuildConfigClass() {
return somefinal.app.package.BuildConfig.class;
}
}
USAGE IN LIBRARY:
Class<?> buildConfigClass = BaseApp.getAppBuildConfigClass(Context);
if(buildConfigClass !- null) {
// do your job
}
*there are couple of things need to be watched out:
getApplicationContext() - could return a context which is not an App ContexWrapper implementation - see what Applicatio class extends & get to know of the possibilities of context wrapping
the class returned by final app could be loaded by different class loaders than those who will use it - depends of loader implementation and some principals typical (chierarchy, visibility) for loaders
everything depends on the implemmentation of as in this case simple DELEGATION!!! - the solution could be more sophisticetaded - i wanted only to show here the usage of DELEGATION pattern :)
** why i downwoted all of reflection based patterns because they all have weak points and they all in some certain conditions will fail:
Class.forName(className); - because of not speciified loader
context.getPackageName() + ".BuildConfig"
a) context.getPackageName() - "by default - else see b)" returns not package defined in manifest but application id (somtimes they both are the same), see how the manifest package property is used and its flow - at the end apt tool will replace it with applicaton id (see ComponentName class for example what the pkg stands for there)
b) context.getPackageName() - will return what the implementaio wants to :P
*** what to change in my solution to make it more flawless
replace class with its name that will drop the problems wchich could appear when many classes loaded with different loaders accessing / or are used to obtain a final result involving class (get to know what describes the equality between two classes (for a compiler at runtime) - in short a class equality defines not a self class but a pair which is constituted by the loader and the class. (some home work - try load a inner class with different loader and access it by outer class loaded with different loader) - it would turns out that we will get illegal access error :) even the inner class is in the same package has all modificators allowing access to it outer class :) compiler/linker "VM" treats them as two not related classes...

TestNG Test Case failing with JMockit "Invalid context for the recording of expectations"

The following TestNG (6.3) test case generates the error "Invalid context for the recording of expectations"
#Listeners({ Initializer.class })
public final class ClassUnderTestTest {
private ClassUnderTest cut;
#SuppressWarnings("unused")
#BeforeMethod
private void initialise() {
cut = new ClassUnderTest();
}
#Test
public void doSomething() {
new Expectations() {
MockedClass tmc;
{
tmc.doMethod("Hello"); result = "Hello";
}
};
String result = cut.doSomething();
assertEquals(result, "Hello");
}
}
The class under test is below.
public class ClassUnderTest {
MockedClass service = new MockedClass();
MockedInterface ifce = new MockedInterfaceImpl();
public String doSomething() {
return (String) service.doMethod("Hello");
}
public String doSomethingElse() {
return (String) ifce.testMethod("Hello again");
}
}
I am making the assumption that because I am using the #Listeners annotation that I do not require the javaagent command line argument. This assumption may be wrong....
Can anyone point out what I have missed?
The JMockit-TestNG Initializer must run once for the whole test run, so using #Listeners on individual test classes won't work.
Instead, simply upgrade to JMockit 0.999.11, which works transparently with TestNG 6.2+, without any need to specify a listener or the -javaagent parameter (unless running on JDK 1.5).