dooo

得意分野よりも、勉強中の内容を書きたい

属性ベースのCSV出力

using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;
using System.IO;
using System.Text;

namespace CsvWriter
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                var basicList = new List<ICsvFieldsDto>();

                for (int i = 0; i < 10; i++)
                {
                    var basic = new BasicDto();
                    basic.Id = i.ToString();
                    basic.Name = "Name" + i;
                    basic.Memo = "Memo" + i;

                    basicList.Add(basic);
                }

                var csvWriter = new CsvWriter();
                csvWriter.Write(basicList, "example.csv", false);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
            }
            Console.Write("終了");
            Console.Read();
        }
    }

    /// <summary>
    /// CSV書き込み
    /// </summary>
    class CsvWriter
    {
        internal void Write(List<ICsvFieldsDto> csvFieldsDtoList, string path, bool appends)
        {
            List<List<string>> csvRecords = csvFieldsDtoList.Select(csvFieldsDto => GetCsvFieldValueListByProperties(csvFieldsDto)).ToList();

            using (var sw = new StreamWriter(path, appends))//, Encoding.GetEncoding("shift_jis")))
            {
                csvRecords.ForEach(record =>
                {
                    sw.WriteLine(String.Join(",", record));
                });
            }
        }

        /// <summary>
        /// 引数のDTOのCsvFied属性の付いたプロパティの値を、CSV出力におけるフィールド順の値のリストに変換
        /// </summary>
        /// <returns>CSV出力におけるフィールド順の値のリスト</returns>
        /// <param name="csvFieldsDto">CSVFieldsDtoインターフェースを実装したクラス</param>
        private List<string> GetCsvFieldValueListByProperties(ICsvFieldsDto csvFieldsDto)
        {
            // CSVFied属性の付いたプロパティを指定された順番でリストする
            var csvFieldProperties = csvFieldsDto.GetType()
                               .GetProperties()
                               .Where(property => property.GetCustomAttributes(typeof(CsvFieldAttribute), true).Length > 0)
                                                 .OrderBy(property => ((CsvFieldAttribute)property.GetCustomAttributes(typeof(CsvFieldAttribute), true).First()).Order);

            if (!csvFieldProperties.All(csvProperty => csvProperty.PropertyType == typeof(string)))
            {
                // string型のみCSV出力の対象とする
                throw new InvalidOperationException("String type is only valid for CsvField.");
            }

            // 特定文字の置換・削除を行い、ダブルクォート付加をしたうえで、値のリストにして返却
            var csvFields = csvFieldProperties.Select(property =>
            {
                var value = (string)property.GetValue(csvFieldsDto);

                var csvFieldAttr = (CsvFieldAttribute)property.GetCustomAttributes(typeof(CsvFieldAttribute)).First();

                if (csvFieldAttr.NeedsCharReplacing)
                {
                    // 要件に定められた文字の置換
                }

                if (csvFieldAttr.NeedsCharRemoving)
                {
                    // 要件に定められた文字の削除
                }

                return "\"" + value + "\"";
            }).ToList();

            return csvFields;
        }
    }

    /// <summary>
    /// CSVのフィールドをプロパティとして持つことを表すマーカーインターフェース
    /// </summary>
    /// <remarks>
    /// CSVのフィールドとして出力したいstring型のpublicなプロパティにCsvField属性を付与してください。
    /// </remarks>
    interface ICsvFieldsDto { }

    /// <summary>
    /// CSV出力の対象フィールドであることを示すAttribute
    /// </summary>
    [AttributeUsage(AttributeTargets.Property)]
    class CsvFieldAttribute : Attribute
    {
        /// <summary>
        /// 何番目のフィールドか
        /// </summary>
        /// <value>The order.</value>
        public int Order { get; set; }

        /// <summary>
        /// 置換が必要な文字が存在するか
        /// </summary>
        /// <value><c>true</c> if needs char replacing; otherwise, <c>false</c>.</value>
        public bool NeedsCharReplacing { get; set; }

        /// <summary>
        /// 削除が必要な文字が存在するか
        /// </summary>
        /// <value><c>true</c> if needs char replacing; otherwise, <c>false</c>.</value>
        public bool NeedsCharRemoving { get; set; }

        public CsvFieldAttribute(int order)
        {
            this.Order = order;
        }
    }

    /// <summary>
    /// DTO Example
    /// </summary>
    class BasicDto : ICsvFieldsDto
    {
        [CsvField(1)]
        public string Id { set; get; }
        [CsvField(2)]
        public string Name { set; get; }
        [CsvField(3, NeedsCharReplacing = true)]
        public string Memo { set; get; }
    }
}

コロン区切りの文字列をツリー構造にする

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;

public class Main {

    static ObjectMapper mapper = new ObjectMapper();

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws Exception {

        List<String> properties = Arrays.asList(
                "hoge:fuga:piyo",
                "hoge:fuga:moge",
                "hoge:ho:ge",
                "foo:bar"
        );

        JsonNode tree = parsePropertiesAsTree(properties);

        String json = mapper.writeValueAsString(tree);
        System.out.println(json); // {"hoge":{"ho":"ge","fuga":"moge"},"foo":"bar"}
    }

    private static JsonNode parsePropertiesAsTree(List<String> properties) {

        List<String[]> elementsList = properties.stream().map((property -> property.split(":"))).collect(Collectors.toList());

        ObjectNode root = mapper.createObjectNode();
        ObjectNode currentNode = null;

        for (String[] elements : elementsList) {
            currentNode = root;
            for (int i = 0; i < elements.length - 1; i++) {
                String key = elements[i];

                if (i == elements.length - 2) {
                    currentNode.put(key, elements[i + 1]);
                    break;
                }

                ObjectNode childNode = null;
                if (currentNode.has(key)) {
                    childNode = (ObjectNode) currentNode.get(key);

                } else {
                    childNode = mapper.createObjectNode();
                    currentNode.set(key, childNode);
                }

                currentNode = childNode;
            }
        }
        return root;
    }
}

MDC-WebのSnackbarをメッセージを表示するためだけに使う

Material Components for the web(MDC-Web)のSnackbarを、メッセージを表示するためだけに使う。

<!DOCTYPE html>
<html>

  <head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="https://unpkg.com/material-components-web@latest/dist/material-components-web.min.css">   
    <script src="https://unpkg.com/material-components-web@latest/dist/material-components-web.js"></script>
  </head>

  <body>
    <h1>snackbar-scripting</h1>

    <button type="button" onclick="showMessagebar('メッセージ')">button</button>

    <script>
    
      // showMessagebar関数を定義
      window.showMessagebar = (function (message) {
        
        var element;
        var snackbar;

        var createElement = function () {
          var element = document.createElement('div');
          element.id = 'messagebar';
          element.className = 'mdc-snackbar';
          element.setAttribute('aria-live', 'assertive');
          element.setAttribute('aria-atomic', true)
          element.setAttribute('aria-hidden', true);

          element.innerHTML = [
            '<div class="mdc-snackbar__text"></div>',
            '<div class="mdc-snackbar__action-wrapper">',
            '    <button type="button" class="mdc-button mdc-snackbar__action-button"></button>',
            '</div>'
          ].join('\n');

          return element;  
        };
        
        // showMessagebarの実体
        return function (message) {
          
          if (!element) {
            element = createElement();
            document.body.appendChild(element);
          }
          
          snackbar = snackbar || new mdc.snackbar.MDCSnackbar(element);
          snackbar.show({ message: message });

          // 必ず一定時間後に消去するために、アクションがクリックされたことにする
          snackbar.foundation_.actionWasClicked_ = true;
        };
      })();
      
    </script>
  </body>

</html>

Javaエンジニアのフロントエンド入門

僕は(おそらく僕の周りの人も)、仕事ではJavaでサーバーサイドの開発をすることが多い。JavaScriptはお飾り程度に使っていただけだった。

少し前に、React.jsを中心にフロントエンドの流行りの技術を勉強した。開発環境として実に様々なツールが使われており、普段使っているJavaScriptとはあまりにも世界が違った。 ここに特徴的な点を書き留めておきたい。

あくまで概論だが、サーバーサイド中心のJavaエンジニアに、最近のフロントエンドの雰囲気がなんとなく伝わればいいなと思う。

SPA

最近は、SPA(Single Page Application)とよばれるアーキテクチャが注目されている。ひとつのWebページで完結するアプリケーションだ。ページ遷移をしないことで流れるようなUXが実現できる。

SPAでは、クライアント側のロジックの記述が多くなる。そのため、通常Backbone.js、AngularJS、Reactなどのフレームワーク(Reactは厳密にはライブラリ)が使用される。

「フロントエンド」は、本来単にUI部分を指す言葉であるが、本記事ではSPAを開発する場合を想定する(世間的にそんな雰囲気だし)。

npm

Node.jsといえばサーバサイドのJavaScript実行環境だ。 npm(Node Package Manager)は、そのNode.jsのパッケージマネージャである。

普通に考えると、サーバーサイド開発のための色々なモジュールをインストールするもの、ということになるのだろう。 しかし、実のところサーバーサイドをNode.jsで作らなくても、フロントエンドのためだけにnpmは使われる。主な役割はプロジェクトの定義と、開発ツールを含めたコンポーネントの依存性管理だ。

Javaでいうと、Mavenによるプロジェクト定義と依存性管理に近い。

JavaScriptで"ビルド"

フロントエンドの開発環境が重厚化している理由として、開発のプロセスでビルドが必要になっていることが挙げられる。

JavaScriptでビルドと言われてもピンとこない人もいるかもしれない。 以下のような処理を行う。

  • minify(ミニファイ・圧縮) - JavaScriptCSSから余分な空白文字を削除したり、変数名を短くして、サイズを小さくする

  • altJS、CSSメタ言語といった簡略記法を、実際のJavaScriptCSSに変換する

  • モジュール化したJavaScriptの依存性解決(詳しくは別記事webpackで説明予定)

下の2つについては、要するに作業の軽減や保守性UPのために仮の書き方でコードを書いておいて、最終的に正式な(=ブラウザが解釈できる)コードに変換するということだ。

ビルドを行うツールとしては GruntGulp が有名だ(正確にはビルドツールというよりタスクランナー)。 Javaの人的にはAntやMavenのビルド機能・タスク実行機能のイメージでいいと思う。

モジュール化

Javaでは基本的に、1ファイルに1クラスを定義し、あるクラスから別のクラスを使うときは、あらかじめimport文でインポートする。 このようにコードをモジュール化し、必要なときに取り込んで使う機能が、JavaScriptには長らく存在しなかった。 そのため、JavaScriptをモジュール化するためにRequireJSやBrowserify、webpackが使用される。 (話がややこしくなるのだが、2015年6月にリリースされた ECMAScript 6 からimport文が規定された)

RequireJSは実行時に依存性を解決し、Browserify・webpackは先に述べたGruntやGulpを使って事前にビルドする。詳しい使い方はここでは述べないが、前述の通りJavaのimport文を実現するイメージだ。

最後に

さて、最近のフロントエンドの開発環境について、Javaの場合に例えるなどして簡単にまとめてみた。ただ、あくまで一部の内容であるし、具体的なコード等にもまだ触れていない。

正直、イマドキのフロントエンドの開発は学習コストが高いように思う。けれども僕自身触ってみてすごく面白いと思ったので、普段触る機会がない人に紹介するつもりで書いた。

ぜひ仲間のJavaエンジニアにも触ってみていただきたい。

午後の留意点リスト

  • 解答はまず問題文の中から探す。

  • 解答の内容とは別に、使う表現・言葉についてもできるだけ問題文のなかで使われているものを探して使う。

  • 問題作成者が意図するとおりの解答を書く必要がある。

    • 設問の問われ方で述語・語尾などが決まる(たとえば肯定形or否定形)。
    • 問題文・設問を読み返し制約条件を確認する。
    • 設問の微妙なニュアンスを感じ取ることも必要。
  • 問題文から解答を導き出しても、答えが浮かんでも常識的におかしい・なんとなく違う気がすると思ったら、別の納得できる答えを検討する。

  • 問題文から引用せず自分の頭で解答を作るタイプの問題で、単に常識に従って書けばいいこともある。常識結構大事。

  • 当たり前すぎる「こんなんでいいの?」と思える簡単な解答も意外と多い。総合的に判断して妥当であれば勇気をもって答える。

  • 設問に関係する図表は、一部を注視せず全体に目を通す(設問で実際に問われたときでよい)。

  • 日本語名で答えるのか変数名で答えるのかといった、細かいとこまで設問を読む。

  • 散在するヒントに対し論理的整合性をとる。

  • 解答の「軸」を意識する。

  • 「確認」をめんどくさいと思った自分がいたら厳重注意する。

  • 字数指定に収めるために、言葉を足したり省略するときは、「別の意味に解釈できてしまわないか」を意識して、言葉を選ぶ([送信元]とか[送信先]とか特に大事)。

  • 多めの字数指定(60文字ぐらい)のときは、事情を知らない人にもわかるような表現をする。

  • 安易に答えない。別の可能性を検討する。

  • 読むときも書くときも、言葉の定義を明確に意識する。ふんわりとしたイメージで考えない。特に似て非なる概念をしっかり分けて考える。

OpenFlow/SDN

SDNとは

Software Defined Network

ネットワークの構成やフロー制御をソフトウェアによって集中的に管理する技術の総称。

OpenFlowとは

SDNを実現する技術・プロトコルの標準仕様。 フレームの制御をOpenFlowコントローラ(OFC)で、フレームの転送をOpenFlowスイッチ(OFS)で行うように機能が分離されれている。

OpenFlow/SDNの利点

OpenFlowを例にSDNの利点を挙げる。

  • ネットワークをソフトウェア的に定義するので、設定・構成を柔軟活動的に変更できる。
  • OpenFlowはパケットの統計情報も管理する。たとえば異常なパケットがOFSに届いたときなどは、OFCに判断を仰ぎパケットを破棄するなど、DDoS対策ができる。
  • OpenFlowの転送アクションのひとつWayPointsを使えば、本来ノードAからノードBに流れるパケットを、条件が合致するときだけ強制的にノードCを経由させるといったことができる。このノードCの部分にIDSやIPSを配置すればセキュリティの向上が見込まれる。

SASLとSAML(名前似てるけど直接は関係ないよ、でも両方とも認証関連の用語だよ)

SASLとSAML(名前似てるけど直接は関係ないよ、でも両方とも認証関連の用語だよ)

SASL

  • Simple Authentication and Security Layer
  • インターネットプロトコルおける認証(とデータセキュリティ)のためのフレームワーク
  • 最初RFC 2222 → RFC 4422に置換、現在IETFのインターネット標準の標準化過程にある
  • アプリケーションプロトコルから認証機構を分離できる
  • 認証機構は複数から選択できる
    • Kerberos
    • GSSAPI
    • CRAM-MD5(チャレンジレスポンス方式)
    • DIGEST-MD5(CRAM-MD5の改良版)
  • 実際に使われる認証方式のことはSASLにまかせてしまうことで、特定の認証方式に縛られなくて済むようになる
  • SMTP AUTH実現の方法である

SAML

f:id:nununux:20150404163048p:plain