Array.prototype.reduce() の initialValueに関する勘違い

結論

  1. [ ... ].reduce((prev, curr) => ..., null) のようなことをすると、配列に要素が存在する場合、最初の prev は null になる
  2. ちゃんと MDN 読もうな

何がしたかったか

  • [ ... ].filter( ... ).reduce(...) のようなことがしたくて、filter 後の配列が空だったときの考慮として、initialValue を null にした
    • null は filter 後の配列が空だったとき にのみ利用されるものと思い込んでいた
  • 実際には filter 後の配列に要素が存在する場合、null が最初の要素として利用される
    • reduce の関数内に prev の要素にアクセスするようなコードを書いていたので、 Uncaught TypeError: Cannot read property 'hoge' of null として死んだ

まとめ

  • ちゃんと MDN 読もうな
  • 久々に自分のブログ書いた

OpenSAML 3 で AuthnRequest を 生成する

(毎日更新すると約束したな。アレは嘘だ。ごめんなさい)

概要

自前で SAML 認証用の ServiceProvider (SP) を作っている。

Java で書いてるので Shibboleth 社のOSSライブラリである OpenSAML を利用しているが、 7月末でOpenSAML 2 系が EOL になるため、v3 系に移行しようと頑張っている。

問題点

v2 系では以下のように AuthnRequest の生成を実装していた。

DefaultBootstrap.bootstrap();

IssuerBuilder issuerBuilder = new IssuerBuilder();
Issuer issuer = issuerBuilder.buildObject(SAMLConstants.SAML20_NS, "Issuer", "samlp");
issuer.setValue("http://hogehoge.hoge");

NameIDPolicyBuilder nameIdPolicyBuilder = new NameIDPolicyBuilder();
NameIDPolicy nameIdPolicy = nameIdPolicyBuilder.buildObject();
nameIdPolicy.setFormat("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified");

AuthnRequestBuilder authnRequestBuilder = new AuthnRequestBuilder();
AuthnRequest authnRequest = authnRequestBuilder.buildObject("urn:oasis:names:tc:SAML:2.0:protocol", "AuthnRequest", "samlp");
authnRequest.setIssuer(issuer);
authnRequest.setNameIDPolicy(nameIdPolicy);
authnRequest.setAssertionConsumerServiceURL("http://hogehoge.hogehoge/acs");
authnRequest.setIssueInstant(new DateTime());
authnRequest.setProtocolBinding("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST");
authnRequest.setVersion(SAMLVersion.VERSION_20);
authnRequest.setID("ctestid890123456789012345678901234567890");

Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(authnRequest);
Element authDOM = marshaller.marshall(authnRequest);

DOMImplementationLS domImplLS = (DOMImplementationLS) authDOM.getOwnerDocument().getImplementation();
LSSerializer serializer = domImplLS.createLSSerializer();
serializer.getDomConfig().setParameter("xml-declaration", false);

String messageXml = serializer.writeToString(authDOM);

Deflater deflater = new Deflater(Deflater.DEFLATED, true);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DeflaterOutputStream deflaterOutputStream =
new DeflaterOutputStream(byteArrayOutputStream, deflater);
deflaterOutputStream.write(messageXml.getBytes());
deflaterOutputStream.close();

String samlRequest = new String(Base64.encodeBase64(byteArrayOutputStream.toByteArray(), false));
samlRequest = URLEncoder.encode(samlRequest, "UTF-8");

ひとまずこのソースのまま OpenSAML 2.6.4 から OpenSAML 3.1.1 に置き換えたところ、

  • DefaultBootstrap.bootstrap();
  • Marshaller marshaller = Configuration.getMarshallerFactory().getMarshaller(authnRequest);

の2箇所でエラーが出てしまった。

解決策

DefaultBootstrap.bootstrap();

v2 系では DefaultBootstrap.bootstrap(); のようにライブラリの初期化を行っていたが、v3 系ではそれとは異なり、 InitializationService.initialize(); のように初期化を行うようだ。

Initialization and Configuration - OpenSAML 3 - Confluence

Various components of the OpenSAML library must be initialized before they can be used. Since the library is in itself modular, the initialization process takes a modular approach use the Java Service API.
The 2 primary components with which to be familiar are:

  • org.opensaml.core.config.Initializer
  • org.opensaml.core.config.InitializationService

Configuration.getMarshallerFactory()〜

Configuration クラスが存在しないため、エラーが出ていた。

上記の DefaultBootstrap のこともあり、どうも初期化の扱いがいろいろ変わっているようで、インスタンスをどこから取得すればいいのかなーと調べていたところ、Shibboleth 社の Developers Forum で以下のトピックを発見した。

shibboleth.1660669.n2.nabble.com

トピック内でやりとりされている通り、InitializationService#initialize() で初期化された各種インスタンスXMLObjectProviderRegistrySupport クラスから直接取得することができるようで、XMLObjectProviderRegistrySupport#getMarshallerFactory() を利用することで無事取得することができた。

(単にMarshallerFactory クラスを new してもからっぽのインスタンスができるだけ、だそうです。)
(また Unmarshaller の場合も同様のようです。)

結論

上記をふまえ、以下のように実装することで解決。

IssuerBuilder issuerBuilder = new IssuerBuilder();
Issuer issuer = issuerBuilder.buildObject(SAMLConstants.SAML20_NS, "Issuer", "samlp");
issuer.setValue("http://hogehoge.hoge");

NameIDPolicyBuilder nameIdPolicyBuilder = new NameIDPolicyBuilder();
NameIDPolicy nameIdPolicy = nameIdPolicyBuilder.buildObject();
nameIdPolicy.setFormat("urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified");

AuthnRequestBuilder authnRequestBuilder = new AuthnRequestBuilder();
AuthnRequest authnRequest = authnRequestBuilder.buildObject("urn:oasis:names:tc:SAML:2.0:protocol", "AuthnRequest", "samlp");
authnRequest.setIssuer(issuer);
authnRequest.setNameIDPolicy(nameIdPolicy);
authnRequest.setAssertionConsumerServiceURL("http://hogehoge.hogehoge/acs");
authnRequest.setIssueInstant(new DateTime());
authnRequest.setProtocolBinding("urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST");
authnRequest.setVersion(SAMLVersion.VERSION_20);
authnRequest.setID("ctestid890123456789012345678901234567890");

MarshallerFactory marshallerFactory = XMLObjectProviderRegistrySupport.getMarshallerFactory();
Marshaller marshaller = marshallerFactory.getMarshaller(authnRequest);
Element authDOM = marshaller.marshall(authnRequest);

DOMImplementationLS domImplLS = (DOMImplementationLS) authDOM.getOwnerDocument().getImplementation();
LSSerializer serializer = domImplLS.createLSSerializer();
serializer.getDomConfig().setParameter("xml-declaration", false);

String messageXml = serializer.writeToString(authDOM);

Deflater deflater = new Deflater(Deflater.DEFLATED, true);
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
DeflaterOutputStream deflaterOutputStream =
new DeflaterOutputStream(byteArrayOutputStream, deflater);
deflaterOutputStream.write(messageXml.getBytes());
deflaterOutputStream.close();

String samlRequest = new String(Base64.encodeBase64(byteArrayOutputStream.toByteArray(), false));
samlRequest = URLEncoder.encode(samlRequest, "UTF-8");

本日、勤めている会社を退職致しました

まさか自分が退職エントリーを書くことになるとは思ってもいませんでした…!

というわけで、本日8/31(月)を持って、現在勤めている会社を退職致しました。
理由は色々ありますが、

 ■よりエンジニアとしてチャレンジングな環境で働きたい

ということが一番の理由かと思います。

今まで

現職は良くも悪くも受託開発、ウォーターフローがメインでして、
言われたものを言われた通りに作ることはでき、
そういった意味での成長はできるかもしれませんが、
開発に関する考え方を深めたり、
新しい技術に挑戦したり、
学んだスキルをどこかで活かしたり、
そういったことがあまり望めない環境でした。
ここ1年ほどでいろんな勉強会や趣味のプログラミングを経験し、
どうせならそれを自分の仕事にも直接的に活かせるような職場で働きたいと、
自然と思うようになりました。

それこそ最初は「あー職場に馴染めないなー」とか「もっと面白いことしたいなー」とか、
かなり漠然としていて、かつ自己中心的な理由で他の会社に興味を持っていたことも事実ですが、
きちんと自分と向き合って、いろんなことに興味を持てるようになって、
自分が何を望んでいるか、どんな仕事や開発があって何がしたいのか、
いろんなことを考えるきっかけにもなったと思ってます。

これから

明日から働き始める会社では、主に自社サービスのサーバーサイド(Java)の部分に携わることになります。
ただメインはスマートフォンアプリとのやりとりの部分でもありますので、
できればSwiftなどを勉強しながら、そちらにも携われたらいいなぁと密かに思っています。
今まで学んできたことを活かしながら、新しい分野に関してもどんどん勉強できたらいいなと思います。
より積極的に勉強会や日々の開発を進めていきたいです。

最後に

今までお世話になった先輩方には感謝してもしたりないです。
本当にお世話になりました。
また、勉強会等でご一緒させていただいた方々にもスキル面だけでなく、
いろんな考え方を学ばせていただいたと思っております。
ありがとうございました。
これからも頑張りますので、
むしろこれからまた頑張りどきだと思っておりますので、
何卒よろしくお願い致します。
仲良くして下さい!


(こういう退職エントリではほしい物リストを晒す記事をよく見かけるのですが今回は自重しときます)
(というかブログも最近全然書いてないし勉強会にも参加出来てなかったし自分の趣味のプログラミングも全然進んでなかったのでぼちぼち再開していきます)

DB勉強会に参加した話

こちらの勉強会に参加してきました。


すろっくとゆかいななかまたちの技術系勉強会.vol1 Theme:DB - connpass


今回はDBに関する勉強会ということで、
業務ではよく使ってるけどきちんと勉強したことってあんまりなかったなと思い、参加してみました。

会場はココ。(秋葉原ルノアール
f:id:iidaapp:20150305211231j:plain

会議室貸してくれるの知らなかった〜。


【勉強会概要】
・最初はDB勉強会ってことだったけど、
 定期的に技術キャッチアップ的に開催したいとのこと。

・土日中心?


どちらもありがたい!
自分から発信するのはまだちょっと自信ないけど、
みんなで集まって勉強しよう!っていうスタンスは非常にありがたい。
僕もいつかこういう場で発言してみたい。


【内容まとめ】

RDBとNoSQLの基礎

主催者であるsrockstyleさんのLT。
NoSQLってなにそれおいしいの?状態だったので興味津々でした。


RDBのおさらいと使いドコロ

そもそもRDBってなに?
関係データベース (Relational Data Base)

・2つのテーブルは別々のデータだけど、
 ひとつの情報で連結して情報抽出出来るモデル。

・関連モデルで使うのが基本。


■ NoSQLの基礎

NoSQLNot Only SQL

RDB以外のDBプロダクト全部
・3つの型に分類される

① キーバリュー型 - Redis、Memcached
② ソート済みカラム型 - Cassandra、HBase
③ ドキュメント指向型 - MongoDB、CouchDB


■ 列指向DBとは違う
- Redshift、BigQuery

列指向DBってなに?
→列のデータをひとまとまりにして、
 取り出すときに効率的であるように設計されたDB

つまり、でっかいデータをガバっと持ってくるのに適している。


■ Key-Value Storeのポイント

・keyを元に検索するため、問い合わせがシンプル
・更新が細かい単位で不可分(atomic)に行われる
(補足:トランザクションはアトミックを行うための仕組み)
・整合性がわりと緩めなので、データが厳密すぎる場面は向いてない
(データの複製とかは向いてない)


■ NoSQLの使いドコロ

・出し入れが高速なので、パッと入れてパッと出すとき
・大容量のデータを保存するとき

例外:ドキュメント指向DB

ドキュメント指向DBとは?
→「ドキュメント」と呼ばれる構造的データを
 JSONライクな形式で表現し、
 そのドキュメントの集合を「コレクション」として管理する。


・NoSQLはRDBをサポートするためのもの
・適材適所で使う
・データはRDB、キャッシュはNoSQL


■ まとめ
RDBはテーブル同士を関連する形で使う
・NoSQLはRDBをサポートする形で使う
・NoSQLをRDBのように扱わないように!


・怖くない象さん

kamichiduさんのTL。
PostgreSQLは怖くないよって内容。
スライドにところどころ出てきたポスグレのマスコットが怖すぎた…

PostgreSQLとは
OSS?なDBMS

OSSってなに?
オープンソースソフトウェア (Open Source Software)


PostgreSQLの特徴

・どぎつくない方言
→基本的にどのSQLにも方言はあるそう。
 その中でも素のSQLに近い構文で書ける。

・つまり
→素直な可愛い子
→手軽で単純な実行性能も高い


■ そもそもSQLとは

DBMSに対する問い合わせ言語
・ISO規格

ISO規格ってなに?
国際標準化機構International Organization for Standardization)
 が制定する国際規格。


SQLには3つの種類がある

DDL(Data Definition Language:データ定義言語)
→create、alter、drop

DML(Data Manipulation Language:データ操作言語)
→select、insert、update、delete

・DCL(Data Control Language:データ制御言語)
commit, rollback


■ ISO規格は後付で生まれた。

それまで、DBMSの各ベンダが独自拡張していた
→それが方言

ポータビリティ
→どれだけISO規格に準拠しているか


■ 主要DBMSのISO規格への準拠

・どんどん準拠していってる
・方言が問題になることはあまりない
OracleですらSQL2003あたりに準拠している
(10gあたりから)
DBMSのドキュメントに規格にどれだけ準拠しているか書いてる


■ ポータビリティ

・同じSQLPostgreSQLでもMySQLでも動くかどうか
・複雑なSQLになるほどポータビリティは下がりやすい
・完璧にするのは現実的には不可能に近い
・ライブラリに任せるのも手
 perlSQL;;Maker、SQL::abstract
 Java→JOOQ、Querydsl
・ORMを使うのも手
(速度の面で劣る)


MySQL vs PostgreSQL

クラスタリングする際の課題
MySQLには標準にある、PostgreSQLには3rd party製しかない)
レプリケーション
MySQLには標準にある、PostgreSQLにもある!)


■ あとは好み
・シンプルなものならPostgreSQL
・ただし実運用では何かとツールを導入する必要がある


■まとめ
PostgreSQLかわいい
SQLには3つの分類がある
・主に使うのはDML、DCL
・ISO規格は実用性について微妙

(ところどころに氷菓が出てきたような…うっ頭が)

・ハンズオン

すろっくさん扮するクソPMが設計したクソDBを、
いかに正規化&最適化するかっていうハンズオン。


…。



【概要】

音楽アプリ用のDBだが、
ひとつのテーブルにめちゃくちゃなカラムが存在しているため、
それを整理しつつ、綺麗なDBを目指す。

2チームに分かれて作業。
作業後にお互いの正規化についてプレゼンし、
ツッコミを入れ合う(マサカリを投げ合う)。


【考えたこと】

(メモを全く取っていなかったため、細かく思い出せず)
(次からはきちんとメモ取ります…)


【まとめ】

・情報は要素ごとにテーブルにまとめる
・idを振ってデータを関連付ける
・どのデータからでもある程度どんな情報でも引っ張ってこれるようにする
・狙い所はなにか?
→早さ、データ取得の容易さ、わかりやすさ
・そもそもアプリの要件によっても設計が変わる



お疲れ様でした。


・勉強会を通してのまとめ

RDBとNoSQLを用途によって使いわけよう
RDB:関連付いたデータをまとめる
→NoSQL:それ以外のぽんぽん出し入れするデータを入れる

PostgreSQLMySQLにもそれぞれの良さがある
→大きな機能差は無い
→導入する上で必要なものが変わる可能性があるから気をつける

正しいDB設計を学ぶには実際に触ってみるのが一番
→その際にきちんと考えて設計すること

正規化にあたってデータを正しく整理する
→目的と用途によって、テーブルの関連付けを考える

クソPMは楽だけど夜道に気をつける
→恨みを買う可能性大



以上です。

楽しかったので次回も期待!

GoogleChrome拡張機能を製作中の話

お疲れ様です、iidaです。

もう一ついま作っているものの話をします。

GoogleChrome拡張機能を製作しています


f:id:iidaapp:20150213112150p:plain


Webページ閲覧中にぴしっと付箋を貼るためのツールです。
職場の先輩に作ってと言われたので頑張ってます。
まだまだ未完成です。
ちゃんと出来上がったら正式に公開したいと思ってます。

実はきちんとJavaScript触るのも初めてなので、
いろんなことが新鮮で結構楽しいです。
これからはその辺の話題についても記事に出来ればと思います。

がんばりますー。

Twitterクライアントを製作中の話

お疲れ様です、iidaです。

かなり久しぶりなのでお話したいことはたくさんあるのですが、
まずは今の僕のメインの製作物の話です。

Twitterクライアントを製作しています


http://iidaapp.com/beartter/



今一番頑張って作ってます。
きっとツッコミどころたくさんあると思いますので、
何かあればつっこんでやってくださいorz

概要はgithubにもサイトにも記載しておりますが、
ツイートを解析してキャラクターの姿が変化するよ、というものです。

自覚している課題はたくさんあるので、
今はそれを改良してるとこです。

これの製作中にいろいろお勉強になることも多かったので、
技術的なお話は別途で記事にできればと思います。

がんばるぞー。

・余談


せっかく自分のドメイン取得したのに、
デフォルトのページすら作ってないので、
そっちもきちんと作らなきゃ…!

プロパティファイルを外に置きたい!

バッチなんかを作ってると、よく思いますよね。
ぼくもそうでした。
なので、調べてわかったことや感じたことをまとめてみます。

・「外に配置する」ということ


メリット

  • 固定値が変更になってもいちいちコンパイルせずに済む。

例えば…

APIの接続先が変更になるかもしれない!とか、
リトライの間隔を調節したい!とか、
コンパイルした後で固定値が変わる可能性があるときに便利ですね。


デメリット

  • jarファイルと扱いが別になるので、独自ルールが出来る。

例えば…

「jarファイルとpropertiesファイルは必ず一緒のディレクトリに配置しなきゃならない」
というのは、作った人以外知らないわけですよね。
プログラムエラー以外の人的エラーが起こる可能性がある、
ってことかなと思いました。

・とりあえずやってみる

僕の場合は固定値の変更をするたびに、
いちいちコンパイルしなおすっていうのが煩わしかったので、
プロパティファイルの外だしを試してみることにしました。


1.プロパティファイルの位置を認識させるには

参考記事:
JARファイルの外部に配置したプロパティファイルを読み込む - 2007-03-17 - sakihiroの日記

なるほど、jarファイルとして固める際、
カレントディレクトリをクラスパスに指定してあげれば良い、
ということですね。

私のプロジェクトではMavenを利用しているので、
Mavenでjarファイルを生成する際のクラスパスの指定方法について調べました。


2.Mavenでjarファイル外にクラスパスを設定するには
参考記事:
build - Maven - how can I add an arbitrary classpath entry to a jar? - Stack Overflow

[Class-Path]要素をmaven-jar-pluginの中に設定すれば良さそうですね。


3.実装

pom.xml

<!-- 省略 -->

<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-jar-plugin</artifactId>
  <version>2.4</version>
  <configuration>
    <archive>
      <manifest>
        <mainClass>jp.co.iida.testprogram.Main</mainClass>
        <addClasspath>true</addClasspath>
      </manifest>
      <manifestEntries>
        <Class-Path>./</Class-Path>
      </manifestEntries>
    </archive>
  </configuration>
</plugin>

<!-- 省略 -->


Main.java

public class Main
{
  public static void main( String[] args )
  {
    String key = "property.hello";
    ResourceBundle bundle = ResourceBundle.getBundle("batch"); 
    System.out.println(bundle.getString(key));
  }
}


batch.properties

property.hello = Hello, World


4.実行

実装が終わりましたので、実際にjarを生成して試してみたいと思います。


メニューバーから、
[Run(実行)] > [Run Configurations(実行構成)]を選択。
f:id:iidaapp:20141008155424p:plain


[Maven build]をダブルクリックし、必要な箇所に記入する。
f:id:iidaapp:20141008160257p:plain



[実行]ボタンを押下すれば、pom.xmlに設定されたフォルダにjarファイルが出来ていると思います!
後は生成したjarファイルとprioertiesファイルをサーバ内の同じディレクトリに配置し、
Javaコマンドから実行すると…

[root@hogehoge ~]# ls /var/tmp/test/
batch.properties  test.jar  test2.jar
[root@hogehoge ~]# cd /var/tmp/test/    
[root@hogehoge test]# java -jar testprogram.jar
Hello, World

やったーできたー!

Eclipseの場合

Eclipse上でプロジェクトをそのまま実行(デバッグ)しようとすると、以下のエラーが出てしまいました。

Exception in thread "main" java.util.MissingResourceException: Can't find bundle for base name batch, locale ja_JP
	at java.util.ResourceBundle.throwMissingResourceException(ResourceBundle.java:1427)
	at java.util.ResourceBundle.getBundleImpl(ResourceBundle.java:1250)
	at java.util.ResourceBundle.getBundle(ResourceBundle.java:705)
	at jp.co.iida.testprogram.Main.main(Main.java:14)


Eclipse上での実行時には、別途クラスパスを指定してあげないといけません。

1.実行ボタンの横の▼をクリックして、「実行の構成」を押下します。
f:id:iidaapp:20141007153832p:plain

2.「クラスパス」タブの中から「ユーザー・エントリー」を選択し、「拡張」ボタンを押下します。
f:id:iidaapp:20141007154729p:plain

3.「拡張オプション」ウインドウの「外部フォルダーを追加」を選択して、「OK」ボタンを押下します。
f:id:iidaapp:20141007155014p:plain

4.クラスパスに追加したいフォルダを選択し、「OK」ボタンを押下します。
f:id:iidaapp:20141007155402p:plain

5.選択したフォルダが追加され、実行時にクラスパスとして認識するようになります。
f:id:iidaapp:20141007155530p:plain


デバッグの場合も、「デバッグの構成」から上記と同じように設定すれば無事実行できました!
f:id:iidaapp:20141008171922p:plain

・まとめ

無事に実践までできました。

こういったプロパティファイルの外だしは、
これからも使うことがあると思うので、
忘れないでおきたいです。