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

org.beangle.sqlplus.transport.Config.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2005, The Beangle Software.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 */

package org.beangle.sqlplus.transport

import org.beangle.commons.collection.Collections
import org.beangle.commons.io.Files
import org.beangle.commons.lang.{Numbers, Strings}
import org.beangle.jdbc.ds.{DataSourceUtils, Source}
import org.beangle.jdbc.meta.Schema.NameFilter
import org.beangle.jdbc.meta.{Identifier, Relation}

import java.io.InputStream
import scala.collection.immutable.Seq
import scala.xml.{Node, NodeSeq}

object Config {

  private val defaultBulkSize = 50000

  def apply(workdir: String, is: InputStream): Config = {
    val xml = scala.xml.XML.load(is)
    val threads = Config.maxtheads(xml)
    val source = Config.db(xml, "source", threads)
    val target = Config.db(xml, "target", threads)
    val tasks = Config.tasks(xml, source, target)
    val config = new Config(source, target, tasks, threads,
      Config.bulkSize(xml), Config.datarange(xml))

    config.beforeActions = actions(workdir, xml \\ "actions" \ "before" \ "sql")
    config.afterActions = actions(workdir, xml \\ "actions" \ "after" \ "sql")
    config
  }

  def apply(source: Source, target: Source,
            tasks: collection.Seq[Config.Task]): Config = {
    new Config(source, target, tasks, Runtime.getRuntime.availableProcessors(), defaultBulkSize, (1, 10000000))
  }

  private def maxtheads(xml: scala.xml.Elem): Int = {
    val mt = (xml \ "@maxthreads").text.trim
    val maxthreads = Numbers.toInt(mt, 5)
    if (maxthreads > 0) maxthreads else 5
  }

  private def datarange(xml: scala.xml.Elem): Tuple2[Int, Int] = {
    val mt = (xml \ "@datarange").text.trim
    if (Strings.isEmpty(mt)) {
      Tuple2(0, Int.MaxValue)
    } else {
      val min = Strings.trim(Strings.substringBefore(mt, "-"))
      val max = Strings.trim(Strings.substringAfter(mt, "-"))
      Tuple2(Integer.parseInt(min), if (max == "*") Int.MaxValue else Integer.parseInt(max))
    }
  }

  private def bulkSize(xml: scala.xml.Elem): Int = {
    val bs = (xml \ "@bulksize").text.trim
    Numbers.toInt(bs, defaultBulkSize)
  }

  private def attr(n: Node, name: String): String = {
    (n \ s"@${name}").text.trim()
  }

  private def lowcaseAttr(n: Node, name: String): String = {
    (n \ s"@${name}").text.toLowerCase.trim()
  }

  private def tasks(xml: scala.xml.Elem, source: Source, target: Source): Seq[Task] = {
    val tasks = Collections.newBuffer[Task]
    (xml \\ "task") foreach { ele =>
      val task = new Task(source, target)
      val from = source.parse(attr(ele, "from"))
      val to = target.parse(attr(ele, "to"))

      require(Strings.isNotBlank(from._2.value), "task need from schema property")
      require(Strings.isNotBlank(to._2.value), "task need to schema property")

      task.path(from, to)
      val tableConfig = new TableConfig
      (ele \ "tables" \ "@lowercase") foreach { e =>
        if (e.text == "true") tableConfig.lowercase = Some(true)
      }
      tableConfig.withIndex = "true" == (ele \ "tables" \ "@index").text
      tableConfig.withConstraint = "true" == (ele \ "tables" \ "@constraint").text
      tableConfig.includes = (ele \ "tables" \ "includes") flatten (e => Strings.split(e.text.trim.toLowerCase()))
      tableConfig.excludes = (ele \ "tables" \ "excludes") flatten (e => Strings.split(e.text.trim.toLowerCase()))
      tableConfig.wheres = (ele \ "tables" \ "where").map(e => lowcaseAttr(e, "table") -> attr(e, "value")).toMap
      task.table = tableConfig

      val viewConfig = new ViewConfig
      (ele \ "views" \ "@lowercase") foreach { e =>
        if (e.text == "true") viewConfig.lowercase = Some(true)
      }
      viewConfig.includes = (ele \ "views" \ "includes") flatten (e => Strings.split(e.text.trim.toLowerCase()))
      viewConfig.excludes = (ele \ "views" \ "excludes") flatten (e => Strings.split(e.text.trim.toLowerCase()))
      viewConfig.wheres = (ele \ "views" \ "where").map(e => lowcaseAttr(e, "table") -> attr(e, "value")).toMap
      task.view = viewConfig

      val seqConfig = new SeqConfig
      seqConfig.includes = Strings.split((ele \ "sequences" \ "includes").text.trim).toSeq
      seqConfig.excludes = Strings.split((ele \ "sequences" \ "excludes").text.trim).toSeq
      task.sequence = seqConfig
      tasks.addOne(task)
    }
    tasks.toList
  }

  private def db(xml: scala.xml.Elem, target: String, threads: Int): Source = {
    val dbconf = DataSourceUtils.parseXml((xml \\ target).head)
    val maximumPoolSize = dbconf.props.getOrElse("maximumPoolSize", "1").toInt
    if (maximumPoolSize <= threads) {
      dbconf.props.put("maximumPoolSize", (threads + 1).toString)
    }
    Source(dbconf)
  }

  private def actions(workdir: String, nodes: NodeSeq): Iterable[ActionConfig] = {
    nodes.flatMap { x =>
      val contents = if (Strings.isBlank(x.text)) None else Some(x.text.trim())
      if (contents.isEmpty) {
        var filePath = (x \ "@file").text
        if (Strings.isNotBlank(filePath)) {
          filePath = Files.forName(workdir, filePath).getAbsolutePath
          Some(ActionConfig("script", contents, Map("file" -> filePath)))
        } else None
      } else {
        Some(ActionConfig("script", contents, Map.empty))
      }
    }
  }

  abstract class DataflowConfig {
    var includes: Seq[String] = List.empty
    var excludes: Seq[String] = List.empty
    var lowercase: Option[Boolean] = None
    var wheres: Map[String, String] = Map.empty

    def buildNameFilter(): NameFilter = {
      val filter = new NameFilter()
      for (include <- includes) filter.include(include)
      for (exclude <- excludes) filter.exclude(exclude)
      filter
    }

    def getWhere(r: Relation): Option[String] = {
      wheres.get(r.name.value.toLowerCase)
    }
  }

  object TableConfig {
    def all: TableConfig = {
      val cfg = new TableConfig
      cfg.includes = Seq("*")
      cfg
    }

    def none: TableConfig = {
      val cfg = new TableConfig
      cfg.excludes = Seq("*")
      cfg
    }
  }

  object ViewConfig {
    def all: ViewConfig = {
      val cfg = new ViewConfig
      cfg.includes = Seq("*")
      cfg
    }

    def none: ViewConfig = {
      val cfg = new ViewConfig
      cfg.excludes = Seq("*")
      cfg
    }
  }

  final class TableConfig extends DataflowConfig {
    var withIndex: Boolean = true
    var withConstraint: Boolean = true
  }

  final class ViewConfig extends DataflowConfig {
  }

  final class SeqConfig {
    var includes: Seq[String] = _
    var excludes: Seq[String] = _
  }

  class Task(val source: Source, val target: Source) {
    var table: TableConfig = _
    var view: ViewConfig = _
    var sequence: SeqConfig = _

    def path(from: (Option[Identifier], Identifier), to: (Option[Identifier], Identifier)): this.type = {
      this.fromCatalog = from._1
      this.fromSchema = from._2

      this.toCatalog = to._1
      this.toSchema = to._2
      this
    }

    var fromSchema: Identifier = _
    var fromCatalog: Option[Identifier] = None
    var toSchema: Identifier = _
    var toCatalog: Option[Identifier] = None
  }
}

class Config(val source: Source, val target: Source,
             val tasks: collection.Seq[Config.Task], val maxthreads: Int,
             val bulkSize: Int,
             val dataRange: (Int, Int)) {

  var beforeActions: Iterable[ActionConfig] = List.empty
  var afterActions: Iterable[ActionConfig] = List.empty
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy