makotan _at_ gmail dot com

Smalltalk, Clojure, Lispと並んでるのを見てふと・・・

Smalltalkは使ったこと無いけど、ClojureLISPは良い言語だと思ってる(という前提で)
言語とVMにある程度のエコシステムとお金を沢山出す人の中核部分にどれくらい深入りしてるかがその言語の寿命を決定すると思ってて
その点で言えば、COBOL、C、Javaの優位性は圧倒的だなぁ〜と
その次にPython,RubyとかVB(&.NET)とJavascriptが来て・・・・
逆に言えばそれ以外の言語とVMは今のところ結構辛い立場にありそうな気がするなと(という流れがあるなという感想)


お金を出す人が居ればそこに群がる人も出てくるので、能力とか言語の良さ云々とかとは別のところで人が集まる
結果としてその言語とVMでは生産性10倍とかそういうのが当たり前の世界になる
逆にそこまでお金を無駄遣い出しても惜しくないほどの所にその言語かVMが入り込んでるって証拠じゃ無いかと


ということは、お金が集まる言語には色んな人が集まって巨大なエコシステムが構築される
お金がそれほど集まらない言語には少数精鋭が集まって小さいながらも良質なエコシステムが構築される
という結構自然な流れがある気がする


巨大な物は小さい物に駆逐されていくイノベーションのジレンマの通りだとすると、
大量の人とお金が集まって巨大なエコシステムを持てば持つほど、次のステップでは良質なエコシステムを持ってる所に人とお金が集まるような流れになる
ただし、次の所に移る前に圧倒的な優位性を獲得した言語とVMは、大きすぎて潰せないのでそれを維持するためのお金も人も集まるので安定的継続(成長は無いとして)が見込める
小さいエコシステムのまま沢山のお金と人が集まらず次のエコシステムへ流れる事もあるのでゆっくり消滅の流れがありそう


ということで、結論としてClojureは良い言語だと思います。
そんなこと言いつつ、Elixirが後々流行りそうだと思ってます。
他の言語は・・・今後のエコシステム次第かなぁ〜
Rustはだいぶ期待してる
JSはさっさと他のに変わって欲しい
LISP(系列の言語)はある種のエンジニアが手を出すべき言語ような気はしてる
そんな感じ

エラーを一級市民にしたいなぁ〜とふと思ってた

ここ数年そんなことを考えてた
例外で飛ばすのも別に良いんだけど、例外が本来例外では無いことにも多用されすぎてるのが嫌だなぁ〜って某ライブラリ(?)使ってて思ってた
そこで仕様としてのエラーはプログラムの一級市民(値)として扱いたいなと。(VMレベルは例外で良いと思ってる)
そろそろそういうのを普通のプログラム言語も普通にサポートして欲しい
あの言語とかあれ使えば書けるのは判ってるんだけど、個人的に好きじゃ無い言語とかまだ安定してる気がしないライブラリとかに全力で依存したくないなぁ〜って考え中
ってことで相変わらず言語探しの旅に出てる今日この頃

といいつつ、Javaで書くときは普通に例外使ってますw

‎REST APIでロールバック的な処理を考えてみた

REST APIで困るのが複数のAPIを呼び出したときのロールバック処理
何がどう困るかというと、API-1,API-2,API-3って更新処理をするAPIがあって、API-3でエラーが出たときにAPI-1とAPI-2のロールバック処理は行えない!

これまではどうやってたか

API-1とAPI-2の呼び出し前の状態を保持して、API-3がエラーになるとAPI-1とAPI-2を呼び出し前に更新する
この問題は、呼び出し前の状態を意識的に保持する必要があるのと、最初の更新方法(追加・更新・削除)によってその逆を実行する必要がある
しかもAPI-1とAPI-2の呼び出しが両方成功しないとロールバックができたことにもならない
設計と実装に結構な負担を強いる方法なのでだいぶ辛かった(実体験)

ロールバックの実現方法

APIの呼び出し時にヘッダにTransaction-ID(以後TID)を呼び出し側が指定する
TIDが指定されてる変更系の呼び出しでは必ず呼び出し前の状態に戻せるような情報を保持する
あと、安全なロールバック手段も一緒に。
一定時間(TID指定とペアで指定することもある)過ぎたTIDに紐づく情報は破棄する(Transactionが成功したものとみなす)
呼び出し元のAPIロールバックすべき状況と判断すると、rollbackにTIDを渡して呼び出すとAPI呼び出し前の処理が無かったことになる

この方法のメリット

呼び出し元がロールバック方法をいちいち検討しなくていいので全体の設計と実装は相当楽になる
呼び出し先のAPIロールバックを任意に実装できるので適切なロールバックが可能になる
呼び出しの経路全てで同じTIDを使用すると情報をトレースできる

この方法のデメリット

呼び出し先のAPIの負担は確実に上がる(ロールバック実現についてAPI別に検討と実装が必要)
サーバの障害などでロールバックが呼び出せなかったときの対処は呼び出し元に必要
呼び出し先のAPIロールバックを受け入れてないとやっぱり面倒なまま

速くて(物理的にも)軽い開発環境つくる事を考えてみた

速いのを実現するにはやっぱり速いCPUとかでっかいメモリーとか・・・
でも軽いマシンにはそんなの入らない


で、考えた
X11とかRDPつかってVDIやれば良いんじゃ無い?


そうするとマシンは何処にあっても良くなるので・・・クラウド環境で実機無し!
って事も出来るなぁ〜と
実機を自宅に置くとしてもVPN経由すれば安全に接続したり出来るし


そしたらどんな環境でもネットワークさえあればいつでも作業出来るし
本体マシンにこだわらなくて良いのでRaspberry Pi3とキーボードとマウスがあればいける!
それ以前に手持ちの非力なマシンたちをクライアントに出来るw


そんな事を考えたのも全ては一番使ってるメインのMacがなんか不調なため
ホントどうしよw

2017年、平成29年あけましておめでとうございます。

今年もよろしくお願いします。


この頃自分より若い人たちと仕事する機会が多くてよく思うこと

  • 自分がその年代だった頃より色々知ってるなぁ〜

とはいえ、古い知識が(入手経路含めて)少なくなってるので知ってればショートカット出来るのに勿体ないと思うこともある

  • 必要情報量が増えてるきがするけど、よく追いかけてるなぁ〜

変化の速度も上がってたりするのでなかなか大変

  • 勉強会多い

参加してる人も開催してる人も多い印象
開催側はなかなか大変そうだけどw

  • ライブラリもツールが凄い便利になってるからだいぶうらやましい

Listとか当たり前にあるし、メモリー豊富だしコンパイラIDEも無料でまともなのがあるのが当たり前だもんなぁ・・・良いなぁ・・・っていつの時代と比較してるんだw

  • 技術レベルがでこぼこなのは相変わらず

圧倒的に年下でも凄い人はホント凄いと当たり前に思う
でも、こればっかりは昔もそうだったし色んな理由で仕方ない所なんだろうなぁ〜と思った


ということで、今年も(自分より若い人たちと一緒に)楽しく仕事したいです

今年ぼんやり考えてたこと

(注:結論は出てない)


たとえば、トータルで従来の5倍の工数がかかるけどバグの出る心配の無い開発手法Aがあるとする
開発プロセス的には要件定義、設計、実装で完了して即リリースしてもバグが出ない


もう一つ、トータルで従来の5分の1の工数で済むけどリリースしたら確実に問題が発生する開発手法Bがあるとする
開発プロセス的には要件定義、設計、実装、テストでリリースしたら問題発生


一見すると開発手法Bはだれも採用しない様な手法に見える
だけど、ソフトウェアの開発で問題が発生しないことはレアケースで新規にリリースすればなんらかの問題が発生するという事実もある


一般的には開発手法AとBの間のどこかを落としどころに開発手法、開発プロセスを選択しているはず
開発手法Aの考え方は未だに一部でしか聞いたことが無いので殆どの人はBに近い物を選んでるはず
開発手法Bは問題が無いことをテスト工数を掛けて問題の発生を未然に防ぐ方法をとるのが最善策になる
すなわち、開発手法Bの類似の手法をとり続ける限り複雑度に応じてテスト工数が上昇し続ける可能性すらある根本的な問題を持ってることになる


開発手法Bに開発手法Aの核になる技術を投入してテスト工数の増大を抑えたり
そもそもテスト工数の増大を直接的に押さえれる手法の開発がそろそろ必要なのかなぁ〜
そんな事を考えてた元気に過ごした2016年もあと1日と半分になりました
さて、来年はどんな1年になるのかなぁ〜o(^-^)oワクワクですね

jooby + pac4j + Auth0でログイン認証してみる

諦めてSpringにしようかと思いつつ、丸C日頑張ってみた成果


このクラスを用意する。jooby + pack4jで必要になるクラス

public class Auth0Client extends GenericOAuth20StateClient {
    public Auth0Client(final String key, // Client IDのこと
                       final String secret, // Client Secretのこと
                       final String authUrl,  // Advanced SettingsのOAuth Authorization URL
                       final String tokenUrl,  // Advanced SettingsのOAuth Token URL
                       final String profileUrl,  // Advanced SettingsのOAuth User Info URL
                       final String scope) { // 任意だけど openid email nickname こんな感じで色々取れる
        super(key, secret, authUrl, tokenUrl, profileUrl, scope);

    }

    protected String getOAuthScope() {
        return getScope(); // 何故Genericなのに上書きしない・・・
    }

    protected boolean hasOAuthGrantType() {
        return true; // GrantTypeを引数に渡す
    }
    @Override
    protected GenericOAuth20Profile extractUserProfile(String body) {
        final GenericOAuth20Profile profile = new GenericOAuth20Profile();
        if (attributesDefinition != null) {
            profile.setAttributesDefinition(attributesDefinition);
        }
        final JsonNode json = JsonHelper.getFirstNode(body);
        if (json != null) {
            profile.setId(JsonHelper.getElement(json, "user_id"));  // デフォルトはidなので変えたかっただけ
            for (final String attribute : profile.getAttributesDefinition().getPrimaryAttributes()) {
                profile.addAttribute(attribute, JsonHelper.getElement(json, attribute));
            }
        }
        return profile;
    }

}


基本的にdemoから色々取ってくるファイル群
html5.html,index.html,profile.html
あとAppからgetUserProfileもコピペ忘れずに

        get("/", () -> Results.html("index"));
        use(new Auth()
                .client("/oauth/**", conf -> {
                    return new Auth0Client(
                            conf.getString("auth.clientId")
                            , conf.getString("auth.clientSeacret")
                            , conf.getString("auth.authorizeUrl")
                            , conf.getString("auth.tokenUrl")
                            , conf.getString("auth.userinfoUrl")
                            , conf.getString("auth.scope")
                    );
                })
        );

        /** One handler for logged user. */
        Route.OneArgHandler handler = req -> {
            UserProfile profile = getUserProfile(req);

            return Results.html("profile")
                    .put("client", profile.getClass().getSimpleName().replace("Profile", ""))
                    .put("profile", profile);
        };

        get("/oauth", handler);


application.confにはこんな感じで書く

auth {
  # use default callback, require for oidc and others
  callback = "http://"${application.host}":"${application.port}${application.path}"callback"

  authorizeUrl = "https://xxxxxx.auth0.com/authorize"
  tokenUrl = "https://xxxxxxx.auth0.com/oauth/token"
  userinfoUrl = "https://xxxxxx.auth0.com/userinfo"
  clientId = "XXXXXXXXXXXXXXXXXXXXXX"
  clientSeacret = "YYYYYYYYYYYYYYYYYYYYYYYYYYYYYY"
  scope = "openid email nickname"
}


この状態で起動した後、 localhost:8080/oauth?client_name=Auth0Client を表示すると、Auth0の画面が出てくる
その場でアカウントを作ってログインしたりすると
profileページが出てきてメアドなどの情報がゲット出来る



これ、すぐ終わると思ったのに長かった・・・
本当はconfigの自動設定もできるんだけど、そこに興味ないのでやってないw