SDKMAN! で Oracle JDK はインストールできない

sdkman/sdkman-db-migrations #24:

Firstly, I would prefer if we drop the Oracle JDK and just go with OpenJDK for EA releases.

Dropped Oracle JDK

ということで、Oracle JDK を使いたい場合は別の手段を考えなければなりません。一例を以下に示します。

  • Mac: Hombebrew で java をインストールして jenv (※ jenv も Homebrew でインストールできる)でバージョンを設定する
  • Win: scoop の java bucketで各バージョンインストールできるようなので、 JAVA_HOME などを自前で変更して頑張る。
  • Ubuntu: update-alternatives はシステムデフォルトの設定なのであまりそぐわないと思う。こちらもやはり JAVA_HOME を自前で切り替えか。有名所の PPA は JDK8 は webupd8team/java だったり JDK11 は linuxuprising/java だったりするようだ。

ControllerにHttpServletRequestをautowiringできるのはなぜ?

@ContollerHttpServletRequest インスタンスをインジェクション(autowired)するためにはいくつか解決しなければならない問題があると思われました。

  • HttpServletRequest をインジェクション対象としなければならない
  • @Contoller はsingleton-scopeであるのに対し、 HttpServletRequest は普通に考えるとrequest-scope。そのままではsingletonにrequestはインジェクションできない(参考)。

これらはこの辺りの実装で対処しているようです。
ここで用いられている RequestObjectFactorygetObject()メソッドが返すのがインジェクションされる HttpServletRequestオブジェクトのようです。
詳しくはコールスタックをたどっていく必要がありそう。

参考:

Spring Security を組み込むとPOSTで403

POST/PATCHを投げると403 Forbiddenが返ってきました。GETは特に問題なく200 OKを返します。
特にエラーログも出ず途方に暮れていましたが、なんとかこの質問にたどり着きました。

DEBUGレベルでログを出力するようにしてみたところ、確かに “Invalid CSRF token” という文字列が。

無効化する方法は、上のリンクにもありますが、オフィシャルリファレンスでいうとこの部分。

    @EnableWebSecurity
    public class WebSecurityConfig extends
    WebSecurityConfigurerAdapter {

        @Override
        protected void configure(HttpSecurity http) throws Exception {
            http
                .csrf().disable();
        }
    }

ログ抜粋:

o.s.web.servlet.DispatcherServlet        : Completed initialization in 18 ms
o.s.security.web.FilterChainProxy        : /test at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
o.s.security.web.FilterChainProxy        : /test at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
w.c.HttpSessionSecurityContextRepository : No HttpSession currently exists
w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: null. A new one will be created.
o.s.security.web.FilterChainProxy        : /test at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
o.s.security.web.FilterChainProxy        : /test at position 4 of 11 in additional filter chain; firing Filter: 'CsrfFilter'
o.s.security.web.csrf.CsrfFilter         : Invalid CSRF token found for http://localhost:8080/test
o.s.s.w.header.writers.HstsHeaderWriter  : Not injecting HSTS header since it did not match the requestMatcher org.springframework.security.web.header.writers.HstsHeaderWriter$SecureRequestMatcher@5d22153b
w.c.HttpSessionSecurityContextRepository : SecurityContext is empty or contents are anonymous - context will not be stored in HttpSession.
s.s.w.c.SecurityContextPersistenceFilter : SecurityContextHolder now cleared, as request processing completed
o.a.c.c.C.[Tomcat].[localhost]           : Processing ErrorPage[errorCode=0, location=/error]
o.s.security.web.FilterChainProxy        : /error at position 1 of 11 in additional filter chain; firing Filter: 'WebAsyncManagerIntegrationFilter'
o.s.security.web.FilterChainProxy        : /error at position 2 of 11 in additional filter chain; firing Filter: 'SecurityContextPersistenceFilter'
w.c.HttpSessionSecurityContextRepository : HttpSession returned null object for SPRING_SECURITY_CONTEXT
w.c.HttpSessionSecurityContextRepository : No SecurityContext was available from the HttpSession: org.apache.catalina.session.StandardSessionFacade@580c882f. A new one will be created.
o.s.security.web.FilterChainProxy        : /error at position 3 of 11 in additional filter chain; firing Filter: 'HeaderWriterFilter'
o.s.security.web.FilterChainProxy        : /error at position 4 of 11 in additional filter chain; firing Filter: 'CsrfFilter'
o.s.security.web.FilterChainProxy        : /error at position 5 of 11 in additional filter chain; firing Filter: 'LogoutFilter'
o.s.s.w.u.matcher.AntPathRequestMatcher  : Checking match of request : '/error'; against '/logout'
o.s.security.web.FilterChainProxy        : /error at position 6 of 11 in additional filter chain; firing Filter: 'RequestHeaderAuthenticationFilter'
.w.a.p.RequestHeaderAuthenticationFilter : Checking secure context token: null
.w.a.p.RequestHeaderAuthenticationFilter : preAuthenticatedPrincipal = xxxx, trying to authenticate
o.s.s.authentication.ProviderManager     : Authentication attempt using org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider
p.PreAuthenticatedAuthenticationProvider : PreAuthenticated authentication request: org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken@200906e6: Principal: bfle01@ts02; Credentials: [PROTECTED]; Authenticated: false; Details: org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails@1c07a: RemoteIpAddress: 127.0.0.1; SessionId: 14A4672A48F954DD55368ED79EAD3641; []; Not granted any authorities
.w.a.p.RequestHeaderAuthenticationFilter : Authentication success: org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken@dff6f93d: Principal: org.springframework.security.core.userdetails.User@2009ec43: Username: bfle01@ts02; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Not granted any authorities; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails@1c07a: RemoteIpAddress: 127.0.0.1; SessionId: 14A4672A48F954DD55368ED79EAD3641; []; Not granted any authorities
o.s.security.web.FilterChainProxy        : /error at position 7 of 11 in additional filter chain; firing Filter: 'RequestCacheAwareFilter'
o.s.s.w.s.HttpSessionRequestCache        : saved request doesn't match
o.s.security.web.FilterChainProxy        : /error at position 8 of 11 in additional filter chain; firing Filter: 'SecurityContextHolderAwareRequestFilter'
o.s.security.web.FilterChainProxy        : /error at position 9 of 11 in additional filter chain; firing Filter: 'AnonymousAuthenticationFilter'
o.s.s.w.a.AnonymousAuthenticationFilter  : SecurityContextHolder not populated with anonymous token, as it already contained: 'org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken@dff6f93d: Principal: org.springframework.security.core.userdetails.User@2009ec43: Username: bfle01@ts02; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Not granted any authorities; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails@1c07a: RemoteIpAddress: 127.0.0.1; SessionId: 14A4672A48F954DD55368ED79EAD3641; []; Not granted any authorities'
o.s.security.web.FilterChainProxy        : /error at position 10 of 11 in additional filter chain; firing Filter: 'SessionManagementFilter'
s.CompositeSessionAuthenticationStrategy : Delegating to org.springframework.security.web.authentication.session.ChangeSessionIdAuthenticationStrategy@4ad8d36f
s.CompositeSessionAuthenticationStrategy : Delegating to org.springframework.security.web.csrf.CsrfAuthenticationStrategy@5cf64230
w.c.HttpSessionSecurityContextRepository : SecurityContext 'org.springframework.security.core.context.SecurityContextImpl@dff6f93d: Authentication: org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken@dff6f93d: Principal: org.springframework.security.core.userdetails.User@2009ec43: Username: bfle01@ts02; Password: [PROTECTED]; Enabled: true; AccountNonExpired: true; credentialsNonExpired: true; AccountNonLocked: true; Not granted any authorities; Credentials: [PROTECTED]; Authenticated: true; Details: org.springframework.security.web.authentication.preauth.PreAuthenticatedGrantedAuthoritiesWebAuthenticationDetails@1c07a: RemoteIpAddress: 127.0.0.1; SessionId: 14A4672A48F954DD55368ED79EAD3641; []; Not granted any authorities' stored to HttpSession: 'org.apache.catalina.session.StandardSessionFacade@580c882f
o.s.security.web.FilterChainProxy        : /error at position 11 of 11 in additional filter chain; firing Filter: 'ExceptionTranslationFilter'

Scope ‘request’ is not active for the current thread

に回答がありますが、自分なりのまとめ。

CDIと異なり、SpringのDIは、

  • デフォルトではSingleton、つまりアプリケーションスコープ。
  • デフォルトではインジェクションするインスタンスはnewされたもの。(CDIではプロキシオブジェクト)

つまり、あるオブジェクト(A)を生成する際、そのオブジェクトにインジェクトするオブジェクト(B)も生成する必要があるので、BはAより長い寿命を持っていないといけない。そしてデフォルトではAはシングルトンなのでBもシングルトンでなければならない、ということです。

@Component // シングルトン
class A {
    @Autowired // シングルトン
    private B b;
}

ここで、 B がシングルトンでない場合、このままではインジェクトできないので何らかの修正が必要になります。

  • 実オブジェクトでなくプロキシオブジェクトをインジェクトしておく
  • Aのスコープを狭める(ここではrequestスコープ以下にする)

(補足: 冒頭リンク先ではコメント欄にこれ以外の方法も書かれています)

このうち前者は問題を複雑化させる可能性があるのではと考えます。
本来requestスコープ外の場所にもインジェクションできてしまう、そして通常はHTTPリクエストが起点になるので正しく動いてしまいます。
が、例えばunit testコードを書こうとするとrequestスコープ外なのでうまくいかない、そして理由もわからない、ということに。

それよりは、各コンポーネントの本来あるべきスコープを考えたほうが良さそうです。
RestControllerはシングルトンで良いでしょうか。自分はrequestスコープが妥当であると考えます。そうするとBはインジェクションできるクラス、ということになります。
DDDでいうところのサービスはどうでしょう。これはシングルトンで良いと思います。とすると、Bをインジェクションしては駄目な場所です。

Sybaseのfrom句、2連ドットの意味

(正確にはSybaseでなくTransact-SQL仕様、つまりMSSQL(SQL Server)も同じみたいです。)

select * from database2..person

のような文を見ることがあります。 database2はデータベース名、personはテーブル名と推測できるのですが、 .. はどういう意味なんだろうかと気になっていました。

に回答がありました。

省略無く書くとfrom句は

from database.schema.table

となるようですが、このときスキーマ名を省略して書くことができ、その場合はデフォルトスキーマが用いられるようです。
通常、デフォルトスキーマ名はdboに設定されていますので、冒頭のselect文は大抵の場合

select * from database2.dbo.person

と同義、ということになります。

bash: scp: command not found

scp を実行すると表題のエラーが出ました。
原因は、送信先のホストに scp コマンドが無いせいだそうで。自分だけでなく相手にも必要なんですね…
そして、CentOS7で scp コマンドが提供されるパッケージは openssh-clients
見てみるとopeenssh-server, opensshはインストールしていたのですが、サーバ用途だから…と思ってopenssh-clientsは見逃してしまっていたようです。罠ですね…