ハロの外部記憶インターフェイス

そろそろ覚える努力が必要かも…

robocopyメモ

robocopy

使う機会がなかったが、今回、サーバー移行の案件が有ったため、使うことになった。
備忘録としてオプションなどをメモる。

オプション

  • /mir : コピー元と先を同期する。(元に削除されたら、先にも削除される)
  • /xo : 古いファイルをコピーしない
  • /r:n : コピーに失敗した場合の再トライ回数(規定値:1000,000)
  • /w:n : 再実行時の待機時間(規定値:30秒)
  • /b : バックアップモードコピー
  • /s : 空のフォルダーを除外
  • /e : 空のフォルダーを含む
  • /zb : 再起動可能モードを使用
  • /NP : No Progress 進捗率を非表示
  • /NFL : No FileList コピー成功ファイルを表示しない
  • /NDL : No DirectoryList コピー成功ファイルを表示しない

サンプル

run.bat

@@echo off

set source="c:\source"
set target="d:\target"

echo %date% %time% > robocopy.log

robocopy %source% %target% /MIR /R:1 /W:1 /NFL /NDL >> robocopy.log

echo %date% %time% >> robocopy.log

@@echo on

Java 7環境でのMavenのPluginがダウンロード失敗する

Java 7環境のMavenでダウンロードが行われない

環境を設定してもMavenの命令のダウンロード処理が失敗する事がある。

環境

現象

命令に対し、以下のように「Received fatal alert: protocol_version」になりダウンロードが失敗する。

C:\work\test>mvn archetype:generate
[INFO] Scanning for projects...
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-archetype-plugin/maven-metadata.xml
[WARNING] Could not transfer metadata org.apache.maven.plugins:maven-archetype-plugin/maven-metadata.xml from/to central (https://repo.maven.apache.org/maven2): Received fatal alert: protocol_version

原因

参考サイトにあるようにレポジトリへの接続には「TLS」というプロトコルを使うらしく、2018/06/18 からはTLSv1.0, 1.1はサポート外になったらしい

解決

Java7はデフォルトでTLSv1.0で通信するらしい、幸いにJAVA7はv1.2もサポートしているらしく、MavenでTSLを指定して実行することが可能だった

mvn -Dhttps.protocols=TLSv1.2 archetype:generate
[INFO] Scanning for projects...
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-archetype-plugin/maven-metadata.xml
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-archetype-plugin/maven-metadata.xml (918 B at 0.4 KB/sec)
Downloading: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-archetype-plugin/3.1.2/maven-archetype-plugin-3.1.2.pom
Downloaded: https://repo.maven.apache.org/maven2/org/apache/maven/plugins/maven-archetype-plugin/3.1.2/maven-archetype-plugin-3.1.2.pom (11 KB at 18.1 KB/sec)
...省略

自動化

少し探しづらいが、以下のファイルから一番長い行から、最後に「-Dhttps.protocols=TLSv1.2」をつけとけば常に適用される。 C:\apache-maven-3.3.9\bin\mvn.cmd

%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %CLASSWORLDS_JAR% "-Dclassworlds.conf=%M2_HOME%\bin\m2.conf" "-Dmaven.home=%M2_HOME%" "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %CLASSWORLDS_LAUNCHER% %MAVEN_CMD_LINE_ARGS%

以下に修正

%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %CLASSWORLDS_JAR% "-Dclassworlds.conf=%M2_HOME%\bin\m2.conf" "-Dmaven.home=%M2_HOME%" "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %CLASSWORLDS_LAUNCHER% %MAVEN_CMD_LINE_ARGS%-Dhttps.protocols=TLSv1.2

参考

このサイトがすごく役に立った。
Java7 環境で maven が失敗する (Received fatal alert: protocol_version) - Qiita

Ubuntu 18にJava開発環境構築

今回は仕事でJavaのソースを渡されていて、開発環境を構築してみることになったんで、手順をメモしてみる

JreJDKのインストール

Javaの開発なのでまずはJRE(Java Rutime Environment)とJDK(Java Development Kit)をインストールする。

※ 参考までにJreJdkの種類 - Oracle Jre/Jdk : 基本Oracleが提供する有料Javaの指す(月額3千円ぐらい) - Open Jre/Jdk : オープンソースJAVA(無料) 基本はOpenJre/Jdkを使う感じかな…

ローカルの最新Jreをインストールする場合、

$ sudo apt install default-jre 
$ sudo apt install openjdk-8-jre
$ sudo apt install openjdk-8-jre-headless
$ sudo apt install openjdk-11-jre
$ sudo apt install openjdk-11-jre-headless

GUIが不要な場合はheadless版にする

続けてJDKのインストール、バージョンを指定してインストールする。

$ sudo apt install default-jdk 
$ sudo apt install openjdk-8-jdk 
$ sudo apt install openjdk-8-jdk-headless 
$ sudo apt install openjdk-11-jre
$ sudo apt install openjdk-11-jre-headless

GUIが不要な場合はheadless版にする

インストールされたJavaのバージョンを確認

$ java -version

aptの中からインストール可能なバージョンを探すコマンド

$ sudo apt search openjdk-\(\.\)\+-jdk$
Sorting... Done
Full Text Search... Done
openjdk-11-jdk/bionic-updates,bionic-security 11.0.4+11-1ubuntu2~18.04.3 amd64
  OpenJDK Development Kit (JDK)

openjdk-8-jdk/bionic-updates,bionic-security 8u222-b10-1ubuntu1~18.04.1 amd64
  OpenJDK Development Kit (JDK)

結果として、openjdk-11-jdk,openjdk-8-jdkが検索されている。

Javaのバージョンを切り替える場合

「update-alternatives --config java」命令で使用するJavaJDKバージョンを切替可能

$ sudo update-alternatives --config java
There are 2 choices for the alternative java (providing /usr/bin/java).

  Selection    Path                                            Priority   Status
------------------------------------------------------------
* 0            /usr/lib/jvm/java-11-openjdk-amd64/bin/java      1111      auto mode
  1            /usr/lib/jvm/java-11-openjdk-amd64/bin/java      1111      manual mode
  2            /usr/lib/jvm/java-8-openjdk-amd64/jre/bin/java   1081      manual mode
  Press <enter> to keep the current choice[*], or type selection number: 

JAVA_HOME設定

上記からのJre PathをJAVA_HOMEに設定する。

$ export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64
$ export PATH=${JAVA_HOME}/bin:${PATH}

自動化

.profileに設定を追加

export JAVA_HOME="/usr/lib/jvm/java-8-openjdk-amd64"
export PATH=${JAVA_HOME}/bin:${PATH}

Mavenのインストール

今回のソースはMavenでビルド管理しているみたいなので、Mavenもインストールします。

特定バージョンをダウンロードしてインストール

  • Maven 3.3.9をダウンロード ソースでは3.3.9が使われていたので、最新ではなく、古いバージョンんをダウンロードします。
$ cd /usr/local/lib/
$ sudo wget https://archive.apache.org/dist/maven/maven-3/3.3.9/binaries/apache-maven-3.3.9-bin.tar.gz
$ sudo tar -zxvf apache-maven-3.3.9-bin.tar.gz
$ sudo rm apache-maven-3.3.9-bin.tar.gz
  • 環境設定
$ export M2_HOME=/usr/local/lib/apache-maven-3.3.9
$ export PATH=${M2_HOME}/bin:${PATH}
  • 自動化
    .profileに設定を追加
$ export M2_HOME="/usr/local/lib/apache-maven-3.3.9"
$ export PATH=${M2_HOME}/bin:${PATH}

aptを利用したインストール

$ sudo apt policy maven
maven:
  Installed: (none)
  Candidate: 3.6.0-1~18.04.1
  Version table:
     3.6.0-1~18.04.1 500
        500 http://us.archive.ubuntu.com/ubuntu bionic-updates/universe amd64 Packages
        500 http://us.archive.ubuntu.com/ubuntu bionic-updates/universe i386 Packages
        500 http://security.ubuntu.com/ubuntu bionic-security/universe amd64 Packages
        500 http://security.ubuntu.com/ubuntu bionic-security/universe i386 Packages
     3.5.2-2 500
        500 http://us.archive.ubuntu.com/ubuntu bionic/universe amd64 Packages
        500 http://us.archive.ubuntu.com/ubuntu bionic/universe i386 Packages
  • すでにある場合は削除する
$ sudo apt remove maven
  • Mavenの最新版をインストール
$ sudo apt install maven
  • インストールされた場所
$ ls -lsa/usr/share/maven
$ ls -lsa/etc/maven

Mavenのバージョンを確認

$ mvn -v

Maven archetypeからプロジェクトを作成する

sourceフォルダーの直下に新しいプロジェクトを作成する場合

$ cd soruce
$ mvn archetype:generate

最初に必要なプロジェクトテンプレートをダウンロードする。
その後からはInteractiveに設定を進めればOK

  • 最終的に以下のフォルダー構成が生成される
source
  {artifactId}
    src
      main
        java
          {packageName}
    test
      ...
    pom.xml

{}の部分はプロジェクト生成時に入れた値

Maven コマンド

作成したプロジェクトルートから実行する

  • mvn compile : ソースをコンパイルする
  • mvn test-compile : テストコードをコンパイルする
  • mvn test : テストを実施
  • mvn clean : ソース以外のフォルダをクリアする
  • mvn package : コンパイル、テスト後、Jarファイルまで生成
  • mvn exec:java : pom.xmlにexec-maven-pluginを追加することで実行可能になる
<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>exec-maven-plugin</artifactId>
    <version>1.5.0</version>
    <configuration>
    <mainClass>{プロジェクトのスタートクラス}</mainClass>
    </configuration>
</plugin>

Eclipse インストール

  • Eclipse IDE Mars 2 Eclipse IDE for Java Developersをダウンロード ソースが古いため、旧バージョンのEclipseをダウンロードする。
$ wget https://www.eclipse.org/downloads/download.php?file=/technology/epp/downloads/release/mars/2/eclipse-java-mars-2-linux-gtk-x86_64.tar.gz

中の実行ファイルで起動できたので、とりあえずはここまで

CsvHelperで出力にダブルクォーテーションをつける

概要

CsvHelperでcsv出力でダブルクォーテーションを設定しようとして、かなり苦労したのでメモっておく
検索してみると「CsvWriter.Configuration.QuoteAllFields」をTrueにするSampleばかり出てきたけど、CsvHelper Ver12にはこのプロパティは存在しなかった。
プロパティで検索してみたら「Configuration.ShouldQuote」を使えと作者からの回答が見つかったため、解決した。

Configuration.ShouldQuote 使ってダブルクォーテーションを出力する

using (StreamWriter sw = new StreamWriter(File.Create(_outFilePath), utf8_Bom))
{
    using (var writer = new CsvHelper.CsvWriter(sw))
    {
        //writer.Configuration.QuoteAllFields = true;
        writer.Configuration.ShouldQuote = (field, context) => true;
        writer.WriteRecords<GeocodingOutput>(output);
    }
}

CustomConverterを使ってみる

ShouldQuoteでは全カラムに適用されるため、任意の項目にだけ適用する場合は、TypeConverterでMapしないと行けないらしい。
とりあえず、サンプルになりそうな部分だけメモ
ClassMapで各出力項目毎にConverterを指定して、出力内容をカスタムできる

public class CsvOutput
{
  public string a { get; set; }
  public string b { get; set; }
}

// ClassMap
public class CsvOutputMap : CsvHelper.Configuration.ClassMap<CsvOutput>
{
  public CsvOutputMap()
  {
    Map( x => x.a).TypeConverter(new CsvDoubleQuoteConverter);
    Map( x => x.b).TypeConverter(new CsvNonDoubleQuoteConverter);
  }
}

// Converter
public class CsvDoubleQuoteConverter : CsvHelper.TypeConversion.StringConverter
{
 public override string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData)
 {
    row.Configuration.ShouldQuote = (field, context) => true;
    string ret = base.ConvertToString(value, row, memberMapData);
    row.Configuration.ShouldQuote = (field, context) => false;
   return ret;
 }
}
public class CsvNonDoubleQuoteConverter : CsvHelper.TypeConversion.StringConverter
{
 public override string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData)
 {
    string ret = base.ConvertToString(value, row, memberMapData);
   return ret;
 }
}

using (StreamWriter sw = new StreamWriter(File.Create(_outFilePath), utf8_Bom))
{
    using (var writer = new CsvHelper.CsvWriter(sw))
    {
        writer.Configuration.RegisterClassMap(new CsvOutputMap());
        writer.WriteRecords<GeocodingOutput>(output);
    }
}

参考

https://github.com/JoshClose/CsvHelper/issues/1180 https://teratail.com/questions/97817

AppSettingから設定値を取得する

.Netの環境変数ファイル(*.config)から値を取得する方法について

以前のやり方が使えなくなったみたいなので、新しい方法をメモる
今はうまくいかない以下の方法

Configuration.ConfigurationManager.AppSettings("KEY")

.config

<configuration>
  <appSettings>
    <add key="KEY_ID1" value="abcd" />
    <add key="KEY_ID2" value="9999" />
  </appSettings>
</configuration>

System.Configuration.AppSettingsReader

System.Configuration.AppSettingsReader setting = new System.Configuration.AppSettingsReader();
var keyId1= setting.GetValue(KEY_CLIENT_ID, Type.GetType("System.String"));

うまく取得できたが、キーがなかったりすると、そのままエラーになるので、エラー処理が必要

Google Map Apiを利用してみる

諸事情により、Wep APIを利用するコーディングを試すことになったので、メモる

Google Cloud Platformの登録

Google Map Apiを利用するためにはGoogle Cloud Platformへの登録が必要、クレジットカード必須。 https://cloud.google.com 登録を完了すると、デフォルトのプロジェクトが生成される。

認証キーを生成する。

サイドメニューから「APIとサービス→認証情報」へ f:id:haronoid:20191006201447p:plain 認証情報を作成から、APIキーを選択し、認証キーを作成する。
APIキーの制限設定画面からはアプリケーション制限からHTTPやIPアドレスでの制限をかけれるらしい、とりあえず、テストだけにはなしで問題なし

Google Map Apiを有効にする。

サイドメニューから「APIとサービス→ライブラリー」へ f:id:haronoid:20191006201841p:plain 「Maps JavaScript API」を探して、「有効」にする。 f:id:haronoid:20191006202318p:plain 管理画面へ移動 f:id:haronoid:20191006203041p:plain これで、Javascript用Map Apiが利用可能になった。
用件によって「Geocoding API」や「Places API」の有効化が必要になる。

Map APIを利用して座標の地図を表示してみる。

HTML

googleapiが読まれたタイミングでinitMapが実行され、表示される

<div id="map" style="height:500px;width:500px;"></div>

<script>
    function initMap() {
    var MyLatLng = new google.maps.LatLng(34.7088812,135.4881584);
    var Options = {
        zoom: 15,      //地図の縮尺値
        center: MyLatLng,    //地図の中心座標
        mapTypeId: 'roadmap'   //地図の種類
    };
    var map = new google.maps.Map(document.getElementById('map'), Options);
}
</script>

<script src="http://maps.google.com/maps/api/js?key={API_KEY}&language=ja&callback=initMap"></script>

Geocoding APIを利用して、住所から座標を取得する方法

もともとこれをやって見るための準備だったので、これを有効にする。 f:id:haronoid:20191006204058p:plain これにより、Web APIからのResponseを取得する様にしてみる。

.NETのコンソールプロジェクトを作成し、必要なパッケージを追加する。

先ずはパッケージの追加 + Microsoft.AspNet.WebApi.Client + Newtonsoft.Json

レスポンス用クラスを宣言(Google GeocodeからのレスポンスのJson型)

    public class MyResponse
    {
        public List<AddressComponent> results { get; set; }
        public string status { get; set; }
    }

    public class AddressComponent
    {
        public List<address_components> address_components { get; set; }
        public string formatted_address { get; set; }
        public Geometry geometry { get; set; }
        public string place_id { get; set; }
        public List<string> types { get; set; }
    }

    public class Geometry
    {
        public Bounds bounds { get; set; }
        public Point location { get; set; }
        public string location_type { get; set; }
        public Bounds viewport { get; set; }
    }

    public class Bounds
    {
        public Point northeast { get; set; }
        public Point southwest { get; set; }
    }

    public class Point
    {
        public decimal lat { get; set; }
        public decimal lng { get; set; }
    }
    public class address_components
    {
        public string long_name { get; set; }
        public string short_name { get; set; }
        public List<string> types { get; set; }
    }

実行プログラム

    class Program
    {
        static HttpClient _client = new HttpClient();
        static List<String> _list = new List<string>();
        static Dictionary<String,string> _result = new Dictionary<String, string>();

        static void Main(string[] args)
        {
            _list.Add("東京タワー");
            _list.Add("543-0001");

            RunAsync().GetAwaiter().GetResult();
            foreach (var item in _result)
            {
                // JsonからNewtonsoft.Jsonを利用してDeserializeしている。下のresult3と結果は同じ
                var conv = Newtonsoft.Json.JsonConvert.DeserializeObject<MyResponse>(item.Value);
                Console.WriteLine($"{item.Key}=X:{conv.results[0].geometry.location.lat},Y:{conv.results[0].geometry.location.lng}");
            }
            Console.ReadLine();
        }

        static async Task<String> GetResultAsync(string address)
        {
            //Json形式で返す様にしている。
            HttpResponseMessage response = await _client.GetAsync(new Uri($"https://maps.googleapis.com/maps/api/geocode/json?address={address}&key={API_KEY}"));

            var result = response.EnsureSuccessStatusCode();
            var result2 = await response.Content.ReadAsStringAsync(); //Jsonの文字列を取得する。
            var result3 = await response.Content.ReadAsAsync<MyResponse>();  //Deserializeした結果を受け取る

            return result2;
        }

        static async Task RunAsync()
        {
             // BaseAddressは指定しているが、今回は毎回Full Urlを送っているため、何でも良い(はず)
            _client.BaseAddress = new Uri("https://maps.googleapis.com/maps/api/geocode/");
            _client.DefaultRequestHeaders.Accept.Clear();
            _client.DefaultRequestHeaders.Accept.Add(
                new MediaTypeWithQualityHeaderValue("application/json"));

            try
            {
                foreach (var item in _list)
                {
                    var result = await GetResultAsync(item);
                    _result.Add(item, result);
                }

            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
            }

        }
    }

テスト用コードがいっぱい残っているが、とりあえず、ここまで

参考サイト developers.google.com docs.microsoft.com

トリガー作成メモ

TRIGGER作成

基本文法

CREATE TRIGGER [dbo].[TR_TABLE_NAME]
   ON  [dbo].[TABLE_NAME]
   AFTER INSERT,DELETE,UPDATE
AS 
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

    -- Insert statements for trigger here

END
GO

トリガーでのINSERT,UPDATE,DELETEの判定

少し面倒だが、こんな形で判定が可能だった

-- Insert
IF EXISTS (SELECT * FROM inserted) AND NOT EXISTS(SELECT * FROM deleted)
BEGIN 
    --inserted action
END

-- Update
IF EXISTS (SELECT * FROM inserted) AND EXISTS (SELECT * FROM deleted)
BEGIN
    --updated action
END

-- Delete
IF EXISTS (SELECT * FROM deleted) AND NOT EXISTS(SELECT * FROM inserted)
BEGIN
    --deleted action
END

トリガーで更新値を変更する

自分自身へのUPDATEを発行する

UPDATE [dbo].[TABLE_NAME]
SET
    [CLM_NAME] = 'ABC'
FROM [dbo].[TABLE_NAME] T
INNER JOIN inserted I
    ON I.KEY = T.KEY