play 2.6 새로운 기능

scala 2018. 2. 8. 10:21



play 2.6에 엄청 많은 기능이 추가/변경되었다. playframwork는 조금 불친절하다.


https://www.playframework.com/documentation/2.6.x/Highlights26





자세한 내용은 아래 블로그에 play 2.6에 대한 설명이 잘 되어 있다. 



https://nvisium.com/resources/blog/2017/10/04/play-2-6-security-analysis.html

Posted by '김용환'
,


자바 개발자가 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 '김용환'
,



초딩은 유부트 메신저를 사용하고..

http://outstanding.kr/discussion/?action=readpost&post_id=296123&bbspaged=1


10대는 페메를 많이 사용한다고 한다.

http://www.bloter.net/archives/301054


Posted by '김용환'
,


k8s의 로그 수집 툴은 fluentd 이다. 


https://github.com/GoogleCloudPlatform/k8s-stackdriver/tree/master/fluentd-gcp-image

Posted by '김용환'
,




학습이 제대로 되지 않으면 under fitting이라 한다.
너무 학습을 잘 시켜 표준에 대한 정밀도는 너무 완벽하지만 실제 데이터에 대한 정확도가 떨어지면 over fitting이라 한다.





Posted by '김용환'
,



주택 정보 포털(http://housta.khug.or.kr/khhi/web/hi/pr/hipr020003.jsp)에 따르면 작년에 대해서 소득 분위별 PIR(price to income ratio)를 조사하고 있다. 




 전국서울
가구 연소득가구 연소득
1분위2분위3분위4분위5분위1분위2분위3분위4분위5분위
2017.03평균 주택가격1분위7.13.52.51.81.115.27.75.44.02.3
2분위11.95.94.13.11.822.911.68.26.03.5
3분위16.17.95.64.22.429.514.910.57.84.5
4분위21.810.77.65.63.339.920.214.210.56.0
5분위35.917.712.59.35.469.435.124.818.410.5




1996년부터 2016년에는 어땠을까? 우리나라를 포함한 국가별 추세는 다음과 같다.


http://cafe.daum.net/_c21_/bbs_search_read?grpid=1yKm&fldid=FlzC&datanum=220966&q=pir&_referer=V7kfJwkeLEGMZxGlgqZEmddb_hC7E12H







'나의 경제' 카테고리의 다른 글

accrual 인식  (0) 2019.03.06
주식 투자 - 1조원 할아버지  (0) 2018.08.24
자산 가격과 GDP 간격 (거품론)  (0) 2017.12.07
[펌] 주식투자는 방정식이 아니다.  (0) 2015.12.29
하나 SK 패밀리 카드 - PP카드  (0) 2013.07.24
Posted by '김용환'
,



외부 클래스에서 trait의 내부 필드에 접근할 때는 java interface처럼 접근할 수 없다. 

즉 public처럼 사용할 수 없다.


trait SeperatorTrait {

  val SEP: String = "____"

}



따라서 컴패년 오브젝트를 사용하면 외부 클래스에서 사용할 수 있다.


trait SeparatorTrait {

}


object SeparatorTrait {

 val SEP: String = "____"

}



Posted by '김용환'
,