Le sdk google cloud dataflow a une classe qui enregistre les codeurs pour différents types d'Avro.

CoderRegistry cr = p.getCoderRegistry();
cr.registerStandardCoders();
cr.registerCoder(Row.class, AvroCoder.of(Row.class));
cr.registerCoder(Destination.class, AvroCoder.of(Destination.class));
cr.registerCoder(Device.class, AvroCoder.of(Device.class));
cr.registerCoder(Location.class, AvroCoder.of(Location.class));
cr.registerCoder(Source.class, AvroCoder.of(Source.class));
cr.registerCoder(DimensionalMetric.class, AvroCoder.of(DimensionalMetric.class));
cr.registerCoder(DimensionSet.class, AvroCoder.of(DimensionSet.class));
cr.registerCoder(MetricSet.class, AvroCoder.of(MetricSet.class));

Mais comme vous pouvez l'imaginer, cela devient assez fastidieux et j'aimerais utiliser l'API de réflexion java pour enregistrer automatiquement toutes les classes dans le package com.brightcove.rna.model. J'imagine que cela ressemblerait à quelque chose comme ceci:

void registerAllModels(Pipeline p) {
    CoderRegistry cr = p.getCoderRegistry();
    Reflections r = new Reflections("com.brightcove.rna.model");
    Set<Class<? extends IndexedRecord>> classes = r.getSubTypesOf(IndexedRecord.class);
    for (Class<? extends IndexedRecord> clazz : classes) {
        cr.registerCoder(clazz, AvroCoder.of(clazz));
    }
}

Malheureusement, cela ne fonctionne pas. Qu'est-ce que je fais de mal ici?

1
Ankur Chauhan 29 déc. 2015 à 06:47

2 réponses

Meilleure réponse

Vous avez une erreur de compilation dans la ligne suivante:

cr.registerCoder(clazz, AvroCoder.of(clazz));

L'erreur est:

The method registerCoder(Class<?>, Class<?>) in the type CoderRegistry is not applicable for the arguments (Class<capture#1-of ? extends IndexedRecord>, AvroCoder<capture#2-of ? extends IndexedRecord>).

Fondamentalement, le problème est que le compilateur Java ne peut pas déduire que les types de caractères génériques sur les deux paramètres sont les mêmes et signale une erreur. Il s'agit d'un problème Java courant. Consultez cette question, par exemple.

Un correctif est le suivant, qui peut vous obliger à supprimer les avertissements du compilateur concernant les types bruts et une conversion non vérifiée:

  cr.registerCoder(clazz, (Coder) AvroCoder.of(clazz.newInstance().getSchema()));

Cela résout deux problèmes:

  • S'assure que registerCoder(Class<T>, Coder<T>) est utilisé à la place de registerCoder(Class<?>, Class<?>) surchargé.
  • AvroCoder.of(Class<T>) utilise le ReflectData.get().getSchema() d'Avro pour générer un schéma, qui peut ne pas fonctionner sur tous les types, tels que GenericRecord. Une alternative est de construire le codeur via AvroCoder.of(Schema) et d'obtenir le schéma de la classe générée automatiquement.
4
Community 23 mai 2017 à 11:52

Il s'est avéré qu'un peu plus de lecture sur le message d'erreur que je recevais m'a aidé à atteindre la racine du problème.

AnalyticsPipeline.java:110: error: no suitable method found for registerCoder(Class<CAP#1>,AvroCoder<CAP#2>)
            cr.registerCoder(clazz, AvroCoder.of(clazz));
              ^
    method CoderRegistry.registerCoder(Class<?>,Class<?>) is not applicable
      (argument mismatch; no instance(s) of type variable(s) T#1 exist so that AvroCoder<T#1> conforms to Class<?>)
    method CoderRegistry.registerCoder(Class<?>,CoderFactory) is not applicable
      (argument mismatch; no instance(s) of type variable(s) T#1 exist so that AvroCoder<T#1> conforms to CoderFactory)
    method CoderRegistry.<T#2>registerCoder(Class<T#2>,Coder<T#2>) is not applicable
      (inferred type does not conform to equality constraint(s)
        inferred: CAP#3
        equality constraints(s): CAP#3,CAP#1)
  where T#1,T#2 are type-variables:
    T#1 extends Object declared in method <T#1>of(Class<T#1>)
    T#2 extends Object declared in method <T#2>registerCoder(Class<T#2>,Coder<T#2>)
  where CAP#1,CAP#2,CAP#3 are fresh type-variables:
    CAP#1 extends IndexedRecord from capture of ? extends IndexedRecord
    CAP#2 extends IndexedRecord from capture of ? extends IndexedRecord
    CAP#3 extends IndexedRecord from capture of ? extends IndexedRecord

Comme indiqué ci-dessus, javac pense que les types d'arguments ne correspondent pas (correctement car je n'ai fourni aucune information à ce sujet). Ma solution s'est avérée être une modification assez simple. En ajoutant une fonction d'assistance qui corrige le type.

void registerCoders(Pipeline p) {
        CoderRegistry cr = p.getCoderRegistry();
        Reflections r = new Reflections("com.brightcove.rna.model");
        Set<Class<? extends IndexedRecord>> classes = r.getSubTypesOf(IndexedRecord.class);
        for (Class<? extends IndexedRecord> clazz : classes) {
            registerAvroType(cr, clazz);
        }
    }

    <T extends IndexedRecord> void registerAvroType(CoderRegistry cr, Class<T> clazz) {
        cr.registerCoder(clazz, AvroCoder.of(clazz));
    }

J'ai pu transmettre au compilateur que les types sont en effet les mêmes et que le problème a été résolu.

0
Ankur Chauhan 29 déc. 2015 à 19:51