diff --git a/.travis.sh b/.travis.sh index be124ef..2af6874 100755 --- a/.travis.sh +++ b/.travis.sh @@ -31,12 +31,4 @@ function client_test() { docker-compose down } -dir=$(pwd) - -client_test && - if $(test "${TRAVIS_REPO_SLUG}" == "jendakol/rbackup-scala-client" && test "${TRAVIS_PULL_REQUEST}" == "false" && test "$TRAVIS_TAG" != ""); then - cd $pwd - sbt +publish - else - exit 0 # skipping publish, it's regular build - fi +client_test diff --git a/README.md b/README.md index f656e38..72542d3 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,17 @@ This is Scala implementation of client for [RBackup](https://github.com/jendakol/rbackup). -Readme TBD :-) \ No newline at end of file +Readme TBD :-) + +## Build (release) +``` +#!/usr/bin/fish + +./.travis.sh + +env VERSION=$argv[1] \ + SENTRY_DSN="https://abcd@sentry.io/1234" \ + sbt ";clean;setVersionInSources;setSentryDsnInSources;dist" +``` + +The SENTRY_DSN is optional. \ No newline at end of file diff --git a/app/AppModule.scala b/app/AppModule.scala index 7d3c05e..e5954fb 100644 --- a/app/AppModule.scala +++ b/app/AppModule.scala @@ -20,7 +20,7 @@ import org.http4s.client.middleware.FollowRedirect import play.api.{Configuration, Environment} import scalikejdbc._ import scalikejdbc.config.DBs -import updater._ +import updater.{GithubConnector, LinuxServiceUpdaterExecutor, ServiceUpdaterExecutor, WindowsServiceUpdaterExecutor} import utils.AllowedWsApiOrigins import scala.collection.JavaConverters._ @@ -33,15 +33,21 @@ class AppModule(environment: Environment, configuration: Configuration) with StrictLogging { private val config = configuration.underlying - if (config.getBoolean("sentry.enabled") && config.getString("sentry.environment") != "dev") { - logger.info("Sentry configured") - val sentry = Sentry.init(config.getString("sentry.dsn")) - sentry.setRelease(App.versionStr) - sentry.setEnvironment(config.getString("sentry.environment")) - sentry.setServerName(config.getString("deviceId")) - sentry.setDist(config.getString("sentry.dist")) - } else { - logger.info("Sentry NOT configured") + App.SentryDsn match { + case Some(dsn) => + if (config.getBoolean("sentry.enabled") && config.getString("environment") != "dev") { + logger.info("Sentry configured") + val sentry = Sentry.init(dsn) + sentry.setRelease(App.versionStr) + sentry.addTag("app", "client") + sentry.setEnvironment(config.getString("environment")) + sentry.setServerName(config.getString("deviceId")) + sentry.setDist(if (SystemUtils.IS_OS_WINDOWS) "win" else "linux") + } else { + logger.info("Sentry NOT enabled") + } + + case None => logger.info("Sentry NOT configured") } DBs.setupAll() @@ -67,10 +73,13 @@ class AppModule(environment: Environment, configuration: Configuration) bind[AllowedWsApiOrigins].toInstance(AllowedWsApiOrigins(config.getStringList("allowedWsApiOrigins").asScala)) + val deviceId = DeviceId(config.getString("deviceId")) + bind[DeviceId].toInstance(deviceId) + val cloudConnector = CloudConnector.fromConfig(config.getConfig("cloudConnector"), blockingScheduler) val dao = new Dao(blockingScheduler) val settings = new Settings(dao) - val stateManager = new StateManager(DeviceId(config.getString("deviceId")), cloudConnector, dao, settings) + val stateManager = new StateManager(deviceId, cloudConnector, dao, settings) bind[CloudConnector].toInstance(cloudConnector) bind[Dao].toInstance(dao) @@ -116,9 +125,9 @@ class AppModule(environment: Environment, configuration: Configuration) private def bindServiceUpdater(): Unit = { val updater = if (SystemUtils.IS_OS_WINDOWS) { - new WindowsServiceUpdater - } else new LinuxServiceUpdater + new WindowsServiceUpdaterExecutor + } else new LinuxServiceUpdaterExecutor - bind[ServiceUpdater].toInstance(updater) + bind[ServiceUpdaterExecutor].toInstance(updater) } } diff --git a/app/lib/App.scala b/app/lib/App.scala index 5773218..56c3166 100644 --- a/app/lib/App.scala +++ b/app/lib/App.scala @@ -13,7 +13,7 @@ import monix.eval.Task import monix.execution.{Cancelable, Scheduler} import org.http4s.Uri import play.api.inject.ApplicationLifecycle -import updater.{AppVersion, Updater} +import updater.Updater import scala.collection.generic.CanBuildFrom import scala.concurrent.Future @@ -38,9 +38,11 @@ class App @Inject()(backupSetsExecutor: BackupSetsExecutor, updater: Updater)(li } object App { - final val versionStr: String = "0.1.2" + final val versionStr: String = "0.1.3" final val version: AppVersion = AppVersion(versionStr).getOrElse(throw new IllegalArgumentException("Could not parse versionStr")) + final val SentryDsn: Option[String] = Some("https://74fe77b3e5024c18bd850d09c4c775c4@sentry.io/1340234") + type Result[A] = EitherT[Task, AppException, A] def pureResult[A](a: => A): Result[A] = { @@ -205,4 +207,6 @@ object App { case class ServerSession(rootUri: Uri, sessionId: String, serverVersion: AppVersion) -case class DeviceId(value: String) extends AnyVal +case class DeviceId(value: String) { + override def toString: String = value +} diff --git a/app/updater/AppVersion.scala b/app/lib/AppVersion.scala similarity index 96% rename from app/updater/AppVersion.scala rename to app/lib/AppVersion.scala index 5cd1651..8b69e0a 100644 --- a/app/updater/AppVersion.scala +++ b/app/lib/AppVersion.scala @@ -1,7 +1,7 @@ -package updater +package lib import lib.AppException.ParsingFailure -import updater.AppVersion._ +import lib.AppVersion._ case class AppVersion(major: Int, minor: Int, build: Int, suffix: Option[String] = None) { def >(other: AppVersion): Boolean = { diff --git a/app/lib/client/clientapi.scala b/app/lib/client/clientapi.scala index fb75953..221bc35 100644 --- a/app/lib/client/clientapi.scala +++ b/app/lib/client/clientapi.scala @@ -10,14 +10,13 @@ import com.typesafe.scalalogging.StrictLogging import io.circe.generic.extras.Configuration import io.circe.syntax._ import io.circe.{Decoder, Json} -import lib.App +import lib.{App, AppVersion} import lib.App._ import lib.App.StringOps import lib.client.clientapi.FileTreeNode.{Directory, RegularFile} import lib.server.serverapi import lib.server.serverapi.{RemoteFile, RemoteFileVersion} import org.http4s.Uri -import updater.AppVersion object clientapi extends StrictLogging { diff --git a/app/lib/commands/CommandExecutor.scala b/app/lib/commands/CommandExecutor.scala index 4ecf43b..6b86e38 100644 --- a/app/lib/commands/CommandExecutor.scala +++ b/app/lib/commands/CommandExecutor.scala @@ -24,7 +24,6 @@ import monix.eval.Task import monix.execution.Scheduler import org.http4s.Uri import utils.CirceImplicits._ -import utils.ConfigProperty @Singleton class CommandExecutor @Inject()(cloudConnector: CloudConnector, @@ -35,7 +34,7 @@ class CommandExecutor @Inject()(cloudConnector: CloudConnector, fileCommandExecutor: FileCommandExecutor, settings: Settings, stateManager: StateManager, - @ConfigProperty("deviceId") deviceId: String)(implicit scheduler: Scheduler) + deviceId: DeviceId)(implicit scheduler: Scheduler) extends StrictLogging { wsApiController.setEventCallback(processEvent) @@ -51,7 +50,7 @@ class CommandExecutor @Inject()(cloudConnector: CloudConnector, import scala.concurrent.duration._ - tasksManager.start(RunningTask.FileUpload("theName"))(EitherT.right(Task.unit.delayResult(10.seconds))) >> + tasksManager.start(RunningTask.FileUpload(deviceId.value))(EitherT.right(Task.unit.delayResult(10.seconds))) >> cloudConnector.status .flatMap { str => parse(s"""{"serverResponse": "$str"}""").toResult diff --git a/app/lib/server/CloudConnector.scala b/app/lib/server/CloudConnector.scala index 42190c4..05540a8 100644 --- a/app/lib/server/CloudConnector.scala +++ b/app/lib/server/CloudConnector.scala @@ -15,7 +15,7 @@ import io.circe.Json import io.circe.generic.extras.auto._ import lib.App._ import lib.AppException.ServerNotResponding -import lib._ +import lib.{AppVersion, _} import lib.server.serverapi.ListFilesResponse.FilesList import lib.server.serverapi._ import monix.eval.Task @@ -28,7 +28,6 @@ import org.http4s.multipart._ import org.http4s.{Headers, Method, Request, Response, Status, Uri} import pureconfig.modules.http4s.uriReader import pureconfig.{CamelCase, ConfigFieldMapping, ProductHint} -import updater.AppVersion import utils.CirceImplicits._ import utils.{FileCopier, InputStreamWithSha256, StatsInputStream, StatsOutputStream} @@ -50,12 +49,15 @@ class CloudConnector(httpClient: Client[Task], chunkSize: Int, blockingScheduler } } - def login(rootUri: Uri, deviceId: String, username: String, password: String): Result[LoginResponse] = { + def login(rootUri: Uri, deviceId: DeviceId, username: String, password: String): Result[LoginResponse] = { logger.debug(s"Logging device $deviceId with username $username") App.leaveBreadcrumb("Logging in", Map("uri" -> rootUri, "username" -> username)) val request = - plainRequestToHost(Method.GET, rootUri, "account/login", Map("device_id" -> deviceId, "username" -> username, "password" -> password)) + plainRequestToHost(Method.GET, + rootUri, + "account/login", + Map("device_id" -> deviceId.value, "username" -> username, "password" -> password)) status(rootUri).flatMap { status => exec(request) { diff --git a/app/updater/GithubConnector.scala b/app/updater/GithubConnector.scala index 2c33336..f2d3f3f 100644 --- a/app/updater/GithubConnector.scala +++ b/app/updater/GithubConnector.scala @@ -10,14 +10,14 @@ import cats.syntax.all._ import com.typesafe.scalalogging.StrictLogging import io.circe.Decoder import lib.App._ -import lib.AppException import lib.AppException._ +import lib.{AppException, AppVersion} import monix.eval.Task import monix.execution.Scheduler import org.http4s.client.Client import org.http4s.headers.`Content-Length` import org.http4s.{Response, Status, Uri} -import updater.GithubConnector._ +import GithubConnector._ import scala.concurrent.ExecutionContext diff --git a/app/updater/ServiceUpdater.scala b/app/updater/ServiceUpdater.scala deleted file mode 100644 index eb393bd..0000000 --- a/app/updater/ServiceUpdater.scala +++ /dev/null @@ -1,39 +0,0 @@ -package updater - -import better.files.File -import com.typesafe.scalalogging.StrictLogging - -import scala.language.postfixOps - -sealed trait ServiceUpdater { - def restartAndReplace(dirWithUpdate: File): Unit -} - -class WindowsServiceUpdater extends ServiceUpdater with StrictLogging { - override def restartAndReplace(dirWithUpdate: File): Unit = { - Runtime.getRuntime.exec( - Array( - "cmd", - "/C", - "start", - "\"\"", - "restart_replace.cmd", - dirWithUpdate.pathAsString - )) - () - } -} - -class LinuxServiceUpdater extends ServiceUpdater { - override def restartAndReplace(dirWithUpdate: File): Unit = { - Runtime.getRuntime.exec( - Array( - "/bin/bash", - "-c", - "restart_replace.sh", - dirWithUpdate.pathAsString, - "&", - )) - () - } -} diff --git a/app/updater/ServiceUpdaterExecutor.scala b/app/updater/ServiceUpdaterExecutor.scala new file mode 100644 index 0000000..5fd61e6 --- /dev/null +++ b/app/updater/ServiceUpdaterExecutor.scala @@ -0,0 +1,56 @@ +package updater + +import better.files.File +import com.typesafe.scalalogging.StrictLogging +import lib.{AppVersion, DeviceId} + +import scala.language.postfixOps + +sealed trait ServiceUpdaterExecutor { + def executeUpdate(currentVersion: AppVersion, newVersion: AppVersion, env: String, deviceId: DeviceId, dirWithUpdate: File): Unit +} + +class WindowsServiceUpdaterExecutor extends ServiceUpdaterExecutor with StrictLogging { + override def executeUpdate(currentVersion: AppVersion, + newVersion: AppVersion, + env: String, + deviceId: DeviceId, + dirWithUpdate: File): Unit = { + logger.info(s"Starting the update with args: $currentVersion, $newVersion, $env, $deviceId, $dirWithUpdate") + + Runtime.getRuntime.exec( + Array( + "cmd", + "/C", + "start", + "\"\"", + "java", + "-jar", + "updater.jar", + currentVersion.toString, + newVersion.toString, + env, + deviceId.value, + dirWithUpdate.pathAsString + )) + () + } +} + +class LinuxServiceUpdaterExecutor extends ServiceUpdaterExecutor { + override def executeUpdate(currentVersion: AppVersion, + newVersion: AppVersion, + env: String, + deviceId: DeviceId, + dirWithUpdate: File): Unit = { + Runtime.getRuntime.exec( + Array( + "/bin/bash", + "-c", + "restart_replace.sh", + dirWithUpdate.pathAsString, + "&", + )) + () + } +} diff --git a/app/updater/Updater.scala b/app/updater/Updater.scala index 73e4a2f..769e027 100644 --- a/app/updater/Updater.scala +++ b/app/updater/Updater.scala @@ -1,5 +1,6 @@ package updater +import java.net.ConnectException import java.util.concurrent.atomic.AtomicBoolean import better.files.File @@ -8,16 +9,20 @@ import com.typesafe.scalalogging.StrictLogging import javax.inject.{Inject, Named, Singleton} import lib.App._ import lib.AppException.UpdateException +import lib.{App, AppVersion, DeviceId} import monix.eval.Task import monix.execution.{Cancelable, Scheduler} import updater.GithubConnector.Release +import utils.ConfigProperty import scala.concurrent.duration._ import scala.util.control.NonFatal @Singleton class Updater @Inject()(connector: GithubConnector, - serviceUpdater: ServiceUpdater, + serviceUpdater: ServiceUpdaterExecutor, + @ConfigProperty("environment") env: String, + deviceId: DeviceId, @Named("updaterCheckPeriod") checkPeriod: FiniteDuration)(implicit scheduler: Scheduler) extends StrictLogging { private val updateRunning = new AtomicBoolean(false) @@ -26,45 +31,49 @@ class Updater @Inject()(connector: GithubConnector, logger.info(s"Started updates checker, will check every $checkPeriod") scheduler.scheduleAtFixedRate(1.seconds, checkPeriod) { - connector.checkUpdate - .flatMap { - case Some(rel) => - if (updateRunning.compareAndSet(false, true)) { - logger.debug(s"Found update: $rel") - updateApp(rel) - } else pureResult(()) - - case None => - logger.debug("Didn't find update for current version") - pureResult(()) - } - .recoverWith { - case ae => - updateRunning.set(false) - logger.warn("Could not download update", ae) - EitherT.leftT[Task, Unit](ae) - } - .value - .onErrorRecover { - case e: java.net.ConnectException => - updateRunning.set(false) - logger.warn("Could not download update", e) - Left(UpdateException("Could not update the app", e)) - - case e: UpdateException => - updateRunning.set(false) - logger.warn("Could not download update", e) - Left(e) - - case NonFatal(e) => - updateRunning.set(false) - logger.warn("Unknown error while updating the app", e) - Left(UpdateException("Could not update the app", e)) - } + tryUpdate.value .runSyncUnsafe(Duration.Inf) } } + def tryUpdate: Result[Unit] = EitherT { + connector.checkUpdate + .flatMap { + case Some(rel) => + if (updateRunning.compareAndSet(false, true)) { + logger.debug(s"Found update: $rel") + updateApp(rel) + } else pureResult(()) + + case None => + logger.debug("Didn't find update for current version") + pureResult(()) + } + .recoverWith { + case ae => + updateRunning.set(false) + logger.warn("Could not download update", ae) + EitherT.leftT[Task, Unit](ae) + } + .value + .onErrorRecover { + case e: ConnectException => + updateRunning.set(false) + logger.warn("Could not download update", e) + Left(UpdateException("Could not update the app", e)) + + case e: UpdateException => + updateRunning.set(false) + logger.warn("Could not download update", e) + Left(e) + + case NonFatal(e) => + updateRunning.set(false) + logger.warn("Unknown error while updating the app", e) + Left(UpdateException("Could not update the app", e)) + } + } + private def updateApp(release: Release): Result[Unit] = { // TODO setting @@ -83,8 +92,13 @@ class Updater @Inject()(connector: GithubConnector, // TODO don't do it if task is running - logger.info("Starting the update") - serviceUpdater.restartAndReplace(dirWithUpdate) + val newVersion = release.appVersion.getOrElse { + AppVersion(0, 0, 0) + } + + logger.info(s"Starting the update from ${App.version} to $newVersion") + + serviceUpdater.executeUpdate(App.version, newVersion, env, deviceId, dirWithUpdate) } } diff --git a/app/utils/CirceImplicits.scala b/app/utils/CirceImplicits.scala index 0f818f7..b19ff43 100644 --- a/app/utils/CirceImplicits.scala +++ b/app/utils/CirceImplicits.scala @@ -6,9 +6,8 @@ import java.util.UUID import cats.syntax.either._ import io.circe.generic.extras.Configuration import io.circe.{Decoder, Encoder} -import lib.DeviceId +import lib.{AppVersion, DeviceId} import org.http4s.Uri -import updater.AppVersion object CirceImplicits { implicit val configuration: Configuration = Configuration.default.withSnakeCaseConstructorNames.withSnakeCaseMemberNames diff --git a/build.sbt b/build.sbt index 09f30d9..e0f0900 100644 --- a/build.sbt +++ b/build.sbt @@ -13,7 +13,7 @@ mappings in Universal ++= directory(baseDirectory.value / "public") name := "rbackup-client" -version := sys.env.getOrElse("TRAVIS_TAG", "0.1.0") +version := sys.env.getOrElse("VERSION", "0.1.0") scalaVersion := "2.12.7" @@ -37,9 +37,13 @@ libraryDependencies ++= Seq( "io.circe" %% "circe-generic-extras" % "0.10.1", "com.avast.metrics" %% "metrics-scala" % Versions.metricsVersion, "com.avast.metrics" % "metrics-statsd" % Versions.metricsVersion, + "com.softwaremill.sttp" %% "core" % "1.5.1", + "org.apache.commons" % "commons-lang3" % "3.8.1", + "com.github.pathikrit" %% "better-files" % "3.6.0", + "org.typelevel" %% "cats-core" % "1.5.0", "io.sentry" % "sentry-logback" % "1.7.14", "com.typesafe.scala-logging" %% "scala-logging" % "3.9.0", - "org.scalatest" %% "scalatest" % "3.0.5" % "test", + "org.scalatest" %% "scalatest" % "3.0.5", "org.mockito" % "mockito-core" % "2.23.0" % "test" ) @@ -83,26 +87,51 @@ frontEndBuild := (frontEndBuild dependsOn cleanFrontEndBuild).value dist := (dist dependsOn frontEndBuild).value -lazy val setVersionInSources = taskKey[Unit]("Sets build version into") +lazy val AppModulePath = "app/lib/App.scala" + + +lazy val setVersionInSources = taskKey[Unit]("Sets build version into sources") setVersionInSources := { import java.io.PrintWriter import scala.io.Source - val version = sys.env.getOrElse("TRAVIS_TAG", "0.1.0") + val version = sys.env.getOrElse("VERSION", throw new IllegalArgumentException("Missing VERSION env property")) println(s"Setting app version to $version") - val src = Source.fromFile("app/lib/App.scala").mkString + val src = Source.fromFile(AppModulePath).mkString val updated = src.replaceAll( """final val versionStr: String = "\d+.\d+.\d+"""", s"""final val versionStr: String = "$version"""" ) - - val writer = new PrintWriter(new File("app/lib/App.scala")) + + val writer = new PrintWriter(new File(AppModulePath)) writer.write(updated) writer.close() } +lazy val setSentryDsnInSources = taskKey[Unit]("Sets Sentry DSN into sources") + +setSentryDsnInSources := { + import java.io.PrintWriter + + import scala.io.Source + + sys.env.get("SENTRY_DSN").foreach { dsn => + println(s"Setting Sentry DSN") + + val src = Source.fromFile(AppModulePath).mkString + val updated = src.replace( + """SentryDsn: Option[String] = None""", + s"""SentryDsn: Option[String] = Some("$dsn")""" + ) + + val writer = new PrintWriter(new File(AppModulePath)) + writer.write(updated) + writer.close() + } +} + sources in (Compile, doc) := Seq.empty publishArtifact in (Compile, packageDoc) := false diff --git a/conf/reference.conf b/conf/reference.conf index d7176ba..8797814 100644 --- a/conf/reference.conf +++ b/conf/reference.conf @@ -1,4 +1,5 @@ // deviceId = "" // REQUIRED +environment = "prod" cloudConnectorDefaults { requestTimeout = 30 minutes @@ -21,9 +22,7 @@ updater { } sentry { - enabled = false - // dsn = "" - environment = "prod" + enabled = true } allowedWsApiOrigins = ["http://localhost:3370"] diff --git a/test/updater/GithubConnectorTest.scala b/test/updater/GithubConnectorTest.scala index 13bfea7..06e3cab 100644 --- a/test/updater/GithubConnectorTest.scala +++ b/test/updater/GithubConnectorTest.scala @@ -2,7 +2,7 @@ package updater import java.time.Instant -import lib.AppException +import lib.{AppException, AppVersion} import monix.eval.Task import monix.execution.Scheduler.Implicits.global import org.http4s.client.Client diff --git a/test/utils/AppVersionTest.scala b/test/utils/AppVersionTest.scala index ba66dd9..9913732 100644 --- a/test/utils/AppVersionTest.scala +++ b/test/utils/AppVersionTest.scala @@ -1,8 +1,8 @@ package utils import lib.AppException.ParsingFailure +import lib.AppVersion import org.scalatest.FunSuite -import updater.AppVersion class AppVersionTest extends FunSuite { test("parse from tag name") { diff --git a/windeploy/rbackup-client-updater.bat b/windeploy/rbackup-client-updater.bat new file mode 100644 index 0000000..5677397 --- /dev/null +++ b/windeploy/rbackup-client-updater.bat @@ -0,0 +1,175 @@ +@REM rbackup-client launcher script +@REM +@REM Environment: +@REM JAVA_HOME - location of a JDK home dir (optional if java on path) +@REM CFG_OPTS - JVM options (optional) +@REM Configuration: +@setlocal enabledelayedexpansion + +@echo off + + +if "%RBACKUP_CLIENT_UPDATER_HOME%"=="" ( + set "APP_HOME=%~dp0\\.." + + rem Also set the old env name for backwards compatibility + set "RBACKUP_CLIENT_UPDATER_HOME=%~dp0\\.." +) else ( + set "APP_HOME=%RBACKUP_CLIENT_UPDATER_HOME%" +) + +set "APP_LIB_DIR=%APP_HOME%\lib\" + +rem Detect if we were double clicked, although theoretically A user could +rem manually run cmd /c +for %%x in (!cmdcmdline!) do if %%~x==/c set DOUBLECLICKED=1 + +rem FIRST we load the config file of extra options. +set "CFG_FILE=%APP_HOME%\RBACKUP_CLIENT_UPDATER_CONFIG" +set CFG_OPTS= +call :parse_config "%CFG_FILE%" CFG_OPTS + +rem We use the value of the JAVACMD environment variable if defined +set _JAVACMD=%JAVACMD% + +if "%_JAVACMD%"=="" ( + if not "%JAVA_HOME%"=="" ( + if exist "%JAVA_HOME%\bin\java.exe" set "_JAVACMD=%JAVA_HOME%\bin\java.exe" + ) +) + +if "%_JAVACMD%"=="" set _JAVACMD=java + +rem Detect if this java is ok to use. +for /F %%j in ('"%_JAVACMD%" -version 2^>^&1') do ( + if %%~j==java set JAVAINSTALLED=1 + if %%~j==openjdk set JAVAINSTALLED=1 +) + +rem BAT has no logical or, so we do it OLD SCHOOL! Oppan Redmond Style +set JAVAOK=true +if not defined JAVAINSTALLED set JAVAOK=false + +if "%JAVAOK%"=="false" ( + echo. + echo A Java JDK is not installed or can't be found. + if not "%JAVA_HOME%"=="" ( + echo JAVA_HOME = "%JAVA_HOME%" + ) + echo. + echo Please go to + echo http://www.oracle.com/technetwork/java/javase/downloads/index.html + echo and download a valid Java JDK and install before running rbackup-client. + echo. + echo If you think this message is in error, please check + echo your environment variables to see if "java.exe" and "javac.exe" are + echo available via JAVA_HOME or PATH. + echo. + if defined DOUBLECLICKED pause + exit /B 1 +) + + +rem We use the value of the JAVA_OPTS environment variable if defined, rather than the config. +set _JAVA_OPTS=%JAVA_OPTS% +if "!_JAVA_OPTS!"=="" set _JAVA_OPTS=!CFG_OPTS! + +rem We keep in _JAVA_PARAMS all -J-prefixed and -D-prefixed arguments +rem "-J" is stripped, "-D" is left as is, and everything is appended to JAVA_OPTS +set _JAVA_PARAMS= +set _APP_ARGS= + +set "APP_CLASSPATH=%APP_HOME%\conf\;%APP_LIB_DIR%\*" +set "APP_MAIN_CLASS=lib.updater.app.Main" +set "SCRIPT_CONF_FILE=%APP_HOME%\conf\application.ini" + +rem if configuration files exist, prepend their contents to the script arguments so it can be processed by this runner +call :parse_config "%SCRIPT_CONF_FILE%" SCRIPT_CONF_ARGS + +call :process_args %SCRIPT_CONF_ARGS% %%* + +set _JAVA_OPTS=!_JAVA_OPTS! !_JAVA_PARAMS! + +set MAIN_CLASS=!APP_MAIN_CLASS! + +rem Call the application and pass all arguments unchanged. +"%_JAVACMD%" !_JAVA_OPTS! !RBACKUP_CLIENT_UPDATER_OPTS! -cp "%APP_CLASSPATH%" %MAIN_CLASS% !_APP_ARGS! + +@endlocal + +exit /B %ERRORLEVEL% + + +rem Loads a configuration file full of default command line options for this script. +rem First argument is the path to the config file. +rem Second argument is the name of the environment variable to write to. +:parse_config + set _PARSE_FILE=%~1 + set _PARSE_OUT= + if exist "%_PARSE_FILE%" ( + FOR /F "tokens=* eol=# usebackq delims=" %%i IN ("%_PARSE_FILE%") DO ( + set _PARSE_OUT=!_PARSE_OUT! %%i + ) + ) + set %2=!_PARSE_OUT! +exit /B 0 + + +:add_java + set _JAVA_PARAMS=!_JAVA_PARAMS! %* +exit /B 0 + + +:add_app + set _APP_ARGS=!_APP_ARGS! %* +exit /B 0 + + +rem Processes incoming arguments and places them in appropriate global variables +:process_args + :param_loop + call set _PARAM1=%%1 + set "_TEST_PARAM=%~1" + + if ["!_PARAM1!"]==[""] goto param_afterloop + + + rem ignore arguments that do not start with '-' + if "%_TEST_PARAM:~0,1%"=="-" goto param_java_check + set _APP_ARGS=!_APP_ARGS! !_PARAM1! + shift + goto param_loop + + :param_java_check + if "!_TEST_PARAM:~0,2!"=="-J" ( + rem strip -J prefix + set _JAVA_PARAMS=!_JAVA_PARAMS! !_TEST_PARAM:~2! + shift + goto param_loop + ) + + if "!_TEST_PARAM:~0,2!"=="-D" ( + rem test if this was double-quoted property "-Dprop=42" + for /F "delims== tokens=1,*" %%G in ("!_TEST_PARAM!") DO ( + if not ["%%H"] == [""] ( + set _JAVA_PARAMS=!_JAVA_PARAMS! !_PARAM1! + ) else if [%2] neq [] ( + rem it was a normal property: -Dprop=42 or -Drop="42" + call set _PARAM1=%%1=%%2 + set _JAVA_PARAMS=!_JAVA_PARAMS! !_PARAM1! + shift + ) + ) + ) else ( + if "!_TEST_PARAM!"=="-main" ( + call set CUSTOM_MAIN_CLASS=%%2 + shift + ) else ( + set _APP_ARGS=!_APP_ARGS! !_PARAM1! + ) + ) + shift + goto param_loop + :param_afterloop + +exit /B 0