From 02810f5eac3b4ce6a5d1128281a01b2a2ed0647c Mon Sep 17 00:00:00 2001 From: vlad Date: Fri, 21 Oct 2016 16:07:05 -0400 Subject: Renamed package to xyz, New formatting, authorize directive supporting multiple permissions --- src/main/scala/com/drivergrp/core/app.scala | 234 --------------------- src/main/scala/com/drivergrp/core/auth.scala | 130 ------------ src/main/scala/com/drivergrp/core/config.scala | 24 --- src/main/scala/com/drivergrp/core/core.scala | 46 ---- src/main/scala/com/drivergrp/core/crypto.scala | 27 --- src/main/scala/com/drivergrp/core/database.scala | 52 ----- src/main/scala/com/drivergrp/core/file.scala | 151 ------------- src/main/scala/com/drivergrp/core/generators.scala | 72 ------- src/main/scala/com/drivergrp/core/json.scala | 106 ---------- src/main/scala/com/drivergrp/core/logging.scala | 176 ---------------- src/main/scala/com/drivergrp/core/messages.scala | 59 ------ src/main/scala/com/drivergrp/core/rest.scala | 126 ----------- src/main/scala/com/drivergrp/core/stats.scala | 97 --------- src/main/scala/com/drivergrp/core/time.scala | 72 ------- 14 files changed, 1372 deletions(-) delete mode 100644 src/main/scala/com/drivergrp/core/app.scala delete mode 100644 src/main/scala/com/drivergrp/core/auth.scala delete mode 100644 src/main/scala/com/drivergrp/core/config.scala delete mode 100644 src/main/scala/com/drivergrp/core/core.scala delete mode 100644 src/main/scala/com/drivergrp/core/crypto.scala delete mode 100644 src/main/scala/com/drivergrp/core/database.scala delete mode 100644 src/main/scala/com/drivergrp/core/file.scala delete mode 100644 src/main/scala/com/drivergrp/core/generators.scala delete mode 100644 src/main/scala/com/drivergrp/core/json.scala delete mode 100644 src/main/scala/com/drivergrp/core/logging.scala delete mode 100644 src/main/scala/com/drivergrp/core/messages.scala delete mode 100644 src/main/scala/com/drivergrp/core/rest.scala delete mode 100644 src/main/scala/com/drivergrp/core/stats.scala delete mode 100644 src/main/scala/com/drivergrp/core/time.scala (limited to 'src/main/scala/com/drivergrp') diff --git a/src/main/scala/com/drivergrp/core/app.scala b/src/main/scala/com/drivergrp/core/app.scala deleted file mode 100644 index f6100f0..0000000 --- a/src/main/scala/com/drivergrp/core/app.scala +++ /dev/null @@ -1,234 +0,0 @@ -package com.drivergrp.core - -import akka.actor.ActorSystem -import akka.http.scaladsl.Http -import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport -import akka.http.scaladsl.model.StatusCodes._ -import akka.http.scaladsl.model.{HttpResponse, StatusCodes} -import akka.http.scaladsl.server.Directives._ -import akka.http.scaladsl.server.RouteResult._ -import akka.http.scaladsl.server.{ExceptionHandler, Route, RouteConcatenation} -import akka.stream.ActorMaterializer -import com.drivergrp.core.logging.{Logger, TypesafeScalaLogger} -import com.drivergrp.core.rest.Swagger -import com.drivergrp.core.stats.SystemStats -import com.drivergrp.core.time.Time -import com.drivergrp.core.time.provider.{SystemTimeProvider, TimeProvider} -import com.typesafe.config.Config -import org.slf4j.LoggerFactory -import spray.json.DefaultJsonProtocol - -import scala.compat.Platform.ConcurrentModificationException -import scala.concurrent.duration._ -import scala.concurrent.{Await, Future} - -object app { - - class DriverApp(version: String, - gitHash: String, - modules: Seq[Module], - time: TimeProvider = new SystemTimeProvider(), - log: Logger = new TypesafeScalaLogger( - com.typesafe.scalalogging.Logger(LoggerFactory.getLogger(classOf[DriverApp]))), - config: Config = com.drivergrp.core.config.loadDefaultConfig, - interface: String = "::0", - baseUrl: String = "localhost:8080", - port: Int = 8080) { - - implicit private lazy val actorSystem = ActorSystem("spray-routing", config) - implicit private lazy val executionContext = actorSystem.dispatcher - implicit private lazy val materializer = ActorMaterializer()(actorSystem) - private lazy val http = Http()(actorSystem) - - def run(): Unit = { - activateServices(modules) - scheduleServicesDeactivation(modules) - bindHttp(modules) - Console.print(s"${this.getClass.getName} App is started\n") - } - - def stop(): Unit = { - http.shutdownAllConnectionPools().onComplete { _ => - val _ = actorSystem.terminate() - val terminated = Await.result(actorSystem.whenTerminated, 30.seconds) - val addressTerminated = if (terminated.addressTerminated) "is" else "is not" - Console.print(s"${this.getClass.getName} App $addressTerminated stopped ") - } - } - - protected def bindHttp(modules: Seq[Module]): Unit = { - val serviceTypes = modules.flatMap(_.routeTypes) - val swaggerService = new Swagger(baseUrl, version, actorSystem, serviceTypes, config) - val swaggerRoutes = swaggerService.routes ~ swaggerService.swaggerUI - val versionRt = versionRoute(version, gitHash, time.currentTime()) - - val generalExceptionHandler = ExceptionHandler { - - case is: IllegalStateException => - extractUri { uri => - // TODO: extract `requestUuid` from request or thread, provided by linkerd/zipkin - def requestUuid = java.util.UUID.randomUUID.toString - - log.debug(s"Request is not allowed to $uri ($requestUuid)", is) - complete( - HttpResponse(BadRequest, - entity = s"""{ "requestUuid": "$requestUuid", "message": "${is.getMessage}" }""")) - } - - case cm: ConcurrentModificationException => - extractUri { uri => - // TODO: extract `requestUuid` from request or thread, provided by linkerd/zipkin - def requestUuid = java.util.UUID.randomUUID.toString - - log.debug(s"Concurrent modification of the resource $uri ($requestUuid)", cm) - complete( - HttpResponse(Conflict, - entity = s"""{ "requestUuid": "$requestUuid", "message": "${cm.getMessage}" }""")) - } - - case t: Throwable => - extractUri { uri => - // TODO: extract `requestUuid` from request or thread, provided by linkerd/zipkin - def requestUuid = java.util.UUID.randomUUID.toString - - log.error(s"Request to $uri could not be handled normally ($requestUuid)", t) - complete( - HttpResponse(InternalServerError, - entity = s"""{ "requestUuid": "$requestUuid", "message": "${t.getMessage}" }""")) - } - } - - val _ = Future { - http.bindAndHandle(route2HandlerFlow(handleExceptions(generalExceptionHandler) { - logRequestResult("log")(modules.map(_.route).foldLeft(versionRt ~ healthRoute ~ swaggerRoutes)(_ ~ _)) - }), interface, port)(materializer) - } - } - - protected def versionRoute(version: String, gitHash: String, startupTime: Time): Route = { - import DefaultJsonProtocol._ - import SprayJsonSupport._ - - path("version") { - val currentTime = time.currentTime().millis - complete( - Map( - "version" -> version, - "gitHash" -> gitHash, - "modules" -> modules.map(_.name).mkString(", "), - "startupTime" -> startupTime.millis.toString, - "serverTime" -> currentTime.toString, - "uptime" -> (currentTime - startupTime.millis).toString - )) - } - } - - protected def healthRoute: Route = { - import DefaultJsonProtocol._ - import SprayJsonSupport._ - import spray.json._ - - val memoryUsage = SystemStats.memoryUsage - val gcStats = SystemStats.garbageCollectorStats - - path("health") { - complete( - Map( - "availableProcessors" -> SystemStats.availableProcessors.toJson, - "memoryUsage" -> Map( - "free" -> memoryUsage.free.toJson, - "total" -> memoryUsage.total.toJson, - "max" -> memoryUsage.max.toJson - ).toJson, - "gcStats" -> Map( - "garbageCollectionTime" -> gcStats.garbageCollectionTime.toJson, - "totalGarbageCollections" -> gcStats.totalGarbageCollections.toJson - ).toJson, - "fileSystemSpace" -> SystemStats.fileSystemSpace.map { f => - Map("path" -> f.path.toJson, - "freeSpace" -> f.freeSpace.toJson, - "totalSpace" -> f.totalSpace.toJson, - "usableSpace" -> f.usableSpace.toJson) - }.toJson, - "operatingSystem" -> SystemStats.operatingSystemStats.toJson - )) - } - } - - /** - * Initializes services - */ - protected def activateServices(services: Seq[Module]): Unit = { - services.foreach { service => - Console.print(s"Service ${service.name} starts ...") - try { - service.activate() - } catch { - case t: Throwable => - log.fatal(s"Service ${service.name} failed to activate", t) - Console.print(" Failed! (check log)") - } - Console.print(" Done\n") - } - } - - /** - * Schedules services to be deactivated on the app shutdown - */ - protected def scheduleServicesDeactivation(services: Seq[Module]) = { - Runtime.getRuntime.addShutdownHook(new Thread() { - override def run(): Unit = { - services.foreach { service => - Console.print(s"Service ${service.name} shutting down ...") - try { - service.deactivate() - } catch { - case t: Throwable => - log.fatal(s"Service ${service.name} failed to deactivate", t) - Console.print(" Failed! (check log)") - } - Console.print(" Done\n") - } - } - }) - } - } - - import scala.reflect.runtime.universe._ - - trait Module { - val name: String - def route: Route - def routeTypes: Seq[Type] - - def activate(): Unit = {} - def deactivate(): Unit = {} - } - - class EmptyModule extends Module { - val name = "Nothing" - def route: Route = complete(StatusCodes.OK) - def routeTypes = Seq.empty[Type] - } - - class SimpleModule(val name: String, val route: Route, routeType: Type) extends Module { - def routeTypes: Seq[Type] = Seq(routeType) - } - - /** - * Module implementation which may be used to composed a few - * - * @param name more general name of the composite module, - * must be provided as there is no good way to automatically - * generalize the name from the composed modules' names - * @param modules modules to compose into a single one - */ - class CompositeModule(val name: String, modules: Seq[Module]) extends Module with RouteConcatenation { - - def route: Route = modules.map(_.route).reduce(_ ~ _) - def routeTypes = modules.flatMap(_.routeTypes) - - override def activate() = modules.foreach(_.activate()) - override def deactivate() = modules.reverse.foreach(_.deactivate()) - } -} diff --git a/src/main/scala/com/drivergrp/core/auth.scala b/src/main/scala/com/drivergrp/core/auth.scala deleted file mode 100644 index e857ef0..0000000 --- a/src/main/scala/com/drivergrp/core/auth.scala +++ /dev/null @@ -1,130 +0,0 @@ -package com.drivergrp.core - -import akka.http.scaladsl.model.headers.HttpChallenges -import akka.http.scaladsl.server.AuthenticationFailedRejection.CredentialsRejected - -import scala.concurrent.Future -import scala.util.{Failure, Success, Try} -import scalaz.OptionT - -object auth { - - sealed trait Permission - case object CanSeeUser extends Permission - case object CanSeeAssay extends Permission - case object CanSeeReport extends Permission - case object CanCreateReport extends Permission - case object CanEditReport extends Permission - case object CanReviewReport extends Permission - case object CanEditReviewingReport extends Permission - case object CanSignOutReport extends Permission - case object CanAmendReport extends Permission - case object CanShareReportWithPatient extends Permission - case object CanAssignRoles extends Permission - - trait Role { - val id: Id[Role] - val name: Name[Role] - val permissions: Set[Permission] - - def hasPermission(permission: Permission): Boolean = permissions.contains(permission) - } - - case object ObserverRole extends Role { - val id = Id(1L) - val name = Name("observer") - val permissions = Set[Permission](CanSeeUser, CanSeeAssay, CanSeeReport) - } - - case object PatientRole extends Role { - val id = Id(2L) - val name = Name("patient") - val permissions = Set.empty[Permission] - } - - case object CuratorRole extends Role { - val id = Id(3L) - val name = Name("curator") - val permissions = ObserverRole.permissions ++ Set[Permission](CanEditReport, CanReviewReport) - } - - case object PathologistRole extends Role { - val id = Id(4L) - val name = Name("pathologist") - val permissions = ObserverRole.permissions ++ - Set[Permission](CanEditReport, CanSignOutReport, CanAmendReport, CanEditReviewingReport) - } - - case object AdministratorRole extends Role { - val id = Id(5L) - val name = Name("administrator") - val permissions = CuratorRole.permissions ++ - Set[Permission](CanCreateReport, CanShareReportWithPatient, CanAssignRoles) - } - - trait User { - def id: Id[User] - def roles: Set[Role] - def permissions: Set[Permission] = roles.flatMap(_.permissions) - } - - final case class Macaroon(value: String) - - final case class Base64[T](value: String) - - final case class AuthToken(value: Base64[Macaroon]) - - final case class PasswordHash(value: String) - - object AuthService { - val AuthenticationTokenHeader = "WWW-Authenticate" - } - - trait AuthService[U <: User] { - - import akka.http.scaladsl.server._ - import Directives._ - - protected def authStatus(authToken: AuthToken): OptionT[Future, U] - - def authorize(permission: Permission): Directive1[(AuthToken, U)] = { - parameters('authToken.?).flatMap { parameterTokenValue => - optionalHeaderValueByName(AuthService.AuthenticationTokenHeader).flatMap { headerTokenValue => - verifyAuthToken(headerTokenValue.orElse(parameterTokenValue), permission) - } - } - } - - private def verifyAuthToken(tokenOption: Option[String], permission: Permission): Directive1[(AuthToken, U)] = - tokenOption match { - case Some(tokenValue) => - val token = AuthToken(Base64[Macaroon](tokenValue)) - - onComplete(authStatus(token).run).flatMap { tokenUserResult => - checkPermissions(tokenUserResult, permission, token) - } - - case None => - reject(MissingHeaderRejection(AuthService.AuthenticationTokenHeader)) - } - - private def checkPermissions(userResult: Try[Option[U]], - permission: Permission, - token: AuthToken): Directive1[(AuthToken, U)] = { - userResult match { - case Success(Some(user)) => - if (user.roles.exists(_.hasPermission(permission))) provide(token -> user) - else { - val challenge = HttpChallenges.basic(s"User does not have the required permission $permission") - reject(AuthenticationFailedRejection(CredentialsRejected, challenge)) - } - - case Success(None) => - reject(ValidationRejection(s"Wasn't able to find authenticated user for the token provided")) - - case Failure(t) => - reject(ValidationRejection(s"Wasn't able to verify token for authenticated user", Some(t))) - } - } - } -} diff --git a/src/main/scala/com/drivergrp/core/config.scala b/src/main/scala/com/drivergrp/core/config.scala deleted file mode 100644 index 29cd9ed..0000000 --- a/src/main/scala/com/drivergrp/core/config.scala +++ /dev/null @@ -1,24 +0,0 @@ -package com.drivergrp.core - -import java.io.File -import com.typesafe.config.{Config, ConfigFactory} - -object config { - - def loadDefaultConfig: Config = { - val configDefaults = ConfigFactory.load(this.getClass.getClassLoader, "application.conf") - - scala.sys.props.get("application.config") match { - - case Some(filename) => - val configFile = new File(filename) - if (configFile.exists()) { - ConfigFactory.parseFile(configFile).withFallback(configDefaults) - } else { - throw new IllegalStateException(s"No config found at $filename") - } - - case None => configDefaults - } - } -} diff --git a/src/main/scala/com/drivergrp/core/core.scala b/src/main/scala/com/drivergrp/core/core.scala deleted file mode 100644 index 158447f..0000000 --- a/src/main/scala/com/drivergrp/core/core.scala +++ /dev/null @@ -1,46 +0,0 @@ -package com.drivergrp - -import scalaz.Equal - -package object core { - import scala.language.reflectiveCalls - - def make[T](v: => T)(f: T => Unit): T = { - val value = v; f(value); value - } - - def using[R <: { def close() }, P](r: => R)(f: R => P): P = { - val resource = r - try { - f(resource) - } finally { - resource.close() - } - } - - object tagging { - private[core] trait Tagged[+V, +Tag] - } - type @@[+V, +Tag] = V with tagging.Tagged[V, Tag] - - type Id[+Tag] = Long @@ Tag - object Id { - def apply[Tag](value: Long) = value.asInstanceOf[Id[Tag]] - } - implicit def idEqual[T]: Equal[Id[T]] = Equal.equal[Id[T]](_ == _) - implicit def idOrdering[T]: Ordering[Id[T]] = Ordering.by(i => i: Long) - - type Name[+Tag] = String @@ Tag - object Name { - def apply[Tag](value: String) = value.asInstanceOf[Name[Tag]] - } - - implicit def nameEqual[T]: Equal[Name[T]] = Equal.equal[Name[T]](_ == _) - implicit def nameOrdering[T]: Ordering[Name[T]] = Ordering.by(n => n: String) - - object revision { - final case class Revision[T](id: String) - - implicit def revisionEqual[T]: Equal[Revision[T]] = Equal.equal[Revision[T]](_.id == _.id) - } -} diff --git a/src/main/scala/com/drivergrp/core/crypto.scala b/src/main/scala/com/drivergrp/core/crypto.scala deleted file mode 100644 index 5f63eb9..0000000 --- a/src/main/scala/com/drivergrp/core/crypto.scala +++ /dev/null @@ -1,27 +0,0 @@ -package com.drivergrp.core - -import com.drivergrp.core.auth.AuthToken - -object crypto { - - final case class EncryptionKey(value: String) - - final case class DecryptionKey(value: String) - - trait Crypto { - - def keyForToken(authToken: AuthToken): EncryptionKey - - def encrypt(encryptionKey: EncryptionKey)(message: Array[Byte]): Array[Byte] - - def decrypt(decryptionKey: EncryptionKey)(message: Array[Byte]): Array[Byte] - } - - object NoCrypto extends Crypto { - - override def keyForToken(authToken: AuthToken): EncryptionKey = EncryptionKey(authToken.value.value) - - override def decrypt(decryptionKey: EncryptionKey)(message: Array[Byte]): Array[Byte] = message - override def encrypt(encryptionKey: EncryptionKey)(message: Array[Byte]): Array[Byte] = message - } -} diff --git a/src/main/scala/com/drivergrp/core/database.scala b/src/main/scala/com/drivergrp/core/database.scala deleted file mode 100644 index 581c5de..0000000 --- a/src/main/scala/com/drivergrp/core/database.scala +++ /dev/null @@ -1,52 +0,0 @@ -package com.drivergrp.core - -import com.drivergrp.core.time.Time - -import scala.concurrent.Future -import slick.backend.DatabaseConfig -import slick.driver.JdbcProfile - -object database { - - trait Database { - val profile: JdbcProfile - val database: JdbcProfile#Backend#Database - } - - object Database { - - def fromConfig(databaseName: String): Database = { - val dbConfig: DatabaseConfig[JdbcProfile] = DatabaseConfig.forConfig(databaseName) - - new Database { - val profile: JdbcProfile = dbConfig.driver - val database: JdbcProfile#Backend#Database = dbConfig.db - } - } - } - - trait IdColumnTypes { - val database: Database - - import database.profile.api._ - - implicit def idColumnType[T] = - MappedColumnType.base[Id[T], Long](id => id: Long, Id[T](_)) - - implicit def nameColumnType[T] = - MappedColumnType.base[Name[T], String](name => name: String, Name[T](_)) - - implicit val timeColumnType = MappedColumnType.base[Time, Long](time => time.millis, Time(_)) - } - - trait DatabaseObject extends IdColumnTypes { - - def createTables(): Future[Unit] - def disconnect(): Unit - } - - abstract class DatabaseObjectAdapter extends DatabaseObject { - def createTables(): Future[Unit] = Future.successful(()) - def disconnect(): Unit = {} - } -} diff --git a/src/main/scala/com/drivergrp/core/file.scala b/src/main/scala/com/drivergrp/core/file.scala deleted file mode 100644 index 20bd36e..0000000 --- a/src/main/scala/com/drivergrp/core/file.scala +++ /dev/null @@ -1,151 +0,0 @@ -package com.drivergrp.core - -import java.io.File -import java.nio.file.{Path, Paths} -import java.util.UUID._ - -import com.amazonaws.services.s3.AmazonS3 -import com.amazonaws.services.s3.model.{Bucket, GetObjectRequest, ListObjectsV2Request} -import com.drivergrp.core.revision.Revision -import com.drivergrp.core.time.Time - -import scala.concurrent.{ExecutionContext, Future} -import scalaz.{ListT, OptionT} - -object file { - - final case class FileLink( - name: Name[File], - location: Path, - revision: Revision[File], - lastModificationDate: Time - ) - - trait FileService { - - def getFileLink(id: Name[File]): FileLink - - def getFile(fileLink: FileLink): File - } - - trait FileStorage { - - def upload(localSource: File, destination: Path): Future[Unit] - - def download(filePath: Path): OptionT[Future, File] - - def delete(filePath: Path): Future[Unit] - - def list(path: Path): ListT[Future, FileLink] - - /** List of characters to avoid in S3 (I would say file names in general) - * - * @see http://stackoverflow.com/questions/7116450/what-are-valid-s3-key-names-that-can-be-accessed-via-the-s3-rest-api - */ - private val illegalChars = "\\^`><{}][#%~|&@:,$=+?; " - - protected def checkSafeFileName[T](filePath: Path)(f: => T): T = { - filePath.toString.find(c => illegalChars.contains(c)) match { - case Some(illegalCharacter) => - throw new IllegalArgumentException(s"File name cannot contain character `$illegalCharacter`") - case None => f - } - } - } - - class S3Storage(s3: AmazonS3, bucket: Name[Bucket], executionContext: ExecutionContext) extends FileStorage { - implicit private val execution = executionContext - - def upload(localSource: File, destination: Path): Future[Unit] = Future { - checkSafeFileName(destination) { - val _ = s3.putObject(bucket, destination.toString, localSource).getETag - } - } - - def download(filePath: Path): OptionT[Future, File] = - OptionT.optionT(Future { - val tempDir = System.getProperty("java.io.tmpdir") - val randomFolderName = randomUUID().toString - val tempDestinationFile = new File(Paths.get(tempDir, randomFolderName, filePath.toString).toString) - - if (!tempDestinationFile.getParentFile.mkdirs()) { - throw new Exception(s"Failed to create temp directory to download file `$tempDestinationFile`") - } else { - Option(s3.getObject(new GetObjectRequest(bucket, filePath.toString), tempDestinationFile)).map { _ => - tempDestinationFile - } - } - }) - - def delete(filePath: Path): Future[Unit] = Future { - s3.deleteObject(bucket, filePath.toString) - } - - def list(path: Path): ListT[Future, FileLink] = - ListT.listT(Future { - import scala.collection.JavaConverters._ - val req = new ListObjectsV2Request().withBucketName(bucket).withPrefix(path.toString).withMaxKeys(2) - - def isInSubFolder(path: Path)(fileLink: FileLink) = - fileLink.location.toString.replace(path.toString + "/", "").contains("/") - - Iterator.continually(s3.listObjectsV2(req)).takeWhile { result => - req.setContinuationToken(result.getNextContinuationToken) - result.isTruncated - } flatMap { result => - result.getObjectSummaries.asScala.toList.map { summary => - FileLink(Name[File](summary.getKey), - Paths.get(path.toString + "/" + summary.getKey), - Revision[File](summary.getETag), - Time(summary.getLastModified.getTime)) - } filterNot isInSubFolder(path) - } toList - }) - } - - class FileSystemStorage(executionContext: ExecutionContext) extends FileStorage { - implicit private val execution = executionContext - - def upload(localSource: File, destination: Path): Future[Unit] = Future { - checkSafeFileName(destination) { - val destinationFile = destination.toFile - - if (destinationFile.getParentFile.exists() || destinationFile.getParentFile.mkdirs()) { - if (localSource.renameTo(destinationFile)) () - else { - throw new Exception( - s"Failed to move file from `${localSource.getCanonicalPath}` to `${destinationFile.getCanonicalPath}`") - } - } else { - throw new Exception(s"Failed to create parent directories for file `${destinationFile.getCanonicalPath}`") - } - } - } - - def download(filePath: Path): OptionT[Future, File] = - OptionT.optionT(Future { - Option(new File(filePath.toString)).filter(file => file.exists() && file.isFile) - }) - - def delete(filePath: Path): Future[Unit] = Future { - val file = new File(filePath.toString) - if (file.delete()) () - else { - throw new Exception(s"Failed to delete file $file" + (if (!file.exists()) ", file does not exist." else ".")) - } - } - - def list(path: Path): ListT[Future, FileLink] = - ListT.listT(Future { - val file = new File(path.toString) - if (file.isDirectory) { - file.listFiles().toList.filter(_.isFile).map { file => - FileLink(Name[File](file.getName), - Paths.get(file.getPath), - Revision[File](file.hashCode.toString), - Time(file.lastModified())) - } - } else List.empty[FileLink] - }) - } -} diff --git a/src/main/scala/com/drivergrp/core/generators.scala b/src/main/scala/com/drivergrp/core/generators.scala deleted file mode 100644 index 10df7db..0000000 --- a/src/main/scala/com/drivergrp/core/generators.scala +++ /dev/null @@ -1,72 +0,0 @@ -package com.drivergrp.core - -import java.math.MathContext - -import com.drivergrp.core.revision.Revision -import com.drivergrp.core.time.{Time, TimeRange} - -import scala.reflect.ClassTag -import scala.util.Random - -object generators { - - private val random = new Random - import random._ - - private val DefaultMaxLength = 100 - private val StringLetters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ".toSet - - def nextId[T](): Id[T] = Id[T](scala.math.abs(nextLong())) - - def nextId[T](maxValue: Int): Id[T] = Id[T](scala.math.abs(nextInt(maxValue).toLong)) - - def nextName[T](maxLength: Int = DefaultMaxLength): Name[T] = Name[T](nextString(maxLength)) - - def nextUuid() = java.util.UUID.randomUUID - - def nextRevision[T]() = Revision[T](nextUuid().toString) - - def nextString(maxLength: Int = DefaultMaxLength): String = - (oneOf[Char](StringLetters) +: arrayOf(oneOf[Char](StringLetters), maxLength - 1)).mkString - - def nextOption[T](value: => T): Option[T] = if (nextBoolean) Option(value) else None - - def nextPair[L, R](left: => L, right: => R): (L, R) = (left, right) - - def nextTriad[F, S, T](first: => F, second: => S, third: => T): (F, S, T) = (first, second, third) - - def nextTime(): Time = Time(math.abs(nextLong() % System.currentTimeMillis)) - - def nextTimeRange(): TimeRange = { - val oneTime = nextTime() - val anotherTime = nextTime() - - TimeRange(Time(scala.math.min(oneTime.millis, anotherTime.millis)), - Time(scala.math.max(oneTime.millis, anotherTime.millis))) - } - - def nextBigDecimal(multiplier: Double = 1000000.00, precision: Int = 2): BigDecimal = - BigDecimal(multiplier * nextDouble, new MathContext(precision)) - - def oneOf[T](items: T*): T = oneOf(items.toSet) - - def oneOf[T](items: Set[T]): T = items.toSeq(nextInt(items.size)) - - def arrayOf[T: ClassTag](generator: => T, maxLength: Int = DefaultMaxLength): Array[T] = - Array.fill(nextInt(maxLength))(generator) - - def seqOf[T](generator: => T, maxLength: Int = DefaultMaxLength): Seq[T] = - Seq.fill(nextInt(maxLength))(generator) - - def vectorOf[T](generator: => T, maxLength: Int = DefaultMaxLength): Vector[T] = - Vector.fill(nextInt(maxLength))(generator) - - def listOf[T](generator: => T, maxLength: Int = DefaultMaxLength): List[T] = - List.fill(nextInt(maxLength))(generator) - - def setOf[T](generator: => T, maxLength: Int = DefaultMaxLength): Set[T] = - seqOf(generator, maxLength).toSet - - def mapOf[K, V](maxLength: Int, keyGenerator: => K, valueGenerator: => V): Map[K, V] = - seqOf(nextPair(keyGenerator, valueGenerator), maxLength).toMap -} diff --git a/src/main/scala/com/drivergrp/core/json.scala b/src/main/scala/com/drivergrp/core/json.scala deleted file mode 100644 index 9a30161..0000000 --- a/src/main/scala/com/drivergrp/core/json.scala +++ /dev/null @@ -1,106 +0,0 @@ -package com.drivergrp.core - -import akka.http.scaladsl.model.Uri.Path -import akka.http.scaladsl.server.PathMatcher.Matched -import akka.http.scaladsl.server.{PathMatcher, _} -import akka.http.scaladsl.unmarshalling.Unmarshaller -import com.drivergrp.core.revision.Revision -import com.drivergrp.core.time.Time -import spray.json.{DeserializationException, JsNumber, _} - -import scala.reflect.runtime.universe._ - -object json { - - def IdInPath[T]: PathMatcher1[Id[T]] = - PathMatcher("""[+-]?\d*""".r) flatMap { string => - try Some(Id[T](string.toLong)) - catch { case _: IllegalArgumentException => None } - } - - implicit def idFormat[T] = new RootJsonFormat[Id[T]] { - def write(id: Id[T]) = JsNumber(id) - - def read(value: JsValue) = value match { - case JsNumber(id) => Id[T](id.toLong) - case _ => throw new DeserializationException("Id expects number") - } - } - - def NameInPath[T]: PathMatcher1[Name[T]] = new PathMatcher1[Name[T]] { - def apply(path: Path) = Matched(Path.Empty, Tuple1(Name[T](path.toString))) - } - - implicit def nameFormat[T] = new RootJsonFormat[Name[T]] { - def write(name: Name[T]) = JsString(name) - - def read(value: JsValue): Name[T] = value match { - case JsString(name) => Name[T](name) - case _ => throw new DeserializationException("Name expects string") - } - } - - def TimeInPath: PathMatcher1[Time] = - PathMatcher("""[+-]?\d*""".r) flatMap { string => - try Some(Time(string.toLong)) - catch { case _: IllegalArgumentException => None } - } - - implicit val timeFormat = new RootJsonFormat[Time] { - def write(time: Time) = JsObject("timestamp" -> JsNumber(time.millis)) - - def read(value: JsValue): Time = value match { - case JsObject(fields) => - fields - .get("timestamp") - .flatMap { - case JsNumber(millis) => Some(Time(millis.toLong)) - case _ => None - } - .getOrElse(throw new DeserializationException("Time expects number")) - case _ => throw new DeserializationException("Time expects number") - } - } - - def RevisionInPath[T]: PathMatcher1[Revision[T]] = - PathMatcher("""[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}""".r) flatMap { string => - Some(Revision[T](string)) - } - - implicit def revisionFromStringUnmarshaller[T]: Unmarshaller[String, Revision[T]] = - Unmarshaller.strict[String, Revision[T]](Revision[T](_)) - - implicit def revisionFormat[T] = new RootJsonFormat[Revision[T]] { - def write(revision: Revision[T]) = JsString(revision.id.toString) - - def read(value: JsValue): Revision[T] = value match { - case JsString(revision) => Revision[T](revision) - case _ => throw new DeserializationException("Revision expects uuid string") - } - } - - class EnumJsonFormat[T](mapping: (String, T)*) extends JsonFormat[T] { - private val map = mapping.toMap - - override def write(value: T): JsValue = { - map.find(_._2 == value).map(_._1) match { - case Some(name) => JsString(name) - case _ => serializationError(s"Value $value is not found in the mapping $map") - } - } - - override def read(json: JsValue): T = json match { - case JsString(name) => - map.getOrElse(name, throw new DeserializationException(s"Value $name is not found in the mapping $map")) - case _ => deserializationError("Expected string as enumeration value, but got " + json) - } - } - - class ValueClassFormat[T: TypeTag](writeValue: T => BigDecimal, create: BigDecimal => T) extends JsonFormat[T] { - def write(valueClass: T) = JsNumber(writeValue(valueClass)) - def read(json: JsValue): T = json match { - case JsNumber(value) => create(value) - case _ => deserializationError(s"Expected number as ${typeOf[T].getClass.getName}, but got " + json) - } - } -} diff --git a/src/main/scala/com/drivergrp/core/logging.scala b/src/main/scala/com/drivergrp/core/logging.scala deleted file mode 100644 index 2c8c670..0000000 --- a/src/main/scala/com/drivergrp/core/logging.scala +++ /dev/null @@ -1,176 +0,0 @@ -package com.drivergrp.core - -import java.text.SimpleDateFormat -import java.util.Date - -import ch.qos.logback.classic.spi.ILoggingEvent -import ch.qos.logback.core.LayoutBase -import org.apache.commons.lang3.StringUtils -import org.slf4j.Marker - -object logging { - - trait Logger { - - def fatal(message: String): Unit - def fatal(message: String, cause: Throwable): Unit - def fatal(message: String, args: AnyRef*): Unit - def fatal(marker: Marker, message: String): Unit - def fatal(marker: Marker, message: String, cause: Throwable): Unit - def fatal(marker: Marker, message: String, args: AnyRef*): Unit - - def error(message: String): Unit - def error(message: String, cause: Throwable): Unit - def error(message: String, args: AnyRef*): Unit - def error(marker: Marker, message: String): Unit - def error(marker: Marker, message: String, cause: Throwable): Unit - def error(marker: Marker, message: String, args: AnyRef*): Unit - - def audit(message: String): Unit - def audit(message: String, cause: Throwable): Unit - def audit(message: String, args: AnyRef*): Unit - def audit(marker: Marker, message: String): Unit - def audit(marker: Marker, message: String, cause: Throwable): Unit - def audit(marker: Marker, message: String, args: AnyRef*): Unit - - def debug(message: String): Unit - def debug(message: String, cause: Throwable): Unit - def debug(message: String, args: AnyRef*): Unit - def debug(marker: Marker, message: String): Unit - def debug(marker: Marker, message: String, cause: Throwable): Unit - def debug(marker: Marker, message: String, args: AnyRef*): Unit - } - - /** - * Logger implementation which uses `com.typesafe.scalalogging.Logger` on the back. - * It redefines the meaning of logging levels to fit to the Driver infrastructure design, - * and as using error and warn, debug and trace was always confusing and mostly done wrong. - * - * @param scalaLogging com.typesafe.scalalogging.Logger which logging will be delegated to - */ - class TypesafeScalaLogger(scalaLogging: com.typesafe.scalalogging.Logger) extends Logger { - - def fatal(message: String): Unit = scalaLogging.error("FATAL " + message) - def fatal(message: String, cause: Throwable): Unit = scalaLogging.error("FATAL " + message, cause) - def fatal(message: String, args: AnyRef*): Unit = scalaLogging.error("FATAL " + message, args) - def fatal(marker: Marker, message: String): Unit = scalaLogging.error(marker, "FATAL " + message) - def fatal(marker: Marker, message: String, cause: Throwable): Unit = - scalaLogging.error(marker, "FATAL " + message, cause) - def fatal(marker: Marker, message: String, args: AnyRef*): Unit = - scalaLogging.error(marker, "FATAL " + message, args) - - def error(message: String): Unit = scalaLogging.warn("ERROR " + message) - def error(message: String, cause: Throwable): Unit = scalaLogging.warn("ERROR " + message, cause) - def error(message: String, args: AnyRef*): Unit = scalaLogging.warn("ERROR " + message, args) - def error(marker: Marker, message: String): Unit = scalaLogging.warn(marker, "ERROR " + message) - def error(marker: Marker, message: String, cause: Throwable): Unit = - scalaLogging.warn(marker, "ERROR " + message, cause) - def error(marker: Marker, message: String, args: AnyRef*): Unit = - scalaLogging.warn(marker, "ERROR " + message, args) - - def audit(message: String): Unit = scalaLogging.info("AUDIT " + message) - def audit(message: String, cause: Throwable): Unit = scalaLogging.info("AUDIT " + message, cause) - def audit(message: String, args: AnyRef*): Unit = scalaLogging.info("AUDIT " + message, args) - def audit(marker: Marker, message: String): Unit = scalaLogging.info(marker, "AUDIT " + message) - def audit(marker: Marker, message: String, cause: Throwable): Unit = - scalaLogging.info(marker, "AUDIT " + message, cause) - def audit(marker: Marker, message: String, args: AnyRef*): Unit = - scalaLogging.info(marker, "AUDIT " + message, args) - - def debug(message: String): Unit = scalaLogging.debug("DEBUG " + message) - def debug(message: String, cause: Throwable): Unit = scalaLogging.debug("DEBUG " + message, cause) - def debug(message: String, args: AnyRef*): Unit = scalaLogging.debug("DEBUG " + message, args) - def debug(marker: Marker, message: String): Unit = scalaLogging.debug(marker, "DEBUG " + message) - def debug(marker: Marker, message: String, cause: Throwable): Unit = - scalaLogging.debug(marker, "DEBUG " + message, cause) - def debug(marker: Marker, message: String, args: AnyRef*): Unit = - scalaLogging.debug(marker, "DEBUG " + message, args) - } - - class NoLogger() extends Logger { - - def fatal(message: String): Unit = {} - def fatal(message: String, cause: Throwable): Unit = {} - def fatal(message: String, args: AnyRef*): Unit = {} - def fatal(marker: Marker, message: String): Unit = {} - def fatal(marker: Marker, message: String, cause: Throwable): Unit = {} - def fatal(marker: Marker, message: String, args: AnyRef*): Unit = {} - - def error(message: String): Unit = {} - def error(message: String, cause: Throwable): Unit = {} - def error(message: String, args: AnyRef*): Unit = {} - def error(marker: Marker, message: String): Unit = {} - def error(marker: Marker, message: String, cause: Throwable): Unit = {} - def error(marker: Marker, message: String, args: AnyRef*): Unit = {} - - def audit(message: String): Unit = {} - def audit(message: String, cause: Throwable): Unit = {} - def audit(message: String, args: AnyRef*): Unit = {} - def audit(marker: Marker, message: String): Unit = {} - def audit(marker: Marker, message: String, cause: Throwable): Unit = {} - def audit(marker: Marker, message: String, args: AnyRef*): Unit = {} - - def debug(message: String): Unit = {} - def debug(message: String, cause: Throwable): Unit = {} - def debug(message: String, args: AnyRef*): Unit = {} - def debug(marker: Marker, message: String): Unit = {} - def debug(marker: Marker, message: String, cause: Throwable): Unit = {} - def debug(marker: Marker, message: String, args: AnyRef*): Unit = {} - } - - class DriverLayout extends LayoutBase[ILoggingEvent] { - import scala.collection.JavaConverters._ - - private val AVERAGE_MAXIMAL_MESSAGE_LENGTH = 256 - private val FieldSeparator = "=" - private val DateFormatString = "MM/dd/yyyy HH:mm:ss" - private val newline = System.getProperty("line.separator") - private val IgnoredClassesInStack = Set("org.apache.catalina", "org.apache.coyote", "sun.reflect", "javax.servlet") - - override def doLayout(loggingEvent: ILoggingEvent): String = { - - val message = new StringBuilder(AVERAGE_MAXIMAL_MESSAGE_LENGTH) - .append(new SimpleDateFormat(DateFormatString).format(new Date(loggingEvent.getTimeStamp))) - .append(" [") - .append(StringUtils.rightPad(loggingEvent.getLevel.toString, 5)) - .append(']') - .append(" - ") - .append(loggingEvent.getMessage) - .append(newline) - - logContext(message, loggingEvent) - - Option(loggingEvent.getCallerData) foreach { stacktrace => - val stacktraceLength = stacktrace.length - - if (stacktraceLength > 0) { - val location = stacktrace.head - - val _ = message - .append(s"Location: ${location.getClassName}.${location.getMethodName}:${location.getLineNumber}$newline") - .append("Exception: ") - .append(location.toString) - .append(newline) - - if (stacktraceLength > 1) { - message.append(stacktrace.tail.filterNot { e => - IgnoredClassesInStack.forall(ignored => !e.getClassName.startsWith(ignored)) - } map { - _.toString - } mkString newline) - } - } - } - - message.toString - } - - private def logContext(message: StringBuilder, loggingEvent: ILoggingEvent) = { - Option(loggingEvent.getMDCPropertyMap).map(_.asScala).filter(_.nonEmpty).foreach { context => - message.append( - context map { case (key, value) => s"$key$FieldSeparator$value" } mkString ("Context: ", " ", newline) - ) - } - } - } -} diff --git a/src/main/scala/com/drivergrp/core/messages.scala b/src/main/scala/com/drivergrp/core/messages.scala deleted file mode 100644 index 3a97401..0000000 --- a/src/main/scala/com/drivergrp/core/messages.scala +++ /dev/null @@ -1,59 +0,0 @@ -package com.drivergrp.core - -import java.util.Locale - -import com.drivergrp.core.logging.Logger -import com.typesafe.config.Config - -import scala.collection.JavaConverters._ - -/** - * Scala internationalization (i18n) support - */ -object messages { - - object Messages { - def messages(config: Config, log: Logger, locale: Locale = Locale.US): Messages = { - val map = config.getConfig(locale.getLanguage).root().unwrapped().asScala.mapValues(_.toString).toMap - Messages(map, locale, log) - } - } - - final case class Messages(map: Map[String, String], locale: Locale, log: Logger) { - - /** - * Returns message for the key - * - * @param key key - * @return message - */ - def apply(key: String): String = { - map.get(key) match { - case Some(message) => message - case None => - log.error(s"Message with key '$key' not found for locale '${locale.getLanguage}'") - key - } - } - - /** - * Returns message for the key and formats that with parameters - * - * @example "Hello {0}!" with "Joe" will be "Hello Joe!" - * - * @param key key - * @param params params to be embedded - * @return formatted message - */ - def apply(key: String, params: Any*): String = { - - def format(formatString: String, params: Seq[Any]) = - params.zipWithIndex.foldLeft(formatString) { - case (res, (value, index)) => res.replace(s"{$index}", value.toString) - } - - val template = apply(key) - format(template, params) - } - } -} diff --git a/src/main/scala/com/drivergrp/core/rest.scala b/src/main/scala/com/drivergrp/core/rest.scala deleted file mode 100644 index d97e13e..0000000 --- a/src/main/scala/com/drivergrp/core/rest.scala +++ /dev/null @@ -1,126 +0,0 @@ -package com.drivergrp.core - -import akka.actor.ActorSystem -import akka.http.scaladsl.Http -import akka.http.scaladsl.model._ -import akka.http.scaladsl.model.headers.RawHeader -import akka.http.scaladsl.unmarshalling.Unmarshal -import akka.stream.ActorMaterializer -import akka.stream.scaladsl.Flow -import akka.util.ByteString -import com.drivergrp.core.auth.{AuthService, AuthToken} -import com.drivergrp.core.crypto.Crypto -import com.drivergrp.core.logging.Logger -import com.drivergrp.core.stats.Stats -import com.drivergrp.core.time.TimeRange -import com.drivergrp.core.time.provider.TimeProvider -import com.github.swagger.akka.model._ -import com.github.swagger.akka.{HasActorSystem, SwaggerHttpService} -import com.typesafe.config.Config - -import scala.concurrent.{ExecutionContext, Future} -import scala.util.{Failure, Success} -import scalaz.{Failure => _, Success => _} - -object rest { - - trait Service - - trait ServiceTransport { - - def sendRequest(authToken: AuthToken)(requestStub: HttpRequest): Future[Unmarshal[ResponseEntity]] - } - - trait ServiceDiscovery { - - def discover[T <: Service](serviceName: Name[Service]): T - } - - class HttpRestServiceTransport(actorSystem: ActorSystem, executionContext: ExecutionContext, - crypto: Crypto, log: Logger, stats: Stats, time: TimeProvider) extends ServiceTransport { - - protected implicit val materializer = ActorMaterializer()(actorSystem) - protected implicit val execution = executionContext - - def sendRequest(authToken: AuthToken)(requestStub: HttpRequest): Future[Unmarshal[ResponseEntity]] = { - - val requestTime = time.currentTime() - val encryptionFlow = Flow[ByteString] map { bytes => - ByteString(crypto.encrypt(crypto.keyForToken(authToken))(bytes.toArray)) - } - val decryptionFlow = Flow[ByteString] map { bytes => - ByteString(crypto.decrypt(crypto.keyForToken(authToken))(bytes.toArray)) - } - - val request = requestStub - .withEntity(requestStub.entity.transformDataBytes(encryptionFlow)) - .withHeaders( - RawHeader(AuthService.AuthenticationTokenHeader, authToken.value.value)) - - log.audit(s"Sending to ${request.uri} request $request") - - val responseEntity = Http()(actorSystem).singleRequest(request)(materializer) map { response => - if(response.status == StatusCodes.NotFound) { - Unmarshal(HttpEntity.Empty: ResponseEntity) - } else if(response.status.isFailure()) { - throw new Exception("Http status is failure " + response.status) - } else { - Unmarshal(response.entity.transformDataBytes(decryptionFlow)) - } - } - - responseEntity.onComplete { - case Success(r) => - val responseTime = time.currentTime() - log.audit(s"Response from ${request.uri} to request $requestStub is successful") - stats.recordStats(Seq("request", request.uri.toString, "success"), TimeRange(requestTime, responseTime), 1) - - case Failure(t: Throwable) => - val responseTime = time.currentTime() - log.audit(s"Failed to receive response from ${request.uri} to request $requestStub") - log.error(s"Failed to receive response from ${request.uri} to request $requestStub", t) - stats.recordStats(Seq("request", request.uri.toString, "fail"), TimeRange(requestTime, responseTime), 1) - } (executionContext) - - responseEntity - } - } - - import scala.reflect.runtime.universe._ - - class Swagger(override val host: String, - version: String, - override val actorSystem: ActorSystem, - override val apiTypes: Seq[Type], - val config: Config) extends SwaggerHttpService with HasActorSystem { - - val materializer = ActorMaterializer()(actorSystem) - - override val basePath = config.getString("swagger.basePath") - override val apiDocsPath = config.getString("swagger.docsPath") - - override val info = Info( - config.getString("swagger.apiInfo.description"), - version, - config.getString("swagger.apiInfo.title"), - config.getString("swagger.apiInfo.termsOfServiceUrl"), - contact = Some(Contact( - config.getString("swagger.apiInfo.contact.name"), - config.getString("swagger.apiInfo.contact.url"), - config.getString("swagger.apiInfo.contact.email") - )), - license = Some(License( - config.getString("swagger.apiInfo.license"), - config.getString("swagger.apiInfo.licenseUrl") - )), - vendorExtensions = Map.empty[String, AnyRef]) - - def swaggerUI = get { - pathPrefix("") { - pathEndOrSingleSlash { - getFromResource("swagger-ui/index.html") - } - } ~ getFromResourceDirectory("swagger-ui") - } - } -} diff --git a/src/main/scala/com/drivergrp/core/stats.scala b/src/main/scala/com/drivergrp/core/stats.scala deleted file mode 100644 index 2af4b6a..0000000 --- a/src/main/scala/com/drivergrp/core/stats.scala +++ /dev/null @@ -1,97 +0,0 @@ -package com.drivergrp.core - -import java.io.File -import java.lang.management.ManagementFactory -import java.lang.reflect.Modifier - -import com.drivergrp.core.logging.Logger -import com.drivergrp.core.time.{Time, TimeRange} - -object stats { - - type StatsKey = String - type StatsKeys = Seq[StatsKey] - - trait Stats { - - def recordStats(keys: StatsKeys, interval: TimeRange, value: BigDecimal): Unit - - def recordStats(keys: StatsKeys, interval: TimeRange, value: Int): Unit = - recordStats(keys, interval, BigDecimal(value)) - - def recordStats(key: StatsKey, interval: TimeRange, value: BigDecimal): Unit = - recordStats(Vector(key), interval, value) - - def recordStats(key: StatsKey, interval: TimeRange, value: Int): Unit = - recordStats(Vector(key), interval, BigDecimal(value)) - - def recordStats(keys: StatsKeys, time: Time, value: BigDecimal): Unit = - recordStats(keys, TimeRange(time, time), value) - - def recordStats(keys: StatsKeys, time: Time, value: Int): Unit = - recordStats(keys, TimeRange(time, time), BigDecimal(value)) - - def recordStats(key: StatsKey, time: Time, value: BigDecimal): Unit = - recordStats(Vector(key), TimeRange(time, time), value) - - def recordStats(key: StatsKey, time: Time, value: Int): Unit = - recordStats(Vector(key), TimeRange(time, time), BigDecimal(value)) - } - - class LogStats(log: Logger) extends Stats { - def recordStats(keys: StatsKeys, interval: TimeRange, value: BigDecimal): Unit = { - val valueString = value.bigDecimal.toPlainString - log.audit(s"${keys.mkString(".")}(${interval.start.millis}-${interval.end.millis})=$valueString") - } - } - - final case class MemoryStats(free: Long, total: Long, max: Long) - - final case class GarbageCollectorStats(totalGarbageCollections: Long, garbageCollectionTime: Long) - - final case class FileRootSpace(path: String, totalSpace: Long, freeSpace: Long, usableSpace: Long) - - object SystemStats { - - def memoryUsage: MemoryStats = { - val runtime = Runtime.getRuntime - MemoryStats(runtime.freeMemory, runtime.totalMemory, runtime.maxMemory) - } - - def availableProcessors: Int = { - Runtime.getRuntime.availableProcessors() - } - - def garbageCollectorStats: GarbageCollectorStats = { - import scala.collection.JavaConverters._ - - val (totalGarbageCollections, garbageCollectionTime) = - ManagementFactory.getGarbageCollectorMXBeans.asScala.foldLeft(0L -> 0L) { - case ((total, collectionTime), gc) => - (total + math.max(0L, gc.getCollectionCount)) -> (collectionTime + math.max(0L, gc.getCollectionTime)) - } - - GarbageCollectorStats(totalGarbageCollections, garbageCollectionTime) - } - - def fileSystemSpace: Array[FileRootSpace] = { - File.listRoots() map { root => - FileRootSpace(root.getAbsolutePath, root.getTotalSpace, root.getFreeSpace, root.getUsableSpace) - } - } - - def operatingSystemStats: Map[String, String] = { - val operatingSystemMXBean = ManagementFactory.getOperatingSystemMXBean - operatingSystemMXBean.getClass.getDeclaredMethods - .map(method => { method.setAccessible(true); method }) - .filter(method => method.getName.startsWith("get") && Modifier.isPublic(method.getModifiers)) - .map { method => - try { - method.getName -> String.valueOf(method.invoke(operatingSystemMXBean)) - } catch { - case t: Throwable => method.getName -> t.getMessage - } - } toMap - } - } -} diff --git a/src/main/scala/com/drivergrp/core/time.scala b/src/main/scala/com/drivergrp/core/time.scala deleted file mode 100644 index 9bafb00..0000000 --- a/src/main/scala/com/drivergrp/core/time.scala +++ /dev/null @@ -1,72 +0,0 @@ -package com.drivergrp.core - -import java.text.SimpleDateFormat -import java.util._ - -import scala.concurrent.duration._ - -object time { - - // The most useful time units - val Second = 1000L - val Seconds = Second - val Minute = 60 * Seconds - val Minutes = Minute - val Hour = 60 * Minutes - val Hours = Hour - val Day = 24 * Hours - val Days = Day - val Week = 7 * Days - val Weeks = Week - - final case class Time(millis: Long) extends AnyVal { - - def isBefore(anotherTime: Time): Boolean = millis < anotherTime.millis - - def isAfter(anotherTime: Time): Boolean = millis > anotherTime.millis - - def advanceBy(duration: Duration): Time = Time(millis + duration.toMillis) - } - - final case class TimeRange(start: Time, end: Time) { - def duration: Duration = FiniteDuration(end.millis - start.millis, MILLISECONDS) - } - - implicit def timeOrdering: Ordering[Time] = Ordering.by(_.millis) - - def startOfMonth(time: Time) = { - Time(make(new GregorianCalendar()) { cal => - cal.setTime(new Date(time.millis)) - cal.set(Calendar.DAY_OF_MONTH, cal.getActualMinimum(Calendar.DAY_OF_MONTH)) - }.getTime.getTime) - } - - def textualDate(timezone: TimeZone)(time: Time): String = - make(new SimpleDateFormat("MMMM d, yyyy"))(_.setTimeZone(timezone)).format(new Date(time.millis)) - - def textualTime(timezone: TimeZone)(time: Time): String = - make(new SimpleDateFormat("MMM dd, yyyy hh:mm:ss a"))(_.setTimeZone(timezone)).format(new Date(time.millis)) - - object provider { - - /** - * Time providers are supplying code with current times - * and are extremely useful for testing to check how system is going - * to behave at specific moments in time. - * - * All the calls to receive current time must be made using time - * provider injected to the caller. - */ - trait TimeProvider { - def currentTime(): Time - } - - final class SystemTimeProvider extends TimeProvider { - def currentTime() = Time(System.currentTimeMillis()) - } - - final class SpecificTimeProvider(time: Time) extends TimeProvider { - def currentTime() = time - } - } -} -- cgit v1.2.3