Not the right Xstream converter selected by Cucumber - java

I've been using Cucumber for dozens of Java projects so far an never encountered this issue before, so I'm a bit puzzled.
I have a simple table that I want to map to a List in my step definition.
And deal repository contains
| dealPid | closingDate | expenseCode |
| 1 | 01/06/2015 | test |
I started by creating my own POJO with only required fields, following standard camel case convention (getters/setters are omitted for clarity)
public class Deal {
private String dealPid ;
private Date closingDate ;
private String expenseCode;
}
my step definition :
#Given("^deal repository contains$")
public void deal_repository_contains(
#Format("dd/MM/yyyy") List<DEAL> deals) throws Throwable {
...
}
Fields get mapped properly and I'm getting a List with one Deal item, fine. When I go in debug, up to cucumber.runtime.xstream.LocalizedXStreams
Converter converter = converterLookup.lookupConverterForType(clazz);
I see a xStream ReflectionConverted gets selected for the Datatable parsing.
This is a legacy project, and other developers then told me there was already a class existing for this. So now I want to switch to that class, that follows really strange conventions : class name is the table name on which it's mapped in DB, and most of the attributes names are actually the column names..
So now I'm using this legacy DEAL class from another package in my step definition, so I'm expecting a List to come up, but it doesn't. I get a List but even first row gets parsed. In debug, I see the converter that gets selected is a DynamicClassWithStringAssignableConverter instead of a ReflectionConverter previously, which is why the parsed result is different in the end.
Unfortunately, I'm unable to go further in debug and understand why this implementation gets selected, as Xstream is repackaged in cucumber-jvm-deps and Eclipse gets lost (or I don't know how to attach sources correctly in that case).
I tried adding temporarily the fields I need with proper names (ie same as in my initial Deal class) in DEAL class, but it doesn't work.
Initially, DEAL class was implementing Serializable : I removed it, but still the same behavior.
It actually looks like because the class name is full upper case, a different Xstream converter gets selected...
Can it really be the root cause of the issue ?
Thanks

Just hit this very surprising behaviour myself - in my case the issue turned out to be that DynamicClassWithStringAssignableConverter.canConvert returns true if the type to be converted has a constructor that takes a single, String argument.
Work around was to remove the constructor!

Related

Using Lombok's SuperBuilder with Hibernate Validator (jakarta.validation.x) annotation on a container type leads to "Type mismatch"

I have a class ActivitiesModel which uses Lombok's SuperBuilder.
import jakarta.validation.NotBlank;
// other imports and statements omitted for brevity.
#Data
#SuperBuilder
#NoArgsConstructor
public class ActivitiesModel {
public static final String ACTIVITIES_NOT_NULL_MESSAGE = "Activities cannot be null";
public static final String ACTIVITY_NOT_BLANK_MESSAGE = "Activity cannot be blank";
#NotNull(message = ACTIVITIES_NOT_NULL_MESSAGE)
private List<#NotBlank(message = ACTIVITY_NOT_BLANK_MESSAGE) String> activities;
}
I am using this builder to create an object of ActivitiesModel, and then validating it using Hibernate's Validator interface:
// Somewhere else in the application.
// Create an object using the builder method.
ActivitiesModel activitiesModel = ActivitiesModel.builder()
.activities(List.of("hello", "world")) // <----- Point A
.build();
// Validate the object using Hibernate's validator.
validator.validate(activitiesModel);
However, running this code gives me the following error:
java.lang.Error:
Unresolved compilation problem:
Type mismatch: cannot convert from List<String> to List<E>
The stack trace seems to be pointing at Point A.
I have tried the following approaches:
Replacing the #SuperBuilder with #Builder and #AllArgsConstructor.
Replacing the message attribute with a string literal instead of a static final variable, i.e:
private List<#NotBlank(message = "Activity cannot be blank") String> activities;
1st approach seems to fix this error, however, it's not something I can use as I need to extend the builder functionality to a subclass of ActivitiesModel. Also, this issue is also present in another abstract class, so the super builder functionality for parent classes is definitely required.
2nd approach also works in solving the error. However, going with it is a bit problematic because I then need to have the same message string in the validation test for this model class, which is something I would like to avoid as it duplicates the string.
Another thing to note is that this error only seems to occur in the presence of an annotation on the generic type parameter of the container, which is NotBlank in this case. It is not influenced by any annotations which are present directly on the field itself (NotNull in this case).
So, all in all, these are the questions that I would like to get some answers to:
Somehow, Lombok is able to figure out the types in case of a string literal but not in case of a static final String. Why is that?
Am I going about this totally wrong? The problem occurs because I'm trying to store the message string in a variable, and I'm trying to re-use the same variable at two places: the annotation's message attribute, and in the validation test for the model class. Should I not be checking for the presence of the message in my validation tests, but be checking for something else instead?
For anyone who comes across this later on, the research for this issue has led me to believe that comparing message strings in tests is not the way to go about writing validation test cases. Another downside to this approach is that you might have different validation messages for different locales. In that case, the message string itself might be a template e.g. my.message.key with its values in a ResourceBundle provided to Hibernate, i.e. files such as ValidationMessages.properties and ValidationMessages_de.properties.
In such a scenario, you could compare message for one locale in your validation test case, however, a better approach might be to check the annotation and the field for which the validation has failed. We can get both of these pieces of information via the ConstraintViolation and subsequently the ConstraintDescriptor types, provided by Hibernate. This way we can circumvent checking the message itself, but rely on the actual validation annotation which has failed.
As for the solution to this question, it seems it was a build cache issue. Cleaning maven's build cache results in this code working perfectly fine, but VSCode still seems to have an issue. For now, I will choose to ignore that.

Change the name of duplicated events in #region "Event implementation for <interface>" for Xamarin Android Binding library

I'm attempting to create a Xamarin Binding Library for the Android UseButton library. I created my binding project, included the .aar and set Build Action = LibraryProjectZip. As was expected, a bunch of errors popped up and I've managed to get rid of most using the Metadata.xml file, except for a bunch that follow the same pattern:
Error CS0102 The type 'CrossBridgeCommunicator' already contains a definition for 'WebViewDismiss'
I checked the CrossBridgeCommunicator class, and found it has 2 copies of about 12 events. Here's a snippet of one of these duplicates
#region "Event implementation for Com.Usebutton.Sdk.Internal.Bridge.BridgeMessageParser.IListener"
...
public event EventHandler WebViewDismiss {
add {
global::Java.Interop.EventHelper.AddEventHandler<global::Com.Usebutton.Sdk.Internal.Bridge.BridgeMessageParser.IListener, global::Com.Usebutton.Sdk.Internal.Bridge.BridgeMessageParser.IListenerImplementor>(
ref weak_implementor___SetMainBridge,
__CreateIListenerImplementor,
__v => MainBridge = __v,
__h => __h.OnWebViewDismissHandler += value);
}
...
}
...
public event EventHandler WebViewDismiss {
add {
global::Java.Interop.EventHelper.AddEventHandler<global::Com.Usebutton.Sdk.Internal.Bridge.BridgeMessageParser.IListener, global::Com.Usebutton.Sdk.Internal.Bridge.BridgeMessageParser.IListenerImplementor>(
ref weak_implementor___SetWidgetBridge,
__CreateIListenerImplementor,
__v => WidgetBridge = __v,
__h => __h.OnWebViewDismissHandler += value);
}
...
}
The only difference between them is that they access different attributes in their bodies (the "weak_implementor__"). I checked the original class in a Java decompiler and it doesn't implement any of these events; in fact, it doesn't even implement the interface. What it does have are 2 fields of this interface type. I'm guessing Xamarin creates these methods for some reason, but I don't know why or how. They don't even appear on the api.xml (nor do the fields).
I tried to change the name of the events using the Metadata.xml file, but, since these events don't even exist in the Java class, I don't know how to find them. I even tried to remove the fields using "remove-node", but am getting the warning no nodes matched. Does anyone know how can I change these events' names? Again, they come from the same interface, but are created directly on the class that has 2 fields of the interface type.
Thanks in advance.

Swagger/Openapi-Annotations: How to produce allOf with $ref?

I'm generating Rest endpoints including adding Openapi/Swagger annotations to the generated code.
While it works quite well with basic types, I have some problems with custom classes.
Right now I have a lot of duplicate schema entries for the custom classes (using #Schema(implementation = MyClass.class)) but at least the needed information is there. However I'd like to find a way to remove the duplicate schema entries while retaining the additional information.
On a github-issue discussing the $ref and lack of sibling properties I found an example how you would write it manually in yaml in order to get the result I'm looking for, however I can't figure out how to set the annotations to produce it.
This is how I think the annotation should look like if I follow the example (just to be on the safe side it is added to both the getter and the setter):
import io.swagger.v3.oas.annotations.media.Schema;
...
public class SepaPaymentRequest {
...
#Schema(name = "w307BetrBeg", description = "BETRAG BEGUENSTIGTER ", allOf = { com.diesoftware.services.utils.Betrag.class }, required = true)
public void setW307BetrBeg(final Betrag w307BetrBeg) {
this.w307BetrBeg = w307BetrBeg;
}
...
}
However what I get when I fetch the openapi.yaml (snippet):
w307BetrBeg:
$ref: '#/components/schemas/Betrag'
What I'd like to have:
w307BetrBeg:
title: 'Betrag'
description: 'BETRAG BEGUENSTIGTER'
allOf:
- $ref: '#/components/schemas/Betrag'
Any hints are more than welcome.
I haven't found a way to do it using annotations, i.e. by annotating the class.
I think it's possible to do, by:
Creating a model
Injecting the model using a ModelConverter
When I say "a model" I mean an instance of io.swagger.v3.oas.models.media.Schema.
In particular I think you'd want to create and inject a io.swagger.v3.oas.models.media.ComposedSchema instance, which supports allOf.
Doing this (i.e. creating model instances) isn't very different from hand-writing the YAML.
Another possibility -- which I haven't tried -- might be to write a slightly different ModelConverter, which you install into the chain of converters. Then, intercept calls to resolve which return a SchemaObject whose name is Betrag, and (sometimes?) replace that with a ComposedSchema instance which uses allOf.

How can I map a PostgreSQL varchar[] (array column) to a List<String> in Java using Hibernate?

I have a column in PostgreSQL 9.6 of type "character varying[]" which is essentially an array of strings. I am using Dropwizard with Hibernate to handle the database connection. Normally I just need to provide an annotation to define the data type, however, Hibernate is complaining about the deserialization of a varchar[] type. How do I map this to a List in Java?
I have tried implement my own UserType extended class to handle the (de)serialization with no luck. Any help would be most appreciated.
UPDATE:
I took a look at this link posted by #Waqas and I was able to at least create a type that extends UserType to implement the mapping of varchar[] to String[] in Java.
Some differences in my implementation:
In the nullSafeSet() and nullSafeGet() methods that need to be implemented (#Override), I had to use a (newer?) class called SharedSessionContractImplementor from org.hibernate.engine.spi instead of the (older?) class SessionImplementor.
When I implemented this change and added the #Type annotation to my column mapping (in my entity data class) my runtime was complaining about an #Override that apparently wasn't valid for a certain HibernateBundle class (error below). Even though maven built the jar without any issues and I only have Java 1.8 installed on my machine (OpenSuse). P.S. I am using Dropwizard 1.2 and I took the declaration of the HibernateBundle straight from their documentation. Nevertheless, I deleted the #Override annotation and it works now. Not sure why, or how, but it does.
Error as promised:
INFO [2017-11-08 22:39:06,220] org.eclipse.jetty.util.log: Logging initialized #1137ms to org.eclipse.jetty.util.log.Slf4jLog
INFO [2017-11-08 22:39:06,310] io.dropwizard.server.DefaultServerFactory: Registering jersey handler with root path prefix: /
INFO [2017-11-08 22:39:06,312] io.dropwizard.server.DefaultServerFactory: Registering admin handler with root path prefix: /
java.lang.Error: Unresolved compilation problem:
The method getDataSourceFactory(ApplicationConfiguration) of type new HibernateBundle<ApplicationConfiguration>(){} must override a superclass method
at com.tksoft.food.Application$1.getDataSourceFactory(Application.java:24)
at com.tksoft.food.Application$1.getDataSourceFactory(Application.java:1)
at io.dropwizard.hibernate.HibernateBundle.run(HibernateBundle.java:61)
at io.dropwizard.hibernate.HibernateBundle.run(HibernateBundle.java:15)
at io.dropwizard.setup.Bootstrap.run(Bootstrap.java:200)
at io.dropwizard.cli.EnvironmentCommand.run(EnvironmentCommand.java:42)
at io.dropwizard.cli.ConfiguredCommand.run(ConfiguredCommand.java:85)
at io.dropwizard.cli.Cli.run(Cli.java:75)
at io.dropwizard.Application.run(Application.java:93)
at com.tksoft.food.Application.main(Application.java:30)
Any way, this has left me super confused, but it is working so I am happy :) (for now). I just have to figure out if I can map it to a List instead of an array :/

Error java.lang.NoSuchMethodError

SOLVED
I'm using DTO to map my DB to a Java object.
I had an error like this doku.eds2.dto.Transaction.getListedStatus()Ljava/lang/Character;
I have checked my Transaction.java file, it contains the getListedStatus() method.
#Column(name="listed_status", length=1)
public Character getListedStatus() {
return this.listedStatus;
}
public void setListedStatus(Character listedStatus) {
this.listedStatus = listedStatus;
}
and also my table contains this field :
Column | Type | Modifiers
listed_status | character(1) |
How can I fix this error?
Thank in advance.
This sort of error often happens if you have two different versions of a class, one with the method and one without. If the one without the method ends up in the classpath earlier than the one with, then you will get an error like this.
Make sure that your classpath does not contain any other classes of the type "doku.eds2.dto.Transaction". If you're in Eclipse, you can do this by pressing Ctrl-Shift-T and entering the class name into the search. If you get multiple matches, you probably have a class path or dependency problem.

Categories