殴り書き Win + Intellij + VirtualBox + Scala + Spark でとりあえず開発準備(3)

ここからは「実際には要らないかも」ということや、内部要素をきっちり検証してないってことが含まれます。シェイプアップしたものを上げなおすかは気分次第

■sbt assembly使うためにplugins.sbt要るらしいぞ
多分、なんもせんでもprojectフォルダ内に存在はすると思う。
いなかったら[project_root]/project下にplugins.sbtを作ろう

中身に

addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.14.3")

これを追記する。

なお、↑だけなら恐らく必要ないけども(実際自分の環境では動いてる)

resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"

こんな感じにresolversに必要なライブラリのリポジトリ追加できるみたいね。

■build.sbtに追記しよう

```
name := "PJ名"

version := "1.0"

scalaVersion := "2.11.8"

scalacOptions := Seq("-unchecked", "-deprecation", "-encoding", "utf8")

crossPaths := false

javacOptions ++= Seq("-encoding", "UTF-8")

libraryDependencies ++=
  Seq(
    "org.apache.spark" %% "spark-core" % "1.6.0",
    "org.apache.hadoop" % "hadoop-aws" % "2.6.0",
    "com.amazonaws" % "aws-java-sdk-s3" % "1.10.72",
    "com.typesafe.akka" %% "akka-actor" % "2.3.9",
    "com.typesafe.akka" %% "akka-slf4j" % "2.3.9",
    "org.slf4j" % "slf4j-simple" % "1.7.7",
    "org.slf4j" % "slf4j-api" % "1.7.12" % "provided"
  )


assemblyMergeStrategy in assembly := {
  case PathList("javax", "servlet", xs @ _*)         => MergeStrategy.first
  case PathList(ps @ _*) if ps.last endsWith ".properties" => MergeStrategy.first
  case PathList(ps @ _*) if ps.last endsWith ".xml" => MergeStrategy.first
  case PathList(ps @ _*) if ps.last endsWith ".types" => MergeStrategy.first
  case PathList(ps @ _*) if ps.last endsWith ".class" => MergeStrategy.first
  case "application.conf"                            => MergeStrategy.concat
  case "unwanted.txt"                                => MergeStrategy.discard
  case x =>
    val oldStrategy = (assemblyMergeStrategy in assembly).value
    oldStrategy(x)
}
```

nameとかverとかは多分最初からあるので割愛。
恐らくいらない記述がたくさんあるが、それをちゃんと削るのは後日。

■libraryDependencies

sparkとhadoopはそりゃ今回の要件的に要るでしょってことで。
aws-java-sdkは後々どうせ必要になりそうなので今のうちに。
typesafe.akkaは現段階で何なのか分かってません。
slf4jはロギングのために突っ込んでます。ただ、よくわからんで突っ込んでることは否定できない。

■assemblyMergeStrategy in assembly
sbt assemblyした時、重複したファイル名がある場合、それを参照してる箇所があるとスッ転ぶ。
なのでassemblyMergeStrategyを追記してやると良いようだ。
なお、↑で記述してるものの中で本当は要らないものがいくつかあると思うので
いずれ綺麗にするかも。
多分これ内部で「重複してたら最初に見つかったほうを使う」っていう意味だと思うんだけど
ちゃんと追ったわけじゃないから「なんとなくそうなんだろう」くらいの理解しかないです

("javax", "servlet", xs @ _*) 　は javaxもしくはservletが含まれてたら
マッチした値を全部(_*)を xsにバインド(xs @)ってことで良いのか？

正直ここマジで読めん。そもそもPathListってObjectなのか？

■SLF4Jさんにおこられる
SLF4J: Class path contains multiple SLF4J bindings.
...
SLF4J: Actual binding is of type [...]

未解決
クラスパスの被りがあると出るようなんだけども困ってはいないので後回し

■ろぎんぐできねえけど
いろんなサイトにLoggerFactoryの使い方的なものは書いてあったけど、
何をどうやっても

log4j:WARN No appenders could be found for logger (...).
log4j:WARN Please initialize the log4j system properly.

これが出てロギングできなかった。

どうにも、sparkがlog4jでロギングしようとするらしく、それの絡みなのか？

http://qiita.com/uryyyyyyy/items/0f5026ffa998c1bac9c7
http://d.hatena.ne.jp/gungnir_odin/20130116/1358347663

この2つの神々しい記事を参考に
src/resources下にmyLog4j.propertiesを生成
中身はとりあえずlog4j.propertiesの書き方を丸パクリ
ログファイルのロケーションとかは各自書き換えてね

```
import java.util.Properties
import org.apache.log4j.PropertyConfigurator
import org.apache.spark.{SparkConf, SparkContext}
import org.slf4j.LoggerFactory

object GanbareOre {

  def main(args: Array[String]) {
    val props = new Properties()
    props.load(getClass.getClassLoader.getResourceAsStream("myLog4j.properties"))
    PropertyConfigurator.configure(props)

    val driverLogger = LoggerFactory.getLogger("operation")
    driverLogger.info("Hello logger")
  }
}
```

出たアアアアアアアアアアアアアアアアアア
よろこび。

一旦以上。

■2016/07/26 編集
build.sbtのsparkのverを1.4.1って書いてましたが、この状態で
sbt assemblyでfat jarを生成してEC2インスタンスで実行したところ、Akkaのverが2.3.4だけどお前2.3.9って指定しとるやんアホちゃう？って言われる。
sparkのverを1.6.0に変えたところ、解決されました。