Gakuo
Gakuo

Reputation: 855

Scala files read and write locks problems

Problem description

I have an application where I am to write values into a file and read them back into the program in a while loop. This fails because the file is only written when I exit the loop and not in every iteration. Therefore, in the next iterations, I cannot access values that were supposed to be written into the file in the previous iterations. This is an application to read in a test-file from a scala project and its dependencies, then edit it and write back to its original file. The editing iteratively in a loop. I use scalafix and scalameta.

My files

After some earlier deliberations, It occurred to me that I have two files: local and sourceFile. local contains sourceFile. local is a Scala project directory and sourceFile is .scala file in the test/scala/ subdirectory.

Code Description

In the main method, I get classes and jars from local and build the syntactic document from sourceFile. I then perform edits using the SemanticDocument via ScalaFix patch API. Finally, I want to write back the edited string back to sourceFile and this fails until I exit the loop (and perhaps the JVM). See the comments on the code. This looks like an issue to do with locking. It might be that my earlier functions that were looking for jars, classes, and semanticdb do not release the locks for sourceFile and local. I tested this out by writing in a different file outside local and I got the expected behaviour.

Question

How can I make scala release the lock on these files? I tried going through these functions and I got all mixed up. I did not find a way to release the files. related to: Scala writing and reading from a file inside a while loop

package scalafix

import java.io.File
import java.net.URLClassLoader
import java.nio.file.Paths

import org.apache.commons.io.FileUtils
import org.apache.commons.io.filefilter.{DirectoryFileFilter, TrueFileFilter}
import scalafix.internal.patch.PatchInternals
import scalafix.internal.v1.InternalSemanticDoc
import scalafix.rule.RuleIdentifier
import scalafix.v1.{Patch, SemanticDocument, SyntacticDocument, _}
import scalafix.{Patch, RuleCtx, RuleName}

import scala.meta.Term.ApplyInfix
import scala.meta._
import scala.meta.inputs.Input
import scala.meta.internal.semanticdb.{Locator, TextDocument}
import scala.meta.internal.symtab.GlobalSymbolTable
import scala.meta.io.{AbsolutePath, Classpath}
import scala.meta.transversers.{SimpleTraverser, Transformer}
import scala.meta.{Name, Source}
import FileIO.enrichFile

import scala.sys.process._
import java.io.PrintWriter
import java.io.BufferedWriter
import java.io.FileWriter


object Printer{

//My function to write back to file
  def saveFile(filename: File, data: String): Unit ={
    val fileWritter: FileWriter = new FileWriter(filename)
    fileWritter.write(data)
    fileWritter.close()
  }
}

object Main extends App {
  //My variables to be updated after every loop
  var doc: SyntacticDocument = null
  var ast: Source = null
  var n = 3
  val firstRound = n
  var editedSuite:String = null
  do {
    val base  = "/Users/soft/Downloads/simpleAkkaProject/"
    // The local file
    val local = new File(base)
    // run an external command to compile source files in local into SemanticDocuments
    val result = sys.process.Process(Seq("sbt","semanticdb"), local).!
    // find jars in local.
    val jars = FileUtils.listFiles(local, Array("jar"), true).toArray(new Array[File](0))
      .toList
      .map(f => Classpath(f.getAbsolutePath))
      .reduceOption(_ ++ _)
    // find classes in local
    val classes = FileUtils.listFilesAndDirs(local, TrueFileFilter.INSTANCE, DirectoryFileFilter.DIRECTORY).toArray(new Array[File](0))
      .toList
      .filter(p => p.isDirectory && !p.getAbsolutePath.contains(".sbt") && p.getAbsolutePath.contains("target") && p.getAbsolutePath.contains("classes"))
      .map(f => Classpath(f.getAbsolutePath))
      .reduceOption(_ ++ _)
    // compute the classpath
    val classPath = ClassLoader.getSystemClassLoader.asInstanceOf[URLClassLoader].getURLs
      .map(url => Classpath(url.getFile))
      .reduceOption(_ ++ _)
    val all = (jars ++ classes ++ classPath).reduceOption(_ ++ _).getOrElse(Classpath(""))
    //combine classes, jars, and classpaths as dependencies into GlobalSymbolTable
    val symbolTable = GlobalSymbolTable(all)
    val filename = "AkkaQuickstartSpec.scala"
    val root = AbsolutePath(base).resolve("src/test/scala/")
    println(root)
    val abspath = root.resolve(filename)
    println(root)
    val relpath = abspath.toRelative(AbsolutePath(base))
    println(relpath)
    // The sourceFile
    val sourceFile = new File(base+"src/test/scala/"+filename)
   // use source file to compute a syntactic document
    val input = Input.File(sourceFile)
    println(input)
    if (n == firstRound){
      doc = SyntacticDocument.fromInput(input)
    }
    //println(doc.tree.structure(30))
    var documents: Map[String, TextDocument] = Map.empty
    //use scalameta internalSemantic document to locate semantic documents in the local directory
    Locator.apply(local.toPath)((path, db) => db.documents.foreach({
      case document@TextDocument(_, uri, text, md5, _, _, _, _, _) if !md5.isEmpty => { // skip diagnostics files
        if (n == firstRound){
          ast= sourceFile.parse[Source].getOrElse(Source(List()))
        }
        documents = documents + (uri -> document)
        println(uri)
      }
       println(local.canWrite)
        if (editedSuite != null){
          Printer.saveFile(sourceFile,editedSuite)
        }
    }))

    //compute an implicit semantic document of the sourceFile for editing
   val impl = new InternalSemanticDoc(doc, documents(relpath.toString()), symbolTable)
    implicit val sdoc = new SemanticDocument(impl)
    val symbols = sdoc.tree.collect {
      case t@ Term.Name("<") => {
        println(s"symbol for $t")
        println(t.symbol.value)
        println(symbolTable.info(t.symbol.value))
      }
    }
    //edit the sourceFile semanticDocument
    val staticAnalyzer = new StaticAnalyzer()  
    val p3 = staticAnalyzer.duplicateTestCase()
    val r3 = RuleName(List(RuleIdentifier("r3")))
    val map:Map[RuleName, Patch] = Map(r3->p3)
    val r = PatchInternals(map, v0.RuleCtx(sdoc.tree), None)
    val parsed = r._1.parse[Source]
    ast = parsed.getOrElse(Source(List()))
    doc =SyntacticDocument.fromTree(parsed.get)
    val list: List[Int] = List()

    editedSuite = r._1
     println(local.canWrite)
    //Write back to the sourceFile for every loop. This works only when we exit!
    Printer.saveFile(sourceFile,r._1)
    println("Loop: "+ n)
    n-=1

  } while(n>0)

}


  [1]: https://stackoverflow.com/questions/54804642/scala-writing-and-reading-from-a-file-inside-a-while-loop

Upvotes: 0

Views: 466

Answers (1)

Tim
Tim

Reputation: 27421

Have you tried closing local and sourceFile at the end of the loop? You really can't be certain that data is flushed to the file system while a file is still open.

It will also make things much easier to understand if you to move invariants out of the loop (candidates include base, filename, root, abspath, and probably more) and group your code into separate functions so that you can see the structure of your code more clearly, and focus on the parts that cause the problems.

[ This is a repeat of the advice I gave in my answer to your previous question ]

Upvotes: 1

Related Questions