SpringアプリのテストでHikariDataSource has been closedなどと言われたのでなんとかした
TL; DR
- Springのテスト時に使われるコンテキストキャッシュ
DefaultContextCache
はデフォルトの保持数が32で、それを超えると古いコンテキストが破棄される。そのときにDBのコネクションプールも破棄されてしまって困る。 - キャッシュの保持数はSpringのプロパティ
spring.test.context.cache.maxSize
で設定できる。
環境
- Spring Boot (spring-boot-starter 2.4.4)
- spring-framework 5.3.5
- HikariCP 3.4.5
- AdoptOpenJDK 16.0.1
- IntelliJ IDEA 2021.2 (Ultimate)
本題
なにごと?
JUnitでSpring Bootアプリのテストをゴリゴリ追加していたところ、いきなりエラーが。
logger:o.s.b.w.embedded.tomcat.TomcatWebServer LEVEL:INFO thread:main msg:Tomcat started on port(s): 52394 (http) with context path '' logger:xxx.xxx.XxxTest LEVEL:INFO thread:main msg:Started XxxTest in 1.134 seconds (JVM running for 65.714) logger:o.s.s.concurrent.ThreadPoolTaskExecutor LEVEL:INFO thread:main msg:Shutting down ExecutorService 'applicationTaskExecutor' logger:com.zaxxer.hikari.HikariDataSource LEVEL:INFO thread:main msg:HikariPool-1 - Shutdown initiated... logger:com.zaxxer.hikari.HikariDataSource LEVEL:INFO thread:main msg:HikariPool-1 - Shutdown completed. logger:o.s.test.context.TestContextManager LEVEL:WARN thread:main msg:Caught exception while invoking 'beforeTestMethod' callback on TestExecutionListener [org.springframework.test.context.jdbc.SqlScriptsTestExecutionListener@347d1586] for test method [com.intuit.karate.junit5.Karate xxx.xxx.XxxTest.testXxx()] and test instance [xxx.xxx.XxxTest.testXxx@5e462cc] org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is java.sql.SQLException: HikariDataSource HikariDataSource (HikariPool-1) has been closed. at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:309) at org.springframework.transaction.support.AbstractPlatformTransactionManager.startTransaction(AbstractPlatformTransactionManager.java:400) at org.springframework.transaction.support.AbstractPlatformTransactionManager.getTransaction(AbstractPlatformTransactionManager.java:373)
下記の行に注目。DBへのコネクションを開始しようとしたのにDataSourceが閉じられてしまっているのでエラー、ということらしい。
Could not open JDBC Connection for transaction; nested exception is java.sql.SQLException: HikariDataSource HikariDataSource (HikariPool-1) has been closed.
DataSourceが勝手に閉じられるのはよろしくないので、なぜ閉じられているのかを調べることにする。
調べてみる
HikariCPで何か起こっているということはわかったので、HikariCPのログレベルを debug
に変更し、ログの情報量を増やしてみる。
application.yaml
を下記のように設定して再度テストを実行。
logging: level: com.zaxxer.hikari: debug
そうすると、テスト開始時にDBとのコネクションが閉じられている様子が表示される。(connection evicted)
ですって。
logger:o.s.b.w.embedded.tomcat.TomcatWebServer LEVEL:INFO thread:main msg:Tomcat started on port(s): 54354 (http) with context path '' logger:xxx.xxx.XxxTest LEVEL:INFO thread:main msg:Started XxxTest in 1.068 seconds (JVM running for 64.084) logger:o.s.s.concurrent.ThreadPoolTaskExecutor LEVEL:INFO thread:main msg:Shutting down ExecutorService 'applicationTaskExecutor' logger:com.zaxxer.hikari.HikariDataSource LEVEL:INFO thread:main msg:HikariPool-1 - Shutdown initiated... logger:com.zaxxer.hikari.pool.HikariPool LEVEL:DEBUG thread:main msg:HikariPool-1 - Before shutdown stats (total=10, active=0, idle=10, waiting=0) logger:com.zaxxer.hikari.pool.PoolBase LEVEL:DEBUG thread:HikariPool-1 connection closer msg:HikariPool-1 - Closing connection org.postgresql.jdbc.PgConnection@36d7a68a: (connection evicted) logger:com.zaxxer.hikari.pool.PoolBase LEVEL:DEBUG thread:HikariPool-1 connection closer msg:HikariPool-1 - Closing connection org.postgresql.jdbc.PgConnection@1eeacb6e: (connection evicted) logger:com.zaxxer.hikari.pool.PoolBase LEVEL:DEBUG thread:HikariPool-1 connection closer msg:HikariPool-1 - Closing connection org.postgresql.jdbc.PgConnection@102d26c3: (connection evicted) logger:com.zaxxer.hikari.pool.PoolBase LEVEL:DEBUG thread:HikariPool-1 connection closer msg:HikariPool-1 - Closing connection org.postgresql.jdbc.PgConnection@1412a01e: (connection evicted) logger:com.zaxxer.hikari.pool.PoolBase LEVEL:DEBUG thread:HikariPool-1 connection closer msg:HikariPool-1 - Closing connection org.postgresql.jdbc.PgConnection@51102cf8: (connection evicted) logger:com.zaxxer.hikari.pool.PoolBase LEVEL:DEBUG thread:HikariPool-1 connection closer msg:HikariPool-1 - Closing connection org.postgresql.jdbc.PgConnection@19692c0f: (connection evicted) logger:com.zaxxer.hikari.pool.PoolBase LEVEL:DEBUG thread:HikariPool-1 connection closer msg:HikariPool-1 - Closing connection org.postgresql.jdbc.PgConnection@600b1adf: (connection evicted) logger:com.zaxxer.hikari.pool.PoolBase LEVEL:DEBUG thread:HikariPool-1 connection closer msg:HikariPool-1 - Closing connection org.postgresql.jdbc.PgConnection@61984490: (connection evicted) logger:com.zaxxer.hikari.pool.PoolBase LEVEL:DEBUG thread:HikariPool-1 connection closer msg:HikariPool-1 - Closing connection org.postgresql.jdbc.PgConnection@313fd3b7: (connection evicted) logger:com.zaxxer.hikari.pool.PoolBase LEVEL:DEBUG thread:HikariPool-1 connection closer msg:HikariPool-1 - Closing connection org.postgresql.jdbc.PgConnection@61e698cb: (connection evicted) logger:com.zaxxer.hikari.pool.HikariPool LEVEL:DEBUG thread:main msg:HikariPool-1 - After shutdown stats (total=0, active=0, idle=0, waiting=0) logger:com.zaxxer.hikari.HikariDataSource LEVEL:INFO thread:main msg:HikariPool-1 - Shutdown completed. logger:o.s.test.context.TestContextManager LEVEL:WARN thread:main msg:Caught exception while invoking 'beforeTestMethod' callback on TestExecutionListener [org.springframework.test.context.jdbc.SqlScriptsTestExe cutionListener@7ef7161d] for test method [com.intuit.karate.junit5.Karate xxx.xxx.XxxTest.testXxx()] and test instance [xxx.xxx.XxxTest@55b5625d] org.springframework.transaction.CannotCreateTransactionException: Could not open JDBC Connection for transaction; nested exception is java.sql.SQLException: HikariDataSource HikariDataSource (HikariPool-1) has been closed. at org.springframework.jdbc.datasource.DataSourceTransactionManager.doBegin(DataSourceTransactionManager.java:309) at org.springframework.transaction.support.AbstractPlatformTransactionManager.startTransaction(AbstractPlatformTransactionManager.java:400)
HikariCPのソースコード内を (connection evicted)
で検索すると、HikariPool
の下記のメソッドが見つかる。
public final class HikariPool extends PoolBase implements HikariPoolMXBean, IBagStateListener { // 中略 /** {@inheritDoc} */ @Override public void softEvictConnections() { connectionBag.values().forEach(poolEntry -> softEvictConnection(poolEntry, "(connection evicted)", false /* not owner */)); }
どうやらDataSourceが閉じられる際には、ここを通っているらしい。 IntelliJ IDEAで上記メソッドにブレークポイントを仕掛けて、コールスタックを追ってみる。
最初の HikariPool#shutdown
, HikariDataSource#close
は正常なコネクションプール終了処理であり、特に気になる部分は無し。
そのまま順番に追っていくと、DefaultContextCache.LruCache#removeEldestEntry
で DefaultContextCache.this.remove(〜)
しているところが見つかる。
public class DefaultContextCache implements ContextCache { // 中略 private class LruCache extends LinkedHashMap<MergedContextConfiguration, ApplicationContext> { LruCache(int initialCapacity, float loadFactor) { super(initialCapacity, loadFactor, true); } protected boolean removeEldestEntry(Entry<MergedContextConfiguration, ApplicationContext> eldest) { if (this.size() > DefaultContextCache.this.getMaxSize()) { DefaultContextCache.this.remove((MergedContextConfiguration)eldest.getKey(), HierarchyMode.CURRENT_LEVEL); // ←これ }
DefaultContextCacheはspring-testでのテストの際にアプリケーションコンテキストのキャッシュを保持するクラスで、LruCacheはその内部クラス。
LRUっていうぐらいだし、キャッシュがいっぱいになったら古いものを削除するんだろうなと想像できる。実際、削除処理の条件も if (this.size() > DefaultContextCache.this.getMaxSize())
だし。
キャッシュ保持数はデフォルトで32の様子*1。 このことから、以下のような事象が起こっていると考えられる。
- テスト実行時に、アプリケーションコンテキストが
DefaultContextCache
に格納される。32番目のテストまでは、問題なく実行される。 - 33番目のテストの際、アプリケーションコンテキストを
DefaultContextCache
に格納しようとすると、キャッシュ保持数の最大値を超えているため、古いアプリケーションコンテキストが削除される。 - アプリケーションコンテキストが削除されると、それに紐づくDBのコネクションプールがクローズされる。
- 33番目のテストでDBに接続しようとすると、3.で閉じられたコネクションプールが使われてしまい、エラーとなる。
キャッシュから削除されないようにキャッシュ保持数の最大値を大きくすれば、ひとまずは解消できそう。
DefaultContextCache
のドキュメントによると、この最大値はプロパティ spring.test.context.cache.maxSize
で変更できるらしい。
最大サイズは、コンストラクターの引数として指定するか、システムプロパティまたは spring.test.context.cache.maxSize という Spring プロパティを介して設定できます。
application.yaml
で設定したり、実行時引数で指定したりすればOK。
# application.yaml spring.test.context.cache.maxSize: 128
$ mvn test -Dspring.test.context.cache.maxSize=128
とはいえ、これだとコンテキストが破棄されないのでリソース消費が気になる…。本当はもうちょっとまともな対応方法がほしいところではあるけど、まぁテストだしこれでいいことにする。
まとめ
つかれた。
ruby-buildでRubyをインストールしようとしたらoptparseが無いって言われる
なにごと?
ruby-build
で rbenv install 3.0.2
したら optparse
が無いって言われてエラーになるんですよ。
$ rbenv install 3.0.2 Downloading ruby-3.0.2.tar.gz... -> https://cache.ruby-lang.org/pub/ruby/3.0/ruby-3.0.2.tar.gz Installing ruby-3.0.2... BUILD FAILED (Ubuntu 20.04 using ruby-build 20210526-11-gebdcf0c) # (略) ./tool/file2lastrev.rb:6:in `require': cannot load such file -- optparse (LoadError) from ./tool/file2lastrev.rb:6:in `<main>'
環境
なんとかする
今までも ruby-build
は普通に使ってたし、これまでのバージョンは入ってるのだけど。
$ rbenv versions 2.7.2 * 3.0.0 (set by /home/*****/.rbenv/version)
ってよく見ると system
のRubyが無いなって思っておもむろにインストール& rbenv
で使うように切り替え。
$ sudo apt-get install ruby $ rbenv global system $ rbenv rehash
この状態で ruby install
するとちゃんとインストールできた。
$ rbenv install 3.0.2 Downloading ruby-3.0.2.tar.gz... -> https://cache.ruby-lang.org/pub/ruby/3.0/ruby-3.0.2.tar.gz Installing ruby-3.0.2... Installed ruby-3.0.2 to /home/*****/.rbenv/versions/3.0.2
KarateでのAPIテストと同時にAssertJ-DBでデータの確認もやりたい
はじめに
Karateを使うとWeb APIのリクエスト&レスポンスのテストがとてもいい感じにできるが、そうすると今度は「POSTされたデータがちゃんとDBに入っているかどうか」とかも確認したくなってきてしまうもの。 そこでKarateでのテストシナリオ実行の際、AssertJ-DBでのデータのアサーションが行われるようにしてみる*1。
環境
- Spring Boot 2.5.0
- Karate (karate-junit5) 1.0.1
- AssertJ-DB 2.0.2
ポイント
Karateのfeatureファイルでは、Java.type('パッケージ+クラス名')
でJavaのクラスを参照できる。
これを利用し、あらかじめDBのテスト用メソッドをAssertJ-DBで書いておいて、それをfeatureファイルから呼び出すようにする。
サンプルコード全体は下記のとおり。
やり方
JavaでDBのアサーションを行うメソッドを定義する
テストランナーのクラスにDBのアサーションを行うメソッドを定義しておく。下記サンプルの public static void assertTable(String expectedMessage)
がそれ。
package demo; // ... @SpringBootTest(classes = DemoApplication.class) public class DemoTestRunner { private static DataSource dataSource; @Autowired private void setDataSource(DataSource dataSource) { DemoTestRunner.dataSource = dataSource; } @Karate.Test Karate testAll() { return Karate.run().relativeTo(getClass()); } public static void assertTable(String expectedMessage) { var table = new Table(dataSource, "messages"); assertThat(table).hasNumberOfRows(1) .row(0) .column("message").value().isEqualTo(expectedMessage); } }
Java.type()
で使いたいのでメソッドは public static
にする必要があり、それに合わせて DataSource
をstaticフィールドにしている。
staticなDataSourceをインジェクションできるようにするため、 setDataSource()
はだいぶ無理やり感がある…。
DBのアサーション自体は普通にAssertJ-DBで書けばよい。
featureファイルのシナリオでJavaのメソッドを呼び出す
こんな感じ。
Feature: Demo Background: * url baseUrl * def DemoTestRunner = Java.type('demo.DemoTestRunner') Scenario: Add a message Given path '/' And request { "message": "Hello" } When method post Then status 200 And DemoTestRunner.assertTable('Hello') Scenario: Find messages # ...
Background
で def DemoTestRunner = Java.type('demo.DemoTestRunner')
することで、引数で指定されたJavaのクラス(今回は DemoTestRunner
)が使えるようにしておく。
シナリオでは DemoTestRunner.assertTable('Hello')
のようにしてメソッドを呼び出す。
実行してみる
いつもどおりテストランナーのクラス(今回は DemoTestRunner
)を実行すればよい。
IntelliJ IDEAだとテスト結果は普通にこんな感じで出てくる。
HTMLレポートも出てくる。
試しにこんな感じでテストをわざと失敗させてみると…
Scenario: Add a message Given path '/' And request { "message": "INVALID" } When method post Then status 200 And DemoTestRunner.assertTable('Hello')
失敗時の情報もちゃんと出てくる。よさそう。
Spring Bootでプロファイルごとに実装を切り替える
TL; DR
@Profile
や @ConditionalOnExpression
を使う。ちょっとつらいけどがんばる。
サンプルコードは下記のとおり。
やりたいこと
システム日時を取得するユーティリティがあるが、テストのときに日時が毎回変わると困るので、特定の場合は固定値を返すようにしたい*1。
TERASOLUNAの下記機能とほぼ同じ。ただしSpring Bootなので、XMLを使わずアノテーションでなんとかしたい。
環境
- Spring Boot 2.5.0
解決方法
@Profile
アノテーションを使用し、指定されたプロファイルに応じてインジェクションされる実装を切り替える。
例えば、インタフェース DateTimeUtils
に対して本物のシステム日時を返す実装クラス SystemDateTimeUtils
と、固定日時を返す実装クラス FixedDateTimeUtils
を作るとする。
下記のような動作にしたい。
- プロファイル
with-fixeddatetime
が指定されている場合はFixedDateTimeUtils
を使う - そうでない場合は
SystemDateTimeUtils
を使う
固定日時を返す実装クラス FixedDateTimeUtils
では、@Profile("with-fixeddatetime")
を付与する。
@Component @Profile("with-fixeddatetime") public class FixedDateTimeUtils implements DateTimeUtils { // ...
本物のシステム日時を返す実装クラス SystemDateTimeUtils
では、@Profile("!with-fixeddatetime")
を付与する。
@Profile
でのプロファイル指定はSpring 式言語 (SpEL)が使える。!with-fixeddatetime
で「with-fixeddatetime
以外」となる。
@Component @Profile("!with-fixeddatetime") public class SystemDateTimeUtils implements DateTimeUtils { // ...
プロファイルの指定には、下記のような方法がある。
application.properties
で指定
spring.profiles.active=with-fixeddatetime
@SpringBootTest @ActiveProfiles("with-fixeddatetime") class DemoApplicationWithFixedDateTimeTests { // ...
その他、実行時の引数や環境変数での指定なども可能。
参考:2.6. アクティブ Spring プロファイルを設定する | Spring Boot 「使い方」ガイド - リファレンスドキュメント
@Profile("!with-fixeddatetime")
って書かないといけないのはつらい
つらいけどしょうがない。
本当は @Profile("!with-fixeddatetime")
って書かなくても with-fixeddatetime
じゃなければデフォルトで @Profile
無しのものを使ってほしいところ。
しかし @Profile
を付与しない場合は「どのプロファイルでも常に使われる」という動作になるらしい。今回の例で SystemDateTimeUtils
から @Profile
を外すと、with-fixeddatetime
プロファイルを指定した場合に下記エラーとなる。
*************************** APPLICATION FAILED TO START *************************** Description: Field dateTimeUtils in com.example.demo.service.impl.DemoServiceImpl required a single bean, but 2 were found: - fixedDateTimeUtils: defined in file [/path/to/springboot-profile-switch-demo/target/classes/com/example/demo/util/impl/FixedDateTimeUtils.class] - systemDateTimeUtils: defined in file [/path/to/springboot-profile-switch-demo/target/classes/com/example/demo/util/impl/SystemDateTimeUtils.class] Action: Consider marking one of the beans as @Primary, updating the consumer to accept multiple beans, or using @Qualifier to identify the bean that should be consumed
@Profile("default")
だと、default
プロファイルでない場合に使われないので、これもNG。
@ComponentScan
でコンポーネントスキャンの対象を変更することでの対応もできそうだが、ちょっと大掛かりではある。
実装クラスが3つ以上あるんですけど???
下記のような動作にする。
- プロファイルが
with-fixeddatetime
の場合はFixedDateTimeUtils
を使う - プロファイルが
with-adjusteddatetime
の場合はAdjustedDateTimeUtils
を使う - それ以外の場合は
SystemDateTimeUtils
を使う
AdjustedDateTimeUtils
には @Profile("with-adjusteddatetime")
を付与するとして…
@Component @Profile("with-adjusteddatetime") public class AdjustedDateTimeUtils implements DateTimeUtils { // ...
SystemDateTimeUtils
の @Profile
は with-fixeddatetime
と with-adjusteddatetime
の両方を除外するために、@Profile("!with-fixeddatetime && !with-adjusteddatetime")
みたいになる。つらい。
@Component @Profile("!with-fixeddatetime && !with-adjusteddatetime") public class SystemDateTimeUtils implements DateTimeUtils { // ...
SpELは正規表現もサポートされているので、@ConditionalOnExpression
を使用して「with-〜
にマッチしない」という指定もできる。プロファイル名をルール化できる場合は、この方法がよいかもしれない。
@Component @ConditionalOnExpression("#{ not ('${spring.profiles.active}' matches '^with-.+') }") public class SystemDateTimeUtils implements DateTimeUtils { // ...
この場合、JUnitテストクラスでは @TestPropertySource
でプロファイルを指定する必要がある。あまりちゃんと調べられていないけど、@ActiveProfiles("プロファイル名")
だと spring.profiles.active
に値が設定されず、@ConditionalOnExpression
が意図通りに動かない(Environment#getProperty("spring.profiles.active")
が null
になる)。
@SpringBootTest @TestPropertySource(properties = "spring.profiles.active=with-fixeddatetime") class DemoApplicationWithFixedDateTimeTests { // ...
SpELについては、下記のドキュメントを見るとよい。
4. Spring 式言語 (SpEL) | Spring Framework コアテクノロジー - リファレンス
演算子については下記の部分を参照。
4.3.7. 演算子 | Spring Framework コアテクノロジー - リファレンス
参考
*1:テストだけであればMockitoを使ったほうが真っ当ではあるが、そうもいかないことが稀によくある。また、テスト以外でも状況に応じて挙動を切り替えたいこともたまにある。実行環境が異なる場合とか、バッチを特定の日付で実行したい場合とか…。
IntelliJ IDEAを使い始めたときに見るとよい情報まとめ
はじめに
最近ようやくIntelliJ IDEAを使い始めたので、役に立った情報をまとめておきます。
想定環境
とりあえずはじめに読むもの
Javaプログラムの作成〜実行
デバッグ
便利な機能・ショートカットなど
個人的によく使うのは下記のもの*1。よく使う順。
超使う
- 複数ファイル内検索 (Find in Files)
- 実装に移動
- 前に戻る(「実装に移動」でジャンプした後、元の場所に戻るときなど)
- どこでも検索 (Search Everywhere)
Shift
Shift
かなり使う
- プロジェクトの使用箇所を検索する (Find Usage):変数やメソッドが使われている場所を検索する
- 右クリックでコンテキストメニューを開き、
Find Usages
- 右クリックでコンテキストメニューを開き、
- ブックマーク:ソースコードの行にブックマークを設定し、ちょっとしたメモやジャンプができる
- 行番号付近を右クリックしてコンテキストメニューを開き、
Add Bookmarks
- 保存したブックマークは画面左下あたりの
Favorites
メニューで表示できる。ショートカットは macOS:Command
+F3
, Ubuntu:Alt
+2
- 行番号付近を右クリックしてコンテキストメニューを開き、
- ソースファイルの構造 (Structure):エディタで開いているクラスのフィールドやメソッドの一覧表示、移動ができる
- なんでも実行 (Run Anything)
その他
他にもいろいろある。下記記事などが参考になる。
公式のショートカット一覧は壁に貼って毎日眺めるとよい。
パンくずリスト
現在見ているクラス名やメソッド名がひと目でわかる。クソデカクラス/メソッドを読んでると、自分が今どこにいるのかわからなくなるので、そういうときに便利。 環境によってはデフォルトで表示されているかもしれないが、表示されていなかったら下記記事を参考に設定する。
いじってたらぶっこわれてまともに動かなくなったときに設定ファイルを消す
下記記事を参考に、ディレクトリごと削除すればよい。
*1:ショートカットは環境によって異なるかもしれないが、リンク先の公式サイトでは上部メニューの「Shortcuts」からOS等が選択できるので、自分の環境に合ったものを確認するとよい。
クリップボード操作コマンドを活用してテキストを手軽に一括編集する
はじめに
macOSの pbcopy
, pbpaste
やLinuxの xsel
を使うと、クリップボードにコピーした値を sed
などのコマンドで編集後、別のエディタやスプレッドシートにペースト、といった操作が簡単にできて便利という話です。
スクリプトを書くほどでもないけど手で編集するのは面倒だからシェル芸したい!という場合におすすめ。
環境
どちらの環境ともシェルは zsh です。
よくある使用例
macOSの場合
例えばソースコードに記載されたエラーコードを抜き出してスプレッドシートに貼り付けたいとき。
当該部分を Command-C
でコピーして…
pbpaste
で出力した結果から grep
や sed
で目的の部分を取り出す。
$ pbpaste | grep 'ErrorCode' | sed -E 's/[^"]+"([0-9A-Z]+)",?/\1/g' E0001 E0002 E9999
そのままパイプで pbcopy
につなげれば、目的の値がクリップボードにコピーされる。
$ pbpaste | grep 'ErrorCode' | sed -E 's/[^"]+"([0-9A-Z]+)",?/\1/g' | pbcopy
あとはスプレッドシートにそのまま Command-V
とかで貼り付ければOK。
Linuxの場合
macOSの例で使用した pbcopy
, pbpaste
をそれぞれ xsel -bi
, xsel -bo
に置き換えるだけ。
$ xsel -bo | grep 'ErrorCode' | sed -E 's/[^"]+"([0-9A-Z]+)",?/\1/g' | xsel -bi
ただし xsel
は標準では入っていないので、事前にインストールが必要。こんな感じ。
$ sudo apt-get install xsel
コマンド説明
macOSの場合
pbcopy
: 標準入力の内容をクリップボードにコピーする
下記の例では HOGE
がクリップボードに登録される。普通にコピーしたときと同じく、Command-V
で結果を貼り付け可能。
$ echo HOGE | pbcopy
pbpaste
: クリップボードの内容を標準出力に書き出す
前述の例でクリップボードに登録された HOGE
が標準出力に表示される。
$ pbpaste HOGE
Linuxの場合
xsel
: 選択範囲を操作する
xsel
はX Window System上で選択された部分を操作するコマンド。オプション -b
または --clipboard
をつけることで、クリップボードの操作ができる。
入力と出力の切り替えはオプション -i
-o
で行う。
xsel -bi
: 標準入力の内容をクリップボードにコピーする
入力は -i
オプションで行う。クリップボードのオプションとまとめて -bi
で指定。
下記の例ではMacでの例と同じく、標準入力から受け取った HOGE
がクリップボードに登録される。こちらも Ctrl-V
で結果を貼り付け可能。
$ echo HOGE | xsel -bi
-i
オプションは省略してもOK。
$ echo HOGE | xsel -b
xsel -bo
: クリップボードの内容を標準出力に書き出す
出力は -o
オプション。
前述の例でクリップボードに登録された HOGE
が標準出力に表示される。
$ xsel -bo
HOGE
ちなみにLinuxには xclip
というコマンドもある
xsel
と同じく、デフォルトでの操作対象は選択部分。-selection c
でクリップボードの操作になる。
$ echo HOGE | xclip -selection c $ xclip -selection c -o HOGE
xclip
は画像などのバイナリデータを扱えたり、パイプを経由せず直接ファイルから入力ができたりと、 xsel
より便利な機能もある。
$ xclip -selection c -t image/jpeg foo.jpg # 画像ファイルをクリップボードにコピー $ xclip -selection c -o > bar.jpg # クリップボード内の画像データをファイルに出力
ただ、クリップボードを指定するためのオプションが -selection c
と少し長いので、普段はやっぱり xsel
を使ってしまう…。
参考
Pleiades (Eclipse)でのSpringアプリ開発時にjarファイルが削除/変更できなくなったので対処した
環境
現象
Springアプリの開発に独自のjarライブラリを使用していたのですが、jarを上書きしたり、jar更新後にGitのブランチ切り替えをしたときにこんなエラーが出ることがありました。
リソースの削除中に問題が発生しました。 'D:\local\pleiades\workspace\***\WebContent\WEB-INF\lib\***.jar' を削除できませんでした。 ファイルの削除中に問題が発生しました。 D:\local\pleiades\workspace\***\WebContent\WEB-INF\lib\***.jar を削除できませんでした。 D:\local\pleiades\workspace\***\WebContent\WEB-INF\lib\***.jar: プロセスはファイルにアクセスできません。別のプロセスが使用中です。
Could not rename file D:\local\pleiades\workspace\***\WebContent\WEB-INF\lib\._***.jar9000261316551232106.tmp to D:\local\pleiades\workspace\***\WebContent\WEB-INF\lib\***.jar
ProcessExplorerで調べてみると、EclipseのSpringプラグインらしきものがjarファイルを掴んでいる様子。
対処方法
Springの言語サーバが原因っぽいので、OFFにしてしまいます。おそらく補完などに影響が出るけど、そこは諦める方針。
メニューから「ウィンドウ」→「設定」 をクリック。
「言語サーバ」をクリックし、言語サーバのチェックを外して「適用して閉じる」*1。
*1:勢いで全部OFFにしたけど、Springのやつだけでよかった気がする