Skip to content

Commit 24b9288

Browse files
author
Lenny Halseth
committed
Add support for .ear files and a less naive short-circuit file checker.
This support allows .ear files to be seen as zip files (since they are typically), and expands the quick check for java class files to search in nested archives instead of only the top-most. Fixed (via workaround) a cleanup issue that only exhibits in development workflows. This creates cleanup phases of preshutdown and shutdown. This still needs to be managed carefully. A better solution would define strict dependency (or have better error checking in the callbacks).
1 parent 745125e commit 24b9288

File tree

6 files changed

+72
-20
lines changed

6 files changed

+72
-20
lines changed

codepulse/src/main/scala/bootstrap/liftweb/AppCleanup.scala

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,23 @@
2020
package bootstrap.liftweb
2121

2222
object AppCleanup {
23-
private var hooks: List[() => Unit] = Nil
23+
private var preShutdownHooks: List[() => Unit] = Nil
2424

25-
def add(cleanup: () => Unit) = hooks ::= cleanup
25+
private var shutdownHooks: List[() => Unit] = Nil
26+
27+
def addPreShutdownHook(cleanup: () => Unit) = preShutdownHooks ::= cleanup
28+
29+
def addShutdownHook(cleanup: () => Unit) = shutdownHooks ::= cleanup
2630

2731
def runCleanup() = {
28-
hooks.reverseIterator.foreach { _() }
32+
try {
33+
println("Running PreShutdownHooks")
34+
preShutdownHooks.reverseIterator.foreach { _() }
35+
36+
println("Running ShutdownHooks")
37+
shutdownHooks.reverseIterator.foreach { _() }
38+
} catch {
39+
case e: Throwable => e.printStackTrace
40+
}
2941
}
3042
}

codepulse/src/main/scala/com/secdec/codepulse/tracer/ProjectFileUploadHandler.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ class ProjectFileUploadHandler(projectManager: ProjectManager) extends RestHelpe
5656
case UploadPath("create") Post req => fallbackResponse {
5757
for {
5858
(inputFile, originalName, cleanup) <- getReqFile(req) ?~! "Creating a new project requires a file"
59-
_ <- ProjectUploadData.checkForBinaryZip(inputFile) ?~ {
59+
_ <- ProjectUploadData.checkForClassesInNestedArchive(inputFile) ?~ {
6060
s"The file you picked doesn't have any compiled Java files."
6161
}
6262
name <- req.param("name") ?~ "You must specify a name"

codepulse/src/main/scala/com/secdec/codepulse/tracer/ProjectManager.scala

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,11 @@ import reactive.Observing
4444
object ProjectManager {
4545
lazy val defaultActorSystem = {
4646
val sys = ActorSystem("ProjectManagerSystem")
47-
AppCleanup.add { () => sys.shutdown() }
47+
AppCleanup.addShutdownHook { () =>
48+
sys.shutdown()
49+
sys.awaitTermination()
50+
println("Shutdown ProjectManager's ActorSystem")
51+
}
4852
sys
4953
}
5054
}
@@ -196,6 +200,9 @@ class ProjectManager(val actorSystem: ActorSystem) extends Observing {
196200
}
197201

198202
// Also make sure any dirty projects are saved when exiting
199-
AppCleanup.add { () => flushProjects }
203+
AppCleanup.addPreShutdownHook { () =>
204+
flushProjects
205+
println("Flushed ProjectManager projects")
206+
}
200207

201208
}

codepulse/src/main/scala/com/secdec/codepulse/tracer/ProjectUploadData.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,19 @@ object ProjectUploadData {
6969
}
7070
}
7171

72+
def checkForClassesInNestedArchive(file: File): Boolean = {
73+
ZipEntryChecker.findFirstEntry(file) { (filename, entry, contents) =>
74+
if (!entry.isDirectory) {
75+
FilenameUtils.getExtension(entry.getName) match {
76+
case "class" => true
77+
case _ => false
78+
}
79+
} else {
80+
false
81+
}
82+
}
83+
}
84+
7285
/** A preliminary check on a File to see if it looks like an
7386
* exported .pulse file.
7487
*/

codepulse/src/main/scala/com/secdec/codepulse/tracer/TraceServer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ object TraceServer {
3636
private implicit lazy val socketServer = {
3737
val ss = SocketServer.default(com.secdec.codepulse.userSettings.tracePort)
3838
ss.start()
39-
AppCleanup.add { () =>
39+
AppCleanup.addPreShutdownHook { () =>
4040
ss.shutdown
4141
println("Shutdown TracerServer's socketServer")
4242
}

codepulse/src/main/scala/com/secdec/codepulse/util/ZipEntryChecker.scala

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,9 @@
1919

2020
package com.secdec.codepulse.util
2121

22-
import java.io.BufferedInputStream
23-
import java.io.File
24-
import java.io.FileInputStream
25-
import java.io.InputStream
26-
import java.util.zip.ZipEntry
27-
import java.util.zip.ZipFile
28-
import java.util.zip.ZipInputStream
29-
30-
import scala.collection.JavaConversions._
31-
import scala.util.Failure
32-
import scala.util.Success
33-
import scala.util.Try
22+
import java.io.{ BufferedInputStream, File, FileInputStream, InputStream }
23+
import java.util.zip.{ ZipEntry, ZipFile, ZipInputStream }
24+
import scala.util.{ Failure, Success, Try }
3425

3526
import org.apache.commons.io.FilenameUtils
3627
import org.apache.commons.io.input.CloseShieldInputStream
@@ -61,7 +52,7 @@ trait ZipEntryChecker {
6152
}
6253

6354
def isZip(name: String): Boolean = FilenameUtils.getExtension(name) match {
64-
case "zip" | "jar" | "war" => true
55+
case "zip" | "ear" | "jar" | "war" => true
6556
case _ => false
6657
}
6758

@@ -95,4 +86,33 @@ trait ZipEntryChecker {
9586
stream.close
9687
}
9788
}
89+
90+
def findFirstEntry(file: File, recursive: Boolean = true)(callback: (String, ZipEntry, InputStream) => Boolean): Boolean = {
91+
val stream = new BufferedInputStream(new FileInputStream(file))
92+
93+
try {
94+
findFirstEntry(file.getName, stream, recursive)(callback)
95+
} finally {
96+
stream.close
97+
}
98+
}
99+
100+
def findFirstEntry(filename: String, stream: InputStream, recursive: Boolean)(callback: (String, ZipEntry, InputStream) => Boolean): Boolean = {
101+
val zipStream = new ZipInputStream(stream)
102+
103+
try {
104+
val entryStream = Stream.continually(Try { zipStream.getNextEntry })
105+
.map(_.toOption.flatMap { Option(_) })
106+
.takeWhile(_.isDefined)
107+
.flatten
108+
.filterNot(ZipCleaner.shouldFilter)
109+
110+
entryStream.exists(entry =>
111+
callback(filename, entry, zipStream)
112+
||(recursive && isZip(entry.getName) && findFirstEntry(s"$filename/${entry.getName}", new CloseShieldInputStream(zipStream), true)(callback))
113+
)
114+
} finally {
115+
zipStream.close
116+
}
117+
}
98118
}

0 commit comments

Comments
 (0)