All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pl.touk.nussknacker.engine.management.FlinkModelJarProvider.scala Maven / Gradle / Ivy

The newest version!
package pl.touk.nussknacker.engine.management

import java.io.{File, FileOutputStream}
import java.net.URL
import java.nio.file.Files
import java.util.jar.{JarEntry, JarOutputStream}
import com.typesafe.scalalogging.LazyLogging
import org.apache.commons.io.{FileUtils, IOUtils}

import scala.util.Using

class FlinkModelJarProvider(modelUrls: List[URL], includeDropwizardLibsImplicitly: Boolean = true) extends LazyLogging {

  private var modelFile: Option[File] = None

  // we want to have *different* file names for *different* model data (e.g. after rebuild etc.)
  // currently we just generate random file names
  def getJobJar(): File = synchronized {
    modelFile match {
      // check whether model file exists - temp files may disappear if application has been running for a long time
      case Some(file) if file.exists() => file
      case _ =>
        val newFile = prepareModelFile()
        modelFile = Some(newFile)
        newFile
    }
  }

  private def generateModelFileName(): File = {
    // currently we want to have one such file for one nussknacker execution
    val tempFile = Files.createTempFile("tempModelJar", ".jar").toFile
    tempFile.deleteOnExit()
    tempFile
  }

  private def additionalArtifactsToIncludeInJar(): List[URL] = {
    // Including dropwizard metrics library implicitly to preserve backward compatibility
    // with flink-metrics-dropwizard library bundled into flinkExecutor
    val additionalJarFiles = List(
      "flink-dropwizard-metrics-deps/flink-metrics-dropwizard.jar",
      "flink-dropwizard-metrics-deps/dropwizard-metrics-core.jar"
    )
    val additionalJarUrls = additionalJarFiles
      .map(filename => new File(filename))
      .filter(_.exists())
      .map(_.toURI.toURL)
    additionalJarUrls
  }

  private def prepareModelFile(): File = {
    val tempFile = generateModelFileName()
    val implicitlyIncludedArtifacts =
      if (includeDropwizardLibsImplicitly)
        additionalArtifactsToIncludeInJar().toSet -- modelUrls.toSet
      else Nil
    if (implicitlyIncludedArtifacts.nonEmpty) {
      logger.warn(s"""Including these files to model jar implicitly: [${implicitlyIncludedArtifacts.mkString(
          ", "
        )}] to keep backward compatibility""")
      logger.warn(
        s"""This implicit inclusion is only transitional, and you should add above files to classPath field in your configuration!!!"""
      )
    }
    implicitlyIncludedArtifacts.toList ++ modelUrls match {
      case single :: Nil if single.getPath.endsWith(".jar") =>
        logger.info(s"Single model URL detected, writing to ${tempFile.getAbsolutePath}")
        FileUtils.copyInputStreamToFile(single.openStream(), tempFile)
      case other =>
        logger.info(s"Multiple model URLs detected, embedding in lib folder and writing to ${tempFile.getAbsolutePath}")
        copyEntriesToLib(tempFile, other)
    }
    tempFile
  }

  private def copyEntriesToLib(tempFile: File, other: List[URL]): Unit = {
    val output = new FileOutputStream(tempFile)
    Using.resource(new JarOutputStream(output)) { jarOutput =>
      other.foreach(putToJar(jarOutput))
    }
  }

  private def putToJar(jarOutputStream: JarOutputStream)(url: URL) = {
    val entry = new JarEntry(s"lib/${url.getPath.replaceAll("/", "_")}")
    jarOutputStream.putNextEntry(entry)
    Using.resource(url.openStream()) { stream =>
      IOUtils.copy(stream, jarOutputStream)
    }
  }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy