2012-06-21 2 views
15

La mia versione hadoop è 1.0.3, quando utilizzo multipleinput, ho ricevuto questo errore.hadoop MultipleInputs ha esito negativo con ClassCastException

java.lang.ClassCastException: org.apache.hadoop.mapreduce.lib.input.TaggedInputSplit cannot be cast to org.apache.hadoop.mapreduce.lib.input.FileSplit 
at org.myorg.textimage$ImageMapper.setup(textimage.java:80) 
at org.apache.hadoop.mapreduce.Mapper.run(Mapper.java:142) 
at org.apache.hadoop.mapreduce.lib.input.DelegatingMapper.run(DelegatingMapper.java:55) 
at org.apache.hadoop.mapred.MapTask.runNewMapper(MapTask.java:764) 
at org.apache.hadoop.mapred.MapTask.run(MapTask.java:370) 
at org.apache.hadoop.mapred.Child$4.run(Child.java:255) 
at java.security.AccessController.doPrivileged(Native Method) 
at javax.security.auth.Subject.doAs(Subject.java:416) 
at org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1121) 
at org.apache.hadoop.mapred.Child.main(Child.java:249) 

ho testato percorso di ingresso singolo, nessun problema. Solo quando uso

MultipleInputs.addInputPath(job, TextInputpath, TextInputFormat.class, 
      TextMapper.class); 
    MultipleInputs.addInputPath(job, ImageInputpath, 
      WholeFileInputFormat.class, ImageMapper.class); 

Googled e trovato questo link https://issues.apache.org/jira/browse/MAPREDUCE-1178 che detto 0.21 avuto questo bug. Ma sto usando 1.0.3, questo bug torna di nuovo. Qualcuno ha lo stesso problema o qualcuno può dirmi come risolverlo? Grazie

qui è il codice di impostazione di immagine mapper, linea 4 è dove si verifica l'errore:

protected void setup(Context context) throws IOException, 
      InterruptedException { 
     InputSplit split = context.getInputSplit(); 
     Path path = ((FileSplit) split).getPath(); 
     try { 
      pa = new Text(path.toString()); 
     } catch (Exception e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
     } 
    } 
+0

Puoi pubblicare il codice per la tua classe 'ImageMapper'? Sembra che tu stia provando a trasmettere la divisione di input a un FileInputSplit nel tuo metodo di installazione. –

+0

Ho un problema simile .. Esiste una soluzione? – sunitha

risposta

29

Facendo seguito a mio commento, il Javadocs per TaggedInputSplit conferma che si sono probabilmente a torto gettando la scissione ingresso un FileSplit:

/** 
* An {@link InputSplit} that tags another InputSplit with extra data for use 
* by {@link DelegatingInputFormat}s and {@link DelegatingMapper}s. 
*/ 

la mia ipotesi è il metodo di configurazione simile a questa:

@Override 
protected void setup(Context context) throws IOException, 
     InterruptedException { 
    FileSplit split = (FileSplit) context.getInputSplit(); 
} 

Sfortunatamente TaggedInputSplit non è visibile al pubblico, quindi non è possibile eseguire facilmente un controllo di stile instanceof, seguito da un cast e quindi chiamare TaggedInputSplit.getInputSplit() per ottenere l'effettivo FileSplit sottostante. Quindi o dovrai aggiornare tu stesso la fonte e ricompilare lo &, pubblicare un ticket JIRA per chiedere che questo venga corretto nella versione futura (se non è già stato attivato in 2+) o eseguire un brutto numero sgradevole riflessione aggiustamenti per arrivare al InputSplit sottostante

Questo è completamente testato:

@Override 
protected void setup(Context context) throws IOException, 
     InterruptedException { 
    InputSplit split = context.getInputSplit(); 
    Class<? extends InputSplit> splitClass = split.getClass(); 

    FileSplit fileSplit = null; 
    if (splitClass.equals(FileSplit.class)) { 
     fileSplit = (FileSplit) split; 
    } else if (splitClass.getName().equals(
      "org.apache.hadoop.mapreduce.lib.input.TaggedInputSplit")) { 
     // begin reflection hackery... 

     try { 
      Method getInputSplitMethod = splitClass 
        .getDeclaredMethod("getInputSplit"); 
      getInputSplitMethod.setAccessible(true); 
      fileSplit = (FileSplit) getInputSplitMethod.invoke(split); 
     } catch (Exception e) { 
      // wrap and re-throw error 
      throw new IOException(e); 
     } 

     // end reflection hackery 
    } 
} 

riflessione hackery spiegato:

Con TaggedInputSplit essere portata protetta dichiarata, non è visibile alle classi esterne al pacchetto org.apache.hadoop.mapreduce.lib.input e pertanto non è possibile fare riferimento a tale classe nel metodo di installazione. Per ovviare a questo, eseguiamo una serie di operazioni basate riflessione:

  1. Controllo il nome della classe, possiamo testare per il tipo TaggedInputSplit usando il suo nome completo

    splitClass.getName().equals("org.apache.hadoop.mapreduce.lib.input.TaggedInputSplit")

  2. Sappiamo vogliamo chiamare il metodo TaggedInputSplit.getInputSplit() per recuperare la scissione ingresso avvolto, in modo da utilizzare il metodo Class.getMethod(..) riflessione per acquisire un riferimento al metodo:

    Method getInputSplitMethod = splitClass.getDeclaredMethod("getInputSplit");

  3. La classe non è ancora visibile al pubblico, quindi utilizziamo setAccessible (..) Metodo per ignorare questa, fermando il gestore di protezione di un'eccezione

    getInputSplitMethod.setAccessible(true);

  4. Infine richiamare il metodo sul riferimento alla suddivisione di ingresso e gettato il risultato a un FileSplit (ottimismo sperando sua un'istanza di questo tipo):!

    fileSplit = (FileSplit) getInputSplitMethod.invoke(split);

+1

È esattamente lo stesso di quello che hai indovinato. Ho provato il tuo codice e ora funziona. Sei così professionale! –

+0

Ho letto di nuovo i codici, ma non sono ancora sicuro di come funzioni. Puoi dare una breve spiegazione sulla riflessione hackeraggio? Grazie. –

+0

Vedere la sezione aggiunta alla fine –

0

ho avuto questo stesso problema, ma il problema reale era che ero ancora l'impostazione del InputFormat dopo la configurazione le MultipleInputs:

job.setInputFormatClass(SequenceFileInputFormat.class); 

Una volta ho rimosso questa linea tutto ha funzionato bene.