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
AppleSaucedeve estenderecom.unboundid.scim2.common.BaseScimResource. - La classe
AppleSaucedeve essere annotata con@com.unboundid.scim2.common.annotations.Schema. - I campi di
AppleSauceche dovrebbero apparire nella serializzazione JSON devono essere annotati con@com.unboundid.scim2.common.annotations.Attribute. - La classe
AppleSaucedeve essere annotata con@org.springframework.ldap.odm.annotations.Entry(in realtà non è un problema, ma menzionato per completezza). - I campi di
AppleSauceche devono essere scritti negli attributi LDAP devono essere annotati con@org.springframework.ldap.odm.annotations.Attribute. -
AppleSaucedeve contenere un campo di tipojavax.naming.Nameannotato con@org.springframework.ldap.odm.annotations.Id. - Sia SCIM 2 SDK che Spring LDAP vogliono
AppleSauceper essere un JavaBean mutabile con getter e setter per tutti i campi ad essi relativi. I voglioAppleSauceper 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
AppleSauceriportano anche le annotazionijavax.validation, aggiungendo ai campi disordine. Mettere queste annotazioni sui getter nell'interfacciaAppleSaucesarebbe una buona idea? - Che cosa succede se ho bisogno di serializzare
AppleSaucein 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"?