Mapstruct : Use of context in source argument of #Mapping - mapstruct

When using multiple arguments in a #Mapper, it seems that the #Context arguments is unreachable
public interface MyMapper {
#Mapping(target="target1", source="arg1.arg") //works
#Mapping(target="target2", source="arg2") //works
#Mapping(target="target3", source="arg2.arg") //works
#Mapping(target="target2", source="context.arg") //NOT WORKING
public MyTarget convert(Object arg1, Object arg2, #Context Object context);
}
I am trying to use and expression="" to work around it, but I can't get it to work.
Any suggestions?
I can see I am not the only one to ever wish this.
https://github.com/mapstruct/mapstruct/issues/1280
Thanks

I ran into the same scenario as I needed a #Context param to be able to pass to a nested mapping function, but also wanted to use it as a source in a #Mapping. I was able to achieve this using expression as follows:
public interface MyMapper {
#Mapping(target="target1", source="arg1")
#Mapping(target="target2", source="arg2")
#Mapping(target="target3", expression="java( contextArg )")
public MyTarget convert(Object arg1, Object arg2, #Context Object contextArg);
}

To answer your second question:
public interface MyMapper {
#Mapping(target="target1", source="arg1.arg")
#Mapping(target="target2", ignore = true ) // leave to after mapping
MyTarget convert(Object arg1, #Context Object context);
#AfterMapping
default convert(Object arg1, #MappingTarget MyTarget target, #Context context) {
target.setTarget2( convert ( context ) );
}
// if you have multipe mappings, you could address them here
#Mapping(target="target2", source="context.arg")
MyInnerTarget convert(Object arg1, Object context);
}

By definition a #Context annotated object is not a source. It is context So you can't use it as source in the #Mapping(target="target2", source="context.arg")
Here is a related github issue with official answer: github issue

Not really clean, but it seems having the same object as source and context allows to use it both ways.
#Mapping(target="target1", source="arg1.arg")
#Mapping(target="target2", source="arg2")
#Mapping(target="target3", source="arg2.arg")
#Mapping(target="target4", source="contextAsSource.arg")
public MyTarget convert(Object arg1, Object arg2, Object contextAsSource, #Context Object context);
Here contextAsSource and context are the same.

Related

Mapstruct: aftermapping with parameters

Here my code:
#Mapping(target = "auditoriaMetas", qualifiedByName = "sdf")
public abstract Auditoria mapToModificacio(QdCF qdcf, QdCFPresenter qdcfPresenter, Integer idInstrument);
#Named("sdf")
public List<AuditoriaMeta> mapToMetas(QdCF current, #Context QdCFPresenter incoming) {
return null;
}
I want that after mapToModificatio is performed, mapToMetas is also executed.
Above code doesn't perform.
Any ideas?
Mapstruct will not consider putting a normal value into one that is annotated with #Context. Therefor if you mark something with #Context, then it should be marked like that through the entire chain of calls.
For example:
#Mapping(target = "auditoriaMetas", source=".", qualifiedByName = "sdf")
public abstract Auditoria mapToModificacio(QdCF qdcf, #Context QdCFPresenter qdcfPresenter, Integer idInstrument);
#Named("sdf")
public List<AuditoriaMeta> mapToMetas(QdCF current, #Context QdCFPresenter incoming) {
return null;
}

How is async interface connected to GWT.create?

When I create a sample project with GWT 2.6, I end up with the following code for RPC proxy initialisation:
private final GreetingServiceAsync greetingService = GWT.create(GreetingService.class);
GreetingService is defined as:
#RemoteServiceRelativePath("greet")
public interface GreetingService extends RemoteService {
String greetServer(String name) throws IllegalArgumentException;
}
and GreetingServiceAsync is defined as:
public interface GreetingServiceAsync {
void greetServer(String input, AsyncCallback<String> callback)
throws IllegalArgumentException;
}
The signature of GWT.Create is:
public static <T> T create(Class<?> classLiteral)
Given all of this, I would expect the assignment of GWT.create to greetingService to fail. Because greetingService is of type GreetingServiceAsync and GWT.create would return GreetingService which has no relation to Async version. I would expect the Java compiler to mark this assignment as invalid, but it is not doing it.
Where is the link between the Async interface and its non async version? Java compiler obviously knows this, but I could not see it in the project source code.
about the fact that it compiles without error: notice how the parameters's type to GWT.create() does not make use of the T type parameter; so what the signature says is: you can pass any class, and it can return any type of object (T is shorthand for T extends Object), there's no connection between the GreetingService.class passed as argument and the GreetingServiceAsync returned, at least not in the GWT.create() method signature.
the connection is in the GWT compiler (actually, in the generator for RPC, which is triggered for every class passed to GWT.create() that extends RemoteService), and it's simply based on a naming rule: return the type whose name is the one of the argument class suffixed with Async. Of course checks are made: both have to be interfaces, and the methods in the async interface have to be the same as the one in the "non async" interface, except the return type of the non-async is moved to a type argument of an additional AsyncCallback parameter. BTW, this specific case is the only reason that GWT.create() still has this signature where the return type is not related to the parameter type.

Morphia converter calling other converters

I want to convert Optional<BigDecimal> in morphia. I created BigDecimalConverter, and it works fine. Now I want to create OptionalConverter.
Optional can hold any object type. In my OptionalConverter.encode method I can extract underlying object, and I'd like to pass it to default mongo conversion. So that if there is string, I'll just get string, if there is one of my entities, I'll get encoded entity. How can I do it?
There are two questions:
1. How to call other converters?
2. How to create a converter for a generic class whose type parameters are not statically known?
The first one is possible by creating the MappingMongoConveter and the custom converter together:
#Configuration
public class CustomConfig extends AbstractMongoConfiguration {
#Override
protected String getDatabaseName() {
// ...
}
#Override
#Bean
public Mongo mongo() throws Exception {
// ...
}
#Override
#Bean
public MappingMongoConverter mappingMongoConverter() throws Exception {
MappingMongoConverter mmc = new MappingMongoConverter(
mongoDbFactory(), mongoMappingContext());
mmc.setCustomConversions(new CustomConversions(CustomConverters
.create(mmc)));
return mmc;
}
}
public class FooConverter implements Converter<Foo, DBObject> {
private MappingMongoConverter mmc;
public FooConverter(MappingMongoConverter mmc) {
this.mmc = mmc;
}
public DBObject convert(Foo foo) {
// ...
}
}
public class CustomConverters {
public static List<?> create(MappingMongoConverter mmc) {
List<?> list = new ArrayList<>();
list.add(new FooConverter(mmc));
return list;
}
}
The second one is much more difficult due to type erasure. I've tried to create a converter for Scala's Map but haven't found a way. Unable to get the exact type information for the source Map when writing, or for the target Map when reading.
For very simple cases, e.g. if you don't need to handle all possible parameter types, and there is no ambiguity while reading, it may be possible though.

Dynamic Query Param

I have requirement where Query Param name is not fixed. i.e.
/Test/Add?a=b,c&a1=b1,c1
/Test/Add?d=e,f&c1=d1,f1
I have read in some article saying use #Context URI, HttpServlerRequest.
Can you please guide me how to implement this in Jersey?
You could add the UriInfo to your class like this:
public class Example {
#Context
UriInfo uriInfo;
#GET
#Path("/")
public void get() {
System.out.println(uriInfo.getPathParameters());
}
}

Using structuremap with log4net wrapper

I have the following interface:
public interface ILogger
{
void Debug(string message, params object[] values);
void Info(string message, params object[] values);
void Warn(string message, params object[] values);
void Error(string message, params object[] values);
void Fatal(string message, params object[] values);
}
and the following implementation:
public class Log4netLogger : ILogger
{
private ILog _log;
public Log4netLogger(Type type)
{
_log = LogManager.GetLogger(type);
}
public void Debug(string message, params object[] values)
{
_log.DebugFormat(message, values);
}
// other logging methods here...
}
My idea was to use structuremap to instantiate the Log4netLogger class with using the Type of the class that did the logging. However, I can't for the life of me figure out how to pass the type of the calling class to structuremap so that it can be passed to the constructor of the logging implementation. Any advice on how to do that (or a better way) would be most appreciated.
We use a similar ILogger wrapper around log4net and typically use constructor injection. We use an interceptor as a factory method responsible for creating the Logger. Here is our typical registry for logging setup.
public class CommonsRegistry : Registry
{
public CommonsRegistry()
{
For<ILogger>()
.AlwaysUnique()
.TheDefault.Is.ConstructedBy(s =>
{
if (s.ParentType == null)
return new Log4NetLogger(s.BuildStack.Current.ConcreteType);
return new Log4NetLogger(s.ParentType);
});
var applicationPath = Path.GetDirectoryName(Assembly.GetAssembly(GetType()).Location);
var configFile = new FileInfo(Path.Combine(applicationPath, "log4net.config"));
XmlConfigurator.ConfigureAndWatch(configFile);
}
}
The parent type null check is necessary when there are dependencies on concrete types.
The rest is optional log4net setup stuff.
One thing I do like about this setup is the ability to use a null loggers for unit testing.
If the type parameter is context-specific, I don't think this is going to work as shown. If you need to pass something context specific in the constructor, you are likely going to have to create a factory interface and implementation that returns an instance of the ILogger:
public interface ILoggerFactory
{
ILogger Create(Type type);
}
public class LoggerFactory : ILoggerFactory
{
public ILogger Create(Type type)
{
return new Log4netLogger(type);
}
}
It might be possible to bootstrap StructureMap to supply the instance you want based on the type, but that assumes a limited number of types that you know in advance.
I really need to get out of the habit of answering my own question, but for those who run across it, here's the answer.
return ObjectFactory.With(type).GetInstance<T>();
I actually have a wrapper to structuremap (to avoid exposing the structuremap dependency to my app) that looks like the following:
public static class ServiceManager
{
public static T Get<T>()
{
return ObjectFactory.GetInstance<T>();
}
public static T Get<T>(Type type)
{
return ObjectFactory.With(type).GetInstance<T>();
}
}
Any time in the code I need a logger, I call the following:
ServiceManager.Get<ILogger>(GetType()).Info("Logging page view...");