AOP for Groovy via closures and pattern matching? - java

I have an abstract base POGO:
abstract class AuthorizingResource {
void authorize(String credential) {
if(!credentialIsValid(credential)) {
throw new AuthorizationException(credential)
}
}
boolean credentialIsValid(String credential) {
// Do stuff to determine yea or nay
}
}
And many concrete subclasses like so:
class FizzResource extends AuthorizingResource {
List<Fizz> getAllFizzes(String credential) {
authorize(credential)
List<Fizz> fizzes
// Do stuff
fizzes
}
Fizz getFizzById(String credential, Long id) {
authorize(credential)
Fizz fizz
// Do stuff
fizz
}
void considerTheLillies(Buzz buzz) {
// Do stuff
}
void upsertFizz(String credential, Fizz fizz) {
authorize(credential)
// Do stuff
}
}
As you can see there's several things going on:
Any FizzResource methods that I want authenticated/authorized, I need to manually call authorize(...) at the top of the method
Some methods (considerTheLillies) do not need to be authed
I was wondering if I could mimic AOP by using a closure to call authorize(...) (so I don't have to keep adding it mindlessly) that can use some sort of pattern for selecting which methods to "wrap" inside the closure. In the particular case of the FizzResource, this would be any method that contains "*Fizz*" in it, but that pattern should be (ideally) any valid regex. The one thing that can't change is that any method that accepts credential arg cannot have its signature modified.
So basically, something like Spring AOP or Google Guice's method interceptors, but using native Groovy closures.
Any ideas?

You can use invokeMethod with GroovyInterceptable. Note that any fizz in the name will be matched:
abstract class AuthorizingResource implements GroovyInterceptable {
def invoked = []
def validator = [credentialIsValid : { true }]
void authorize(String credential) {
if ( !validator.credentialIsValid(credential) ) {
throw new RuntimeException(credential)
}
}
def invokeMethod(String method, args) {
if (method.toLowerCase().contains('fizz')) {
metaClass.getMetaMethod('authorize', String).invoke(this, args[0])
invoked.add( 'authorized ' + method )
}
return metaClass
.getMetaMethod(method, args*.getClass() as Class[])
.invoke(this, args)
}
}
class Fizz { String name }
class FizzResource extends AuthorizingResource {
List<Fizz> getAllFizzes(String credential) { ['all fizzes'] }
Fizz getFizzById(String credential, Long id) { new Fizz(name: 'john doe') }
def considerTheLillies() { 42 }
}
res = new FizzResource()
assert res.getAllFizzes('cred') == ['all fizzes']
assert res.considerTheLillies() == 42
assert res.getFizzById('cred', 10l).name == 'john doe'
assert res.invoked == ['authorized getAllFizzes', 'authorized getFizzById']

I couldn't stop thinking about a closure based solution. I came up with some Javascript style code, using closures and maps. It features no inheritance:
class AuthorizingResource {
void authorize(String credential) {
if(!credentialIsValid(credential)) {
throw new RuntimeException(credential)
}
}
boolean credentialIsValid(String credential) { true }
}
class Fizz {}
abstract class FizzResource {
abstract List<Fizz> getAllFizzes(String credential)
abstract int getFizzById(String credential, Long id)
abstract void considerTheLillies(buzz)
static createFizzResource(authorized) {
def auth = new AuthorizingResource()
def authorize = { auth.authorize it; authorized << it }
return [
getAllFizzes : { String credential -> ['fizz list'] },
getFizzById : { String credential, Long id -> 42 },
considerTheLillies : { buzz -> }
]
.collectEntries { entry ->
entry.key.toLowerCase().contains('fizz') ?
[(entry.key) : { Object[] args ->
authorize(args[0]); entry.value(*args)
}] :
entry
} as FizzResource
}
}
Testing:
def authorized = []
def fizz = FizzResource.createFizzResource(authorized)
assert authorized == []
assert fizz.getAllFizzes('getAllFizzes cred') == ['fizz list']
fizz.considerTheLillies null
assert authorized == ['getAllFizzes cred']
assert fizz.getFizzById('fizz by id cred', 90l) == 42
assert authorized == ['getAllFizzes cred', 'fizz by id cred']
Note the authorized list is very dumb, and only needed for assert purposes.

Related

Using Spock/Groovy to test a Java method that extends HashMap - how to mock HashMap get method?

In our codebase, we use a specific class map which stores the results for authorization requests including whether or not someone has the auth to get in.
Currently writing some unit tests (a thing I have little practice on) that contains this, a modified version of our code for you to look at:
public class TestResultMap extends HashMap<String, TestResult> {
private static final long serial = -1234567890L;
public boolean isAuthorized(String resource) {
TestResult result = get(resource);
if (result == null) {
throw new RunExcept("Authorization not calculated");
}
return result.isAuthorized();
}
}
When testing the isAuthorized() in the groovy file I had made, I've noticed that no matter how I have it arranged, I can not get it to instantiate TestResult result = get(resource) to be anything other than null. isAuthorized() calls upon an enum method in another class that contains the possibilities, but otherwise just returns a boolean.
That's tangential to the point, though. Is there an efficient way to mock this or force get(resource) to output something not null? Alternatively, can I directly set result to a particular value?
Thanks for any help. Incredibly new to this whole process and documentation has been tricky.
I am showing you
how to stub the result of TestResult.isAuthorized to always return true or false,
how to use a spy on a real TestResultMap instance in order to stub the result of get(_) with the rest of the class behaving normally (partial mocking),
how to test your class without using any mocks, because if the methods used in the test are not doing anything expensive, mocking might not be necessary at all. Or maybe you want to also have an integration test in addition to the unit test with mocked dependencies.
Classes under test:
package de.scrum_master.stackoverflow.q70149644;
public class TestResult {
private String result;
public TestResult(String result) {
this.result = result;
}
public boolean isAuthorized() {
return !result.toLowerCase().matches(".*(forbidden|blocked|unauthorized|denied).*");
}
#Override
public String toString() {
return "TestResult(result='" + result + "')";
}
}
package de.scrum_master.stackoverflow.q70149644;
import java.util.HashMap;
public class TestResultMap extends HashMap<String, TestResult> {
private static final long serial = -1234567890L;
public boolean isAuthorized(String resource) {
TestResult result = get(resource);
if (result == null) {
throw new RuntimeException("Authorization not calculated");
}
return result.isAuthorized();
}
}
Spock specification:
package de.scrum_master.stackoverflow.q70149644
import spock.lang.Specification
class TestResultMapTest extends Specification {
def "resource is authorized"() {
given:
TestResultMap map = new TestResultMap()
TestResult testResult = Stub() {
isAuthorized() >> true
}
map.put("resource", testResult)
expect:
map.isAuthorized("resource")
}
def "resource is unauthorized"() {
given:
TestResultMap map = new TestResultMap()
TestResult testResult = Stub() {
isAuthorized() >> false
}
map.put("resource", testResult)
expect:
!map.isAuthorized("resource")
}
def "resource not found"() {
given:
TestResultMap map = Spy() {
get(_) >> null
}
when:
map.isAuthorized("resource")
then:
def rte = thrown RuntimeException
rte.message == "Authorization not calculated"
}
def "test without mocks"() {
given:
TestResultMap map = new TestResultMap()
map.put("OK", new TestResult("Hello world"))
map.put("not OK", new TestResult("Access denied"))
expect:
map.isAuthorized("OK")
!map.isAuthorized("not OK")
when:
map.isAuthorized("foo")
then:
def rte = thrown RuntimeException
rte.message == "Authorization not calculated"
}
}

Kotlin Flows Java Interop Callback

I´ve been looking for a suitable solution or best practice when I want to use Kotlin Flows with ordinary callbacks. My use case is that I write a kotlin library that uses Kotlin Flow internally and i have to assume that the users will use Java for instance. So I thought that the best solution is to overload a basic callback interface to my flow method and call it in collect something like this:
class KotlinClass {
interface Callback {
fun onResult(result: Int)
}
private fun foo() = flow {
for (i in 1..3) {
emit(i)
}
}
fun bar(callback: Callback) {
runBlocking {
foo().collect { callback.onResult(it) }
}
}
private fun main() {
bar(object : Callback {
override fun onResult(result: Int) {
TODO("Not yet implemented")
}
})
}
and in my Java Application i can simply use it like that:
public class JavaClass {
public void main() {
KotlinClass libraryClass = new KotlinClass();
libraryClass.bar(new KotlinClass.Callback() {
#Override
public void onResult(int result) {
// TODO("Not yet implemented")
}
});
}
}
I am not sure whats the way to go because I would like to have my Kotlin library that uses Flows usable in a good fashion for Java and Kotlin.
I came across callbackFlow but that seems to be only if I want to let´s call it flow-ify a callback-based API? Because I am quite new to Kotlin and Flows please apologise if my question is flawed in cause of missing some basic concepts of kotlin.
I would give the Java client more control over the flow. I would add a onStart and onCompletion method to your callback interface. Beside this I would use an own CoroutineScope - maybe customizable from the Java client. And I would not block the calling thread from within the Kotlin function - no runBlocking.
#InternalCoroutinesApi
class KotlinClass {
val coroutineScope = CoroutineScope(Dispatchers.Default)
interface FlowCallback {
#JvmDefault
fun onStart() = Unit
#JvmDefault
fun onCompletion(thr: Throwable?) = Unit
fun onResult(result: Int)
}
private fun foo() = flow {
for (i in 1..3) {
emit(i)
}
}
fun bar(flowCallback: FlowCallback) {
coroutineScope.launch {
foo().onStart { flowCallback.onStart() }
.onCompletion { flowCallback.onCompletion(it) }
.collect { flowCallback.onResult(it) }
}
}
fun close() {
coroutineScope.cancel()
}
}
Now the Java client is in full control how to start, collect and cancel the flow. For example you could use a latch to wait for completion, set an timeout and cancel the couroutine scope. This looks in the first place like a lot of code, but typically you will need this kind of flexibility.
public class JavaClass {
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
KotlinClass libraryClass = new KotlinClass();
libraryClass.bar(new KotlinClass.FlowCallback() {
#Override
public void onCompletion(#Nullable Throwable thr) {
latch.countDown();
}
#Override
public void onResult(int result) {
System.out.println(result);
}
});
try {
latch.await(5, TimeUnit.SECONDS);
} finally {
libraryClass.close();
}
}
}
You don't need to create a interface in the Kotlin code. You can define bar like that:
fun bar(callback: (Int) -> Unit) {
runBlocking {
foo().collect { callback(it) }
}
}
From the Java code you can call the function like that:
public class JavaClass {
public static void main(String[] args) {
KotlinClass libraryClass = new KotlinClass();
libraryClass.bar(v -> { System.out.println(v); return Unit.INSTANCE; });
}
}
In case anyone wondering for a general solution. Here's our version of enhancement from #rene answer here.
Accept a generic type
A configurable coroutineScope
// JavaFlow.kt
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.launch
#InternalCoroutinesApi
class JavaFlow<T>(
private val coroutineScope: CoroutineScope = CoroutineScope(Dispatchers.Default)
) {
interface OperatorCallback <T> {
#JvmDefault
fun onStart() = Unit
#JvmDefault
fun onCompletion(thr: Throwable?) = Unit
fun onResult(result: T)
}
fun collect(
flow: Flow<T>,
operatorCallback: OperatorCallback<T>,
) {
coroutineScope.launch {
flow
.onStart { operatorCallback.onStart() }
.onCompletion { operatorCallback.onCompletion(it) }
.collect { operatorCallback.onResult(it) }
}
}
fun close() {
coroutineScope.cancel()
}
}
Java caller-side:
// code omitted...
new JavaFlow<File>().collect(
// compressImageAsFlow is our actual kotlin flow extension
FileUtils.compressImageAsFlow(file, activity),
new JavaFlow.OperatorCallback<File>() {
#Override
public void onResult(File result) {
// do something with the result here
SafeSingleton.setFile(result);
}
}
);
// or using lambda with method references
// new JavaFlow<File>().collect(
// FileUtils.compressImageAsFlow(file, activity),
// SafeSingleton::setFile
// );
// Change coroutineScope to Main
// new JavaFlow<File>(CoroutineScopeKt.MainScope()).collect(
// FileUtils.compressImageAsFlow(file, activity),
// SafeSingleton::setFile
// );
OperatorCallback.onStart and OperatorCallback.onCompletion is optional, override it as needed.

How to inject a method invocation into another method in android

I have a class with,
a field called something,
a setter method called setSomething, and,
a method called onChange which should be called every time something is changed.
I want to be able to freely add more fields and have the same behavior for all of them.
I don't want to manually call onChange because,
A lot of boilerplate,
Code will be written in Kotlin so I don't want to write setter functions at all.
The ideal solution I've been able to think of has been to somehow inject the onChange call right before the return for each setter method in compile time.
I've looked at annotation processing, but apparently classes aren't actually compiled at that stage, so I'd have to generate the entire class all over again? I don't exactly understand this.
The other option seems to be writing a gradle plugin that will find the relevant class(es) and modify their bytecode.
I've actually started work on this as a pure Java project (gradle plugin is semi-done) and have been able to find the classes and inject the method call. Can't seem to successfully write the results to a class file though.
Here's what I have (using BCEL):
public class StateStoreInjector {
public static void main(String[] args) {
// Find all classes that extends StateStore
Reflections reflections = new Reflections("tr.xip.statestore");
Set<Class<? extends StateStore>> classes = reflections.getSubTypesOf(StateStore.class);
for (Class c : classes) {
try {
JavaClass clazz = Repository.lookupClass(c.getName());
JavaClass superClazz = Repository.lookupClass(StateStore.class.getName());
if (Repository.instanceOf(clazz, superClazz)) {
injectInClass(clazz, superClazz);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
private static void injectInClass(JavaClass clazz, JavaClass superClazz) {
ClassGen classGen = new ClassGen(clazz);
ConstantPoolGen cp = classGen.getConstantPool();
// Find the onChange method
Method onChangeMethod = null;
for (Method m : superClazz.getMethods()) {
if (m.getName().equals("onChange")) {
onChangeMethod = m;
}
}
if (onChangeMethod == null) {
throw new RuntimeException("onChange method not found");
}
ClassGen superClassGen = new ClassGen(superClazz);
ConstantPoolGen superCp = superClassGen.getConstantPool();
// Add onChange method ref to the class ConstantPool
MethodGen onChangeMethodGen = new MethodGen(onChangeMethod, superClassGen.getClassName(), superCp);
cp.addMethodref(onChangeMethodGen);
// Loop through all methods to inject method invocations if applicable
for (Method m : clazz.getMethods()) {
// Skip methods with names shorter than 3 chars - we're looking for setters and setters would be min 4 chars
if (m.getName().length() < 3) continue;
// Check if the method actually starts with the keyword "set"
boolean isSetMethod = m.getName().substring(0, 3).toUpperCase().equals("SET");
// Get method name without the "set" keyword
String methodName = m.getName().substring(3, m.getName().length());
// Check that we actually have a field set by this setter - that this setter is "valid"
boolean fieldWithSameNameExists = false;
for (Field f : clazz.getFields()) {
if (f.getName().toUpperCase().equals(methodName.toUpperCase())) {
fieldWithSameNameExists = true;
break;
}
}
// Proceed with injection if criteria match
Method newMethod = null;
if (isSetMethod && fieldWithSameNameExists) {
newMethod = injectInMethod(m, onChangeMethodGen, classGen, cp);
}
// Injection returned. Do we have a new/modified method? Yes? Update and write class.
if (newMethod != null) {
classGen.removeMethod(m);
classGen.addMethod(newMethod);
classGen.update();
try {
String packageName = clazz.getPackageName().replace(".", "/");
String className = clazz.getClassName();
className = className.substring(className.lastIndexOf(".") + 1, className.length());
clazz.dump(packageName + "/" + className + "Edited.class");
}
catch (IOException e) {
e.printStackTrace();
}
}
}
}
private static Method injectInMethod(Method m, MethodGen onChangeMethodGen, ClassGen cg, ConstantPoolGen cp) {
MethodGen methodGen = new MethodGen(m, cg.getClassName(), cp);
InstructionList il = methodGen.getInstructionList();
println(il.toString() + "pre insert ^");
// Find the "return" instruction
Instruction returnInstruction = null;
for (Instruction i : il.getInstructions()) {
if (i.getOpcode() == 177) returnInstruction = i;
}
// If found, insert onChange invocation instruction before the return instruction
if (returnInstruction != null) {
int index = cp.lookupMethodref(onChangeMethodGen); // Find the index of the onChange method in the CP
il.insert(returnInstruction, new INVOKEVIRTUAL(index)); // Insert the new instruction
println(il.toString() + "post insert ^");
il.setPositions(); // Fix positions
println(il.toString() + "post set pos ^");
il.update();
methodGen.update();
return methodGen.getMethod();
}
return null;
}
private static void println(String message) {
System.out.println(message);
}
}
Input Java class:
public class DummyStateStore extends StateStore {
private int id = 4321;
public void setId(int id) {
this.id = id;
}
public int getId() {
return id;
}
}
Parent Store class:
public class StateStore {
public void onChange() {
// notifies all subscribers
}
}
Output (decompiled) class file:
public class DummyStateStore extends StateStore {
private int id = 4321;
public DummyStateStore() {
}
public void setId(int id) {
this.id = id;
}
public int getId() {
return this.id;
}
}
Log output:
0: aload_0[42](1)
1: iload_1[27](1)
2: putfield[181](3) 2
5: return[177](1)
pre insert ^
0: aload_0[42](1)
1: iload_1[27](1)
2: putfield[181](3) 2
-1: invokevirtual[182](3) 26
5: return[177](1)
post insert ^
0: aload_0[42](1)
1: iload_1[27](1)
2: putfield[181](3) 2
5: invokevirtual[182](3) 26
8: return[177](1)
post set pos ^
(I checked the index 26 by debugging the code and it is the correct index in the CP)
Now, the questions are:
Why can't the invocation be seen in the decompiled code but it seems to be added to the instructions list? What am I missing?
Where would I be exporting the modified class files in an android build for them to be included in the final apk?
You're trying to use reflection, but there should be no need to do so with Kotlin as you can create higher order functions (functions that take functions as inputs).
You could do something like:
class ChangeableType<T>(private var value: T, private val onChange: () -> Unit) {
fun set(value: T) {
this.value = value
this.onChange.invoke()
}
}
class MyRandomClass() {
val something = ChangeableType(0, { System.print("Something new value: $value") })
val anotherThing = ChangeableType("String", { System.print("Another thing new value: $value") })
}
class ConsumingClass {
val myRandomClass = MyRandomClass()
fun update() {
myRandomClass.apply {
something.set(1)
anotherThing.set("Hello World")
}
}
}

How to mock methods/functions provided by Traits in Groovy

Here's an example:
trait Sender {
def send(String msg){
// do something
}
}
class Service implements Sender {
def myMethod1(){
send('Foo')
myMethod2()
}
def myMethod2(){
}
}
I am trying to test the Service class. However, I would like to stub/mock the calls to the methods provided by the trait (send)?
I have tried several different ways to stub/mock the method send, with no success:
// 1
Service.metaclass.send = { String s -> // do nothing }
// 2
def service = new MyService()
service.metaClass.send = { String s -> // do nothing }
// 3
StubFor serviceStub = new StubFor(Service.class)
serviceStub.demand.send { String s -> // do nothing }
//
trait MockedSender {
def send(String msg) { // do nothing }
}
def service = new Service() as MockedSender
These are just some of the things I tried. I even tried using Mock frameworks like Mockito. Unfortunately, nothing seems to work. Any suggestions???
Try using Spy from Spock framework!
Like this:
trait Sender {
def send(String msg){
println msg
}
}
class Service implements Sender {
def myMethod1(){
send('Foo')
myMethod2()
}
def myMethod2(){
println 'real implementation'
}
}
class UnitTest extends Specification {
def "Testing spy on real object"() {
given:
Service service = Spy(Service)
when:
service.myMethod1()
then: "service.send('Foo') should be called once and should print 'mocked' and 'real implementation' on console"
1 * service.send('Foo') >> { println 'mocked' }
}
}

How to implement Java interface anonymously in scala?

Suppose I have a Java inteface
public interface Bar {
public void baz(String st)
public void jaz()
}
I want to implement above interface anonymously in scala within a function body like:
def foo() = {
val bar : Bar = new Bar() {
// how to do that ?
}
}
If I had to, I'd write it as:
val bar = new Bar {
def baz(st: String): Unit = {
// method impl
}
def jaz(): Unit = {
// method impl
}
}
Though my preference is to avoid side-effecting methods as much as possible, they don't play very nicely with functional programming
val bar = new Bar {
def baz(st: String) {
// method impl
}
def jaz() {
// method impl
}
}

Categories

Resources