Serializando objetos en MONGODB Java (POJO) usando Codecs (I)

Estos días he estado trabajando con Java JPA para un proyecto de la oficina; También he estado trabajando con MongoDB y objetos con soporte para JAXP para CVEBrowser.

No es tan fácil como parece :-). En el caso de Mongo no tiene sentido pensar en JPA (aunque hay soporte para esto), así que comencé a jugar con el API del manejado de conexiones.

La razón es que cuando tenemos un esquema previo, por ejemplo una definición que viene de XSD, no hay mucha elección en cuanto a como se ven los atributos.

Veamos por ejemplo una clase que resulta de convertir la definición de una entrada de XSD CVE (https://cve.mitre.org/schema/cve/cve_1.0.xsd).

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802 
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a> 
// Any modifications to this file will be lost upon recompilation of the source schema. 
// Generated on: 2018.02.17 at 08:52:30 PM EST 
//
 
 
package com.kodegeek.cvebrowser.entity;
 
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlEnumValue;
import javax.xml.bind.annotation.XmlType;
 
 
/**
 * Java class for simplePhaseEnumType.
 * 
 * The following schema fragment specifies the expected content contained within this class.
 * 
 * 
 * &lt;simpleType name="simplePhaseEnumType">
 *   &lt;restriction base="{http://www.w3.org/2001/XMLSchema}token">
 *     &lt;enumeration value="Proposed"/>
 *     &lt;enumeration value="Interim"/>
 *     &lt;enumeration value="Modified"/>
 *     &lt;enumeration value="Assigned"/>
 *   &lt;/restriction>
 * &lt;/simpleType>
 * 
 * 
 */
@XmlType(name = "simplePhaseEnumType")
@XmlEnum
public enum SimplePhaseEnumType {
 
    @XmlEnumValue("Proposed")
    PROPOSED("Proposed"),
    @XmlEnumValue("Interim")
    INTERIM("Interim"),
    @XmlEnumValue("Modified")
    MODIFIED("Modified"),
    @XmlEnumValue("Assigned")
    ASSIGNED("Assigned");
    private final String value;
 
    SimplePhaseEnumType(String v) {
        value = v;
    }
 
    public String value() {
        return value;
    }
 
    public static SimplePhaseEnumType fromValue(String v) {
        for (SimplePhaseEnumType c: SimplePhaseEnumType.values()) {
            if (c.value.equals(v)) {
                return c;
            }
        }
        throw new IllegalArgumentException(v);
    }
 
}

Ahora mi problema es que puedo guardar los datos (aparentemente) pero no puedo recuperarlos debido a que el codec no puede transformar la enumeración (la cual por definición no tiene constructores públicos).

Según MongoDB (PojoQuickTour,java), debería ser tan fácil como usar una base de datos con el Codec apropiado:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
 
private final static String [] packages = {  "com.kodegeek.cvebrowser.entity" };
// ...
 
public static CodecRegistry getCodecRegistry() {
        final CodecRegistry defaultCodecRegistry = MongoClient.getDefaultCodecRegistry();
        final CodecProvider pojoCodecProvider = PojoCodecProvider.builder().register(packages).build();
        final CodecRegistry cvePojoCodecRegistry = CodecRegistries.fromProviders(pojoCodecProvider);
        return CodecRegistries.fromRegistries(defaultCodecRegistry, cvePojoCodecRegistry);
    }
 
// Later on, different place we get a database with a POJO codec
// ...
mongoClient.getDatabase("cvebrowser").withCodecRegistry(getCodecRegistry())

Sin embargo si trato de hacer una búsqueda usando el codec (Joses-iMac:CVEBrowser josevnz$ gradle test):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
org.bson.codecs.configuration.CodecConfigurationException: Failed to decode 'phase'. Failed to decode 'value'. Cannot find a public constructor for 'SimplePhaseEnumType'.
	at org.bson.codecs.pojo.PojoCodecImpl.decodePropertyModel(PojoCodecImpl.java:192)
	at org.bson.codecs.pojo.PojoCodecImpl.decodeProperties(PojoCodecImpl.java:168)
	at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:122)
	at org.bson.codecs.pojo.PojoCodecImpl.decode(PojoCodecImpl.java:126)
	at com.mongodb.operation.CommandResultArrayCodec.decode(CommandResultArrayCodec.java:52)
	at com.mongodb.operation.CommandResultDocumentCodec.readValue(CommandResultDocumentCodec.java:60)
	at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:84)
	at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:41)
	at org.bson.codecs.configuration.LazyCodec.decode(LazyCodec.java:47)
	at org.bson.codecs.BsonDocumentCodec.readValue(BsonDocumentCodec.java:101)
	at com.mongodb.operation.CommandResultDocumentCodec.readValue(CommandResultDocumentCodec.java:63)
	at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:84)
	at org.bson.codecs.BsonDocumentCodec.decode(BsonDocumentCodec.java:41)
	at com.mongodb.connection.ReplyMessage.<init>(ReplyMessage.java:51)
	at com.mongodb.connection.InternalStreamConnection.receiveCommandMessageResponse(InternalStreamConnection.java:301)
	at com.mongodb.connection.InternalStreamConnection.sendAndReceive(InternalStreamConnection.java:255)
	at com.mongodb.connection.UsageTrackingInternalConnection.sendAndReceive(UsageTrackingInternalConnection.java:98)
	at com.mongodb.connection.DefaultConnectionPool$PooledConnection.sendAndReceive(DefaultConnectionPool.java:441)
	at com.mongodb.connection.CommandProtocolImpl.execute(CommandProtocolImpl.java:80)
	at com.mongodb.connection.DefaultServer$DefaultServerProtocolExecutor.execute(DefaultServer.java:189)
	at com.mongodb.connection.DefaultServerConnection.executeProtocol(DefaultServerConnection.java:264)
	at com.mongodb.connection.DefaultServerConnection.command(DefaultServerConnection.java:126)
	at com.mongodb.connection.DefaultServerConnection.command(DefaultServerConnection.java:118)
	at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:226)
	at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:217)
	at com.mongodb.operation.CommandOperationHelper.executeWrappedCommandProtocol(CommandOperationHelper.java:120)
	at com.mongodb.operation.FindOperation$1.call(FindOperation.java:717)
	at com.mongodb.operation.FindOperation$1.call(FindOperation.java:711)
	at com.mongodb.operation.OperationHelper.withConnectionSource(OperationHelper.java:471)
	at com.mongodb.operation.OperationHelper.withConnection(OperationHelper.java:415)
	at com.mongodb.operation.FindOperation.execute(FindOperation.java:711)
	at com.mongodb.operation.FindOperation.execute(FindOperation.java:83)
	at com.mongodb.Mongo$3.execute(Mongo.java:826)
	at com.mongodb.MongoIterableImpl.execute(MongoIterableImpl.java:130)
	at com.mongodb.MongoIterableImpl.iterator(MongoIterableImpl.java:77)
	at com.mongodb.MongoIterableImpl.forEach(MongoIterableImpl.java:100)
	at com.kodegeek.cvebrowser.persistence.TestCVEMongoPojoManager.testPrintIssues(TestCVEMongoPojoManager.java:47)
</init>

“Failed to decode ‘phase’. Failed to decode ‘value’. Cannot find a public constructor for ‘SimplePhaseEnumType”. Estoy aprendiendo como enseñarle a MongoDB como guardar y leer estos valores sin que le de un infarto.

(Por cierto, estoy preguntando en StackOverFlow. Vamos a ver que tan complicado es).