hadoop MultipleInputs falla con ClassCastException
(2)
Mi versión hadoop es 1.0.3, cuando uso varias entradas, recibí este error.
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)
He probado la ruta de entrada única, no hay problema. Solo cuando uso
MultipleInputs.addInputPath(job, TextInputpath, TextInputFormat.class,
TextMapper.class);
MultipleInputs.addInputPath(job, ImageInputpath,
WholeFileInputFormat.class, ImageMapper.class);
Busqué en Google y encontré este enlace https://issues.apache.org/jira/browse/MAPREDUCE-1178 que decía que 0.21 tenía este error. Pero estoy usando 1.0.3, este error vuelve otra vez. ¿Alguien tiene el mismo problema o alguien me puede decir cómo solucionarlo? Gracias
Aquí está el código de configuración del asignador de imágenes, la 4ª línea es donde se produce el error:
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();
}
}
Siguiendo con mi comentario, los Javadocs para TaggedInputSplit
confirman que probablemente estás TaggedInputSplit
erróneamente la división de entrada en un FileSplit:
/**
* An {@link InputSplit} that tags another InputSplit with extra data for use
* by {@link DelegatingInputFormat}s and {@link DelegatingMapper}s.
*/
Mi conjetura es que su método de configuración se ve algo como esto:
@Override
protected void setup(Context context) throws IOException,
InterruptedException {
FileSplit split = (FileSplit) context.getInputSplit();
}
Lamentablemente, TaggedInputSplit
no es visible al público, por lo que no puede realizar fácilmente una instanceof
comprobación de estilo, seguido de una TaggedInputSplit.getInputSplit()
y luego llamar a TaggedInputSplit.getInputSplit()
para obtener el FileSplit subyacente real. Por lo tanto, deberá actualizar la fuente usted mismo y volver a compilar e implementar, publicar un ticket JIRA para solicitar que esto se arregle en una versión futura (si ya no se ha activado en la versión 2+) o realizar una piratería de reflejo desagradable para llegar al InputSplit subyacente
Esto está completamente sin probar:
@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
}
}
Reflexión Hackery Explicado:
Dado que TaggedInputSplit se declara ámbito protegido, no es visible para las clases fuera del paquete org.apache.hadoop.mapreduce.lib.input
, y por lo tanto no puede hacer referencia a esa clase en su método de configuración. Para solucionar esto, realizamos una serie de operaciones basadas en la reflexión:
Al inspeccionar el nombre de la clase, podemos probar el tipo TaggedInputSplit usando su nombre completo
splitClass.getName().equals("org.apache.hadoop.mapreduce.lib.input.TaggedInputSplit")
Sabemos que queremos llamar al método
TaggedInputSplit.getInputSplit()
para recuperar la división de entradaClass.getMethod(..)
, por lo que utilizamos el método de reflexiónClass.getMethod(..)
para adquirir una referencia al método:Method getInputSplitMethod = splitClass.getDeclaredMethod("getInputSplit");
La clase aún no es pública, por lo que usamos el método setAccessible (..) para anular esto, evitando que el administrador de seguridad lance una excepción
getInputSplitMethod.setAccessible(true);
Finalmente, invocamos el método en la referencia a la división de entrada y convertimos el resultado en un FileSplit (con optimismo, esperando que sea una instancia de este tipo):
fileSplit = (FileSplit) getInputSplitMethod.invoke(split);
Tuve este mismo problema, pero el problema real era que todavía estaba configurando el InputFormat después de configurar los MultipleInputs:
job.setInputFormatClass(SequenceFileInputFormat.class);
Una vez que quité esta línea todo funcionó bien.