MacでenthumbleのHJKL(vi) modeをKarabinerで再現

2015/03/15 追記
こちらの方法の方が簡単です!無変換キーも無効にならない!qiita.com

### 追記ここまで ###

Windowsでenthumbleを使っているのですが、無変換キーをベースとしたキーバインドに慣れきってしまっており、Macでも同じように無変換+HJKLで移動したり、無変換+SpaceでEnterにしたくなりました。

そこでKarabiner(旧KeyRemap4MacBook)を使って再現してみました。

無変換(=英数)キーがデフォルトではModifierFlagに登録されていないため、別途登録する必要があるのがミソです。private.xmlは以下の通り。
# 一部オレオレ設定が入っています。

<?xml version="1.0"?>
<root>
  <list>
    <item>
      <name>Private</name>
      <list>
        <item>
          <name>Enthumble Mock</name>
	  <list>
            <modifierdef>JIS_EISUU</modifierdef>
            <item>
                <name>change JIS_EISU to modifier key</name>
                <identifier>private.modifier.jis_eisuu</identifier>
                <autogen>__KeyToKey__ KeyCode::JIS_EISUU, KeyCode::VK_MODIFIER_JIS_EISUU</autogen>
            </item>

            <item>
              <name>EISUU + HJKL -> Left,Down,Up,Right</name>
              <identifier>remap.eisuu_hjkl_to_cursor</identifier>
              <autogen>__KeyToKey__ KeyCode::H, ModifierFlag::JIS_EISUU, KeyCode::CURSOR_LEFT</autogen>
              <autogen>__KeyToKey__ KeyCode::J, ModifierFlag::JIS_EISUU, KeyCode::CURSOR_DOWN</autogen>
              <autogen>__KeyToKey__ KeyCode::K, ModifierFlag::JIS_EISUU, KeyCode::CURSOR_UP</autogen>
              <autogen>__KeyToKey__ KeyCode::L, ModifierFlag::JIS_EISUU, KeyCode::CURSOR_RIGHT</autogen>         
            </item>

            <item>
              <name>EISUU + AE -> Home,End</name>
              <identifier>remap.eisuu_ae_to_home_end</identifier>
              <autogen>__KeyToKey__ KeyCode::A, ModifierFlag::JIS_EISUU, KeyCode::CURSOR_LEFT, ModifierFlag::COMMAND_L</autogen>
              <autogen>__KeyToKey__ KeyCode::E, ModifierFlag::JIS_EISUU, KeyCode::CURSOR_RIGHT, ModifierFlag::COMMAND_L</autogen>         
            </item>

            <item>
              <name>EISUU + UD -> PageUp,PageDown</name>
              <identifier>remap.eisuu_ud_to_page_up_down</identifier>
              <autogen>__KeyToKey__ KeyCode::U, ModifierFlag::JIS_EISUU, KeyCode::PAGEUP</autogen>
              <autogen>__KeyToKey__ KeyCode::D, ModifierFlag::JIS_EISUU, KeyCode::PAGEDOWN</autogen>         
            </item>

            <item>
              <name>EISUU + Spase -> Enter</name>
              <identifier>remap.eisuu_space_to_enter</identifier>
              <autogen>__KeyToKey__ KeyCode::SPACE, ModifierFlag::JIS_EISUU, KeyCode::ENTER</autogen>     
            </item>

            <item>
              <name>EISUU + N -> Backspace (Delete)</name>
              <identifier>remap.eisuu_n_to_backspace</identifier>
              <autogen>__KeyToKey__ KeyCode::N, ModifierFlag::JIS_EISUU, KeyCode::DELETE</autogen>     
            </item>

          </list>
        </item>
      </list>
    </item>
  </list>
</root>

HerokuにJavaアプリをデプロイしてみた。

Herokuを試してみたかったので、https://devcenter.heroku.com/articles/getting-started-with-heroku-eclipseに従ってやってみる。

EclipseにHerokuプラグインをインストールする。

Eclipseの[Help]→[Install new Software] で以下のサイトを追加する。

表示された「Heroku Eclipse Integration」にチェックを入れて[Next]を2回。表示されたライセンスをacceptして[Finish]。インストール後にEclipseが再起動される。

APIキー設定

Ecliseで[Window]>[Preferences]>[Heroku]を選択。[Email]、[Password]を入力して、[Login]ボタンを押す。[API Key]が自動的に入力される。

SSHキーの作成と設定。

eGitを使ってHerokuと接続するために必要。Eclipseで[Window]>[Preferences]>[General]>[Network Connections]>[SSH2]を選択する。[Key Management]タグを選択し、[Generate RSA Key...]を押す。[Save Private Key...]を押して、[OK]を押してファイルとして保存する。保存したファイルをPreferencesの[Heroku]にて、[Load SSH Key]を押して読み込ませ、[Add]ボタンでHerokuにSSHキーを追加する。

テンプレートからアプリを作成する。

[File]>[New]>[Other]>[Heroku]>[Create Heroku App from Template]を選択し、[Next]。テンプレートを選択し、アプリ名を設定する。アプリ名を指定しない場合、Herokuが自動的にアプリ名を設定してくれる。
テンプレートを「Spring MVC & Tomcat application」、アプリ名を「heroku-spring-mvc-sample」とした。
が、失敗。どうやらSSH系の設定が良くないらしい。

Gitリポジトリからcloneする。

ググったところどうやら1回、eGitでHerokuからcloneすると良いらしい。Heroku側にアプリケーションはできていたので、Herokuのアプリケーションの[Setting]>[Info]に記載されているgitのURLからeGitでCloneする。

Protocolなどは選ばずに、そのまま[Next]>[Next]>[Finish]。
Working Directoryを右クリックして、[Import Projects...]。[Import as general project]を選んで[Finish]。
Java EEのPespectiveに戻って、インポートされたプロジェクトを右クリックして、[Configure]>[Convert to Maven Projects]、でMavanプロジェクトとなる。そのままEclipse上で[Run as]>[Run on Server]で、ローカルTomcat上で起動できる。

動作確認

Herokuのアプリケーション管理画面のアプリケーション名の横にあるリンクか、SettingsのDomainsでドメインを確認してアクセスしてみる。こんなページが出れば成功。

「./people」にアクセスすればサンプルのアプリが確認できる。

もし、以下の画面が出てしまったら、デプロイ後に1時間以上アクセスがなくて停止してしまっている。再起動すればOK。

EclipseでHerokuの[My Heroku Applications]Viewを開いて、アプリケーションを右クリック→[Restart]を選択すれば起動する(管理画面からもできそうな気はするが...)。

以上

とりえあずサンプルは動かすことができた。gitリポジトリにコミットするだけでリリースされるのはすごいね。

「Web x Java - HTML5で進化したWeb標準を、Java技術でどう扱うのか? -」に参加してきました。

久しぶりにセミナに参加してきたのでメモ。

今のWeb標準Struts/Javaの問題(仮)

Strtusが対応していないもの。

Strutsが対応していない!
→ 独自flameworkの開発!
→ ベンダー(SIer)ロックイン&技術のガラパゴス化

# カスタムタグ作りすぎて、flameworkに沿ったJSPの書き方を
# 一から覚え直したりね...。


Web界はHTML5ガラパゴス化(ブラウザ間の差異)から脱出(標準化)を進めている。
→ サーバサイド(Java)側はどうする???

Java EE の概要と HTML 5 の取り組み

10年前はStruts+Spring+Hibernate(通称SSH)が流行りだったが今はどうか。


寺田さんがもうやめましょうと訴えたいのは以下の2つ。

  • Struts(1.x はEOL)
  • Tomcat(理由はよくわからんけどGlassFishがあるから?EEのWebプロファイルにしか対応していないから?)

TomcatはともかくStrutsは避けたいな。


2012年のEclipse ServeyでもStruts1.x, 2.xを使いたい人は殆どいなかった。


Struts+Spring+Hibernateはメンテナンスコストが大!
今後Strutsでバグが出たら、誰がその修正の影響範囲を調査・試験する??
→ その点、Java EE ならJSFCDIJPAをトータルで管理してますよー。
JBossの保守はサポートしてくれるらしい。
SAStrutsStrutsにバグ出たら治すって言ってた気がする。
# それなりに保守される上位flameworkを使っていれば平気?


Java EE 7ではWebSocketやJAX-RSが標準装備されているので、viewとmodelをサーバ側で結合せずに、別々に取得することができる(modelだけ取得可能)。


WebSocket vs JAX-RS
→WebSocketはHTTP Headerなどを送信するのがはじめの1度のみなので、パフォーマンス的にはWebSocketが有利か。


Java EE 7の良い所

  • JMSの実行が簡単になった。
  • JAX-RSがWebプロファイルに入った。
  • Java EE上でスレッドを作れるようになった。

# 作ろうと思ったことがなかった...。
# 今までは作るとAPサーバと別プロセスにスレッドができて管理しづらいらしい。

Strutsから移行する人のためのJSF基礎


なぜJSF


作れるUI

# 普通のJavaScriptからのAjaxは使えるのかな?
# その時はJAX-RSでも使えばよいのかしら。


拡張ライブラリ

拡張ライブラリ使用上の注意
「※ただし、ブラウザ最新版に限る。」

移行ポイントの詳細はスライドを参照。

  • managed-baenの定義はXMLでもアノテーションでも。
  • JSPと違ってServletに変換されない→エラー時はStackTraceにXHTMLの行数が表示。
  • 画面側でのvalidationを標準装備。

# サーバ側でのvalidationとエラー時の表示は共通化できるのかしら?

  • RequestProcessor の processXXX 系の処理は全てPhaseListenerで外から実装できる。


JSF利用時の注意

  • JSF 1.x はStrutsよりヤバイ。
  • XHTMLはタグライブラリも良いけど、そのままブラウザで見れるjsfc属性を使うのがオススメ。ただし、EE 6とEE 7で属性名が違う。
  • 独自セッション管理するな!(flameworkがセッションを使いまくります。)
  • メモリを大量に積んでおけ!Strutsより倍から10倍だ!(flameworkがセッションを使いまくります。)

JavaプログラマのためのScalaプログラミング

JavaからScalaへ ~ScalaでWeb開発はこう変わる~

Scala系は気が向いた時にでも。全く知らなかったので勉強になりました。

Maven, Seleniumを使った受け入れテストの自動化

Apache Maven 3クックブック Javaソフトウェア開発のための特選レシピ集

Apache Maven 3クックブック Javaソフトウェア開発のための特選レシピ集

上記の書籍を使ってMavenの勉強中なのだが、「2.6 受け入れテストを自動化する」で書籍の内容通りにコーディングをしてもうまく動作しなかったので、うまく動作した結果を残しておく。

環境

Mavanプロジェクトの生成

Webアプリケーションプロジェクトを生成する

$ mvn archetype:generate -DgroupId=net.kuronicle.maven -DartifactId=webappsample
 -DarchetypeArtifactId=maven-archetype-webapp

jUnitのバージョン設定

pom.xmljUnitのバージョンを4.8.2とする。

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.8.1</version>
      <scope>test</scope>
    </dependency>

Seleniumライブラリの追加

pom.xmlのdependenciesに以下を追加することで、Seleniumに必要なライブラリがインポートされる。

    <dependency>
      <groupId>org.seleniumhq.selenium</groupId>
      <artifactId>selenium-java</artifactId>
      <version>2.24.1</version>
      <scope>test</scope>
    </dependency>

テスト対象の用意

デフォルトで存在する/src/main/webapp/index.jspを利用する。

<html>
  <body>
    <h2>Hello World!</h2>
  </body>
</html>

Seleniumテストコードの作成

/src/test/java/Selenium2Example.javaを作成する。

import static org.junit.Assert.*;

import org.junit.Test;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.WebDriverWait;

public class Selenium2Example {

    @Test
    public void testHelloWorld() {
        WebDriver driver = new FirefoxDriver();
        try {
            driver.get("http://localhost:8080/webappsample/index.jsp");
            WebElement element = driver.findElement(By.tagName("h2"));
            assertEquals("Hello World!", element.getText());
        } catch (Exception e) {
            fail(e.getMessage());
        } finally {
            driver.quit();
        }
    }
}

integration-testフェーズの設定

pom.xmlにintegration-testの前後でJettyを起動・停止する設定と、integration-testで先ほど作成したSeleniumテストケースを実行する設定を追加する。

  <build>
    <finalName>webappsample</finalName>

    <plugins>
      <!-- JDK1.6でコンパイル -->
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>

      <!-- integration-testフェーズ前後にJetty起動停止 -->
      <plugin>
        <groupId>org.mortbay.jetty</groupId>
        <artifactId>maven-jetty-plugin</artifactId>
        <version>${maven-jetty-plugin.version}</version>
        <executions>
          <execution>
            <id>start-jetty</id>
            <phase>pre-integration-test</phase>
            <goals>
              <goal>run</goal>
            </goals>
            <configuration>
              <scanIntervalSeconds>0</scanIntervalSeconds>
              <daemon>true</daemon>
            </configuration>
          </execution>
          <execution>
            <id>stop-jetty</id>
            <phase>post-integration-test</phase>
            <goals>
              <goal>stop</goal>
            </goals>
            <configuration>
              <stopKey>foo</stopKey>
              <stopPort>9999</stopPort>
            </configuration>
          </execution>
        </executions>
      </plugin>

      <!-- integration-testフェーズでSeleniumテストケース(*Selenium*.java)を実行 -->
      <plugin>
        <artifactId>maven-surefire-plugin</artifactId>
        <executions>
          <execution>
            <id>selenium-test</id>
            <phase>integration-test</phase>
            <goals>
              <goal>test</goal>
            </goals>
            <configuration>
              <includes>
                <include>**/*Selenium*.java</include>
              </includes>
            </configuration>
          </execution>
        </executions>
      </plugin>
    </plugins>
  </build>

integration-testの実行

integration-testを実行する。Firefoxが自動的に立ち上がり、試験が実行される。

$ mvn integration-test

以上。
なぜ書籍のサンプルコードが動かなかったのかは結局わからず…。

レジストリをいじってキーボードレイアウトをカスタマイズ


キーボードにHappyHackingKeybordLite2を使っているんだけど、WindowsキーがFnキーを利用しないと機能しないため、面倒。
ということで半角/全角キーと左Windowsキーを入れ替えました。

  • [スタート]→[ファイル名を指定して実行]→「regedit」と入力して[OK]
  • 「HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout」を右クリック
  • [新規]-[バイナリ値]
  • 新しい値の名前を「Scancode Map」とする
  • 「Scancode Map」をダブルクリックし、以下の値を設定

00 00 00 00 00 00 00 00
03 00 00 00 5B E0 29 00
29 00 5B E0 00 00 00 00

  • 再ログイン or 再起動

設定値の詳細などは下記のサイトを参照のこと。

Windows Vista/XP/2000/NT4.0のキー配列の変更方法

ちなみに自分は、

  • 無変換キー → IMEオフ
  • 変換キー → IMEオン

に設定しているので半角/全角キーをWindowsキーにしてしまったw

Google App Engine for Java でデータストアに保存したデータからメールを送信

Google App Engine for Java で受信したメールをデータストアに保存 の続き。
データストアに保存したメールデータを利用してメールを送信するよ。

データストアから受信メールデータを取得

受信後すぐに送信するので本当はデータストアから取得する必要はないのだけどw
sentDateで並び替えて最新の1件を取得。

            // データストアから最新の1件を取得
            String query = "select from " + ReceivedMail.class.getName() + " order by sentDate desc range 0,1"; 
            List<ReceivedMail> mailList = (List<ReceivedMail>) pm.newQuery(query).execute();
            ReceivedMail storedMail = mailList.get(0);

取得した受信メールデータを送信メールに入力して送信

送信元に返信。件名はそのままで、本文には送信元、送信先、件名、本文を設定。
件名は文字コードを設定しないと文字化けした。本文は何故か平気だった。

            if(!(storedMail==null)) {
                Message sendMsg = new MimeMessage(session);
                
                // 送信元(From)の設定
                sendMsg.setFrom(new InternetAddress(ADMIN_MAIL_ADDRESS));
                
                // 送信先(To)の設定
                sendMsg.addRecipients(Message.RecipientType.TO, InternetAddress.parse(storedMail.getFrom()));
                
                // 件名(Subject)の設定
                ((MimeMessage)sendMsg).setSubject(storedMail.getSubject(), "UTF-8");
                
                // 本文(text)の設定
                StringBuffer sb = new StringBuffer();
                sb.append("From:" + storedMail.getFrom() + "\n");
                sb.append("To:" + storedMail.getTo() + "\n");
                sb.append("Sbuject:" + storedMail.getSubject() + "\n");
                sb.append("Body:\n" + storedMail.getText());
                sendMsg.setText(sb.toString());
                
                // メールの送信
                Transport.send(sendMsg);
            }

一応、メール関連の動作確認サンプルは完成?

メール受信、データストアへ保存、メール送信を一通り実行するサンプルが完成。
ひとまずこれで終了かな。

package jp.kuronicle.kuromail;

import java.io.IOException;
import java.util.List;
import java.util.Properties;

import javax.jdo.PersistenceManager;
import javax.mail.Address;
import javax.mail.BodyPart;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Multipart;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import jp.kuronicle.kuromail.datastore.ReceivedMail;

public class MailHandlerServlet extends HttpServlet {

    private static final String ADMIN_MAIL_ADDRESS = "hogehoge@fugafuga.appspotmail.com";

    public void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws IOException {

        PersistenceManager pm = PMF.get().getPersistenceManager();
        Properties prop = new Properties();
        Session session = Session.getDefaultInstance(prop, null);

        try {
            // 受信メールを取得
            MimeMessage recMsg = new MimeMessage(session, req.getInputStream());

            // 永続化用JDOに値を入力
            ReceivedMail mail = new ReceivedMail();
            mail.setSentDate(recMsg.getSentDate());
            mail.setFrom(recMsg.getFrom()[0].toString());
            mail.setTo(recMsg.getAllRecipients()[0].toString());
            mail.setSubject(recMsg.getSubject());
            mail.setText(this.getText(recMsg.getContent()));
            
            // 永続化
            pm.makePersistent(mail);
            
            // データストアから最新の1件を取得
            String query = "select from " + ReceivedMail.class.getName() + " order by sentDate desc range 0,1"; 
            List<ReceivedMail> mailList = (List<ReceivedMail>) pm.newQuery(query).execute();
            ReceivedMail storedMail = mailList.get(0);
            
            if(!(storedMail==null)) {
                Message sendMsg = new MimeMessage(session);
                
                // 送信元(From)の設定
                sendMsg.setFrom(new InternetAddress(ADMIN_MAIL_ADDRESS));
                
                // 送信先(To)の設定
                sendMsg.addRecipients(Message.RecipientType.TO, InternetAddress.parse(storedMail.getFrom()));
                
                // 件名(Subject)の設定
                ((MimeMessage)sendMsg).setSubject(storedMail.getSubject(), "UTF-8");
                
                // 本文(text)の設定
                StringBuffer sb = new StringBuffer();
                sb.append("From:" + storedMail.getFrom() + "\n");
                sb.append("To:" + storedMail.getTo() + "\n");
                sb.append("Sbuject:" + storedMail.getSubject() + "\n");
                sb.append("Body:\n" + storedMail.getText());
                sendMsg.setText(sb.toString());
                
                // メールの送信
                Transport.send(sendMsg);
            } 
        } catch (MessagingException e) {
            e.printStackTrace();
        } finally {
            pm.close();
        }
    }

    private String getText(Object content) throws IOException,
            MessagingException {
        String text = null;
        StringBuffer sb = new StringBuffer();

        if (content instanceof String) {
            sb.append((String) content);
        } else if (content instanceof Multipart) {
            Multipart mp = (Multipart) content;
            for (int i = 0; i < mp.getCount(); i++) {
                BodyPart bp = mp.getBodyPart(i);
                sb.append(getText(bp.getContent()));
            }
        }

        text = sb.toString();
        return text;
    }
}