управляемый (ARM) в scala для вложенных ресурсов

Итак, у меня есть этот код в scala, который я конвертирую в управляемый.

val file_out = new FileOutputStream(new java.io.File(filePath, resultFile + ".tar.gz"));
val buffer_out = new BufferedOutputStream(file_out);
val gzip_out = new GzipCompressorOutputStream(buffer_out);
val tar_out = new TarArchiveOutputStream(gzip_out);

try {
    addFileToTarGz(tar_out, filePath + "/" + resultFolder, "");
} finally {
    tar_out.finish();
    tar_out.close();
    gzip_out.close();
    buffer_out.close();
    file_out.close();
}

Первая попытка:

val file = new java.io.File(filePath, s"$resultFile.tar.gz")
managed(new FileOutputStream(file))
        .acquireAndGet(stream => managed(new BufferedOutputStream(stream))
                .acquireAndGet(buffer => managed(new GzipCompressorOutputStream(buffer))
                        .acquireAndGet(gzip => managed(new TarArchiveOutputStream(gzip))
                                .acquireAndGet(tar => {
                                  try {
                                    addFileToTarGz(tar, filePath + "/" + resultFolder, "")
                                  } finally {
                                    tar.finish()
                                  }
                                }))))

Тем не менее, это выглядит не очень читабельно. Есть ли лучший способ сделать его managed, но также и читабельным?


person Asad Iqbal    schedule 04.04.2017    source источник
comment
Лучше использовать for-comprehension с монадическим ManagedResource   -  person cchantep    schedule 05.04.2017
comment
@cchantep, можете ли вы прокомментировать мой ответ ниже? Это ты предложил?   -  person Asad Iqbal    schedule 06.04.2017


Ответы (2)


Вы рассмотрели схему нагрузки?

def withResource[T](block: Resource => T): T = {
  val resource = new Resource
  try {
    block(resource)
  } finally {
    resource.close()
  }
}

то вы бы использовали его как:

withResourse { resource =>
  // do something with resource
}

Если бы вы использовали отдельную загрузку для каждого из этих файлов, вы бы получили вложенные блоки... (что при некоторых обстоятельствах может быть наиболее разумным выбором), но здесь, я думаю, этого будет достаточно:

def withTarStream(filePath: String, resultFile: String)(block: TarArchiveOutputStream => T): T = {
  val fileOut = new FileOutputStream(new java.io.File(filePath, resultFile))
  val bufferOut = new BufferedOutputStream(fileOut)
  val gzipOut = new GzipCompressorOutputStream(bufferOut)
  val tarOut = new TarArchiveOutputStream(gzipOut)

  try {
    block(tarOut)
  } finally {
    tarOut.finish()
    tarOut.close()
    gzipOut.close()
    bufferOut.close()
    fileOut.close()
  }
}

используется как:

withTarStream(filePath, s"$resultFile.tar.gz") { tarStream =>
  addFileToTarGz(tarStream, filePath + "/" + resultFolder, "")
}
person Mateusz Kubuszok    schedule 05.04.2017

Основываясь на предложении @Mateusz Kubuszok, я попробовал эти варианты:

private def withResource[T: Resource : Manifest, X](t: T, block: T => X): X = managed(t).acquireAndGet(x => block(x))

withResource(new FileOutputStream(file),
  (x:FileOutputStream) => withResource(new BufferedOutputStream(x),
    (y: BufferedOutputStream) => withResource(new GzipCompressorOutputStream(y),
      (z: GzipCompressorOutputStream) => withResource(new TarArchiveOutputStream(z),
        (tar: TarArchiveOutputStream) => writeTar(tar, filePath, resultFolder)))));

А затем также реорганизовал приведенное выше в следующую форму:

private def withResource[T: Resource : Manifest, X](t: T, block: T => X): X = managed(t).acquireAndGet(x => block(x))

def writeInFile(x: FileOutputStream): Try[Unit] = withResource(new BufferedOutputStream(x), writeInBuffer)    
def writeInBuffer(y: BufferedOutputStream): Try[Unit] = withResource(new GzipCompressorOutputStream(y), writeGzipStream)    
def writeGzipStream(z: GzipCompressorOutputStream): Try[Unit] = withResource(new TarArchiveOutputStream(z), writeTarStream)    
val file = new File(filePath, s"$resultFile.tar.gz")
withResource(new FileOutputStream(file), writeInFile);

На следующий день коллега упомянул об этом, что выглядит лучше, чем оба вышеперечисленных: я все еще изучаю, как распространить результат/ошибку из этого блока.

val file = new File(filePath, s"$resultFile.tar.gz")    
for {   
  stream <- managed(new FileOutputStream(file)) 
  buffer <- managed(new BufferedOutputStream(stream))   
  gzip <- managed(new GzipCompressorOutputStream(buffer))   
  tar <- managed(new TarArchiveOutputStream(gzip))  
} {
  writeTar(tar)
}

Подобно тому, что предложил @cchantep, я сделал это:

  val tarOutputStream: ManagedResource[TarArchiveOutputStream] = (for {
    stream <- managed(new FileOutputStream(file))
    buffer <- managed(new BufferedOutputStream(stream))
    gzip <- managed(new GzipCompressorOutputStream(buffer))
    tar <- managed(new TarArchiveOutputStream(gzip))
  } yield tar)

  Try (tarOutputStream.acquireAndGet(writeTarStream(_))) match {
    case Failure(e) => Failure(e)
    case Success(_) => Success(new File(s"$filePath/$runLabel.tar.gz"))
  }
person Asad Iqbal    schedule 06.04.2017