Skip to content

Commit

Permalink
Added initialComponents argument to Bootstrap init
Browse files Browse the repository at this point in the history
String argumented Bootstrap init will now try to load
properties from resources if they are not present in the system,
or throw an RuntimeException if they are not present there also.

Added HttpHeaderProvider as an possible initialComponent,
with default to SettingsHeaderProvider with reads from properties.
HttpClient now depends on HttpHeaderProvider to provide it with
authorization headers.

Removed ProjectSettings, java.util.Properties are now a part of the
container.

Added simple specification for Bootstrap and HttpHeaderProvider.
  • Loading branch information
rinmalavi committed Sep 15, 2014
1 parent 7877009 commit 6d44d1a
Show file tree
Hide file tree
Showing 14 changed files with 291 additions and 97 deletions.
2 changes: 1 addition & 1 deletion build.sbt
@@ -1,4 +1,4 @@
version := "0.9.0"
version := "0.9.1"

name := "dsl-client-scala"

Expand Down
@@ -1,9 +1,8 @@
package com.dslplatform.api.patterns

import scala.reflect.runtime.universe.TypeTag
import scala.reflect.ClassTag
import java.lang.reflect.Type
import java.lang.reflect.ParameterizedType
import scala.reflect.runtime.universe.TypeTag
import java.lang.reflect.{ParameterizedType, Type}

/**
* Service for resolving other services.
Expand All @@ -18,7 +17,7 @@ trait ServiceLocator {
/**
* Resolve a service registered in the locator.
*
* @param clazz class or interface
* @param tpe class or interface
* @return registered implementation
*/
def resolve[T](tpe: Type): T
Expand Down
123 changes: 92 additions & 31 deletions http/src/main/scala/com/dslplatform/api/client/Bootstrap.scala
@@ -1,18 +1,18 @@
package com.dslplatform.api.client

import java.util.{Collections, Properties}

import com.dslplatform.api.patterns.PersistableRepository
import com.dslplatform.api.patterns.SearchableRepository
import com.dslplatform.api.patterns.ServiceLocator
import com.dslplatform.api.patterns.Repository

import java.io.InputStream
import java.io.FileInputStream
import java.io.{InputStream, FileInputStream}

import org.slf4j.LoggerFactory
import org.slf4j.{Logger, LoggerFactory}

import java.util.concurrent.Executors
import java.util.concurrent.ExecutorService
import scala.concurrent.ExecutionContext
import java.util.concurrent.{TimeUnit, AbstractExecutorService, Executors, ExecutorService}
import scala.concurrent.{ExecutionContextExecutorService, ExecutionContext}

/**
* DSL client Java initialization.
Expand All @@ -36,31 +36,82 @@ object Bootstrap {

staticLocator
}
/**
* Initialize service locator using provided project.ini stream.
*
* @param inputStream stream for project.props
* @return initialized service locator
* @throws IOException in case of failure to read stream
*/
def init(inputStream: InputStream): ServiceLocator = {
val properties = new Properties()
properties.load(inputStream)
init(properties)
}

/**
* Initialize service locator using provided project.ini stream.
*
* @param iniStream stream for project.ini
* @return initialized service locator
* @throws IOException in case of failure to read stream
* @param properties properties
* @return initialized service locator
* @throws IOException in case of failure to read stream
*/
def init(properties: Properties): ServiceLocator = init(properties, Map.empty[Object, AnyRef])

/**
* Initialize service locator using provided project.ini stream.
*
* @param properties properties
* @param initialComponents Map of initial components Logger, ExecutionContext
* or HttpHeaderProvider
* @return initialized service locator
* @throws IOException in case of failure to read stream
*/
def init(iniStream: InputStream): ServiceLocator = {
val threadPool = Executors.newCachedThreadPool()
val locator =
new MapServiceLocator(LoggerFactory.getLogger("dsl-client-http"))
.register[ProjectSettings](new ProjectSettings(iniStream))
.register[JsonSerialization]
.register[ExecutorService](threadPool)
.register[ExecutionContext](ExecutionContext.fromExecutorService(threadPool))
.register[HttpClient]
.register[ApplicationProxy, HttpApplicationProxy]
.register[CrudProxy, HttpCrudProxy]
.register[DomainProxy, HttpDomainProxy]
.register[StandardProxy, HttpStandardProxy]
.register[ReportingProxy, HttpReportingProxy]
.register(classOf[Repository[_]], classOf[ClientRepository[_]])
.register(classOf[SearchableRepository[_]], classOf[ClientSearchableRepository[_]])
.register(classOf[PersistableRepository[_]], classOf[ClientPersistableRepository[_]])
def init(properties: Properties, initialComponents: Map[Object, AnyRef]): ServiceLocator = {

val locator = new MapServiceLocator(
initialComponents ++
(if (initialComponents.contains(classOf[Logger]))
Map.empty
else Map(classOf[Logger] -> LoggerFactory.getLogger("dsl-client-scala")))
)
locator.register[Properties](properties)

(initialComponents.get(classOf[ExecutionContext]), initialComponents.get(classOf[ExecutorService])) match {
case (Some(ec), None) =>
val ecc = ec.asInstanceOf[ExecutionContext]
val threadPool = new AbstractExecutorService with ExecutionContextExecutorService {
override def prepare(): ExecutionContext = ecc
override def isShutdown = false
override def isTerminated = false
override def shutdown() = ()
override def shutdownNow() = Collections.emptyList[Runnable]
override def execute(runnable: Runnable): Unit = ecc execute runnable
override def reportFailure(t: Throwable): Unit = ecc reportFailure t
override def awaitTermination(length: Long, unit: TimeUnit): Boolean = false
}
locator.register[ExecutorService](threadPool)
case (None, Some(tp)) =>
locator.register[ExecutionContext](ExecutionContext.fromExecutorService(tp.asInstanceOf[ExecutorService]))
case _ =>
val tp = Executors.newCachedThreadPool()
locator.register[ExecutorService](tp)
locator.register[ExecutionContext](ExecutionContext.fromExecutorService(tp))
}
if (!initialComponents.contains(classOf[HttpHeaderProvider])) {
locator.register[HttpHeaderProvider](new SettingsHeaderProvider(properties))
}
locator
.register[JsonSerialization]
.register[HttpClient]
.register[ApplicationProxy, HttpApplicationProxy]
.register[CrudProxy, HttpCrudProxy]
.register[DomainProxy, HttpDomainProxy]
.register[StandardProxy, HttpStandardProxy]
.register[ReportingProxy, HttpReportingProxy]
.register(classOf[Repository[_]], classOf[ClientRepository[_]])
.register(classOf[SearchableRepository[_]], classOf[ClientSearchableRepository[_]])
.register(classOf[PersistableRepository[_]], classOf[ClientPersistableRepository[_]])

staticLocator = locator
locator
Expand All @@ -69,16 +120,26 @@ object Bootstrap {
/**
* Initialize service locator using provided dsl-project.ini path.
*
* @param iniPath path to project.ini
* @param propertiesPath path to project.ini
* @return initialized service locator
* @throws IOException in case of failure to read project.ini
*/
def init(iniPath: String): ServiceLocator = {
val iniStream: InputStream = new FileInputStream(iniPath)
def init(propertiesPath: String, initialComponents: Map[Object, AnyRef] = Map.empty[Object, AnyRef]): ServiceLocator = {
val inputStream: InputStream = {
val file: java.io.File = new java.io.File(propertiesPath)
if (file.exists()) {
new FileInputStream(file)
} else {
getClass.getResourceAsStream(propertiesPath)
}
}
if (inputStream == null) throw new RuntimeException(s"$propertiesPath was not found in the file system or the classpath.")
try {
init(iniStream)
val properties = new Properties()
properties.load(inputStream)
init(properties, initialComponents)
} finally {
iniStream.close()
inputStream.close()
}
}
}
38 changes: 13 additions & 25 deletions http/src/main/scala/com/dslplatform/api/client/HttpClient.scala
@@ -1,5 +1,7 @@
package com.dslplatform.api.client

import java.util.Properties

import com.dslplatform.api.patterns.ServiceLocator

import java.io.IOException
Expand Down Expand Up @@ -48,39 +50,25 @@ private[client] object HttpClientUtil {

class HttpClient(
locator: ServiceLocator,
projectSettings: ProjectSettings,
properties: Properties,
json: JsonSerialization,
authHeaders: HttpHeaderProvider,
logger: Logger,
executorService: java.util.concurrent.ExecutorService) {

import HttpClientUtil._
private val remoteUrl = Option(projectSettings.get("api-url")).getOrElse(
throw new RuntimeException("Missing api-url from the properties.")
)
private val domainPrefix = Option(projectSettings.get("package-name")).getOrElse {
logger.warn("Could not find package-name in the properties defaulting to \"\"")
""
}
private val remoteUrl = Option(properties.getProperty("api-url"))
.getOrElse(throw new RuntimeException("Missing api-url from the properties."))
private val domainPrefix = Option(properties.getProperty("package-name"))
.getOrElse{throw new RuntimeException("Missing package-name from the properties.")}
private val domainPrefixLength = if (domainPrefix.length > 0) domainPrefix.length + 1 else 0
private val basicAuth = makeBasicAuth
private val auth = authHeaders.getHeaders.mapValues(Set(_))
private val MimeType = "application/json"
private [client] implicit val ec = ExecutionContext.fromExecutorService(executorService)
private val commonHeaders: Headers = Map(
"Accept" -> Set(MimeType),
"Content-Type" -> Set(MimeType)
) ++ makeBasicAuth

private def makeBasicAuth: Headers = {
val username = projectSettings.get("username")
val password = projectSettings.get("project-id")

if (username == null || password == null) Map.empty
else {
val token = username + ':' + password
val basicAuth = "Basic " + new String(Base64.encode(token.getBytes("UTF-8")))
Map("Authorization" -> Set(basicAuth))
}
}
) ++ auth

private[client] def getDslName[T](implicit ct: ClassTag[T]): String =
getDslName(ct.runtimeClass)
Expand All @@ -100,9 +88,9 @@ class HttpClient(
username [{}]
api: [{}]
pid: [{}]""",
projectSettings.get("username"),
projectSettings.get("api-url"),
projectSettings.get("project-id"));
properties.get("username"),
properties.get("api-url"),
properties.get("project-id"));
}

private def makeNingHeaders(additionalHeaders: Map[String, Set[String]]): java.util.Map[String, java.util.Collection[String]] = {
Expand Down
@@ -0,0 +1,5 @@
package com.dslplatform.api.client

trait HttpHeaderProvider {
def getHeaders: Map[String, String]
}
Expand Up @@ -21,7 +21,6 @@ import com.fasterxml.jackson.databind.module.SimpleModule
import com.fasterxml.jackson.module.scala.DefaultScalaModule
import com.dslplatform.api.patterns.ServiceLocator
import com.fasterxml.jackson.databind.JavaType
import com.fasterxml.jackson.module.scala.ser.CustomDefaultScalaModule

class JsonSerialization(locator: ServiceLocator) {

Expand Down Expand Up @@ -90,7 +89,7 @@ class JsonSerialization(locator: ServiceLocator) {

private val serializationMapper =
new ObjectMapper()
.registerModule(CustomDefaultScalaModule)
.registerModule(com.fasterxml.jackson.module.scala.DefaultScalaModule)
//.registerModule(DefaultScalaModule)
.setSerializationInclusion(com.fasterxml.jackson.annotation.JsonInclude.Include.NON_NULL)
.registerModule(serializationModule)
Expand Down
Expand Up @@ -8,18 +8,17 @@ import java.lang.reflect.Type
import java.lang.reflect.TypeVariable
import java.lang.reflect.ParameterizedType
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
import scala.reflect.ClassTag
import scala.reflect.{ClassTag, classTag}
import scala.reflect.runtime.universe._
import java.lang.reflect.Modifier

class MapServiceLocator(logger: Logger, cacheResult: Boolean = true)
class MapServiceLocator(initialComponents: Map[Object, AnyRef], cacheResult: Boolean = true)
extends ServiceLocator {

private val components: MMap[Object, AnyRef] =
MMap(
classOf[ServiceLocator] -> this,
classOf[Logger] -> logger
)
private val logger = initialComponents.get(classOf[Logger]).asInstanceOf[Some[Logger]]
.getOrElse(throw new RuntimeException("Logger was not provided with initial components."))

private val components: MMap[Object, AnyRef] = MMap.empty ++ initialComponents + (classOf[ServiceLocator] -> this)

def register(target: Class[_], service: AnyRef): MapServiceLocator = {
logger.trace("About to register class " + target.getName() + " " + service)
Expand Down Expand Up @@ -129,7 +128,7 @@ class MapServiceLocator(logger: Logger, cacheResult: Boolean = true)
val genType = ParameterizedTypeImpl.make(mt, args, null)
val genClazz = genType.getRawType()
tryConstruct(typ, genClazz.getConstructors())
case _ =>
case impl =>
Some(impl)
}
}
Expand Down

This file was deleted.

@@ -0,0 +1,24 @@
package com.dslplatform.api.client

import java.util.Properties

import com.ning.http.util.Base64

class SettingsHeaderProvider(properties: Properties) extends HttpHeaderProvider {

private val headers: Map[String, String] = Option(properties.getProperty("auth")).map {
auth => Map("Authorization" -> ("Basic " + auth))
}.orElse {
Option(properties.getProperty("username")).flatMap {
username =>
Option(properties.getProperty("project-id")).map {
projectId =>
val token = username + ':' + projectId
val basicAuth = "Basic " + new String(Base64.encode(token.getBytes("UTF-8")))
Map[String, String]("Authorization" -> basicAuth)
}
}
}.getOrElse(Map.empty[String, String])

override def getHeaders: Map[String, String] = headers
}
10 changes: 10 additions & 0 deletions http/src/test/resources/logback.xml
@@ -0,0 +1,10 @@
<configuration scan="false" debug="false">
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>[%4p] [%d{HH:mm:ss}]: %m%n</pattern>
</encoder>
</appender>
<root level="Trace">
<appender-ref ref="STDOUT" />
</root>
</configuration>
4 changes: 4 additions & 0 deletions http/src/test/resources/test-project.props
@@ -0,0 +1,4 @@
username=testUser@dsl-platform.com
project-id=test-password
api-url=https://dsl-platform.com/test
package-name=test.package

0 comments on commit 6d44d1a

Please sign in to comment.