Get relative path from inside a scala library (while developing that library) - scala

I'm currently developing a scala library and wanted to get a file that is inside it, event when compiled as a Jar dependency. The problem is that when executed from another project where the library is imported, the path is relative to that project. Here is the code to get the file :
private val pathToDocker: Path = Paths.get("src", "main", "resources", "docker-compose")
What can I do to look for my file inside the imported dependency ?

The file won't be in a file system -- it'll be in the compiled JAR.
You can get a JAR resource using a class loader. Here's an example of how to do that in another codebase.
Utility function:
https://github.com/hail-is/hail/blob/6db198ae06/hail/src/main/scala/is/hail/utils/package.scala#L468
Usage to load version info:
https://github.com/hail-is/hail/blob/6db198ae06/hail/src/main/scala/is/hail/package.scala#L21

There is a JarUtil - from an answer of access-file-in-jar-file translate to Scala:
import java.util.jar.JarFile
val jar = new JarFile("path_to_jar/shapeless_2.12-2.3.3.jar")
val entry = jar.getEntry("shapeless/Annotation.class")
val inStream = jar.getInputStream(entry)
As you mentioned scala.io.Source.fromResource works only within your project:
Source.fromResource("tstdir/source.txt")
Make sure the file is in the resources directory, like:

The way I found to achieve getting the path inside a jar depedency was creating a singleton (scala object) that has a method that loads the files. Since it is inside the Jar, the resulting path is relative to the jar itself. Here is the code :
object ClassLoaderTest {
def dockerFile: File =
new File(getClass.getClassLoader.getResource("docker/docker-compose.yml").getPath)
}
When called from a Trait (or an interface), the resulting path is :
jar:file:/home/myname/.ivy2/cache/com.mypackage/libraryname/jars/libraryname.libraryversion.jar!/docker/docker-compose.yml

Related

Compile scala code mix with java code

I need to compile a scala code which calls a java code from it.
What I did:
1]I have a scala main file check.scala
package com.code
class check {
var Rectvalue = Array.ofDim[Int](5)
val str = Array.ofDim[String](1)
def nativeacces(arg: String, loop: Integer) {
val test = new testing()
test.process(arg, Rectvalue,str)
}
}
2.For creating instance val test = new testing() ,i added two .class(sample.class,testJNI.class) file from java source code inside the folder(package) com/code.
3.When I compile the scala code using
scalac check.scala
It generates the class file for the scala file.
What I have to do:
1.Instead of .class(sample.class,testJNI.class) file added inside the package ,i need to add jar file.
2.I tried, created jar file for the .class file and compile the Scala, it shows the error:
scala:6: error: not found: type testing
val test = new testing()
3.I need to link the .jar file and compile the scala main file
You can reference classes/directories/JARs via classpath option:
scalac -classpath your.jar check.scala
Related question: Adding .jar's to classpath (Scala).
If you want a proper build use SBT, put your JAR in lib directory in the root of project and it will figure out what to do for you. Here is Hello World of SBT.

How to make properties file available to FileInputStream in Scala script?

I have a Scala script that I want to call from sbt. This Scala script refers to some dependencies. One of those dependencies uses a properties file. This properties file is provided by the run time as this dependency is run as a separate application.
Just to have the possibility to run that property-using dependency as a standalone, I wrote this Scala script that I want to call from sbt.
val fis = new FileInputStream("my.properties") // Fails here
val props = new Properties()
When I run the above code, it fails with an exception in my dependency where the properties file is loaded.
How to make this properties file available to the script under sbt?
Place the file my.properties in src/main/resources and use Source.fromURL(getClass.getResource("/my.properties")) instead (as it gives you more flexibility in where you can place the file on file systems as long as it's on CLASSPATH).
As a helper, use the following code to learn about the place where the file is expected when a "bare" File* types are in use:
println(new java.io.File("my.properties").getAbsolutePath)
Since the current working directory is the top-level directory of a project, the file is searched in $PROJECT_ROOT_DIR/my.properties.

Scala IDE: can't read XML file into scala worksheet

New to Scala and having problems reading an XML file in a Scala worksheet. So far I have:
downloaded the Scala IDE (for Windows) and unzipped it to my C:\ drive
created a Scala project with the following file path: C:\eclipse\workspace\xml_data
created the xml file ...\xml_data\music.xml using the following data
created a package sample_data and create the following object (with file path: ...\xml_data\src\sample_data\SampleData.scala):
package sample_data
import scala.xml.XML
object SampleData {
val data = XML.loadFile("music.xml")
}
object PrintSampleData extends Application {
println(SampleData.data)
}
This runs OK, however, when I create the Scala worksheet test_sample_data.sc:
import sample_data.SampleData
object test {
println(SampleData.data)
}
I get a java.lang.ExceptionInInitializerError which includes: Caused by: java.io.FileNotFoundException: music.xml (The system cannot find the file specified).
The workspace is C:\eclipse\workspace. Any help or insight much appreciated. Cheers!
UPDATE:
Following aepurniet's advice, I ran new java.io.File(".").getAbsolutePath() and got the following respectively:
SampleData.scala: C:\eclipse\workspace\xml_data\.
test_sample_data.sc: C:\eclipse\.
So this is what is causing the problem. Does anyone know why this occurs? Absolute file paths resolve the problem. Is this the best solution?
Regarding what is causing different user directory between the scala class and worksheet:
You are likely hitting the Eclipse IDE issue listed here
https://github.com/scala-ide/scala-worksheet/issues/102
Jfyi, I used Intellij and the issue is not reproducible there.
Regarding using absolute paths:
Using absolute path works fine for quick testing, but would NOT be a good practice for the actual implementation. You can consider passing the path along with the filename as input to SampleData.
Some hack mentioned here to get the base path of the workspace from the scala worksheet: Configure working directory of Scala worksheet
If this is just for your testing, hacking the absolute path of workspace inside the worksheets might be the easiest for you.
SampleData.scala
package sample_data
import scala.xml.XML
object SampleData {
def data(filename: String) = XML.loadFile(filename)
}
object PrintSampleData extends Application {
println(SampleData.data(System.getProperty("user.dir") + "/music.xml")
}
Scala worksheet:
import sample_data.SampleData
object test {
val workDir = ... // Using the hack or hardcoding
println(SampleData.data(workDir + "/music.xml"))
}

PlayFramework2 Scala File Map

I'm just starting with Scala and have run into a problem that has me stumped, but I'm guessing that I'm missing something easy.
I was following instructions to use the Clapper ClassFinder:
http://thoughts.inphina.com/2011/09/15/building-a-plugin-based-architecture-in-scala/
val classpath = List("./plugins").map(new File(_))
val finder = ClassFinder(classpath)
val classes = finder.getClasses
val classMap = ClassFinder.classInfoMap(classes)
After executing the first line, I see that classpath is set simply to
List(.\plugins)
I'm running this on windows, so the swapping of the slash seems to be OK.
But I expected to see a list of File objects, although I am not sure about this Scala syntax, and perhaps I'm missing something in the Scala IDE. The value for classes shows an "empty iterator".
It seems not to be finding any files in the path that I specified. I tried using an absolute path, but I had the same results. I have a single jar file in the plugins directory that I'm hoping it will find. The plugins directory is at the root of the Play2 project I'm using.
Edit ---
I did find that when I explicitly list the path to one jar that it is able to find it:
val classpath = List("./plugins/myPlugin.jar").map(new File(_))
But I want to find all jar files in the directory.
The following didn't work:
val classpath = List("./plugins/*").map(new File(_))
Nor did this:
val classpath = List("./plugins/*.jar").map(new File(_))
Judging by this issue on the ClassFinder repo on Github it may be a bug.
I think you need to create an explicit list of jar files or to list the ones contained in your folder like:
val classpath =(new File("./plugins")).listFiles.filter(_.getName.endsWith(".jar"))
EDIT: from a cursory glance at ClassFinder's source on GitHub I think it's not a bug. ClassFinder searches for .class files either in jars or in zip files or directly in folders but it looks like it does not mix these things recursively (i.e. if you give it a folder it will look for classes directly in the folder but it won't look for classes in jars in the folder)
if you objective is to list all jar files, you can use following code:
val classpath = List("./plugins").map(path => Option(new File(path).listFiles).getOrElse(Array.empty[java.io.File]) filter(file => file.isFile && file.getName.endsWith(".jar"))).flatten

Changing sbt project's directory layout

According to sbt tutorial on changing paths I'm trying to change "target" output directory to "someother"
override def outputDirectoryName = "someother"
Everything goes fine except one: sbt automatically creates target directory with ".history" file inside. Why sbt does this when it supposed do create only "someother" dir ? I tryied to override all methods that are inherited from BasicProjectPaths (I use sbt.DefaultProject as superclass of my project descriptor)
override def mainCompilePath = ...
override def testCompilePath = ...
...
But sbt creates "target" folder in spite of paths overriding.
It certainly seems that it should use the overridden outputDirectoryName in trunk...
/** The path to the file that provides persistence for history. */
def historyPath: Option[Path] = Some(outputRootPath / ".history")
def outputPath = crossPath(outputRootPath)
def outputRootPath: Path = outputDirectoryName
def outputDirectoryName = DefaultOutputDirectoryName
(from SBT's current trunk).
It may have been different in a previous version. Have you considered raising a new bug?
In sbt 0.13.5, I found a way to change the target folder by just re-assigning target in the build.sbt file:
target := file("someotherParent") / "someotherSubdir"
This only modifies the directory for the built classes and artifacts, however, the .history file is always in the project root directory.
Unfortunately, some other plugins (xsbt-web-plugin) seem to have problems with that - running the webapp via SBT console produced weird errors, when I switched back to the standard directory layout, these problems disappeared.
A better way to achieve my goals (of all JARS in one directory, whose names contains the JAVA-VM-version) seems to be to specify an appropriate target for publishing - there are less restrictions on "sbt publish", and other plugins are not disturbed by a different directory layout.