Il problema del mix di framework
Utilizzo due framework: SCI 2 SDK di PingIdentity e Spring LDAP - per deserializzare una risorsa SCIM (cioè JSON) su un oggetto Java quindi scriverlo in una directory LDAP e viceversa. Il problema è che entrambi i framework vogliono annotare i campi della mia classe di dati Java e questo sta diventando molto complicato. Esiste un buon progetto per dividere le richieste straniere sulla mia classe - provenienti dai framework - in modo che non si mescolino o addirittura si creino conflitti in una classe? Immagino che questo caso di "JSON < - > object < - > storage" transfer sia piuttosto comune ...
Esempio di codice
Facciamo del mio esempio più concreto con un po 'di codice. Innanzitutto, ecco i requisiti sulla mia classe, diciamo AppleSauce
, imposti dai framework:
- La classe
AppleSauce
deve estenderecom.unboundid.scim2.common.BaseScimResource
. - La classe
AppleSauce
deve essere annotata con@com.unboundid.scim2.common.annotations.Schema
. - I campi di
AppleSauce
che dovrebbero apparire nella serializzazione JSON devono essere annotati con@com.unboundid.scim2.common.annotations.Attribute
. - La classe
AppleSauce
deve essere annotata con@org.springframework.ldap.odm.annotations.Entry
(in realtà non è un problema, ma menzionato per completezza). - I campi di
AppleSauce
che devono essere scritti negli attributi LDAP devono essere annotati con@org.springframework.ldap.odm.annotations.Attribute
. -
AppleSauce
deve contenere un campo di tipojavax.naming.Name
annotato con@org.springframework.ldap.odm.annotations.Id
. - Sia SCIM 2 SDK che Spring LDAP vogliono
AppleSauce
per essere un JavaBean mutabile con getter e setter per tutti i campi ad essi relativi. I voglioAppleSauce
per essere un oggetto valore immutabile.
Ecco come appare AppleSauce
con un solo campo interessante email
(la mia vera classe ha circa 45 campi):
// imports omitted
@com.unboundid.scim2.common.annotations.Schema(id = "MY_SCHEMA_URN", name = "AppleSauce", description = "delicious stuff to go with cake")
@org.springframework.ldap.odm.annotations.Entry(objectClasses = { "inetOrgPerson", "organizationalPerson", "person", "top" }, base = "ou=sauces,dc=example,dc=org")
public class AppleSauce extends com.unboundid.scim2.common.BaseScimResource {
@org.springframework.ldap.odm.annotations.Id javax.naming.Name dn;
@com.unboundid.scim2.common.annotations.Attribute(description = "email address", multiValueClass = String.class, isRequired = true)
@org.springframework.ldap.odm.annotations.Attribute(name = "mail", syntax = "1.3.6.1.4.1.1466.115.121.1.26{256}")
private List<String> email;
// getters and setter omitted
}
Una possibile soluzione
Il mio pensiero iniziale è quello di estrarre un'interfaccia contenente tutti i getter (questo ha anche il vantaggio di fornire una vista di sola lettura della classe) e due classi che la implementano, ciascuna contenente gli stessi campi con annotazioni per i rispettivi framework. Qualcosa del genere:
public interface AppleSauce {
List<String> getEmail();
}
@com.unboundid.scim2.common.annotations.Schema(id = "MY_SCHEMA_URN", name = "AppleSauce", description = "delicious stuff to go with cake")
public class ScimAppleSauce extends com.unboundid.scim2.common.BaseScimResource implements AppleSauce {
@com.unboundid.scim2.common.annotations.Attribute(description = "email address", multiValueClass = String.class, isRequired = true)
private List<String> email;
@Override
List<String> getEmail() {
return this.email;
}
}
@org.springframework.ldap.odm.annotations.Entry(objectClasses = { "inetOrgPerson", "organizationalPerson", "person", "top" }, base = "ou=sauces,dc=example,dc=org")
public class LdapAppleSauce implements AppleSauce {
@org.springframework.ldap.odm.annotations.Id javax.naming.Name dn;
@org.springframework.ldap.odm.annotations.Attribute(name = "mail", syntax = "1.3.6.1.4.1.1466.115.121.1.26{256}")
private List<String> email;
@Override
List<String> getEmail() {
return this.email;
}
}
Lo svantaggio di questo approccio è che richiede codice aggiuntivo per convertire le istanze di ScimAppleSauce
in LdapAppleSauce
(copia di molti campi) e viceversa, perché nessuno dei due framework funzionerà correttamente con il tipo AppleSauce
. Non sono troppo preoccupato per l'etichetta getter / setter grazie alle annotazioni Project Lombok , ma copiare manualmente 45 campi è una prospettiva molto noiosa e soggetta a errori .
Difficoltà di bonus
Naturalmente, c'è dell'altro in questa storia ...
- I campi della classe
AppleSauce
riportano anche le annotazionijavax.validation
, aggiungendo ai campi disordine. Mettere queste annotazioni sui getter nell'interfacciaAppleSauce
sarebbe una buona idea? - Che cosa succede se ho bisogno di serializzare
AppleSauce
in un diverso formato JSON come un semplice JSON senza SCIM? Potrebbe esserci una terza implementazione diAppleSauce
,JsonAppleSauce
, ma poi il numero di convertitori esplode. - Non mi piace la mutabilità di JavaBeans e preferirei che tutti gli oggetti fossero immutabili.
Esiste un design pulito per risolvere questo problema di "framework mix"?