2013年12月2日月曜日

今年の振り返り

2013年もあと一ヶ月となりましたので、今年の振り返りなどしてみようと思います。

今年の目標

「クラウドソーシャルを副業としてはじめてみる」

こんな目標を立ててました。結論から言うと達成できてません。ランサーズJob-Hubに登録こそしましたが受注案件はゼロ。よって売上もゼロ…。

でもいろいろやったよ

でも中長期目標である「地元秋田に帰る」を実現するために、色々やってきました。

秋田IT県人会に参加

秋田出身や秋田在住でITに関係する仕事、事業をやっている人たちの集まりに第一回から参加しました。
多くの方にお会いでき、また地元秋田で頑張っている方や東京で活躍されている方などが沢山いることを知ることができ、大変よい機会になりました。

あきた寺子屋に参加

秋田IT県人会のつながりで誘って頂き参加してきました。こちらでも地元を元気にしたいと起業やUターンを考えている方にお会い出来ました。同じ思いの人って結構いるもんだなと元気になりました。

転職活動

Aターンという秋田県の転職支援事業を活用し、1社採用面談を受けました。結果は最終面談まで進むも不採用。
私にとって魅力的な会社さえ見つかれば、給料のダウンは致し方がないとしても内定貰えるだろうという甘い考えが見事に打ち砕かれましたorz

妻と移住について話し合い

上記の転職が決まりかけたことで、妻と具体的な移住計画や移住後の生活などについて話し合いが出来ました。今まではふわっと同意してもらっていたものの、具体的なところ(親と同居が近居か、共働きになるのか、などなど)まで踏み込んで話せてよかったです。
双方腹をくくったところで不採用となりがっくりきてしまいましたw

サービスづくり

転職活動と並行して、副業で始めるサービスの開発をしていました。現在つめの作業中です。なかなかバグが解消せず公開できず歯がゆい日々が続いています…

まとめ

目標は未達だったものの、色々動けた一年だったかなと思います。今年蓄積したものをどうやって活用していくか、来年はもっと踏み込んだ一年にしようと思います!
ひとまず年内にサービス立ち上げまで持って行きたいですね…

2013年3月10日日曜日

Google+ Sign-in / Moment API

Google+ Platformの大きなアップデート、Google+ Sign-inが発表されました!詳細はニュース記事などに譲るとして、今回はとりあえずサインインしてAPIを呼び出すところまでやってみたのでまとめます。
こちらを参考に、ちょっと分からなかったところを補いつつ、Moment APIでApp Activityを送信するところまでやってみます。環境は、前回の記事で構築したものとなります。

準備その1:Client情報の登録

まずはGoogle+のClient IDとClient Srcretを取得します。公式ドキュメントのStep 1に従えばいいので省略します。

準備その2:必要なjarを配置

今回はMaven2で依存するjarを管理しているので、pom.xmlに設定を追加しておきます。WEB-INF/lib/にコピーも忘れずにしておきます。

  <repositories>
    <repository>
      <id>google-api-services</id>
      <url>http://google-api-client-libraries.appspot.com/mavenrepo</url>
    </repository>
  </repositories>

 ...

    <dependency>
      <groupid>com.google.api-client</groupid>
      <artifactid>google-api-client</artifactid>
      <version>1.13.2-beta</version>
    </dependency>
    <dependency>
      <groupid>com.google.api-client</groupid>
      <artifactid>google-api-client-servlet</artifactid>
      <version>1.13.0-beta</version>
    </dependency>
    <dependency>
      <groupid>com.google.api-client</groupid>
      <artifactid>google-api-client-appengine</artifactid>
      <version>1.13.2-beta</version>
    </dependency>
    <dependency>
      <groupid>com.google.apis</groupid>
      <artifactid>google-api-services-plus</artifactid>
      <version>v1-rev57-1.13.2-beta</version>
    </dependency>
    <dependency>
      <groupid>com.google.apis</groupid>
      <artifactid>google-api-services-oauth2</artifactid>
      <version>v2-rev9-1.7.2-beta</version>
    </dependency>

実装その1:CSRF対策

サインインボタンを表示するControllerを作成します。
この後説明するAccess Tokenの保存処理が不正に呼び出されないように、ランダムなパラメータを生成してセッションに保存しておきます。
同時に、Access Tokenの保存処理を呼び出すJavascriptにパラメータを渡せるように、リクエストにセットしておきます。

    @Override
    public Navigation run() throws Exception {
        String token = new BigInteger(130, new SecureRandom()).toString(32);
        sessionScope("validateToken", token);
        requestScope("validateToken", token);
        return forward("test.vm");
    }

実装その2:Javascriptライブラリの読み込み

Oauth2のAuthorization Code取得まではJavascriptでやるようなので、test.vmでGoogle+ライブラリを読み込んでおきます。
公式ドキュメントのStep 3のコピペでOKです。

実装その3:サインインボタンの設置

test.vmにサインインボタンを設置します。ここがポイントなのですが、
Moment APIを使う場合、data-requestvisibleactionsが必須になります。おそらくAPIの認可が必要ということなのでしょう。見事にハマったorz
他に指定できるパラメータはこちらに載っているのでご参照ください。

<div id="signinButton">
    <span class="g-signin"
        data-scope="https://www.googleapis.com/auth/plus.login"
        data-clientid="あなたのClient ID"
        data-redirecturi="postmessage"
        data-accesstype="offline"
        data-cookiepolicy="single_host_origin"
        data-callback="signInCallback"
        data-requestvisibleactions="http://schemas.google.com/AddActivity">
    </span>
</div>

実装その4:callback関数の実装

上記サインインボタンに設定したcallback関数を実装します。この中でAuthorization Codeを元にAccess Tokenを取得するリクエストを送信します。パラメータには、CSRF対策用のパラメータも設定します。

function signInCallback(authResult) {
    if (authResult['code']) {

        // Hide the sign-in button now that the user is authorized, for example:
        $('#signinButton').attr('style', 'display: none');

        // Send the code to the server
        jQuery.ajax({
            type: 'POST',
            url: '/api/storetoken',
            success: function(result) {
                
            },
            data: {
                code: authResult['code'],
                validateToken: '$validateToken',
            }
        });
    } else if (authResult['error']) {
        alert('Google+ Authorication Error');
    }
}

実装その5:Access Tokenの保存

JavascriptのPOSTを受けるControllerを実装します。まずはCSRF対策でパラメータチェックします。
その後、Authorization CodeとClient ID、Client Secretを使ってAccess Tokenを取得し、セッションに保存します。

    @Override
    public Navigation run() throws Exception {
        if (!validateToken()) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            Map<string string=""> data = new HashMap<string string="">();
            data.put("state", "403");
            return json(data);
        }

        String clientId = "あなたのClient ID";
        String clientSecret = "あなたのClient Secret";
        String code = asString("code");

        HttpTransport transport = new NetHttpTransport();
        JsonFactory jsonFactory = new JacksonFactory();
        GoogleTokenResponse tokenResponse = new GoogleAuthorizationCodeTokenRequest(transport, jsonFactory,
            clientId, clientSecret, code, "postmessage"
        ).execute();
        GoogleCredential credential = new GoogleCredential.Builder()
            .setJsonFactory(jsonFactory)
            .setTransport(transport)
            .setClientSecrets(clientId, clientSecret).build()
            .setFromTokenResponse(tokenResponse);
        Oauth2 oauth2 = new Oauth2.Builder(transport, jsonFactory, credential).build();
        Tokeninfo tokenInfo = oauth2.tokeninfo().setAccessToken(credential.getAccessToken()).execute();
        if (tokenInfo.containsKey("error")) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            Map<string string=""> data = new HashMap<string string="">();
            data.put("state", "403");
            return json(data);
        }
        if (!tokenInfo.getIssuedTo().equals(clientId)) {
            response.setStatus(HttpServletResponse.SC_FORBIDDEN);
            Map<string string=""> data = new HashMap<string string="">();
            data.put("state", "403");
            return json(data);
        }

        sessionScope("gPlusId", tokenInfo.getUserId());
        sessionScope("gAccessToken", credential.getAccessToken());

        Map<string string=""> data = new HashMap<string string="">();
        data.put("state", "200");
        return json(data);
    }

    protected boolean validateToken() {
        String specified = requestScope("validateToken");
        String token = sessionScope("validateToken");
        if (token.equals(specified)) {
            return true;
        }
        return false;
    }

    protected Navigation json(Object data) {
        try {
            JsonFactory jsonFactory = new JacksonFactory();
            JsonGenerator generator = jsonFactory.createJsonGenerator(response.getWriter());
            generator.serialize(data);
            generator.flush();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return null;
    }

実装その6:Moment API 呼び出し

最後に、取得したAccess Tokenを使ってMoment APIを呼び出すControllerを実装します。

    @Override
    public Navigation run() throws Exception {
        String gPlusId = sessionScope("gPlusId");
        String gAccessToken = sessionScope("gAccessToken");

        String clientId = "あなたのClient ID";
        String clientSecret = "あなたのClient Secret";
        String applicationName = "あなたのアプリ名";

        HttpTransport transport = new NetHttpTransport();
        JsonFactory jsonFactory = new JacksonFactory();
        GoogleCredential credential = new GoogleCredential.Builder()
            .setJsonFactory(jsonFactory)
            .setTransport(transport)
            .setClientSecrets(clientId, clientSecret).build()
            .setAccessToken(gAccessToken);

        Plus plus = new Plus.Builder(transport, jsonFactory, credential).setApplicationName(applicationName).build();

        Moment moment = new Moment();
        moment.setType("http://schemas.google.com/AddActivity");
        ItemScope itemScope = new ItemScope();
        itemScope.setId("target-id-1"); 
        itemScope.setType("http://schemas.google.com/AddActivity");
        itemScope.setName("The Google+ Platform");
        itemScope.setDescription("hogehoge!");
        moment.setTarget(itemScope);
        Moment momentResult = plus.moments().insert("me", "vault", moment).execute();

        MomentsFeed moments = plus.moments().list("me", "vault").execute();

        return redirect("/");
    }

自分の英語力不足なのか、公式ドキュメントからは分からなくてハマったところも多かったですが(Oauth2クラスとかTokenInfoクラスを使うのにどのjarが必要か、とか)、なんとか出来ました。
Client IDなどを設定ファイルにしたりなどリファクタリングもまだまだ必要ですが。
道具は揃ったので、これをどう使っていくか考えてみようと思ってます!

2013年3月1日金曜日

Slim3 + Velocity

Google App Engine for Javaを使いたくなり、評判の良さそうなフレームワークとしてSlim3を導入し、独断と偏見でJSPをやめてVelocityをテンプレートエンジンとして利用することにしました。
今回の内容は、その際の開発環境セットアップの備忘録となります。

開発環境

それぞれ現時点での最新版を使います。
  • Eclipse Classic 4.2.1 Mac OS X 64bit
  • Slim3 1.0.16
  • Velocity 1.6.2 + Velocity Tools 2.0

Slim3インストール

基本的にここを参考にEclipseにGoogle PluginとSlim3 Pluginをインストールします。ダウンロード先のURLがEclipseのバージョンに一致しているか確認します。今回は以下を使います。

https://dl.google.com/eclipse/plugin/4.2

http://slim3.googlecode.com/svn/updates/

Pluginのインストールが終わったら、Blankプロジェクトを作成します。Explorerで右クリック -> New -> Others -> Slim3と進めばOKです。上記Wikiに書いてあることのうち、workspaceの設定以外はPluginが勝手にやってくれます。

Velocityインストール

こちらを参考にさせて頂きました。が、いくつか補足。

まずは、http://velocity.apache.org/download.cgiからVelocity Toolsをダウンロード、解凍します。lib内にあるvelocity-1.6.2.jarとvelocity-tools-2.0.jar、それから依存関係のあるcommonsライブラリをコピーして、
先ほど作成したBlankプロジェクトのWEB-INF/lib/にペーストし、ビルドパスに追加します。

次に、src以下にVelocityViewServletを拡張したクラスを作成します。ひとまず以下のように実装しておけばOKです。

package test.hoge;

import java.util.Enumeration;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.velocity.Template;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.context.Context;
import org.apache.velocity.tools.view.VelocityViewServlet;

public class VelocityServlet extends VelocityViewServlet {

    private static final long serialVersionUID = 1L;

    @Override
    protected Template handleRequest(HttpServletRequest request,
            HttpServletResponse response, Context ctx) {
        String uri = request.getRequestURI();
        String templatePath = getTemplatePath(uri);

        Enumeration attrNames = request.getAttributeNames();
        while(attrNames.hasMoreElements()) {
            String key = (String) attrNames.nextElement();
            ctx.put(key, request.getAttribute(key));
        }

        Template template;
        try {
            template = Velocity.getTemplate(templatePath, "UTF-8");
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return template;
    }

    protected String getTemplatePath(String uri) {
        return "/WEB-INF/template" + uri;
    }
}

作成したら、このサーブレットを使用するようにweb.xmlに設定を追加します。

    <servlet>
        <servlet-name>VelocityServlet</servlet-name>
        <servlet-class>test.hoge.VelocityServlet</servlet-class>
       <init-param>
            <param-name>org.apache.velocity.toolbox</param-name>
            <param-value>/WEB-INF/velocity-tools.xml</param-value>
        </init-param>
        <init-param>
            <param-name>org.apache.velocity.properties</param-name>
            <param-value>/WEB-INF/velocity.properties</param-value>
        </init-param>
        <load-on-startup>10</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>VelocityServlet</servlet-name>
        <url-pattern>*.vm</url-pattern>
    </servlet-mapping>

これで、拡張子が.vmのものはVelocityServletを使用するようになります。velocity-tools.xmlとvelocity.propertiesはとりあえず空でいいので作成しておきます。

次に、appengine-web.xmlに以下を追加します。ついでにセッションは有効にしておきましょう。

 <sessions-enabled>true</sessions-enabled>
 <threadsafe>true</threadsafe>

    <static-files>
        <include path="**.js" />
        <include path="**.css" />
        <include path="**.png" />
        <include path="**.jpg" />
        <include path="**.jpeg" />
        <include path="**.swf" />
        <include path="**.gif" />
        <include path="**.ico" />
        <include path="**.html" />
    </static-files>
    <resource-files>
        <include path="**.jsp" />
        <include path="**.vm" />
    </resource-files>

Controller + .vmファイル作成

ここまで来たら、Antのgen-controller-without-viewでSlim3のControllerを作成します。JSPファイルの代わりに手動で.vmファイルをWEB-INF/template/に作成しましょう。.vmファイルの置き場所は拡張VelocityServletに書いてあるので、WEB-INF/の好きな場所に変えてしまってOKです。
Controllerと.vmの準備が出来たら、ローカル環境でサーバを起動してブラウザでアクセスできるか確認します。

ちなみに、Slim3 + Velocityの処理の流れは大まかに以下のようになっています。
  1. Controller#run()が呼ばれる
  2. **.vmにforward
  3. forwardで指定された拡張子が.vmなのでVelocityServletが起動
  4. pathで指定された.vmファイルがレンダリングされる
Velocityを入れるとJSPに比べればパフォーマンス劣るというのをちらっと見かけたのですが、とりあえず気にせずこの環境で色々作ってみようと思います。

2013年2月16日土曜日

meteor触ってみた

実は前々から気になっていたmeteorというWebフレームワークを、とりあえずサンプルを動かすまでやってみました。
先人に感謝しつつ、私のAirちゃんで動かしました。

セットアップ

まずはmeteorをインストールします。とはいえ、たったこれだけ。

$curl install.meteor.com | sh
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  5239  100  5239    0     0   8374      0 --:--:-- --:--:-- --:--:-- 12327
Installing Meteor to /usr/local/meteor

The install script needs to change the permissions on /usr/local so that
administrators can write to it. This may prompt for your password.

Password:
... downloading
######################################################################## 100.0%

Meteor installed! To get started fast:

  $ meteor create ~/my_cool_app
  $ cd ~/my_cool_app
  $ meteor

Or see the docs at:

  docs.meteor.com

これで/usr/local/bin/meteorコマンドが使えるようになったので早速。

$ meteor --help
Usage: meteor [--version] [--help]  []

With no arguments, 'meteor' runs the project in the current
directory in local development mode. You can run it from the root
directory of the project or from any subdirectory.

Use 'meteor create ' to create a new Meteor project.

Commands:
   run        [default] Run this project in local development mode
   create     Create a new project
   update     Upgrade to the latest version of Meteor
   add        Add a package to this project
   remove     Remove a package from this project
   list       List available packages
   bundle     Pack this project up into a tarball
   mongo      Connect to the Mongo database for the specified site
   deploy     Deploy this project to Meteor
   logs       Show logs for specified site
   reset      Reset the project state. Erases the local database.

See 'meteor help ' for details on a command.

サンプル

それではサンプルを動かしてみます。

$ meteor create --example leaderboard
leaderboard: created.

To run your new app:
   cd leaderboard
   meteor

プロジェクトを作成して、

$ cd leaderboard/
$ meteor
[[[[[ ~/workspace/test/meteor/leaderboard ]]]]]

Running on: http://localhost:3000/

これで起動します。さっそく表示されているURLにアクセスしてみます。


まあサンプルなのであんまり意味あるアプリじゃありませんが。

ソースコード

ソースコードはGitHubに上げました。


これから色々いじくってみたいと思います!

2013年2月2日土曜日

GitHub導入

一応私もプログラム書く人間なので(笑)、バージョン管理ツールは必須だよなということで、GitHubをセットアップしました。
実はアカウント自体は数年前に作成していたのですが、有効活用することもなく現在に至っていました…。
今年から色々活動していこうという訳で、GitHubをどんどん使っていこうと思います!ついでにアカウントをFreeからMicroにUpgradeもしました。
やっぱりprivate repositoryを使いたくなることってありますからねー。まあ必要経費ということで。

GitHubは本職の会社でも使っているのですが便利ですよね。gistとかpull requestとか。wikiもついてますし。
それにしてもGUIツールが作られていたことに驚き。pub keyの設定も不要で楽チンですね。
http://mac.github.com/

まあエンジニアにはコマンドの方がちゃちゃっとやれて便利ですが(笑)

2013年1月30日水曜日

2013年の目標

2013年になってから1ヶ月もたって、今更な感じですが今年の目標を書いておこうと思います。

中長期的な目標

今年の目標にいく前に、まずは中長期的な目標です。

「地元秋田に帰る」

理由は色々あります。まあ単純に生まれ育った土地が好きってのがあります。後は、両親が還暦 or 還暦間近で、かつ肉体労働ということもあり、長男の私が近くにいて生活した方がいいかなという現実的なものもあります。あとは子育ては田舎の方がなんとなくいいなーというのも。

幸い埼玉出身の嫁も秋田へいくことはOKしてくれているし、秋田には2011年冬に購入した実家もあるので、もういつでも帰ってやろうな気分なのですが(笑)
最大のネックはやっはり仕事です。

お仕事ないよね

田舎にはお仕事がありません。本当に。求人とか取り寄せたりするのですが全然ありません。
特に私のお仕事(ITエンジニア)は首都圏に集中しているので、秋田で正社員で就職するのはかなり厳しいです。
今の収入を維持して、などという贅沢なことは考えていませんが、一応私も家族がいるのである程度の収入は必要ですし、とはいえ今のお仕事は結構好きなので職種を変えるつもりもないのです。

時代が追いついてきた

仕事は東京にあるけど地方で働きたい。インターネットが普及したんだし、そういう仕組が社会に整ってきて欲しいなと(他力本願ながら)思っていました。
それが最近になってようやく現実的になってきたようです。
クラウドソーシャルというものだったり、ノマドという働き方が出てきたり。
さすがに現段階ではクラウドソーシャルのみで生計を立てるのは難しいようですが、今後新しい働き方として世の中に浸透していって欲しいなと期待しています。

ようやく本題

さて、前置きが長くなりましたが今年の目標です。

「クラウドソーシャルを副業としてはじめてみる」

安定した収入のある今のうちに、どういうものか試してみようかと。それにクラウドソーシャルはネットで仕事が完結する分、実績と信頼が大切になってくるので、
秋田に帰る前に少しでも数をこなしておこうと思っています。収入が増えるのも純粋に嬉しいですし。

この目標を実現するために、日々の生活の見直しをしようと思っています。
まあ普通にしていたら本業以外の時間を確保するのが難しいですよね。なので副業用の時間を捻出するために、
  • 朝早起きをする(夜は集中力が続かないorz)
  • 早起きした分を作業時間にあてる
  • 本業は残業を少なくして帰宅、早めに寝る
これを実践していこうと思います。最近メタボだし、生活習慣の改善にもなって一石二鳥!?

まとめ

という訳で、今年はクラウドソーシャルで副業をはじめてみる予定です。それを理由(言い訳)に自宅PCを新調してしまったので、せめて購入代金くらいは稼ぎたいと思っています(爆)

2013年1月27日日曜日

ブログはじめました

はじめまして。なべと申します。
さて、今更ながらブログを始めることにしました。記念すべき最初の投稿は、なぜブログを始めようと思ったのか、その決意のほどを書き留めておこうと思いますw

文章にすること

普段から頭のなかで色々考えているのですが、アウトプットしていないと忘れてしまったり、うまく整理できなかったりします。
ブログを書くことで頭の中を文章化できますし、不特定多数の方に見られる可能性があるのである程度整理した形で残せますよね。

SNSじゃだめなのよ

文章を公開する場としてはSNSもある訳ですが、「つぶやき」だと文章自体が軽くなりがちな気がします。あと長文は載せづらいですよね。

そんな訳で

きっちり長めの文章でまとめたい場合は、このブログを活用していこうと思います!