読者です 読者をやめる 読者になる 読者になる

この日記は私的なものであり、所属会社の見解ではありません。 GitHub: takahashikzn

自分の精神をコピーしたら何が起きるのか

こんな増田が話題になってます。

anond.hatelabo.jp

僕も先月に「ディアスポラ」を読み終わったところだったのでちょうどよいネタ。

ディアスポラ (ハヤカワ文庫 SF)

ディアスポラ (ハヤカワ文庫 SF)

この作品はマジで面白い。序盤はやや難解ですが、傑作だと思います。

  • 一人の「個人」であることを満たす要件とは何か?
  • 誰かが「存在する」とはどういうことなのか?

についての一つの考え方を垣間見ることが出来ました。


もし機械の脳が実現したとして

あなたは不治の病に侵されて余命半年とします。「なぜ俺がこんな目に…」と世界を呪う日々。

そんなある日、聞いたこともないような小難しい名称の研究所から、一人の研究者がやってきました。

「機械の脳を実用化しました。精神をコピーすれば不老不死も可能ですし、コピー先は外見があなたと瓜二つの生体ボディです。ぜひ被験者になって下さい。」

あなたは大喜び。

自分の精神をコピーすれば病を克服!むしろ永遠の命がこの手に!やったー!!

ぜひ俺を被験者にしてくれ、と二つ返事をするわけです。


しかし現実は甘くない

そんなハッピーエンドにはならないわけです。

精神をコピーしたら何が起きると思いますか?


…さて、コピー作業が終わり、麻酔が切れて目覚めたあなたが見たものは2つ。


  • 「相変わらず余命半年のままの哀れな自分」
  • 「他者から見たら完全に自分そのものに見える、自分ではない誰か」


ただそれだけ。


外見、立ち振舞い、記憶が完全に自分と同じなら、他者から見たらそれは実質的に、あなた以外の何者でもありません。

ここにあなたは病気を克服し、永遠の命を得ました。おめでとうございます。

ただし他者から見たら。


あなたはそのコピーを恨むでしょう。

「あいつは俺が過ごすはずだった幸せな日常を取り戻してやがる…本当の俺はここだ!!あいつはニセモノなんだ!!」

という具合に。


さて、ではこのコピーは何者なのでしょうか?


こんな観点の作品、誰か教えてください

ディアスポラはそういう話ではないんですよね―。

色々探してみたのですが、僕のグーグル力では見つけられませんでした。


個人的に近いと思っている作品

SOMA

Steamのホラーゲームです。 ややネタバレですが、物語の終盤で、上記のような葛藤に主人公は直面することになります。

store.steampowered.com

弟者兄者のプレイ動画はこちら。 www.youtube.com

AIの遺伝子

山田胡瓜さんのマンガです。ITmediaの記者から漫画家へ転身したという変わった経歴の持ち主です。

試し読みできる第1話がそんな感じの、自我同一性に関する話です。

追記

この日記を書いた2時間後にふと「機械の脳」でググったら即出てきました。

なんかすいませんでした。

detail.chiebukuro.yahoo.co.jp

更に追記

順列都市」を読み始めたのですが正にそんな話でした。

順列都市〈上〉 (ハヤカワ文庫SF)

順列都市〈上〉 (ハヤカワ文庫SF)

struts-2.5.5でI18nInterceptorの初期化失敗を無理やり回避する

Struts2

struts-2.5.5がリリースされました。

このリリースはかなり大きなインパクトのある変更がいくつか入っています。


最大のインパクトは、ActionContext#getParameters()の戻り値がjava.util.Mapからorg.apache.struts2.dispatcher.HttpParametersに変更されたこと。 これにより、これまで動作していたコードがコンパイルエラーになります。いわゆるbreaking changeというやつです。

詳しくはこちらの議論を参照。


いやお前、せめて前のAPIをDeprecatedで残すとかしろよ…コレはちょっとやり過ぎ。しかもHttpParametersクラス、APIが使いづらいし。

例えば値を追加する際、Mapのままだったら

ActionContext.getParameters().put("foo", new String[]{ "値" });

で済むところが、

Map<String, Parameter> params = new HashMap<>();
params.put("foo", new Parameter.Request("foo", "値"));

ActionContext.getParameters().appendAll(params);

としなければならなくなりました。単一の値をputする手段がないし、いちいちParameterに変換してやる必要があります。メンドクサイ。

起動すると謎のエラー発生

さて、上記API変更に伴い既存コードを修正して、いざ起動してみると今度は次のような例外が発生。

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'org.apache.struts2.interceptor.I18nInterceptor': Unsatisfied dependency expressed through bean property 'localeProvider'

localeProviderというBeanが未定義なのかと思ってstruts2-core-2.5.5.jar内のstruts-default.xmlをみてみると、きちんと

<bean type="com.opensymphony.xwork2.LocaleProvider" name="struts" class="com.opensymphony.xwork2.DefaultLocaleProvider" scope="singleton" />

は定義されている。なのに何故依存性解決に失敗するのだ。。。

ログをよく見てみると、次のようにも出力されています。

org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.opensymphony.xwork2.LocaleProvider] is defined: expected single matching bean but found 543: jp.root42.r42fw_web.export.web.action.error.ErrorAction,...

これで答えがわかりました。犯人はstruts2-spring-pluginでした。いや、struts2-coreかな?どちらが悪いとは決めづらい微妙な感じです。

Struts2のActionクラスの基底クラスであるcom.opensymphony.xwork2.ActionSupportの定義は次のようになっており、

public class ActionSupport implements Action, Validateable, ValidationAware, TextProvider, LocaleProvider, Serializable {
    ...
}

LocaleProviderインタフェースを実装しています。そして、ActionクラスはSpringのコンテキストが初期化されたときにロードされてしまいます。 従ってI18nInterceptorの依存性解決のときに、struts-default.xmlで定義されているDefaultLocaleProviderと競合してLocaleProviderのBeanを一意に特定できないため失敗するわけです。

ワークアラウンド

解決方法ですが、僕は次のようにしました。

import java.util.Map;

import javax.servlet.ServletContext;

import com.opensymphony.xwork2.DefaultLocaleProvider;
import com.opensymphony.xwork2.config.entities.ActionConfig;
import com.opensymphony.xwork2.inject.Container;
import com.opensymphony.xwork2.inject.Inject;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.apache.struts2.StrutsConstants;
import org.apache.struts2.interceptor.I18nInterceptor;
import org.apache.struts2.spring.StrutsSpringObjectFactory;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;


public class WorkaroundStrutsSpringObjectFactory
    extends StrutsSpringObjectFactory {

    // 親クラスからコピペ
    @Inject
    public WorkaroundStrutsSpringObjectFactory(
        @Inject(value = StrutsConstants.STRUTS_OBJECTFACTORY_SPRING_AUTOWIRE, required = false) final String autoWire,
        @Inject(value = StrutsConstants.STRUTS_OBJECTFACTORY_SPRING_AUTOWIRE_ALWAYS_RESPECT, required = false) final String alwaysAutoWire,
        @Inject(value = StrutsConstants.STRUTS_OBJECTFACTORY_SPRING_USE_CLASS_CACHE, required = false) final String useClassCacheStr,
        @Inject(value = StrutsConstants.STRUTS_OBJECTFACTORY_SPRING_ENABLE_AOP_SUPPORT, required = false) final String enableAopSupport,
        @Inject final ServletContext servletContext, @Inject(StrutsConstants.STRUTS_DEVMODE) final String devMode,
        @Inject final Container container) {

        super(autoWire, alwaysAutoWire, useClassCacheStr, enableAopSupport, servletContext, devMode, container);
    }

    @Override
    public Object buildBean(final Class clazz, final Map<String, Object> extraContext) throws Exception {

        try {
            return super.buildBean(clazz, extraContext);
        } catch (final Exception e) {

            // I18nInterceptorの依存性解決失敗の場合、手動でオブジェクトを作って返す
            if ((clazz == I18nInterceptor.class)
                && (0 <= ExceptionUtils.indexOfType(e, NoUniqueBeanDefinitionException.class))) {

                final I18nInterceptor ret = new I18nInterceptor();
                ret.setLocaleProvider(new DefaultLocaleProvider());
                return ret;
            } else {
                throw e;
            }
        }
    }
}

struts.xmlにこのように記述してデフォルトのBeanを置き換えます。

<constant name="struts.objectFactory" value="[YOUR PACKAGE HERE].WorkaroundStrutsSpringObjectFactory" />

全くエレガントでない手法ですが、そのうち対策されるまでのツナギなので一先ずはこれで。

ついでに、親クラス側でエラーログが出てウザいので

<logger name="com.opensymphony.xwork2.spring.SpringObjectFactory" level="off" />

でログをOFFにしておきました。

Googleからスカウトされました

数日前、いきなりGoogleのリクルーターからメールが来ました。


メールの全文掲載はマズいと思うので要点だけ書くと、

  • ブログ(このブログのこと)とGithubを見て、特にJavaに関するプログラミングの能力が高いとお見受けした
  • Site Reliablity Engineers(SRE)チームで働く気はありませんか?
  • もしこの話に興味があるなら、チャットで話しましょう

とのこと。

メールに載っていたリクルーターの氏名でググってみたところ、アイルランドの方でした。


メールを見て最初に思ったのは「ホントにGoogleから来たメールなのか?」。

もちろんメールヘッダも確かめましたが、怪しい箇所は無し。本物のようです。


いったい僕のどの辺を評価頂けたのか良くわかりませんが、Googleからオファー頂けたことは素直に嬉しいと思いました。

ま、面接を受けたら、間違いなく落ちるんですけどね。。。その厳しさはこの辺が参考になります。

http://shiumachi.hatenablog.com/entry/20090122/1232574613

この記事の応募者の方は僕と同じくSREのようですね。


「僕は自分で会社を持ってますし、従業員もいますので」ということで、できるだけ丁寧に辞退のメールを返したのですが。

もしフツーの会社員だったら。。。果たして応募していたかな?

2016-10-24追記

辞退のメールに、リクルーターの方は次の通り返信をくれました。

「もし気が変わって応募したくなったら、いつでもいいから教えてね」

とのことです。ご丁寧にありがとうございます!万一ウチが倒産したらそうします :)

"永続記憶装置からセッションをロード中の例外です"がウザったいのでセッション永続化をOFFにする

Tomcat

Tomcatを停止して再起動すると、こんな例外が発生することがあります。

java.lang.ClassCastException: java.lang.StackTraceElement cannot be cast to java.lang.String
    at java.io.ObjectInputStream.readTypeString(ObjectInputStream.java:1421)
    at java.io.ObjectStreamClass.readNonProxy(ObjectStreamClass.java:719)
    at java.io.ObjectInputStream.readClassDescriptor(ObjectInputStream.java:833)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1609)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1521)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1781)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2018)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1942)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1808)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
    at java.util.ArrayList.readObject(ArrayList.java:791)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at java.io.ObjectStreamClass.invokeReadObject(ObjectStreamClass.java:1058)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1909)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1808)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
    at java.io.ObjectInputStream.defaultReadFields(ObjectInputStream.java:2018)
    at java.io.ObjectInputStream.readSerialData(ObjectInputStream.java:1942)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1808)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1353)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:373)
    at org.apache.catalina.session.StandardSession.doReadObject(StandardSession.java:1611)
    at org.apache.catalina.session.StandardSession.readObjectData(StandardSession.java:1077)
    at org.apache.catalina.session.StandardManager.doLoad(StandardManager.java:218)
    at org.apache.catalina.session.StandardManager.load(StandardManager.java:162)
    at org.apache.catalina.session.StandardManager.startInternal(StandardManager.java:356)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5196)
    at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1403)
    at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1393)
    at java.util.concurrent.FutureTask.run(FutureTask.java:266)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    at java.lang.Thread.run(Thread.java:745)

Tomcatを停止した時にセッションを永続化し、再起動時に再ロードしているわけなんですがそれに失敗しています。 なにゆえStackTraceElementをStringにキャストしようとしているのかは謎ですが。。。そんなコード書いたっけ?


さて、とりあえず僕はセッションの永続化が不要なのでこの機能をOFFにしたい。 調べてみたところ、context.xmlを書く必要があるようです。

<?xml version="1.0" encoding="UTF-8"?>
<Context>
  <!-- pathnameがnullの場合、永続化は無効 -->
  <Manager className="org.apache.catalina.session.StandardManager" pathname="" />
</Context>

マニュアルによると、

This persistence may be disabled by setting this attribute to an empty string.

だそうですが、あてにならないのでソースを読みます。

以下はorg.apache.catalina.session.StandardManagerのそれっぽい箇所を抜粋したもの。

protected void doUnload() throws IOException {

    if (log.isDebugEnabled())
        log.debug(sm.getString("standardManager.unloading.debug"));

    if (sessions.isEmpty()) {
        log.debug(sm.getString("standardManager.unloading.nosessions"));
        return; // nothing to do
    }

    // Open an output stream to the specified pathname, if any
    File file = file();
    if (file == null) { // nullなら何もせず終了
        return;
    }

    ...
}

...

protected File file() {
    if (pathname == null || pathname.length() == 0) {
        return null; // pathnameがnullか空文字ならnullを返す
    }
    File file = new File(pathname);
    if (!file.isAbsolute()) {
        Context context = getContext();
        ServletContext servletContext = context.getServletContext();
        File tempdir = (File) servletContext.getAttribute(ServletContext.TEMPDIR);
        if (tempdir != null) {
            file = new File(tempdir, pathname);
        }
    }
    return file;
}

見ての通り、pathnameがnullなら永続化をしないことが確認できたのでこれで正しいようです。

なお、context.xmlの配置方法については過去記事を参照して下さい。

Firefox48以降でSelenium-RCが動作しない問題を解消する

Selenium

昨日やっとトレーナーレベル22になりました。わーい。

御存知の通り上野公園は毎週末ヤバイことになってますが、コイキングだらけのおかげでギャラドスを取れました。


さて、ウチは非プログラマもテスターとして戦力化しているので、Selenium IDEベースの環境を構築しています。

ということは当然ながら、Selenium-RCでCIを回すことになります。


先日Firefox48がリリースされたのでアップデートしてみたところ、テストが全く動かなくなりました。 調べてみたところ、アドオンの署名チェックが強制されるようになったことが原因*1のようです。 このスレッドが参考になります。

http://groups.google.com/forum/#!topic/selenium-users/kDsWkujq0Y4

結論→アドオン開発向けFirefoxビルドを使え

このスレッドの結論としては、

Technically Selenium FirefoxDriver should work with Firefox 48 if you 
disable e10s (multiprocessing) and use an unbranded build, such as 
DevEdition or a custom build.  This is because 48 also ships with 
add-on signing, and FirefoxDriver isn’t signed. 
技術的には、FirefoxDriverはFirefox48のunbrandedビルド上でe10s(マルチプロセス化)を無効化すれば動くはずだ。
Firefox48から署名チェックを強制するようになったが、FirefoxDriverは署名なしであることが問題の原因である。

とのことです。


『unbrandedビルド』とは、FAQにある通り

How will the unbranded versions of Firefox work?

They work just like Firefox, with two differences: they will have a setting to disable mandatory signature checks, and they will not have the Firefox name and logo (instead using a generic name and logo). These builds are available in the en-US locale only.
unbrandedビルドって何?

Firefox通常版と全く同じように動く。ただし...

* アドオンの署名チェックが無効化されている
* 『Firefox』という名前やロゴが消されている(かもしれない)
* 英語版のみ

だそうです。


とりあえずこいつをこのへんからダウンロードして、プロファイルで

user_pref('xpinstall.signatures.required', false);
user_pref('browser.tabs.remote.autostart', false);

としておけば、現時点の最新ベータ版である49.0b1でも元通り動くようになりました。


2016-10-06追記

現時点最新版の50.0b4でも動作するので当面はこの方法で行けそうです。

*1:Selenium-RCは内部的にFirefoxのアドオンAPIを利用しています

Jenkins2にアップグレードしたら問題なく動いた

Jenkins

昨晩おもむろにyum upgradeしたらLTSのバージョンもとうとうJenkins2系へ移っていた(1.651.3 → 2.7.1)ので、思い切ってアップグレードしてみました。

…とは言え、仮想マシンのスナップショットは取ってあるのでいつでも戻せますけどね。 Jenkinsサーバに万一のことがあったら全く仕事になりません。CIは超重要。

結果、特に問題なく動いた

再起動時に「オススメプラグインをインストールしますか?」というスプラッシュが出てきました。 光の速さで「No, Thanks.」をクリック。

ぱっと見たところ、細かいデザイン崩れが起きている程度です。(ちなみに僕はこちらを使っています)

かなりプラグイン入れてるつもりなんですが、全て問題なく動いています。いやあスバラシイ。

Eclipse4.6 "Neon"の新機能一覧

f:id:takahashikzn:20160621164756p:plain

毎年恒例の新バージョンリリースです。ネオン(Neon)ということで、なんだか派手になりました。 ティザーサイトによると、6/22にリリースされるとのこと。 http://www.eclipse.org/neon/


ひとつ前のリリース、4.5 "Mars"の解説はこちら。 http://takahashikzn.root42.jp/entry/2015/05/29/214749


これまではマイルストーンビルド毎の変更を追いかける形で解説していましたが、 面倒になってきたので今回からヘルプページのWhat's Newを元にします。

http://help.eclipse.org/neon/index.jsp

2016-07-19注: 記事中の画像がリンク切れになっているようです。上記URLから遷移するとなぜかJunoのヘルプページが表示されてしまうのですが、これが原因かもしれません。

2016-07-24注: 解消しました。

Eclipse本体(ワークベンチ)の新機能


SWT provides resolution-based autoscaling: SWT側で解像度を自動判定して(画像を)自動拡大

タイトル通りです。 高解像度画像が存在しない場合、画像を自動拡大するそうです。 しかも、単に拡大するのではなくベクター画像的にキレイに拡大(proportionately)してくれるとのこと。

NeonでHigh-DPI表示した例。アイコンが拡大されています。

こちらはMars(High-DPI非対応)で表示した例。


Word wrap in text editors: テキストエディタで行折り返し

行折り返しをボタン1つで切り替えられるようになりました(デフォルトでは行折り返しOFF)。 キーボードショートカットはAlt+Shift+Yです。


Commands and shortcuts to zoom in text editors: テキストエディタ上でフォントサイズ変更のコマンドとショートカットを追加

Ctrl++Ctrl+=でズームイン(フォントサイズ増加)、Ctrl--Ctrl+-でズームアウト(フォントサイズ現象)です。

この変更はその場限りの一時的なものではなく、設定画面で変更したことと同じ意味になります。つまり永続化されます。 デフォルトフォントを指定していた場合、デフォルトフォントの設定そのものが変更されます。

この機能、個人的には余計なお世話のような気がしますが…Ctrl++なんて簡単なキーバインドなので、暴発を招くだけかと。 そういうわけで、僕はキーバインドから早速解除させていただきました。 


Pinch to zoom in text editors: ピンチジェスチャーでズームイン

先程の永続的フォントサイズ変更とは別に、「一時的に」ズームしたい場合のピンチジェスチャーが使用できるようになりました。 解除するジェスチャーは「二本指で45度回転させる」だそうです。またはエディタを単に閉じて開く。

ちなみに、現在のところLinuxはサポート外だそうです。(SWTの基礎となるGTKがマルチタッチに対応してない為)


Automatic Save of dirty editors: 編集中エディタの自動セーブ

2時間分の作業がExcelのクラッシュにより失われるという悲しい事件が後を絶ちませんが、みなさんCtrl+S押してますか? 僕は手が止まるたびに押すのがクセになってます。(およそ30秒に1回)

そういう悲しい事件を少しでも減らすため、とうとうEclipseにも自動セーブが搭載されました。(デフォルトでは無効) 無操作の場合に一定時間経つと自動セーブされます。


Search in binary files: バイナリファイル検索

組み込み屋さんが使うんでしょうかね?バイナリファイル検索が搭載されました。 検索ワードに16進を打ち込むとバイナリ検索になるようです。

ただしバイナリ検索はデフォルトでOFFになっており(上画像の"Search In"の"Binary files")、以下のいずれかのファイルのみを対象とするようです。

  • テキストファイル
  • ファイルタイプ不明かつnull文字(U+0000)を含まない


Terminate and Relaunch from history: 履歴からのアプリケーション終了→再起動

履歴からEclipseからアプリケーション(TomcatJUnit、etc...)を起動する際、前回のプロセスを終了してから起動することができるようになりました。 これにより二重起動を防ぐことができます。

デフォルトではこの機能は無効になっていますが、Shiftを押しながら履歴から起動することによっても利用できるようです。

オマケ:Ctrlを押しながら起動履歴を押すとアプリケーション起動設定ダイアログを開けます。(知らなかった…ちょっと便利)


Directly start previously selected workspaces in the launcher: 最近使ったワークスーペースを直接選んで起動

Eclipse起動時にワークスペースを選択する際、最近使ったものをクリックして選べるようになりました。

パスは省略されていますが、マウスを当てるとフルパスが表示されます。 右クリックから消すこともできます。


Workspace name shown in window title by default: ワークスペース名をタイトルバーに表示

ワークスペース情報がタイトルバーに表示されるようになりました。 デフォルトではワークスペースディレクトリパスが表示されます。


Quick Access Improvement: クイックアクセスの改善

Ctrl+3のクイックアクセスで表示される候補をフィルタできるようになりました。 例えば、ビューに関係する操作だけに絞りたい場合はViews: を入力します。


Hide the window toolbars: ツールバーを隠す

クイックアクセスからツールバーの表示非表示をトグルできるようになりました。

ちなみに僕はツールバーを一切使わないので、絶対にツールバーを表示しないようにするプラグインを自作しました。 これがないと、Eclipseがクラッシュ後に再起動するとツールバーが表示される(時がある)んですよね。。


Perspective names hidden by default: パースペクティブ名は非表示に

ツールバー右上に表示されるパースペクティブ一覧ですが、名称が非表示になりました。 右クリックすれば名称が表示されます。

Neon:

Mars:


Full Screen: フルスクリーン

WindowsLinuxでもフルスクリーンがサポートされました。Webブラウザの標準キーバインドと同じくCtrl+F11で有効にできます。

OSX上ではこれまで同様にCtrl + Cmd + Fでフルスクリーンです。


Preference setting to disable the Eclipse styling (CSS base theming): CSSベースのテーマ機能をOFFにする設定

CSSベースのテーマ機能がEclipse4.2から導入されていますが、この機能をOFFにできるようになりました。 OFFにすると、Eclipseが軽くなる(かもしれない)そうです。


Selecting the text editor for unaccosiated file types: 未関連付けファイルタイプをテキストエディタで開く

未関連付けのファイルタイプを開くときのエディタ選択ルールを指定できるようになりました。


"date" template variable with custom format: ${date}変数が展開される時のフォーマットを指定可能に

コードテンプレート等を作るときに使用できるEclipseの変数のうち、${date}が展開される時のフォーマットを指定できるようになりました。


"word_selection" and "line_selection" template variable with default value: ${word_selection}と${line_selection}変数がデフォルト値を持つように

${word_selection}と${line_selection}変数にデフォルト値を指定できるようになりました。何もテキストを選択していない時に適用されます。


Import projects: プロジェクトインポートのウィザード機能

プロジェクトをEclipseワークスペースにインポートする際にウィザードを使えるようになりました。 これにより、ディレクトリまたはアーカイブからインポートするときに同一のUIで操作できます。 また、プロジェクト自動設定の精度も向上しています。

なお、プロジェクト自動設定のロジックは自作することもできるそうです。 (新たに追加された拡張ポイント org.eclipse.ui.ide.projectConfigurator で指定)


"New..." button for creating Working Sets

(特筆すべきものではないので略)


New Welcome theme 'Solstice': 新しいウェルカムテーマ「Solstice」

こんな感じになりました。


Welcome Launchbar restored: ウェルカムページのランチャーが復活

Eclipse3.xの頃にそういうものがあったらしく、それが復活しました。


Improvements in the Mac default theme: Macのデフォルトテーマ改善

OSXの他のアプリケーションと見た目が合うように調整されました。

旧:

新:


Link widget background color can be styled via CSS

リンク要素の背景がCSSでスタイル可能になりました。


Themed scroll bar enabled for editors in dark theme: ダークテーマでスクロールバーのスタイル設定が可能に

Marsまではダークテーマでダークにならない部分があって残念な感じでしたが、とうとう完全なダークテーマが達成されました。

OSネイティブのスクロールバーを廃止してSWTで自作したようです。 -Dswt.enable.themedScrollBar=falseとすればOFFにもできます。(eclipse.iniで指定)

残念ながらこの自作スクロールバー、現時点では以下の問題が残っているようです。

  • 全てのコンポーネントへ適用済みではなく、ネイティブのスクロールバーが残ることがある(例:ツリー、テーブル)
  • これまではスクロールバー非表示だった箇所でスクロールバーが表示されてしまうことがある

これらはEclipse4.7で解消する予定です。


Improved menu support on GTK3

(重要ではないので略)


Tweaking SWT's auto-scaling: SWTのHi-DPI自動スケーリングを微調整

今回のリリースで追加された自動スケーリングですが、微調整ができます。

まずGTKの場合、GTK_DPI_SCALE=1.5のようにすることで、拡大率が150%になります。(GTKの機能)

GTKおよびWindowsの場合、以下のように指定することが可能です。

  • -Dswt.autoScale=false →自動スケーリングOFF、ただしOSXでは未対応
  • -Dswt.autoScale=integer →現在の解像度に応じて100の倍数%でスケーリング(200%, 300%...)、これがデフォルト
  • -Dswt.autoScale=quarter →現在の解像度に応じて25の倍数%でスケーリング(125%, 150%...)
  • -Dswt.autoScale=exact →現在の解像度に応じて1%刻みでスケーリング
  • -Dswt.autoScale=<value>→スケーリングを指定

加えて、スケーリング方式を指定することができます。

  • -Dswt.autoScale.method=nearest最近傍補間、ギザギザした感じ、これがデフォルト
  • -Dswt.autoScale.method=smooth →スムース化、ボケた感じ、GTKのみ可


Advanced capabilities preferences are now displayed in place (no extra dialog)

(重要ではないので略)


Eclipse workspace launcher now fully functional on Wayland: EclipseランチャーがWaylandをサポート

WaylandとはX.orgの代替となるディスプレイサーバを開発することを目標としたプロジェクトです。

そのディスプレイ環境でEclipseのランチャーが起動するようになりました。


Solaris builds are now 64-bit only: Solaris向けビルドは64bitのみ

32bitソラリス上でEclipseを使う人はいないので廃止されました。


Ant 1.9.6

Eclipseの標準はAnt 1.9.6になりました。


Java9 previews: Java9プレビューに関して

そのままではJava9で起動出来ず、

-addmods java.se.ee

が必要です。

JDTの新機能

Javaエディタ


Content Assist highlights matched characters: コンテンツ・アシストでマッチ文字列がハイライトされるように

コンテンツ・アシストのポップアップで、マッチする文字列がハイライトされて判別しやすくなりました。


Substring code completion: コンテンツ・アシストが文字列の一部から補完できるように

部分文字列のマッチングで補完が効くようになりました。かなり便利。 ウザい場合は設定でOFFにもできます。


ifNotNull/ifNull templates: if(xyz == null) { ... } のようなテンプレートが追加

Optionalの登場により登場頻度が減ったお決まりのコードですが、テンプレートに追加されました。


Rename options: 識別子名変更のリファクタリングにおけるオプション指定

識別子名変更のリファクタリングで、optionsというリンクが表示されるようになりました。これを押すと詳細なオプション指定が可能なダイアログが表示されます。

実はこのダイアログ、リンクを押すまでもなくCtrl+Alt+Rをもう一度押せば表示されます。(そして、この機能自体はEclipse3.xの時代からあったはず)

インプレースモード(ダイアログを表示しないモード)は設定でOFFにすることもできます。


Javaエディタ(その他)


Clean up to remove redundant type arguments: コードクリーンアップで、冗長な型引数を除去できるように

タイトル通り。


Create new fields from method parameters: メソッド引数からフィールド宣言を生成

コード補完の一種として、メソッド引数からフィールド宣言を生成できるようになりました。

コンストラクタやセッターの引数からインスタンス変数を生成するというケースが主になるでしょう。


Search on multi-selections: 特定の要素に限定した検索

特定のクラスやメソッド等に限定して検索できるようになりました。

検索結果で表示された要素を選択して、更に検索することもできます。


Ctrl+Shift+V invokes Open from Clipboard: Ctrl+Shift+Vで「クリップボードから開く」

タイトル通り。


Adding meta-annotations while creating a new Java annotation type: アノテーションクラスを作る際にメタアノテーションを追加

アノテーションをウイザードで作成する際、お決まりの@Retention等を予め指定できるようになりました。


Grouping of referenced libraries in Project Explorer: 参照ライブラリをグルーピング

プロジェクトエクスプローラーで、参照ライブラリをツリーノード「参照ライブラリ」にぶら下げられるようになりました。


Hiding empty library containers in Project Explorer: 空のノードを非表示に

プロジェクトエクスプローラーで、空のノードを非表示にできるようになりました。


Javaコンパイラ


New problem severity level to indicate information: コンパイラの新しい警告レベル"Info"

これまではIgnore, Warning, Errorの3つの警告レベルでしたが、Infoが追加されました。

実際はこのように表示されます。


Quick fix to configure problem severity: クイックフィックスでコンパイラ警告レベルを変更可能に

頻繁に使うことはありませんが、クイックフィックスからコンパイラの警告レベル変更画面を開くことができるようになりました。


Null解析


Multiple sets of null annotations: Null解析のためのアノテーションが複数指定可能に

アノテーションベースのNull解析は、まずアノテーションを付けるところから始まります。 しかし現時点でNon-nullを表すアノテーションは統一されていません。Java標準APIだけでも

  • javax.validation.NotNull
  • javax.annotation.NonNull

と2つあります。EclipseがNull解析に力を入れ始めた頃は

  • org.eclipse.jdt.annotation.NonNull

なんてのが標準でした。

何か一つに統一されれば良いのですがそう簡単には行かないよね、というわけで。 Null解析用のアノテーションを複数指定できるようになりました。

複数指定するアノテーションには「プライマリ」と「セカンダリ」の2種類が存在します。

「セカンダリ」はコンテンツアシスト時に、本当にそのアノテーションクラスがビルドパス上に存在するか否かの検査を行わないそうです。(負荷軽減のため)

まあ、実用上はそんなに差がないと思います。


Improved null analysis with generics: ジェネリクスのNull解析の改善

ジェネリクスにおけるNull解析が改善したそうです。

改善点は次の通り。

  1. 非束縛型引数を厳し目に解析
  2. Null解析に対応していない外部ライブラリにおける非束縛型引数の扱いを改善
  3. Nullチェック用の便利クラスorg.eclipse.jdt.annotation.Checksを追加
非束縛型引数を厳し目に解析

まず一つ目ですが、非束縛型引数とは

class Foo<T> {
    T val;
}

におけるTのように、NullableとNonNullのどちらとも指定されていない型引数のことです。

この場合、非束縛型引数はNonNullであることが確定しないので警告を発するようになりました。(デフォルトはOFF)

非束縛型引数を持つクラスの作者は、Null解析の完全性を保つために以下の規約を守る必要があります。

  • TがNonNullであることを意図しているならば、インスタンス化時にNonNullな型引数を渡す必要があるし、具体的な値としてnullが来たらエラーとしなければならない
  • TがNullableであることを意図しているならば、渡された参照を使用する前にnullか否かを確認をしなければならない
Null解析に対応していない外部ライブラリにおける非束縛型引数の扱いを改善

次に二つ目ですが、例えば以下の様なクラスを考えてみます。

ListとMapのどちらもJDKのクラスで、Null解析用のアノテーションは付いていません。 従って宣言時の型引数を信じるしかありません。

List<@NonNull String> list;

// 値はNonNullということで確定でよいが、そもそもキーがあることを保証する方法がない
Map<Integer, @NonNull String> map;

@NonNull String test(int i) {
    if (i >= 0)
        // 値はNonNullなので、この式の評価結果は
        // 『NonNullなString』と『IndexOutOfBoundsException』のいずれかであることが確定
        return list.get(i);
    else
        // キーに対応する値があればNonNullなのだが、そもそもキーがあることが未保証
        // キーがなくても例外がスローされるわけではなく単にnullになる
        return map.get(i);
}

Neonでは、このようなケースで警告を発するようになりました。

この問題を解消するには、Map#get(Object)がNonNullであると指定するしか方法はありません。 そのためには、Eclipse4.5で追加された機能『Nullアノテーションの外部化』を使うことが出来ます。

詳しくは過去記事の「External annotations: Null解析用アノテーションをJarの外部から指定」を参照して下さい。


Nullチェック用の便利クラスorg.eclipse.jdt.annotation.Checksを追加

最後に三つ目ですが、Objects.requireNonNullのような便利メソッドの派生系をたくさん持っているクラスが追加されたというだけの話です。

Javadocこちら


Quick Fix to add @NonNull to local variable: クイックフィックスでローカル変数へ@NonNullアノテーションを付ける

Null解析は非常に複雑なため完璧ではありません。時にはよくわからない理由で警告を出すことがあります。

このようなケースでサクッと警告を消すために、クイックフィックスを使えるようになりました。

ただしこれで解決しないこともあり、コンパイラが次の警告を出すこともあります。


Javaコードフォーマッタ


New options in code formatter: コードフォーマッタの新オプション

コードフォーマッタにオプションが追加されました。

(1) forループ宣言部の行折り返し制御。

(2) 桁揃えのグルーピング。空白行で区切ることでグループを区別します。

(3) enumの値に付与するアノテーションを一つ毎に改行するか否か。

(4) 括弧内の改行でユーザー指定を尊重するか否か。これまでは行コメント//を末尾につけることで、行がフォーマッタにより結合されることを防いでいたのですが、そんな小細工をしなくても良くなりました。

(5) 型引数宣言の行折り返し制御。

(6) 代入演算子三項演算子の前後における行折り返し制御。


Javaデバッガ


Installing breakpoints from unrelated projects: 無関係のプロジェクトからブレークポイントをインポートしないようにする

複数のプロジェクトで、バージョンの異なる同名クラスを使うことがあります。 その場合、ブレークポイントの位置はそれぞれ異なることになります。

Neonでは、依存関係を見てバージョン毎に適切なブレークポイントを切り替えることができるようになっていますが、 この動作が邪魔な場合は設定でOFFにすることが出来ます。


JUnit


Enabled assertions for new JUnit launch configurations: JUnit起動時にデフォルトでassertは有効に

JUnit実行時にassertを有効にするか否かのオプションはありましたが、デフォルトはOFFでした。 NeonからはデフォルトONになります。

最後に

Eclipseの新機能解説を書くのはこれで6回目です。(Eclipse3.7~Eclipse4.6)

毎度毎度、「疲れるからもう今年で最後にしよう」と思うのですが... 今年のこれも、書くのに8時間は使ってますし。でも謎の義務感でやってしまいます。

「玄人はIntelliJEclipseが許されるのは小学生まで」などとディスられることが多いEclipseですが、 僕はまるでかつてのEmacsのように(過去5年ほどEmacs漬けの生活を送っていたことがある)、体に染み付いてしまったので今後も使い続けることでしょう。

では皆さま、楽しいEclipse生活を!!