Automatically wrapping/converting JavaBeans into case classes - scala

We are using Kryo to communicate between a Scala application and a Java application. Since the class definitions have to be used from Java (and we don't want to include the Scala library as a dependency in the Java applicaton) we are using JavaBeans to define the transfer objects.
However, using JavaBeans directly in Scala is a bit of a hassle. No pattern matching, having to use new, etc. What we're doing right now is defining extractors and apply methods in separate objects on the Scala side to make it nicer to work with these classes.
Since most of what we need is boilerplate, we are wondering if there would be a way of doing this automatically. For example, we have this JavaBean (there are about 20+ different message types):
public class HandshakeRequest extends Request {
private String gatewayId;
private String requestId;
private Date timestamp = new Date();
public HandshakeRequest(String gatewayId, String requestId) {
this.gatewayId = gatewayId;
this.requestId = requestId;
}
public String getGatewayId() { return gatewayId; }
public String getRequestId() { return requestId; }
public Date getTimestamp() { return timestamp; }
private HandshakeRequest() { /* For Kryo */ }
}
This is an example of the object we use to bridge to Scala:
object Handshake {
def unapply(msg: HandshakeRequest): Option[ (DateTime, String, String) ] = {
(
new DateTime(msg.getTimestamp.getTime),
msg.getRequestId,
msg.getGatewayId
).some
}
def apply(gatewayId: String, requestId: String) = new HandshakeRequest(gatewayId, requestId)
}
Since all of our object have the Timestamp, it is also part of the boilerplate. We'd like some way (perhaps a macro?) to automatically generate the unapply and apply methods (and ideally, the whole object itself).
Does anyone know of an easy way to accomplish this?

Since there were no answers, I came up with this: https://github.com/yetu/scala-beanutils.

I've started a project https://github.com/limansky/beanpuree which allows convertions from beans to case classes. I'm going to add some more features, like automatic type convertion between Java number classes and Scala classes.

Related

Generic builder of (almost) identical 3rd party classes

I have a bunch of 3rd party classes, these classes are autogenerated in java and do not have any hierarchy
Here is the RulesPropertyList
enum RulesPropertyType {...}
class RulesPropertyValue {...}
class RulesProperty {
public RulesPropertyType getPropertyTypeCode(){...}
public RulesPropertyValue getPropertyValue() {...}
}
class RulesPropertyList {
public void setNumProperties(int numProperties)
public void setProperties(RulesProperty[] properties)
}
And its Characs* sibling
enum CharacsPropertyType {...}
class CharacsPropertyValue {...}
class CharacsProperty {
public CharacsPropertyType getPropertyTypeCode(){...}
public CharacsPropertyValue getPropertyValue() {...}
}
class CharacsPropertyList {
public void setNumProperties(int numProperties)
public void setProperties(CharacsProperty[] properties)
}
There are more than just Rules* and Characs* families of classes, and classes actually have more fields and deeper structures.
All classes are completely identical except for the prefixes in the class names.
Currently, I have a separate builder method for each set of classes.
def buildRulesPropertyList(props: (RulesPropertyType, RulesPropertValue): RulesPropertyList = {
val properties = props.map { case (type, value) =>
RulesProperty(type, value)
}
val propList = RulesPropertyList
propList.setProperties(properties.toArray)
propList.setNumProperties(properties.length)
propList
}
I have to create such a builder for each family of classes.
Now I only see a possibility to make a generic builder using reflection.
Is there a way in Scala to make such a builder using generics in Scala language?
Is there a way in Scala to make such a builder using generics in Scala language?
yes, but I don't think it's going to be any less code. I think your best move here is to just write some simple code generation for each type. you would feed it a list of family names like Seq("Rules", "Characs", ...) and have it spit out your build${family}PropertyList methods.

R2DBC and enum (PostgreSQL)

Update 15/08/2020: Looks like Enum support was added on Jun 16. R2DBC commit.
Does H2DBC support PostgreSQL enums? I checked they git page but it doesn't mention anything about it. If it does, how enums could be used (INSERT, SELECT)?
Lets say PostgreSQL enum
CREATE TYPE mood AS ENUM ('UNKNOWN', 'HAPPY', 'SAD', ...);
Java class
#Data
public class Person {
private String name;
private Mood mood;
// ...
enum Mood{ UNKNOWN, HAPPY, SAD, ...}
}
I tried:
// insert
var person = ...;
client.insert()
.table("people")
.using(person)
.then()
.subscribe(System.out::println);
// select
var query = "SELECT * FROM people";
client.execute(query)
.as(Person.class)
.fetch().all()
.subscribe(System.out::println);
But I'm getting error messages:
# on insert
WARN [reactor-tcp-epoll-1] (Loggers.java:294) - Error: SEVERITY_LOCALIZED=ERROR, SEVERITY_NON_LOCALIZED=ERROR, CODE=42804, MESSAGE=column "mood" is of type mood but expression is of type character varying, HINT=You will need to rewrite or cast the expression., POSITION=61, FILE=parse_target.c, LINE=591, ROUTINE=transformAssignedExpr
# on select
ERROR [reactor-tcp-epoll-1] (Loggers.java:319) - [id: 0x8581acdb, L:/127.0.0.1:39726 ! R:127.0.0.1/127.0.0.1:5432] Error was received while reading the incoming data. The connection will be closed.
reactor.core.Exceptions$ErrorCallbackNotImplemented: org.springframework.data.mapping.MappingException: Could not read property private ...
I found similar post but without luck to solve my problem.. maybe I was applying it wrong..
Any help or tips are welcome.
Tested with org.springframework.data:spring-data-r2dbc:1.0.0.RELEASE and io.r2dbc:r2dbc-postgresql:0.8.1.RELEASE.
Kotlin version.
Define a enum class
enum class Mood {
UNKNOWN,
HAPPY,
SAD
}
Create a custom codec
class MoodCodec(private val allocator: ByteBufAllocator) : Codec<Mood> {
override fun canEncodeNull(type: Class<*>): Boolean = false
override fun canEncode(value: Any): Boolean = value is Mood
override fun encode(value: Any): Parameter {
return Parameter(Format.FORMAT_TEXT, oid) {
ByteBufUtils.encode(allocator, (value as Mood).name)
}
}
override fun canDecode(dataType: Int, format: Format, type: Class<*>): Boolean = dataType == oid
override fun decode(buffer: ByteBuf?, dataType: Int, format: Format, type: Class<out Mood>): Mood? {
buffer ?: return null
return Mood.valueOf(ByteBufUtils.decode(buffer))
}
override fun type(): Class<*> = Mood::class.java
override fun encodeNull(): Parameter =
Parameter(Format.FORMAT_TEXT, oid, Parameter.NULL_VALUE)
companion object {
// Get form `select oid from pg_type where typname = 'mood'`
private const val oid = YOUR_ENUM_OID
}
}
Registe the codec
You may need change runtimeOnly("io.r2dbc:r2dbc-postgresql") to implementation("io.r2dbc:r2dbc-postgresql")
#Configuration
#EnableR2dbcRepositories
class AppConfig : AbstractR2dbcConfiguration() {
override fun connectionFactory(): ConnectionFactory = PostgresqlConnectionConfiguration.builder()
.port(5432) // Add your config here.
.codecRegistrar { _, allocator, registry ->
registry.addFirst(MoodCodec(allocator))
Mono.empty()
}.build()
.let { PostgresqlConnectionFactory(it) }
}
I used the below for Spring boot 2.6.4 + r2dbc-postgresql 0.8.11 by adding a customizer rather than creating the connection factory myself.
Thanks #Hantsy for pointing EnumCodec out. I added it to a customizer therefore it can play nicely with existing autoconfigure procedure. Also, the spring-data keeps converting my enum to string until I added the converter.
Hopefully these can provide a little help to others.
Register EnumCodec to builder customizer as extensions
It is possible to register multiple enum, just repeat the withEnum() call.
/**
* Use the customizer to add EnumCodec to R2DBC
*/
#Bean
public ConnectionFactoryOptionsBuilderCustomizer connectionFactoryOptionsBuilderCustomizer() {
return builder -> {
builder.option(Option.valueOf("extensions"),
List.of(EnumCodec.builder()
.withEnum("enum_foo", FooEnum.class)
.withRegistrationPriority(RegistrationPriority.FIRST)
.build()));
logger.info("Adding enum to R2DBC postgresql extensions: {}", builder);
};
}
Implement spring data converter by extending EnumWriteSupport
public class FooWritingConverter extends EnumWriteSupport<Foo> {
}
Register converters so that spring data won't always convert enum to string.
This step is a slightly enhanced version of R2dbcDataAutoConfiguration in spring-boot-autoconfigure project.
/**
* Register converter to make sure Spring data treat enum correctly
*/
#Bean
public R2dbcCustomConversions r2dbcCustomConversions(DatabaseClient databaseClient) {
logger.info("Apply R2DBC custom conversions");
R2dbcDialect dialect = DialectResolver.getDialect(databaseClient.getConnectionFactory());
List<Object> converters = new ArrayList<>(dialect.getConverters());
converters.addAll(R2dbcCustomConversions.STORE_CONVERTERS);
return new R2dbcCustomConversions(
CustomConversions.StoreConversions.of(dialect.getSimpleTypeHolder(), converters),
List.of(
new FooWritingConverter()
));
}
Step 1 and 3 can be added to your application class or any other valid configuration.
Check my article about Postgres specific features supported in R2dbc.
There are two options.
use custom Postgres enum type and Java enum type, and register EnumCodec in the connection factory builder.
use a textual type as data type(such as varchar), and Java Enum type, Spring data r2dbc will convert them directly.

Need help understand interfaces and/or abstract classes in angular2

I am new to the angular2 world. I am trying to create interfaces for certain components and then implement these interfaces in my models so I can make sure they will be able to work properly.
One thing I have noticed is that if I create new instances of these objects they work fine but when I pull data from a restful call, I use the type casting to turn the data into the type of object I expect. The following code is a pseudo example.
I come from a Java/C++ background so I am hoping someone can see what I'm trying to do and explain to me how to get this working correctly.
Thanks In Advance!
Doesn't work ---
private vehicles: Vehicle[];
this._vehicleService.loadVehicles().subscribe(
vehicles => this.vehicles = <Vehicle[]>vehicles);
Does Work ---
vehicles : Vehicle[];
vehicles.push(new Vehicle(1, 'Old Junker'));
vehicles.push(new Vehicle(2, 'Old Junker2'));
Example class/interface setup.
#Component
export class SelectableComponent {
options : Selectable[]
// Access options.label and options.value
}
export interface Selectable {
label(): string;
value(): any;
}
export class Vehicle implements Selectable {
constructor (
public id: number,
public description: string,
){}
get label() : any {
return this.description;
}
get value() : any {
return this.id;
}
}
What happens here is that the object retrieved from the backend is just a plain Javascript object that gets cast to a Vehicle:
this._vehicleService.loadVehicles().subscribe(
vehicles => this.vehicles = <Vehicle[]>vehicles);
Objects in this array will have all the data of a Vehicle, but none of the behavior of an instance of the Vehicle class, which can be quite confusing as you mention.
The simplest is instead of casting them, calling new and creating an instance of Vehicle immediately while retrieving them from the backend.
But using a long constructor call can be cumbersome, especially if Vehicle has a lot of properties and you need to pass them all to the constructor one by one.
A way to fix this is to create an auxiliary method in the Vehicle class:
class Vehicle {
constructor(private name, private year) {
}
static fromJson({name,year}) {
return new Vehicle(name, year);
}
}
And then use it in return from the backend to create an array of Vehicles, instead of casting them:
this._vehicleService.loadVehicles().subscribe(
vehicles => this.vehicles = vehicles.map(Vehicle.fromJson));
This way the vehicles in the result will have not only all the data of a vehicle, but also the behavior of the Vehicle class, because they are instances on Vehicle.
The main difference between classes and interfaces in TypeScript is that interfaces don't exist at runtime. They are "only" there for compilation and type checking.
Casting an element to an interface / class "only" tells TypeScript that the object follows the structure of it but it's not actually an instance of the type. It's a bit disturbing at a first sight. The main consequence (in the case of a class) is that casting doesn't allow you to use methods of the class.
I already casted this way:
private vehicles: Vehicle[];
this._vehicleService.loadVehicles().subscribe(
vehicles => this.vehicles = <Vehicle[]>vehicles);
What is the exact compilation error you have?

Mapping custom types in the ScalaQuery O/R framework

In his comparison of ScalaQuery and Squeryl, Stefan Zeiger (author of ScalaQuery) says in the third bullet-point:
ScalaQuery comes with support for a basic set of JDBC types and can be
extended with DBMS- or application-specific types.
I have been unable to find examples or explanations for how to actually do this, however. I am trying to write a ScalaQuery schema for a Postgres database, in which some columns are of custom enum types that I created within Postgres.
For example, I have a enum type called gender, with possible values male and female. This is NOT a Java enum, persisted to the database as an integer. Rather, it is a custom Postgres type defined within the DBMS. Postgres stores those with a special 4-byte data structure rather than as a primitive.
How could I incorporate Postgres columns of type gender into a ScalaQuery schema?
(I would also appreciate comments, if you think a different strongly-typed O/R approach would be better suited for the task. I have already looked at Squeryl, and do not believe it can handle custom types unless they are persisted as primitives in the DBMS.)
import org.scalaquery.ql.{MappedTypeMapper => Mapper}
object TypeMapper {
type Stamp = java.sql.Timestamp
val joda2Stamp =
Mapper.base[JodaTime, Stamp](
dt => new Stamp(dt.getMillis),
ts => new JodaTime(ts.getTime) )
}
and then, for example, in your DAO (or wherever you run queries), use it:
import TypeMapper._
implicit val j2Stamp = joda2Stamp // type conversion automatically
You'll need to experiment to achieve the same for Enums and PostGres' enum storage type. I tend not to bother, preferring to go with Java Enums and storing as primitive type.
For example:
public enum CardType implements ILabel {
V("Visa"),
M("MasterCard"),
D("Discover"),
A("American Express");
private CardType(String label) { this.label = label; }
public String getLabel() { return this.label; }
final String label;
public static List<String> asList() {
return EnumHelper.asList(CardType.class);
}
public static Map<String,String> asMap() {
return EnumHelper.asMap(CardType.class);
}
}
and then store as char(1) in DB a la Orders.insert(cardType = cardType.toString), or you could create a type mapper Enum-String conversion and omit the enum.toString on inserts...

Groovy getProperty() on a static member

This question is probably going to illustrate a lack of knowledge on my part about how Groovy classes work, but I have tried to figure this out on my own with no luck. I want to create a getProperty() method on a class so I can reference member variables in a Groovyish way. This is NOT the same as just making them public because I do want some logic done when they are referenced. Basically, I'm trying to create a configuration Groovy class that uses ConfigSlurper:
class Configuration implements GroovyObject {
private static ConfigObject config = new ConfigSlurper().parse(new File("testing.conf").toURI().toURL())
//This method is illegal, but it illustrates what I want to do
public static String getProperty(String prop){
config.getProperty(prop)
}
}
If the above class were legal, I could then reference config items like so:
Configuration.dbUser
instead of this, which would require making the ConfigObject available:
Configuration.config.dbUser
I know, it would be worlds easier to just make the config object public, but knowing how to do this (or know why it's impossible) would help me understand Groovy a little better.
The only way I can get it to work is via the metaClass:
class Configuration {
private static ConfigObject config = new ConfigSlurper().parse( "foo = 'bar'" )
}
Configuration.metaClass.static.propertyMissing = { name ->
delegate.config[ name ]
}
println Configuration.foo
There may be a better way however...