1+ /*
2+ * Code Pulse: A real-time code coverage testing tool. For more information
3+ * see http://code-pulse.com
4+ *
5+ * Copyright (C) 2014 Applied Visions - http://securedecisions.avi.com
6+ *
7+ * Licensed under the Apache License, Version 2.0 (the "License");
8+ * you may not use this file except in compliance with the License.
9+ * You may obtain a copy of the License at
10+ *
11+ * http://www.apache.org/licenses/LICENSE-2.0
12+ *
13+ * Unless required by applicable law or agreed to in writing, software
14+ * distributed under the License is distributed on an "AS IS" BASIS,
15+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+ * See the License for the specific language governing permissions and
17+ * limitations under the License.
18+ */
19+
20+ import sbt ._
21+ import Keys ._
22+ import BuildKeys ._
23+ import Project .Initialize
24+
25+ import scala .collection .JavaConversions ._
26+
27+ import java .io .{ BufferedInputStream , InputStream , File , FileInputStream , FileOutputStream }
28+ import java .net .{ HttpURLConnection , URL , URLConnection }
29+
30+ import org .apache .commons .compress .archivers .tar .TarArchiveInputStream
31+ import org .apache .commons .compress .archivers .zip .ZipArchiveInputStream
32+ import org .apache .commons .compress .compressors .gzip .GzipCompressorInputStream
33+ import org .apache .commons .io .IOUtils
34+
35+ /** Helper for fetching the Code Pulse dependencies from the web.
36+ *
37+ * @author robertf
38+ */
39+ object DependencyFetcher extends BuildExtra {
40+
41+ import Distributor .Keys .{ Distribution , distDeps }
42+
43+ sealed trait Platform
44+ object Platform {
45+ case object Unspecified extends Platform
46+ case object Windows extends Platform
47+ case object Linux extends Platform
48+ case object OSX extends Platform
49+ }
50+
51+ sealed trait FileFormat
52+ object FileFormat {
53+ case object Raw extends FileFormat
54+ case object Zip extends FileFormat
55+ case object TarGz extends FileFormat
56+ }
57+
58+ case class DependencyFile (platform : Platform , url : String , format : FileFormat )
59+
60+ sealed trait Dependency { def name : String ; def destPath : String }
61+ sealed trait PackageHelper { def trimPath (platform : Platform )(path : String ): String }
62+ case class JreDependency (name : String , rawPath : String , destPath : String , files : List [DependencyFile ]) extends Dependency with PackageHelper {
63+ private val trimPathRegex = (" ^\\ Q" + rawPath + " \\ E(?:\\ .jre)?/" ).r
64+ def trimPath (platform : Platform )(path : String ) = trimPathRegex.replaceFirstIn(path, " " )
65+ }
66+ case class PlatformDependency (name : String , destPath : String , files : List [DependencyFile ]) extends Dependency
67+ case class CommonDependency (name : String , destPath : String , file : DependencyFile ) extends Dependency
68+ case class ToolDependency (name : String , destPath : String , file : DependencyFile ) extends Dependency
69+
70+ val BufferLen = 4096
71+
72+ object Keys {
73+ val PackageDependencies = config(" package-dependencies" )
74+
75+ val jreWindows = SettingKey [String ](" jre-windows" )
76+ val jreLinux = SettingKey [String ](" jre-linux" )
77+ val jreOsx = SettingKey [String ](" jre-osx" )
78+
79+ val nwkWindows = SettingKey [String ](" nwk-windows" )
80+ val nwkLinux = SettingKey [String ](" nwk-linux" )
81+ val nwkOsx = SettingKey [String ](" nwk-osx" )
82+
83+ val jetty = SettingKey [String ](" jetty" )
84+ val resourcer = SettingKey [String ](" resourcer" )
85+
86+ val dependencyList = TaskKey [Seq [Dependency ]](" dependency-list" )
87+ }
88+
89+ import Keys ._
90+
91+ private class ProgressInputStream (underlying : InputStream , message : String , size : Long ) extends InputStream {
92+ private var bytesRead = 0L
93+ private def progress (chunk : Int ) {
94+ bytesRead += chunk
95+ val pct = 100 * bytesRead / size
96+ print(message + " ... " + pct + " % complete\r " )
97+ }
98+
99+ override def read () = {
100+ val b = underlying.read
101+ if (b != - 1 ) progress(1 )
102+ b
103+ }
104+
105+ override def read (b : Array [Byte ]) = {
106+ val chunk = underlying.read(b)
107+ if (chunk >= 0 ) progress(chunk)
108+ chunk
109+ }
110+
111+ override def read (b : Array [Byte ], off : Int , len : Int ) = {
112+ val chunk = underlying.read(b, off, len)
113+ if (chunk >= 0 ) progress(chunk)
114+ chunk
115+ }
116+
117+ override def available () = underlying.available
118+ override def close () = underlying.close
119+ override def mark (readlimit : Int ) = underlying.mark(readlimit)
120+ override def markSupported () = underlying.markSupported
121+ override def reset () = underlying.reset
122+ override def skip (n : Long ) = underlying.skip(n)
123+ }
124+
125+ private def doDownload (label : String , log : Logger , dep : Dependency , file : DependencyFile , dest : File , preConnect : URLConnection => Unit = _ => ()) {
126+ val conn = {
127+ def connect (url : URL ): HttpURLConnection = {
128+ val conn = url.openConnection.asInstanceOf [HttpURLConnection ]
129+ preConnect(conn)
130+
131+ conn setInstanceFollowRedirects true
132+ conn setDoInput true
133+
134+ conn.connect
135+
136+ val status = conn.getResponseCode
137+ status match {
138+ case HttpURLConnection .HTTP_MOVED_TEMP | HttpURLConnection .HTTP_MOVED_PERM | HttpURLConnection .HTTP_SEE_OTHER =>
139+ val redirect = conn getHeaderField " Location"
140+ connect(new URL (redirect))
141+
142+ case _ => conn
143+ }
144+ }
145+
146+ connect(new URL (file.url))
147+ }
148+
149+ val stream = new BufferedInputStream (
150+ new ProgressInputStream (conn.getInputStream, " Downloading " + label, conn.getContentLengthLong)
151+ )
152+
153+ try {
154+ lazy val pathTrimmer : String => String = dep match {
155+ case ph : PackageHelper => ph.trimPath(file.platform)
156+ case _ => identity
157+ }
158+
159+ file.format match {
160+ case FileFormat .Raw => processRaw(stream, dest)
161+ case FileFormat .Zip => processZip(stream, dest, pathTrimmer)
162+ case FileFormat .TarGz => processTarGz(stream, dest, pathTrimmer)
163+ }
164+ } finally {
165+ IOUtils closeQuietly stream
166+ }
167+
168+ // "Downloading <label>... xxx% complete" = 18 extra characters to clear
169+ log.info(" Downloaded " + label + " " )
170+ }
171+
172+ private def processRaw (stream : InputStream , destFile : File ) {
173+ val fos = new FileOutputStream (destFile)
174+ try {
175+ IOUtils .copyLarge(stream, fos)
176+ } finally {
177+ IOUtils closeQuietly fos
178+ }
179+ }
180+
181+ private def processZip (stream : InputStream , destFolder : File , pathTrim : String => String ) {
182+ destFolder.mkdirs
183+
184+ val zin = new ZipArchiveInputStream (stream)
185+
186+ try {
187+ val entries = Iterator .continually { zin.getNextZipEntry }.takeWhile { _ != null }
188+ val buffer = new Array [Byte ](BufferLen )
189+
190+ for (entry <- entries if ! entry.isDirectory) {
191+ val out = destFolder / pathTrim(entry.getName)
192+ out.getParentFile.mkdirs
193+
194+ val fos = new FileOutputStream (out)
195+ try {
196+ val reads = Iterator .continually { zin.read(buffer, 0 , BufferLen ) }.takeWhile { _ > 0 }
197+ for (read <- reads) fos.write(buffer, 0 , read)
198+ } finally {
199+ IOUtils closeQuietly fos
200+ }
201+
202+ out setLastModified entry.getLastModifiedDate.getTime
203+ }
204+ } finally {
205+ IOUtils closeQuietly zin
206+ }
207+ }
208+
209+ private def processTarGz (stream : InputStream , destFolder : File , pathTrim : String => String ) {
210+ destFolder.mkdirs
211+
212+ val gzin = new GzipCompressorInputStream (stream)
213+ val tarin = new TarArchiveInputStream (gzin)
214+
215+ try {
216+ val entries = Iterator .continually { tarin.getNextTarEntry }.takeWhile { _ != null }
217+ val buffer = new Array [Byte ](BufferLen )
218+
219+ for (entry <- entries if ! entry.isDirectory) {
220+ val out = destFolder / pathTrim(entry.getName)
221+ out.getParentFile.mkdirs
222+
223+ val fos = new FileOutputStream (out)
224+ try {
225+ val reads = Iterator .continually { tarin.read(buffer, 0 , BufferLen ) }.takeWhile { _ > 0 }
226+ for (read <- reads) fos.write(buffer, 0 , read)
227+ } finally {
228+ IOUtils closeQuietly fos
229+ }
230+
231+ out setLastModified entry.getLastModifiedDate.getTime
232+ }
233+ } finally {
234+ IOUtils closeQuietly tarin
235+ IOUtils closeQuietly gzin
236+ }
237+ }
238+
239+ object Settings {
240+ val fetchDependenciesTask : Initialize [Task [Unit ]] = (distDeps in Distribution , dependencyList in PackageDependencies , streams) map { (distDepsFolder, deps, streams) =>
241+ val log = streams.log
242+
243+ val commonFolder = distDepsFolder / " common"
244+ val toolsFolder = distDepsFolder / " tools"
245+ def getPlatformFolder (platform : Platform ) = platform match {
246+ case Platform .Windows => distDepsFolder / " win32"
247+ case Platform .Linux => distDepsFolder / " linux-x86"
248+ case Platform .OSX => distDepsFolder / " osx"
249+ case Platform .Unspecified => commonFolder
250+ }
251+
252+ deps foreach {
253+ case dep @ JreDependency (name, _, destPath, files) =>
254+ for (file @ DependencyFile (platform, url, _) <- files)
255+ doDownload(
256+ name + " [" + platform + " ]" , log,
257+ dep, file,
258+ getPlatformFolder(platform) / destPath,
259+ { _.setRequestProperty(" Cookie" , " oraclelicense=accept-securebackup-cookie" ) }
260+ )
261+
262+ case dep @ PlatformDependency (name, destPath, files) =>
263+ for (file @ DependencyFile (platform, url, _) <- files)
264+ doDownload(
265+ name + " [" + platform + " ]" , log,
266+ dep, file,
267+ getPlatformFolder(platform) / destPath
268+ )
269+
270+ case dep @ CommonDependency (name, destPath, file) =>
271+ doDownload(
272+ name + " [common]" , log,
273+ dep, file,
274+ commonFolder / destPath
275+ )
276+
277+ case dep @ ToolDependency (name, destPath, file) =>
278+ doDownload(
279+ name + " [tool]" , log,
280+ dep, file,
281+ toolsFolder / destPath
282+ )
283+ }
284+ }
285+ }
286+
287+ import Settings ._
288+
289+ lazy val dependencyFetcherSettings : Seq [Setting [_]] = Seq (
290+ dependencyList in PackageDependencies := Nil ,
291+
292+ jreWindows in PackageDependencies := " http://download.oracle.com/otn-pub/java/jdk/7u55-b13/jre-7u55-windows-i586.tar.gz" ,
293+ jreLinux in PackageDependencies := " http://download.oracle.com/otn-pub/java/jdk/7u55-b13/jre-7u55-linux-i586.tar.gz" ,
294+ jreOsx in PackageDependencies := " http://download.oracle.com/otn-pub/java/jdk/7u55-b13/jre-7u55-macosx-x64.tar.gz" ,
295+ dependencyList in PackageDependencies <+= (jreWindows in PackageDependencies , jreLinux in PackageDependencies , jreOsx in PackageDependencies ) map { (jreWin, jreLin, jreOsx) =>
296+ JreDependency (
297+ name = " jre 7u55" , rawPath = " jre1.7.0_55" , destPath = " jre" ,
298+ files = List (
299+ DependencyFile (Platform .Windows , jreWin, FileFormat .TarGz ),
300+ DependencyFile (Platform .Linux , jreLin, FileFormat .TarGz ),
301+ DependencyFile (Platform .OSX , jreOsx, FileFormat .TarGz )
302+ )
303+ )
304+ },
305+
306+ nwkWindows in PackageDependencies := " http://dl.node-webkit.org/v0.9.2/node-webkit-v0.9.2-win-ia32.zip" ,
307+ nwkLinux in PackageDependencies := " http://dl.node-webkit.org/v0.9.2/node-webkit-v0.9.2-linux-ia32.tar.gz" ,
308+ nwkOsx in PackageDependencies := " http://dl.node-webkit.org/v0.9.2/node-webkit-v0.9.2-osx-ia32.zip" ,
309+ dependencyList in PackageDependencies <+= (nwkWindows in PackageDependencies , nwkLinux in PackageDependencies , nwkOsx in PackageDependencies ) map { (nwkWin, nwkLin, nwkOsx) =>
310+ new PlatformDependency (
311+ name = " node-webkit v0.9.2" , destPath = " node-webkit" ,
312+ files = List (
313+ DependencyFile (Platform .Windows , nwkWin, FileFormat .Zip ),
314+ DependencyFile (Platform .Linux , nwkLin, FileFormat .TarGz ),
315+ DependencyFile (Platform .OSX , nwkOsx, FileFormat .Zip )
316+ )
317+ ) with PackageHelper {
318+ private val trimPathRegex = (" ^\\ Qnode-webkit-v0.9.2-linux-ia32\\ E/" ).r
319+ def trimPath (platform : Platform )(path : String ) = platform match {
320+ case Platform .Linux => trimPathRegex.replaceFirstIn(path, " " )
321+ case _ => path
322+ }
323+ }
324+ },
325+
326+ jetty in PackageDependencies := " http://mirrors.xmission.com/eclipse/jetty/9.1.4.v20140401/dist/jetty-distribution-9.1.4.v20140401.zip" ,
327+ // resourcer in PackageDependencies := "http://anolis.codeplex.com/downloads/get/81545",
328+ resourcer in PackageDependencies := " https://dl.dropboxusercontent.com/s/zifogi9efgtsq1s/Anolis.Resourcer-0.9.zip?dl=1" ,
329+ dependencyList in PackageDependencies <++= (jetty in PackageDependencies , resourcer in PackageDependencies ) map { (jetty, resourcer) =>
330+ val jettyDep = new CommonDependency (" Jetty 9.1.4 v20140401" , " jetty" , DependencyFile (Platform .Unspecified , jetty, FileFormat .Zip )) with PackageHelper {
331+ private val trimPathRegex = (" ^\\ Qjetty-distribution-9.1.4.v20140401\\ E/" ).r
332+ def trimPath (platform : Platform )(path : String ) = {
333+ trimPathRegex.replaceFirstIn(path, " " )
334+ }
335+ }
336+
337+ val resourcerDep = ToolDependency (" Resourcer" , " resourcer" , DependencyFile (Platform .Unspecified , resourcer, FileFormat .Zip ))
338+
339+ jettyDep :: resourcerDep :: Nil
340+ },
341+
342+ fetchDependencies <<= fetchDependenciesTask
343+ )
344+ }
0 commit comments