Skip to content

Commit 1ddb3da

Browse files
committed
copy base
1 parent 6000328 commit 1ddb3da

File tree

1 file changed

+222
-0
lines changed

1 file changed

+222
-0
lines changed
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
package org.apache.spark.deploy
19+
20+
import java.io.{File, FileInputStream, FileOutputStream}
21+
import java.nio.file.{Files, Path}
22+
import java.util.jar.{JarEntry, JarOutputStream}
23+
import javax.tools.{JavaFileObject, SimpleJavaFileObject, StandardLocation, ToolProvider}
24+
25+
import com.google.common.io.ByteStreams
26+
27+
import org.apache.spark.deploy.SparkSubmitUtils.MavenCoordinate
28+
29+
import scala.collection.JavaConversions._
30+
31+
private[deploy] object IvyTestUtils {
32+
33+
/**
34+
* Create the path for the jar and pom from the maven coordinate. Extension should be `jar`
35+
* or `pom`.
36+
*/
37+
private def pathFromCoordinate(
38+
artifact: MavenCoordinate,
39+
prefix: Path,
40+
ext: String,
41+
useIvyLayout: Boolean): Path = {
42+
val groupDirs = artifact.groupId.replace(".", File.separator)
43+
val artifactDirs = artifact.artifactId.replace(".", File.separator)
44+
val artifactPath =
45+
if (!useIvyLayout) {
46+
Seq(groupDirs, artifactDirs, artifact.version).mkString(File.separator)
47+
} else {
48+
Seq(groupDirs, artifactDirs, artifact.version, ext + "s").mkString(File.separator)
49+
}
50+
new File(prefix.toFile, artifactPath).toPath
51+
}
52+
53+
private def artifactName(artifact: MavenCoordinate, ext: String = ".jar"): String = {
54+
s"${artifact.artifactId}-${artifact.version}$ext"
55+
}
56+
57+
/** Write the contents to a file to the supplied directory. */
58+
private def writeFile(dir: File, fileName: String, contents: String): File = {
59+
val outputFile = new File(dir, fileName)
60+
val outputStream = new FileOutputStream(outputFile)
61+
outputStream.write(contents.toCharArray.map(_.toByte))
62+
outputStream.close()
63+
outputFile
64+
}
65+
66+
/** Create an example Python file. */
67+
private def createPythonFile(dir: File): File = {
68+
val contents =
69+
"""def myfunc(x):
70+
| return x + 1
71+
""".stripMargin
72+
writeFile(dir, "mylib.py", contents)
73+
}
74+
75+
/** Compilable Java Source File */
76+
private class JavaSourceFromString(val file: File, val code: String)
77+
extends SimpleJavaFileObject(file.toURI, JavaFileObject.Kind.SOURCE) {
78+
override def getCharContent(ignoreEncodingErrors: Boolean) = code
79+
}
80+
81+
/** Create a simple testable Class. */
82+
private def createJavaClass(dir: File, className: String, packageName: String): File = {
83+
val contents =
84+
s"""package $packageName;
85+
|
86+
|import java.lang.Integer;
87+
|
88+
|class $className implements java.io.Serializable {
89+
|
90+
| public $className() {}
91+
|
92+
| public Integer myFunc(Integer x) {
93+
| return x + 1;
94+
| }
95+
|}
96+
""".stripMargin
97+
val sourceFiles = Seq(new JavaSourceFromString(new File(dir, className + ".java"), contents))
98+
val compiler = ToolProvider.getSystemJavaCompiler
99+
val fileManager = compiler.getStandardFileManager(null, null, null)
100+
101+
fileManager.setLocation(StandardLocation.CLASS_OUTPUT, List(dir))
102+
103+
compiler.getTask(null, fileManager, null, null, null, sourceFiles).call()
104+
105+
val fileName = s"${packageName.replace(".", File.separator)}${File.separator}$className.class"
106+
val result = new File(dir, fileName)
107+
assert(result.exists(), "Compiled file not found: " + result.getAbsolutePath)
108+
result
109+
}
110+
111+
/** Helper method to write artifact information in the pom. */
112+
private def pomArtifactWriter(artifact: MavenCoordinate, tabCount: Int = 1): String = {
113+
var result = "\n" + " " * tabCount + s"<groupId>${artifact.groupId}</groupId>"
114+
result += "\n" + " " * tabCount + s"<artifactId>${artifact.artifactId}</artifactId>"
115+
result += "\n" + " " * tabCount + s"<version>${artifact.version}</version>"
116+
result
117+
}
118+
119+
/** Create a pom file for this artifact. */
120+
private def createPom(
121+
dir: File,
122+
artifact: MavenCoordinate,
123+
dependencies: Option[Seq[MavenCoordinate]]): File = {
124+
var content = """
125+
|<?xml version="1.0" encoding="UTF-8"?>
126+
|<project xmlns="http://maven.apache.org/POM/4.0.0"
127+
| xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
128+
| xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
129+
| http://maven.apache.org/xsd/maven-4.0.0.xsd">
130+
| <modelVersion>4.0.0</modelVersion>
131+
""".stripMargin.trim
132+
content += pomArtifactWriter(artifact)
133+
content += dependencies.map { deps =>
134+
val inside = deps.map { dep =>
135+
"\t<dependency>" + pomArtifactWriter(dep, 3) + "\n\t</dependency>"
136+
}.mkString("\n")
137+
"\n <dependencies>\n" + inside + "\n </dependencies>"
138+
}.getOrElse("")
139+
content += "\n</project>"
140+
writeFile(dir, artifactName(artifact, ".pom"), content.trim)
141+
}
142+
143+
/** Create the jar for the given maven coordinate, using the supplied files. */
144+
private def packJar(
145+
dir: File,
146+
artifact: MavenCoordinate,
147+
files: Seq[(String, File)]): File = {
148+
val jarFile = new File(dir, artifactName(artifact))
149+
val jarFileStream = new FileOutputStream(jarFile)
150+
val jarStream = new JarOutputStream(jarFileStream, new java.util.jar.Manifest())
151+
152+
for (file <- files) {
153+
val jarEntry = new JarEntry(file._1)
154+
jarStream.putNextEntry(jarEntry)
155+
156+
val in = new FileInputStream(file._2)
157+
ByteStreams.copy(in, jarStream)
158+
in.close()
159+
}
160+
jarStream.close()
161+
jarFileStream.close()
162+
163+
jarFile
164+
}
165+
166+
/**
167+
* Creates a jar and pom file, mocking a Maven repository. The root path can be supplied with
168+
* `tempDir`, dependencies can be created into the same repo, and python files can also be packed
169+
* inside the jar.
170+
*
171+
* @param artifact The maven coordinate to generate the jar and pom for.
172+
* @param dependencies List of dependencies this artifact might have to also create jars and poms.
173+
* @param tempDir The root folder of the repository
174+
* @param useIvyLayout whether to mock the Ivy layout for local repository testing
175+
* @param withPython Whether to pack python files inside the jar for extensive testing.
176+
* @return
177+
*/
178+
private def createLocalRepository(
179+
artifact: MavenCoordinate,
180+
dependencies: Option[Seq[MavenCoordinate]] = None,
181+
tempDir: Option[Path] = None,
182+
useIvyLayout: Boolean = false,
183+
withPython: Boolean = false): Path = {
184+
// Where the root of the repository exists, and what Ivy will search in
185+
val tempPath = tempDir.getOrElse(Files.createTempDirectory(null))
186+
// Where to temporary class files and such
187+
val root = Files.createTempDirectory(tempPath, null).toFile
188+
val artifactPath = pathFromCoordinate(artifact, tempPath)
189+
Files.createDirectories(artifactPath)
190+
val className = "MyLib"
191+
192+
val javaClass = createJavaClass(root, className, artifact.groupId)
193+
// A tuple of files representation in the jar, and the file
194+
val javaFile = (artifact.groupId.replace(".", "/") + "/" + javaClass.getName, javaClass)
195+
val allFiles =
196+
if (withPython) {
197+
val pythonFile = createPythonFile(root)
198+
Seq(javaFile, (pythonFile.getName, pythonFile))
199+
} else {
200+
Seq(javaFile)
201+
}
202+
val jarFile = packJar(artifactPath.toFile, artifact, allFiles)
203+
assert(jarFile.exists(), "Problem creating Jar file")
204+
val pomFile = createPom(artifactPath.toFile, artifact, dependencies)
205+
assert(pomFile.exists(), "Problem creating Pom file")
206+
tempPath
207+
}
208+
209+
private[databricks] def createLocalRepositoryForTests(
210+
artifact: String,
211+
dependencies: Option[String],
212+
withPython: Boolean = false): Path = {
213+
val art = parseCoordinates(artifact).head
214+
val deps = dependencies.map(parseCoordinates)
215+
val mainRepo = createLocalRepository(art, deps, withPython)
216+
deps.foreach { seq => seq.foreach { dep =>
217+
createLocalRepository(dep, None, false, Some(mainRepo))
218+
}}
219+
220+
mainRepo
221+
}
222+
}

0 commit comments

Comments
 (0)