2016-12-05 13 views
1

getRecordWriter 메서드를 덮어 쓰려면 사용자 정의 ParquetOutputFormat (클래스는 org.apache.parquet.hadoop)을 작성했습니다. getRecordWriter 메서드 내에서 IllegalAccessError의 원인이되는 CodecFactory에 액세스합니다. 문제를 해결하기 위해 내 자신의 클래스 로더를 만들려고 시도했지만 도움이되지 않았습니다. 나는 다음과 같은 나는 CustomParquetOutputFormat 사용 된 사용자 정의 클래스 로더를 생성하기 전에 나는이 블로그 게시물 http://techblog.applift.com/upgrading-spark#advanced-case-parquet-writer동적 클래스 로더에 대한 IllegalAccessError

을 따라 :

override def createOutputFormat: OutputFormat[Void, InternalRow] with Ext = new CustomParquetOutputFormat[InternalRow]() with Ext { 
... 
} 

CustomParquetOutputFormat 시도 라인 (274)에 CodecFactory에 액세스 할 때 getRecordWriter를 호출 할 때 문제가 발생합니다

CodecFactory codecFactory = new CodecFactory(conf); 

(이는 액세스 CustomParquetOutputFormat ParquetOutputFormat 라인 (274)이다)

CodecFactory은 package-private입니다.

사용자 정의 클래스 로더 :

class CustomClassLoader(urls: Array[URL], parent: ClassLoader, whiteList: List[String]) 
    extends ChildFirstURLClassLoader(urls, parent) { 
    override def loadClass(name: String) = { 
    if (whiteList.exists(name.startsWith)) { 
     super.loadClass(name) 
    } else { 
     parent.loadClass(name) 
    } 
    } 
} 

사용법 :

val sc: SparkContext = SparkContext.getOrCreate() 
val cl: CustomClassLoader = new CustomClassLoader(sc.jars.map(new URL(_)).toArray, 
    Thread.currentThread.getContextClassLoader, List(
    "org.apache.parquet.hadoop.CustomParquetOutputFormat", 
    "org.apache.parquet.hadoop.CodecFactory", 
    "org.apache.parquet.hadoop.ParquetFileWriter", 
    "org.apache.parquet.hadoop.ParquetRecordWriter", 
    "org.apache.parquet.hadoop.InternalParquetRecordWriter", 
    "org.apache.parquet.hadoop.ColumnChunkPageWriteStore", 
    "org.apache.parquet.hadoop.MemoryManager" 
)) 


cl.loadClass("org.apache.parquet.hadoop.CustomParquetOutputFormat") 
    .getConstructor(classOf[String], classOf[TaskAttemptContext]) 
    .newInstance(fullPathWithoutExt, taskAttemptContext) 
    .asInstanceOf[OutputFormat[Void, InternalRow] with ProvidesExtension] 

오류 :

java.lang.IllegalAccessError: tried to access class org.apache.parquet.hadoop.CodecFactory from class org.apache.parquet.hadoop.customParquetOutputFormat 
     at org.apache.parquet.hadoop.CustomParquetOutputFormat.getRecordWriter(CustomParquetOutputFormat.scala:40) 
     at org.apache.parquet.hadoop.ParquetOutputFormat.getRecordWriter(ParquetOutputFormat.java:262) 
     at org.apache.spark.custom.hadoop.HadoopWriter.<init>(HadoopWriter.scala:35) 
     at org.apache.spark.sql.execution.datasources.parquet.ParquetWriter.<init>(ParquetWriter.scala:16) 
     at org.apache.spark.sql.execution.datasources.parquet.ParquetWriterFactory.createWriter(ParquetWriterFactory.scala:71) 
     at com.abden.custom.index.IndexBuilder$$anonfun$4.apply(IndexBuilder.scala:55) 
     at com.abden.custom.index.IndexBuilder$$anonfun$4.apply(IndexBuilder.scala:54) 
     at scala.collection.immutable.Stream.map(Stream.scala:418) 
     at com.abden.custom.index.IndexBuilder.generateTiles(IndexBuilder.scala:54) 
     at com.abden.custom.index.IndexBuilder.generateLayer(IndexBuilder.scala:155) 
     at com.abden.custom.index.IndexBuilder.appendLayer(IndexBuilder.scala:184) 
     at com.abden.custom.index.IndexBuilder$$anonfun$appendLayers$1$$anonfun$apply$1.apply(IndexBuilder.scala:213) 
     at com.abden.custom.index.IndexBuilder$$anonfun$appendLayers$1$$anonfun$apply$1.apply(IndexBuilder.scala:210) 
     at scala.collection.Iterator$class.foreach(Iterator.scala:742) 
     at com.abden.custom.util.SplittingByKeyIterator.foreach(SplittingByKeyIterator.scala:3) 
     at com.abden.custom.index.IndexBuilder$$anonfun$appendLayers$1.apply(IndexBuilder.scala:210) 
     at com.abden.custom.index.IndexBuilder$$anonfun$appendLayers$1.apply(IndexBuilder.scala:209) 
     at org.apache.spark.rdd.RDD$$anonfun$foreachPartition$1$$anonfun$apply$33.apply(RDD.scala:920) 
     at org.apache.spark.rdd.RDD$$anonfun$foreachPartition$1$$anonfun$apply$33.apply(RDD.scala:920) 
     at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:1858) 
     at org.apache.spark.SparkContext$$anonfun$runJob$5.apply(SparkContext.scala:1858) 
     at org.apache.spark.scheduler.ResultTask.runTask(ResultTask.scala:66) 
     at org.apache.spark.scheduler.Task.run(Task.scala:89) 
     at org.apache.spark.executor.Executor$TaskRunner.run(Executor.scala:227) 
     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 
     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 
     at java.lang.Thread.run(Thread.java:745) 

오류가 발생 getRecordWriter이 줄에서 :

val codecFactory = new CodecFactory(conf) 

CodecFactory 그것이 자사의 패키지로 제한되도록 더 수정이 없습니다. 동일한 클래스 로더에서 모든 클래스를로드하는 동적 클래스 로더를 사용하더라도 여전히 얻을 수 있습니다. IllegalAccessError

+2

그것은 오류 메시지가'customParquetOutputFormat'을 표시하는 것이 이상하다 (소문자 c) 다른 모든 것은'CustomParquetOutputFormat' (대문자 C)를 참조합니다. 그 외에도'super.loadClass (name)'도 부모 로더를 먼저 검사하고 부모가 찾지 못하면 클래스를 로컬에서 해결하려고 시도한다는 것을 알아야합니다. 음, 그리고 다른 클래스 로더에 의해로드 된 클래스는 이름에 관계없이 항상 다른 (런타임) 패키지로 간주됩니다. – Holger

+0

죄송합니다, 오류 메시지가 해결되었습니다. 이 질문에 대한 클래스 이름을 변경하고 우연히 소문자를 사용했습니다. – abden003

+0

안녕하세요, 이전에 갖고 있던 문제를 이해하기 위해 맞춤 클래스 로더 이전에 코드를 공유 할 수 있습니까? 자신의 클래스 로더를 구현하는 것이 여기에 과도한 것 같습니다. – loicmathieu

답변

1

그래서 Java가 작동하는 방식을 깨뜨리는 것이 낫습니다! JVM의 보호 규칙을 깨뜨릴 수있는 클래스 로더를 구현하여 패키지 외부의 패키지 인 클래스에 액세스하려고합니다 (Java 언어 사양을 위반하기를 원할 것입니다!).

내 대답은 간단합니다 : 마십시오!

개인용 패키지 인 경우 액세스 할 수 없습니다. 기간!

나는 당신이 필요로하는 기능과 현재 API를 사용하여 구현하는 것이 가장 효과적이라고 생각한다. 기술적 인 해킹 방법을 묻는 대신 설명하는 것이 가장 좋다. 무엇을하고 싶습니까 (왜 자신의 getRecordWriter 메소드를 구현하고 싶습니까?

이미 일반 자바/쓰기 마루 파일을 읽는 방법에 대한이 SOW의 질문에 대답을

: Write Parquet format to HDFS using Java API with out using Avro and MR

감사를

루이