cglib proxy and null instance variables [duplicate] - aop

My query is why is an instance variable null when cglib proxy with spring tries to access it inside a final method. This would mean that even if I had declared the variable directly like in the class below -
class A {
String prop="a";
public final eat(){
return prop;
}
}
The call to the proxy class's eat() method would return null as the method is final. This seems to imply that when the proxy is initialised , the prop variable is set to null .
My current understanding which I want to validate -
Spring internally seems to use SpringObjenesis#newInstance to create a proxy object [ I debugged through the code ]. This seems to create the class instance with all instance variable set to null. When the proxy now tries to return the instance variable , it will return null.

The way Spring uses CGLIB proxies is by a delegation pattern, see this answer for a schematic code sample about how this is implemented.
Please understand that when creating the proxy, only method delegation is being considered. The proxy object's instance fields will not be initialised, because usually at some point the proxy calls the delegate's method. Therefore, the delegate will transparently access its own (initialised) fields.
In case of a final method, however, no proxy method can be generated, because final methods cannot be overridden. I.e., in this case the original method is called, but when accessing fields via e.g. return myField (a shorthand for return this.myField), this is the proxy instance, because there was no method call delegation to the original object. This explains why the result is null, 0 or false, depending on the field type.
package de.scrum_master.spring.q72993106;
import org.springframework.stereotype.Component;
#Component
public class MyComponent {
String food = "bread";
String beverage = "water";
public final String eat() {
return food;
}
public String drink() {
return beverage;
}
}
Add an aspect as a simple way to trigger proxy creation:
package de.scrum_master.spring.q72993106;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class MyAspect {
#Before("execution(* MyComponent.*())")
public void myAdvice(JoinPoint jp) {
System.out.println(jp);
}
}
package de.scrum_master.spring.q72993106;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#SpringBootApplication
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class DemoApplication {
public static void main(String[] args) throws Throwable {
try (ConfigurableApplicationContext appContext = SpringApplication.run(DemoApplication.class, args)) {
doStuff(appContext);
}
}
private static void doStuff(ConfigurableApplicationContext appContext) {
MyComponent myComponent = appContext.getBean(MyComponent.class);
System.out.println("Eating " + myComponent.eat());
System.out.println("Drinking " + myComponent.drink());
}
}
Running this application yields the following console log:
Eating null
execution(String de.scrum_master.spring.q72993106.MyComponent.drink())
Drinking water
See? You get the expected value for the non-final method, because delegation to the original object takes place (and the aspect kicks in), but for the final method the result is null, as explained above.

Related

unmapped target property and mapstruct calls constructor with null parameter

I'm exploring mapstruct to map JPA entities and DTO objects. Entities and DTOs have abstract base classes that contain id and version fields that I'd like to keep private so that they can not be modified (public getter, no setter for both types). I made a most simple reproducer to demonstrate the idea. Abstract Base class has a private field name. To copy the field values back and forth Base defines a constructor that has a Base parameter. The constructor picks the private field from the parameter and assigns it to it's own private field:
package de.ruu.lab.map.read_only_field_in_base;
public abstract class Base
{
private String name;
public Base(String name) { this.name = name; }
protected Base(Base source) { name = source.name; }
public String getName() { return name; }
}
These are the subclasses of Base:
package de.ruu.lab.map.read_only_field_in_base;
import de.ruu.lab.map.read_only_field_in_base.SimpleMapper.Default;
public class Source extends Base
{
public Source(String name) { super(name); }
#Default
public Source(Base base) { super(base); }
}
package de.ruu.lab.map.read_only_field_in_base;
import de.ruu.lab.map.read_only_field_in_base.SimpleMapper.Default;
public class Target extends Base
{
public Target(String name) { super(name); }
#Default
public Target(Base base) { super(base); }
}
I have to annotate the default constructor to resolve constructor ambiguity for mapstruct. The mapper looks like this:
package de.ruu.lab.map.read_only_field_in_base;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.RetentionPolicy.CLASS;
import java.lang.annotation.Retention;
import org.mapstruct.Mapper;
import org.mapstruct.Qualifier;
import org.mapstruct.factory.Mappers;
#Mapper
public interface SimpleMapper
{
SimpleMapper INSTANCE = Mappers.getMapper(SimpleMapper.class);
Source toSource(Target target);
Target toTarget(Source source);
#Qualifier // make sure that this is the MapStruct qualifier annotation
#java.lang.annotation.Target(CONSTRUCTOR)
#Retention(CLASS)
public #interface Default { }
}
The first problem is that mapstruct warns that there is an unmapped target property "base". What does that mean? Which target property is not mapped? Wouldn't it be possible to print the name of the property in the warning? I use eclipse as IDE, maybe the behaviour is different with other tools?
I tried annotating the mapping methods with
#Mapping(target="name", ignore = true)
but that does not let the warning disappear.
Because mapstruct just makes a warning I hoped everything would be ok and I created a tiny test class:
package de.ruu.lab.map.read_only_field_in_base;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import org.junit.jupiter.api.Test;
class SimpleMapperTest
{
#Test void shouldMapSourceToTarget()
{
Source source = new Source("map me");
Target target = SimpleMapper.INSTANCE.toTarget(source);
assertThat(target.getName(), is(source.getName()));
}
#Test void shouldMapTargetToSource()
{
Target target = new Target("map me");
Source source = SimpleMapper.INSTANCE.toSource(target);
assertThat(source.getName(), is(target.getName()));
}
}
Both tests fail with a NPE because of some strange code mapstruct generated:
package de.ruu.lab.map.read_only_field_in_base;
import javax.annotation.processing.Generated;
#Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2022-09-18T11:08:20+0200",
comments = "version: 1.5.2.Final, compiler: Eclipse JDT (IDE) 1.4.200.v20220802-0458, environment: Java 17.0.2 (GraalVM Community)"
)
public class SimpleMapperImpl implements SimpleMapper {
#Override
public Source toSource(Target target) {
if ( target == null ) {
return null;
}
Base base = null;
Source source = new Source( base );
return source;
}
#Override
public Target toTarget(Source source) {
if ( source == null ) {
return null;
}
Base base = null;
Target target = new Target( base );
return target;
}
}
Obviously code like this causes the NPE:
Base base = null;
Source source = new Source( base );
IMO this would be correct ("target" is the name of the method's parameter):
Source source = new Source( target );
Maybe this can be solved in an upcoming version. Meanwhile, is there any recommendation how to deal with this now?
Thanks!
The reason why you are getting the warnings is the fact that MapStruct doesn't really care about the private / protected fields you have.
When performing a mapping MapStruct looks at the setters and the constructor parameters to decide which properties need to be mapped.
Looking at your examples you have annotated the constructor that takes Base as a default constructor. This means that from the point of view of MapStruct your objects have properties that are taking Base and thus there is the warning for the unmapped property base.
There are 2 ways that you can do to fix this:
Instruct MapStruct how to map to the base property
#Mapper
public interface SimpleMapper
{
SimpleMapper INSTANCE = Mappers.getMapper(SimpleMapper.class);
#Mapping(target = "base", source = "target")
Source toSource(Target target);
#Mapping(target = "base", source = "source")
Target toTarget(Source source);
}
using the #Mapping you will tell MapStruct that you want to map the source parameters to the base property.
Annotated the constructor with the string a #Default
Instead of annotating the constructor that takes Base as input you can annotate the constructor that takes String as a default constructor.
This way MapStruct will look for how to map a property with the name name and thus will use the public Base#getName method to perform the mapping.
Note: I saw that you have #Qualifier on the #Default annotation, this is not needed. The only requirement for MapStruct for the default annotation is that it needs to be named like that.

cglib proxy and null instance variable internals

My query is why is an instance variable null when cglib proxy with spring tries to access it inside a final method. This would mean that even if I had declared the variable directly like in the class below -
class A {
String prop="a";
public final eat(){
return prop;
}
}
The call to the proxy class's eat() method would return null as the method is final. This seems to imply that when the proxy is initialised , the prop variable is set to null .
My current understanding which I want to validate -
Spring internally seems to use SpringObjenesis#newInstance to create a proxy object [ I debugged through the code ]. This seems to create the class instance with all instance variable set to null. When the proxy now tries to return the instance variable , it will return null.
The way Spring uses CGLIB proxies is by a delegation pattern, see this answer for a schematic code sample about how this is implemented.
Please understand that when creating the proxy, only method delegation is being considered. The proxy object's instance fields will not be initialised, because usually at some point the proxy calls the delegate's method. Therefore, the delegate will transparently access its own (initialised) fields.
In case of a final method, however, no proxy method can be generated, because final methods cannot be overridden. I.e., in this case the original method is called, but when accessing fields via e.g. return myField (a shorthand for return this.myField), this is the proxy instance, because there was no method call delegation to the original object. This explains why the result is null, 0 or false, depending on the field type.
package de.scrum_master.spring.q72993106;
import org.springframework.stereotype.Component;
#Component
public class MyComponent {
String food = "bread";
String beverage = "water";
public final String eat() {
return food;
}
public String drink() {
return beverage;
}
}
Add an aspect as a simple way to trigger proxy creation:
package de.scrum_master.spring.q72993106;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class MyAspect {
#Before("execution(* MyComponent.*())")
public void myAdvice(JoinPoint jp) {
System.out.println(jp);
}
}
package de.scrum_master.spring.q72993106;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#SpringBootApplication
#Configuration
#EnableAspectJAutoProxy(proxyTargetClass = true)
public class DemoApplication {
public static void main(String[] args) throws Throwable {
try (ConfigurableApplicationContext appContext = SpringApplication.run(DemoApplication.class, args)) {
doStuff(appContext);
}
}
private static void doStuff(ConfigurableApplicationContext appContext) {
MyComponent myComponent = appContext.getBean(MyComponent.class);
System.out.println("Eating " + myComponent.eat());
System.out.println("Drinking " + myComponent.drink());
}
}
Running this application yields the following console log:
Eating null
execution(String de.scrum_master.spring.q72993106.MyComponent.drink())
Drinking water
See? You get the expected value for the non-final method, because delegation to the original object takes place (and the aspect kicks in), but for the final method the result is null, as explained above.

Accessing a Kotlin extension function from Java

Is it possible to access extension functions from Java code?
I defined the extension function in a Kotlin file.
package com.test.extensions
import com.test.model.MyModel
/**
*
*/
public fun MyModel.bar(): Int {
return this.name.length()
}
Where MyModel is a (generated) java class. Now, I wanted to access it in my normal java code:
MyModel model = new MyModel();
model.bar();
However, that doesn't work. The IDE won't recognize the bar() method and compilation fails.
What does work is using with a static function from kotlin:
public fun bar(): Int {
return 2*2
}
by using import com.test.extensions.ExtensionsPackage so my IDE seems to be configured correctly.
I searched through the whole Java-interop file from the kotlin docs and also googled a lot, but I couldn't find it.
What am I doing wrong? Is this even possible?
Perhaps like this:
// CallExtensionFunction.java
package com.example.groundup;
public class CallExtensionFunction {
public static void main(String[] args) {
MyModel myModel = new MyModel();
int bar = MyModelKt.bar(myModel);
System.out.println(bar);
}
}
// MyModell.kt
package com.example.groundup
fun MyModel.bar(): Int {
return this.name.length
}
class MyModel() {
val name = "Hugo"
}
The extension function is provided in the corresponding singleton with the suffix "Kt" as a static method.

Problem converting Java class containing a static factory method to Kotlin

I have had no sucess trying to convert this to Kotlin and use it.
It works as Java, just not with my Kotlin (which I converted using IntelliJ Kotlin Plugin)
the problem appears to be this part
#PluginFactory
public static AnsiConsoleAppender createAppender(
I tried to add #JvmStatic and I get this error:
Unable to invoke factory method in class AnsiConsoleAppender for element AnsiConsoleAppender: java.lang.IllegalArgumentException: Parameter specified as non-null is null: method AnsiConsoleAppender$Companion.AnsiConsoleAppender, parameter filter java.lang.reflect.InvocationTargetException
import org.apache.logging.log4j.core.Filter;
import org.apache.logging.log4j.core.Layout;
import org.apache.logging.log4j.core.LogEvent;
import org.apache.logging.log4j.core.appender.AbstractAppender;
import org.apache.logging.log4j.core.appender.AppenderLoggingException;
import org.apache.logging.log4j.core.config.plugins.Plugin;
import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
import org.apache.logging.log4j.core.config.plugins.PluginElement;
import org.apache.logging.log4j.core.config.plugins.PluginFactory;
import org.apache.logging.log4j.core.layout.PatternLayout;
import java.io.Serializable;
/**
* <h2>AnsiConsoleAppender</h2>
*
* <h3>notes</h3>
* <li>class name need not match the #Plugin name</li>
* <h3>more<h3/>
* <li>How to Create a Custom Appender in log4j2?</li>
*/
#Plugin(name="AnsiConsoleAppender", category="Core", elementType="appender", printObject=true)
public final class AnsiConsoleAppender extends AbstractAppender {
protected AnsiConsoleAppender(String name, Filter filter,
Layout<? extends Serializable> layout, final boolean ignoreExceptions) {
super(name, filter, layout, ignoreExceptions);
}
// The append method is where the appender does the work.
// Given a log event, you are free to do with it what you want.
// This example demonstrates:
// 1. Concurrency: this method may be called by multiple threads concurrently
// 2. How to use layouts
// 3. Error handling
//#Override
public void append(LogEvent event) {
try {
final byte[] bytes = getLayout().toByteArray(event);
// output code goes here
} catch (Exception ex) {
if (!ignoreExceptions()) throw new AppenderLoggingException(ex);
}
}
// Your custom appender needs to declare a factory method
// annotated with `#PluginFactory`. Log4j will parse the configuration
// and call this factory method to construct an appender instance with
// the configured attributes.
#PluginFactory
public static AnsiConsoleAppender createAppender(
#PluginAttribute("name") String name,
#PluginElement("Layout") Layout<? extends Serializable> layout,
#PluginElement("Filter") final Filter filter,
#PluginAttribute("otherAttribute") String otherAttribute) {
if (name == null) {
LOGGER.error("No name provided for AnsiConsoleAppenderImpl");
return null;
}
if (layout == null) {
layout = PatternLayout.createDefaultLayout();
}
return new AnsiConsoleAppender(name, filter, layout, true);
}
}
import org.apache.logging.log4j.core.AbstractLifeCycle
import org.apache.logging.log4j.core.Filter
import org.apache.logging.log4j.core.Layout
import org.apache.logging.log4j.core.LogEvent
import org.apache.logging.log4j.core.appender.AbstractAppender
import org.apache.logging.log4j.core.appender.AppenderLoggingException
import org.apache.logging.log4j.core.config.plugins.Plugin
import org.apache.logging.log4j.core.config.plugins.PluginAttribute
import org.apache.logging.log4j.core.config.plugins.PluginElement
import org.apache.logging.log4j.core.config.plugins.PluginFactory
import org.apache.logging.log4j.core.layout.PatternLayout
import java.io.Serializable
#Plugin(name = "AnsiConsoleAppender", category = "Core", elementType = "appender", printObject = true)
class AnsiConsoleAppender /*protected*/ constructor(
name: String, filter: Filter,
layout: Layout<out Serializable>, ignoreExceptions: Boolean
) : AbstractAppender(name, filter, layout, ignoreExceptions) {
// The append method is where the appender does the work.
// Given a log event, you are free to do with it what you want.
// This example demonstrates:
// 1. Concurrency: this method may be called by multiple threads concurrently
// 2. How to use layouts
// 3. Error handling
//#Override
override fun append(event: LogEvent) {
try {
val bytes = layout.toByteArray(event)
//AnsiColor.out(String(bytes), ColorMaps.ASTERIKSY, null, true)
} catch (ex: Exception) {
if (!ignoreExceptions()) throw AppenderLoggingException(ex)
}
}
companion object {
// Your custom appender needs to declare a factory method
// annotated with `#PluginFactory`. Log4j will parse the configuration
// and call this factory method to construct an appender instance with
// the configured attributes.
#JvmStatic
#PluginFactory
fun createAppender(
#PluginAttribute("name") name: String?,
#PluginElement("Layout") layout: Layout<out Serializable>?,
#PluginElement("Filter") filter: Filter,
#PluginAttribute("otherAttribute") otherAttribute: String
): AnsiConsoleAppender? {
val lay = layout ?: PatternLayout.createDefaultLayout()
if (name == null) {
AbstractLifeCycle.LOGGER.error("No name provided for AnsiConsoleAppenderImpl")
return null
}
return AnsiConsoleAppender(name, filter, lay, true)
}
}
}
what am I missing?
The exception says filter is null.
So it should be nullable type.
companion object {
#PluginFactory
#JvmStatic
fun createAppender(
#PluginAttribute("name") name: String,
#PluginElement("Layout") layout: Layout<out Serializable>,
#PluginElement("Filter") filter: Filter?,
#PluginAttribute("otherAttribute") otherAttribute: String?
): AnsiConsoleAppender {
In Kotlin factory methods are usually defined in a companion object of a class, e.g.:
class AnsiConsoleAppender : AbstractAppender() {
companion object {
// add JvmStatic annotation if you want to call this method from Java file
#JvmStatic
#PluginFactory
fun createAppender(/*params*/): AnsiConsoleAppender {
//...
}
}
}
More on companion object

How do I mock an inherited method that has generics with JMockit

I have this abstract class:
public abstract class Accessor<T extends Id, U extends Value>
{
public U find(T id)
{
// let's say
return getHelper().find(id);
}
}
And an implementation:
public FooAccessor extends Accessor<FooId,Foo>
{
public Helper getHelper
{
// ...
return helper;
}
}
And I would like to mock the calls to FooAccessor.find.
This:
#MockClass(realClass=FooAccessor.class)
static class MockedFooAccessor
{
public Foo find (FooId id)
{
return new Foo("mocked!");
}
}
will fail with this error:
java.lang.IllegalArgumentException: Matching real methods not found for the following mocks of MockedFooAccessor:
Foo find (FooId)
and I understand why... but I don't see how else I could do it.
Note: yes, I could mock the getHelper method, and get what I want; but this is more a question to learn about JMockit and this particular case.
The only way around this I have found is to use fields
#Test
public void testMyFooMethodThatCallsFooFind(){
MyChildFooClass childFooClass = new ChildFooClass();
String expectedFooValue = "FakeFooValue";
new NonStrictExpectations(){{
setField(childFooClass, "fieldYouStoreYourFindResultIn", expectedFooValue);
}};
childFooClass.doSomethingThatCallsFind();
// if your method is protected or private you use Deencapsulation class
// instead of calling it directly like above
Deencapsulation.invoke(childFooClass, "nameOfFindMethod", argsIfNeededForFind);
// then to get it back out since you used a field you use Deencapsulation again to pull out the field
String actualFoo = Deencapsulation.getField(childFooClass, "nameOfFieldToRunAssertionsAgainst");
assertEquals(expectedFooValue ,actualFoo);
}
childFooClass doesn't need to be mocked nor do you need to mock the parent.
Without more knowledge of your specific case this strategy has been the best way for me to leverage jMockit Deencapsulation makes so many things possilbe to test without sacrificing visibility. I know this doesn't answer the direct question but I felt you should get something out of it. Feel free to downvote and chastise me community.
Honestly, I do not find it in any way different from mocking regular classes. One way to go is to tell JMockit to mock only the find method and use Expectations block to provide alternate implementation. Like this:
abstract class Base<T, U> {
public U find(T id) {
return null;
}
}
class Concrete extends Base<Integer, String> {
public String work() {
return find(1);
}
}
#RunWith(JMockit.class)
public class TestClass {
#Mocked(methods = "find")
private Concrete concrete;
#Test
public void doTest() {
new NonStrictExpectations() {{
concrete.find((Integer) withNotNull());
result = "Blah";
}}
assertEquals("Blah", concrete.work());
}
}
Hope it helps.