자바 개발자가 https://www.playframework.com/documentation/2.6.x/ScalaHttpFilters를 보면서 스칼라 Play 애플리케이션을 만들 때 조금 헤맬 수 있다. 


아래와 같은 코드가 있다고 가정하자. 특별히 소스를 분석하지 않아도 적당히 문서를 읽으면서 알수도 있지만,,

head first로 개념을 이해할 수 있다. 





Filters

package filters

import javax.inject.Inject

import play.api.http.{DefaultHttpFilters, EnabledFilters}
import play.filters.gzip.GzipFilter

class Filters @Inject() (
defaultFilters: EnabledFilters,
gzip: GzipFilter,
logging: LoggingFilter
) extends DefaultHttpFilters (defaultFilters.filters :+ gzip :+ logging: _*)



LoggingFilter

package filters

import javax.inject.Inject

import akka.stream.Materializer
import play.api.Logger
import play.api.mvc._

import scala.concurrent.{ExecutionContext, Future}

class LoggingFilter @Inject() (implicit val mat: Materializer, ec: ExecutionContext) extends Filter {

def apply(nextFilter: RequestHeader => Future[Result])
(requestHeader: RequestHeader): Future[Result] = {

val startTime = System.currentTimeMillis

nextFilter(requestHeader).map { result =>

val endTime = System.currentTimeMillis
val requestTime = endTime - startTime

val log = s"${requestHeader.method} ${requestHeader.uri} took ${requestTime}ms and returned ${result.header.status}"
if (requestTime > 3000) {
Logger.warn(log)
} else {
Logger.debug(log)
}

result
}
}
}




또는 아래와 같이 사용한다.


play.filters.enabled += filters.LoggingFilter






만약 play.filters.enabled를 사용하지 않으면.. 아래와 같이 써야 한다. 



play.http.filters = filters.Filters









아래와 같이 사용하면  다음 에러가 발생한다. 


play.http.filters += filters.LoggingFilter


Configuration error: Configuration error[reference.conf @ jar:file:/Users/samuel.kim/.ivy2/cache/com.typesafe.play/play_2.12/jars/play_2.12-2.6.6.jar!/reference.conf: 69: Cannot concatenate object or list with a non-object-or-list, ConfigNull(null) and SimpleConfigList(["filters.Filters"]) are not compatible]







아래와 같이 사용하면 에러가 발생한다.


play.filters.enabled += filters.Filters



play.api.UnexpectedException: Unexpected exception[ProvisionException: Unable to provision, see the following errors:

1) Found a circular dependency involving play.api.http.EnabledFilters, and circular dependencies are disabled.
  at play.utils.Reflect$.bindingsFromConfiguration(Reflect.scala:58):
Binding(class play.api.http.EnabledFilters to self) (via modules: com.google.inject.util.Modules$OverrideModule -> play.api.inject.guice.GuiceableModuleConversions$$anon$1)
  while locating play.api.http.EnabledFilters
    for the 1st parameter of filters.Filters.<init>(Filters.scala:12)
  while locating filters.Filters
  at play.api.http.EnabledFilters.<init>(HttpFilters.scala:68)
  at play.utils.Reflect$.bindingsFromConfiguration(Reflect.scala:58):
Binding(class play.api.http.EnabledFilters to self) (via modules: com.google.inject.util.Modules$OverrideModule -> play.api.inject.guice.GuiceableModuleConversions$$anon$1)
  while locating play.api.http.EnabledFilters
  while locating play.api.http.HttpFilters
    for the 4th parameter of play.api.http.JavaCompatibleHttpRequestHandler.<init>(HttpRequestHandler.scala:222)
  while locating play.api.http.JavaCompatibleHttpRequestHandler
  while locating play.api.http.HttpRequestHandler
    for the 6th parameter of play.api.DefaultApplication.<init>(Application.scala:236)
  at play.api.DefaultApplication.class(Application.scala:235)
  while locating play.api.DefaultApplication
  while locating play.api.Application




클래스를 보면 Filters의 첫 번째 매개변수인 EnabledFilters의 내부를 보면.. 필터를 구성하는 개념이다. 


  private val enabledKey = "play.filters.enabled"


  private val disabledKey = "play.filters.disabled"





즉 Filters는 상위 개념인데, 처음에는 None으로 지정되어 있다.  따러서 ConfigNull이 이미 들어가 있다. 따라서 Null에 List를 추가하면 당연히 에러가 발생할 것이다.  play.http.filters는 애플리케이션에서 지정하는 filter 목록을 정의하는 것이라 할 수 있겠다. 


play.http.filters += filters.Filters
=>  (애플리케이션 정의 Filter)


play
.http.filters = filters.MyFilters



다시 이전 Filters 클래스를 살펴보면, 필터가 3개가 추가된다. EnableFilter(enable/disable 할 수 있는 filter 리스트), Gzip, LoggingFilter 매개 변수로 추가되어 Injection 되었다.

부모 클래스인 DefaultHttpFilters에서 사용하는 형태로 되어 있다. 그래서 play.filters.enable/play.filters.disable 리트스 모두와 gizp, logging을 몽땅 리스트로 묶도록 되어 있다. 이게 사용자 정의 Filter인 셈이다.

extends DefaultHttpFilters(defaultFilters.filters :+ gzip :+ logging: _*)

자바 개발자라면 황당할 수 있을 것 같다.

Inject와 extends를 이용한 간단 코드이지만.. ㄷ ㄷ ㄷ 





DefaultHttpFilters는 여러 개의 EseentailFilter를 받는다. 
class DefaultHttpFilters @Inject() (val filters: EssentialFilter*)


Filter에서는 아래 filters는 그냥 Seq인데.
defaultFilters.filters :+ gzip :+ logging)

 이를 : _*) 를 추가하면  EssentailFilter* 타입이 된다.



자바에서는 varargs인데.


스칼라에서는 : _*으로 사용하면 컴파일러에게 seq/array를 varargs로 변환하라는 신호이다.



scala> def foo(args: Int*) = args.map{_ + 1}

foo: (args: Int*)Seq[Int]


scala> foo(-1, 0, 1)

res0: Seq[Int] = ArrayBuffer(0, 1, 2)



Posted by '김용환'
,



openstack4j를 활용해 keystone 인증은 

http://www.openstack4j.com/learn/getting-started



openstack4j의 keystone 인증 방식(v2.0) 관련 예제는 다음과 같다. 



import org.openstack4j.api.OSClient.{OSClientV2, OSClientV3}

import org.openstack4j.core.transport.Config

import org.openstack4j.model.common.Identifier

import org.openstack4j.openstack.OSFactory



      val os: OSClientV2 = OSFactory.builderV2()

        .endpoint("https://internal.k8s.openstack.google.io:5000/v2.0")

        .credentials("sam","password")

        .tenantName("admin")

        .authenticate()


      println(os.getAccess.getToken)

      

     

결과



KeystoneToken{id=3a735957bd0c45d3b64242e0edd150cb, created=Wed Feb 07 20:51:37 KST 2018, expires=Thu Feb 08 11:51:37 KST 2018, tenant=KeystoneTenant{id=ae17dbd7165142808e074579360a8b9c, name=admin, description=NULL, enabled=true}}




v3 인증 방식(https://github.com/ContainX/openstack4j)을 사용하려면 여러 방식이 있다.

sing Identity V3 authentication you basically have 4 options:

(1) authenticate with project-scope

OSClientV3 os = OSFactory.builderV3()
                .endpoint("http://<fqdn>:5000/v3")
                .credentials("admin", "secret", Identifier.byId("user domain id"))
                .scopeToProject(Identifier.byId("project id"))
                .authenticate());

(2) authenticate with domain-scope

OSClientV3 os = OSFactory.builderV3()
                .endpoint("http://<fqdn>:5000/v3")
                .credentials("admin", "secret", Identifier.byId("user domain id"))
                .scopeToDomain(Identifier.byId("domain id"))
                .authenticate());

(3) authenticate unscoped

OSClientV3 os = OSFactory.builderV3()
                .endpoint("http://<fqdn>:5000/v3")
                .credentials("user id", "secret")
                .authenticate();

(4) authenticate with a token

OSClientV3 os = OSFactory.builderV3()
                .endpoint("http://<fqdn>:5000/v3")
                .token("token id")
                .scopeToProject(Identifier.byId("project id"))
                .authenticate());

실제로 아래와 같이 테스트해보면 안되기도 한다.


      val domainid = Identifier.byName("Default")


      val os: OSClientV3 = OSFactory.builderV3()

        .endpoint("https://internal.k8s.openstack.google.io:5000/v3")

        .credentials("sam","password",domainid)

        .scopeToDomain(domainid)

        .authenticate()


      OSFactory.enableHttpLoggingFilter(true)


      println(os.getToken)



인증만 할꺼라 json으로 파싱해서 보내는것이 나은 듯 하다.. ㄷ ㄷ 


예제의 도메인을 그대로 참조해서 

https://internal.k8s.openstack.google.io:5000/v3/auth/tokens 엔드 포인트에 다음 json을 body로 보낸다.


import play.api.libs.json._
val json: JsValue = Json.parse(s"""
{
"auth" : {
"identity": {
"methods": [ "password" ],
"password": {
"user": {
"name": "${userId}",
"domain": { "name" : "Default" },
"password": "${password}"
}
}
}
}
}
"""
)


post 결과는 다음과 같다. 


{  

   "token":{  

      "issued_at":"2018-02-07T20:42:58.000000Z",

      "audit_ids":[  

         "111123132"

      ],

      "methods":[  

         "password"

      ],

      "expires_at":"2018-02-08T20:42:58.000000Z",

      "user":{  

         "domain":{  

            "id":"default",

            "name":"Default"

         },

         "id":"1a1a1aasfdadf",

         "name":"sam"

      }

   }

}







'Cloud' 카테고리의 다른 글

fluentd 정규식 확인하기  (0) 2018.04.10
[fluentd] format json  (0) 2018.04.10
[k8s] 쿠버네티스의 로그 수집 툴  (0) 2018.02.06
[k8s] postgresql 운영 - stateful (펌질)  (0) 2018.01.25
[fluentd]의 fluent-plugin-forest(forest)  (0) 2018.01.22
Posted by '김용환'
,


https://www.playframework.com/documentation/2.6.x/ScalaWS를 살펴보면 WSClient를 

Injection해서 사용하고 있다.


WSClient를 Injection없이 바로 써서 테스트하고 싶다면 다음 예제를 활용한다. 



import play.api.libs.ws.ahc.AhcWSClient
import akka.stream.ActorMaterializer
import akka.actor.ActorSystem
import scala.concurrent.duration._
import scala.concurrent.{Await, Future}

implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()
val ws = AhcWSClient()

val result = ws.url("https://www.google.com")
.withRequestTimeout(10000.millis)
.get()
.map{
resp => resp.body
}(system.dispatcher)

val response = Await.result(result, atMost = 5.second)

println(response)


Posted by '김용환'
,



앙상블, 머신 러닝, 분류와 회귀 관련 랜덤 포레스트(RF) 관련 좋은 글 모음



앙상블, 랜덤 포레스트를 쉽게 설명한 내용


https://medium.com/@deepvalidation/title-3b0e263605de



랜덤 포레스트 알고리즘(부트스트랩 bootstrap, bagging)

http://blog.naver.com/PostView.nhn?blogId=samsjang&logNo=220979751089&parentCategoryNo=&categoryNo=87&viewDate=&isShowPopularPosts=false&from=postView



R 기반

http://r-bong.blogspot.kr/2016/11/classification-and-regression-by.html




텐서플로우 기반

https://tensorflow.blog/%ED%8C%8C%EC%9D%B4%EC%8D%AC-%EB%A8%B8%EC%8B%A0%EB%9F%AC%EB%8B%9D/2-3-6-%EA%B2%B0%EC%A0%95-%ED%8A%B8%EB%A6%AC%EC%9D%98-%EC%95%99%EC%83%81%EB%B8%94/





정보 이득 계산(information gain calculation)

http://seamless.tistory.com/20


Posted by '김용환'
,