PORTABOOK(XMC10)にWindows 10 April 2018 Update(RS4)を入れました

IMG_7231

PORTABOOKにWindows 10 April 2018 Update(バージョン 1803、ビルド番号 17134)をインストールしました。

ストレージが32GBと非常に少ないPORTABOOKなのでオンラインでアップデートというわけにはいきませんが、USBメモリを2本用意し一本をインストールメディアもう一本をテンポラリとするいつもの方法でアップデートすることができました。
(PORTABOOK(XMC10)にWindows 10 Creators Update(Ver. 1703)を入れる方法 を参照)


カテゴリー: Windows, 技術的なメモ | タグ: , , | コメントする

SmartGWTで数値入力を行うためのコンポーネント

SmartGWTのフォーム上で任意スケールの数値入力を行うためのコンポーネントが用意されていなかったので作成しました。

このコンポーネントは、SmartGWTのcom.smartgwt.client.widgets.form.fields.TextItemクラスを拡張して実装しています。

setLengthメソッドで全体の桁数を設定します。
桁数には、入力された文字数そのもので評価され、符号や小数点などもカウントされるので注意が必要です。

setDecimalPadメソッドで小数点以下の桁数を設定します。

getValueメソッド、またはgetValueAsBigDecimalメソッドで入力された結果をBigDecimal型で取得します。
BigDecimal型への変換は、setDecimalPadメソッドで指定された小数点以下の桁数、setRoundingModeメソッドで指定された丸め方法で変換されます。
入力されたテキストが数値としてパースできなかった場合は、nullが返ります。

動作確認を行った環境:

Java

Java SE Development Kit 7

SmartGWT

6.1 PRO

ライセンス

NYSL

ダウンロード

BigDecimalItem.java.zip

// This software is distributed under the license of NYSL.
// ( http://www.kmonos.net/nysl/ )
package com.ria_lab.gwt.smartgwt.client.ui.form;

import java.math.BigDecimal;
import java.math.RoundingMode;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.i18n.client.NumberFormat;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.FormItemValueFormatter;
import com.smartgwt.client.widgets.form.FormItemValueParser;
import com.smartgwt.client.widgets.form.fields.FormItem;
import com.smartgwt.client.widgets.form.fields.TextItem;

/**
 *	SmartGWTの com.smartgwt.client.widgets.form.DynamicForm で
 *	数値型(java.math.BigDecimal)を入力するためのコンポーネント
 */

public class BigDecimalItem extends TextItem {

	/** */
	private Integer scale;
	
	/** */
	private RoundingMode roundingMode = RoundingMode.DOWN;
	
	/** */
	private NumberFormat format;
	
	/** */
	private FormItemValueFormatter formatter =
			new FormItemValueFormatter() {
			
			@Override
			public String formatValue(Object value, Record record, DynamicForm form, FormItem item) {
				
				if (value == null) {
					return "";
				}
				
				try {
					return asBigDecimal(value.toString(), scale, roundingMode).toString();
				} catch (NumberFormatException e) {
					return value.toString();
				}
			}
		}; 
	
	/** */
	private FormItemValueParser parser = 
			new FormItemValueParser() {
				
				@Override
				public Object parseValue(String value, DynamicForm form, FormItem item) {
					
					if (value == null || value.trim().isEmpty()) {
						return null;
					}
					
					try {
						BigDecimal decimal = asBigDecimal(value.toString(), scale, roundingMode);
						return format == null ? decimal : format.format(decimal);
					} catch (NumberFormatException e) {
						return value;
					}
				}
			};

	/**
	 *	Constructor
	 *	
	 */
	public BigDecimalItem() {
		super();
		init();
	}

	/**
	 *	Constructor
	 *	
	 *	@param jsObj
	 */
	public BigDecimalItem(JavaScriptObject jsObj) {
		super(jsObj);
		init();
	}

	/**
	 *	Constructor
	 *	
	 *	@param name
	 */
	public BigDecimalItem(String name) {
		super(name);
		init();
	}

	/**
	 *	Constructor
	 *	
	 *	@param name
	 *	@param title
	 */
	public BigDecimalItem(String name, String title) {
		super(name, title);
		init();
	}
	
	/**
	 *	
	 */
	private void init() {
		
		setEditorValueFormatter(formatter);
		setValueFormatter(formatter);
		setEditorValueParser(parser);
		setKeyPressFilter("[0-9\\-\\+\\.]");
	}
	
	/**
	 *	@see com.smartgwt.client.widgets.form.fields.FormItem#setDecimalPad(java.lang.Integer)
	 */
	@Override
	public void setDecimalPad(Integer decimalPad) {
		
		if (decimalPad != null && decimalPad < 0) {
			throw new IllegalArgumentException();
		}
		
		super.setDecimalPrecision(decimalPad);
		scale = decimalPad;
		format = getNumberFormat(decimalPad);
	}
	
	/**
	 *	@return roundingMode
	 */
	public RoundingMode getRoundingMode() {
		return roundingMode;
	}

	/**
	 *	@param roundingMode roundingMode
	 */
	public void setRoundingMode(RoundingMode roundingMode) {
		
		if (roundingMode == null) {
			throw new IllegalArgumentException();
		}
		
		this.roundingMode = roundingMode;
	}

	/**
	 *	@see com.smartgwt.client.widgets.form.fields.FormItem#getValue()
	 */
	@Override
	public Object getValue() {
		return getValueAsBigDecimal();
	}
	
	/**
	 *	
	 *	@return
	 */
	public BigDecimal getValueAsBigDecimal() {
		
		String s = getValueAsString();
		if (s == null || s.trim().isEmpty()) {
			return null;
		}
		
		try {
			return asBigDecimal(s, scale, roundingMode);
		} catch (NumberFormatException e) {
			return null;
		}
	}
	
	/**
	 *	
	 *	@param s
	 *	@param scale
	 *	@param roundingMode
	 *	@return
	 */
	private static BigDecimal asBigDecimal(String s, Integer scale, RoundingMode roundingMode) {
		return scale == null ? new BigDecimal(s) : new BigDecimal(s).setScale(scale, roundingMode);
	}
	
	/**
	 *	
	 *	@param scale
	 *	@return
	 */
	private static NumberFormat getNumberFormat(Integer scale) {
		
		if (scale == null) {
			return NumberFormat.getFormat("0");
		}
		
		StringBuffer sb = new StringBuffer(scale == 0 ? "#0" : "#0.");
		for (int i = 0; i < scale; i++) {
			sb.append("0");
		}
		return NumberFormat.getFormat(sb.toString());
	}
}

カテゴリー: Java, コードスニペット, 技術的なメモ | タグ: , | コメントする

PORTABOOK(XMC10)にWindows 10 Fall Creators Update(Ver. 1709)を入れる方法

IMG_5697

PORTABOOKにWindows 10 Fall Creators Update(バージョン1709、ビルド番号16299)をインストールしてみました。

基本的な流れは、Windows 10 Creators Update(バージョン1703、ビルド番号15063)の時と同じく、USBメモリを2つ用意して、ひとつをインストールメディア、もうひとつをテンポラリとしてインストールを進めます。

Windows 10 Creators Updateのときはインストールメディアとして使うUSBメモリの容量が4GB以上だったのに対し、Windows 10 Fall Creators Updateでは8GB以上のUSBメモリが必要になっているので注意が必要です。

2017-10-23 (14)

具体的な手順については、過去の記事を参照してください。

PORTABOOK(XMC10)にWindows 10 Creators Update(Ver. 1703)を入れる方法

インストールメディア作成の開始からWindowsの起動ができるまでの所要時間は、だいたい4時間弱でした。

Windows 10 Fall Creators Updateのインストール前に、本体ストレージにインストールしたソフトをいったん削除し、9.3GB程度の空き領域を確保した状態でインストール作業を始めました。
Windows 10 Fall Creators Updateインストール直後の空き容量は、4.7GB程度です。

カテゴリー: Windows, 技術的なメモ | タグ: , , | コメントする

JavaからSQL Serverに接続する

MicrosoftのDBサーバ『SQL Server』に、Microsoft純正のJDBCドライバを用いてJavaから接続した際のハマりメモ。

下記のようなメッセージが表示された人向けです。

  • ERROR-CODE: 0 / SQL-STATE: 08S01

    ホスト 192.168.1.XXX、ポート 55378 への TCP/IP 接続が失敗しました。エラー: “接続のタイムアウト: 詳細情報なし。接続プロパティを調べて、SQL Server のインスタンスがホスト上で実行されていて、ポートへの TCP/IP 接続が許可されており、そのポートへの TCP 接続がファイアウォールにブロックされていないことを確認してください。”。

  • ERROR-CODE: 18456 / SQL-STATE: S0001

    ユーザーユーザ名はログインできませんでした。 ClientConnectionId:XXXXXXXXXX-XXXX-XXXXXXXXX

とりあえずの結論は、「SQL Serverの『名前付きインスタンス』と『TCP動的ポート』、ファイアウォールの設定を確認する」です。

きちんと検証はしていませんが、Accessにリンクテーブルを作ろうとした際、ODBCでも同様の現象が発生しました。
この記事に記載した内容を確認・設定しJDBC接続できるようにしたあとで、ODBC接続を試みたら難なく接続することができました。ODBC経由でSQL Serverに接続できない問題が起きているかたも、この記事の内容が役に立つと思います。


対処

  • SQL Server Browserは起動しているか?

コントロールパネルの『サービス』から確認する。
起動していない場合は、SQL Server Browserサービスを起動する。
(その他、SQL Server関連のサービスも起動されていることを確認する。これを忘れて少しハマりました。)

詳細は、Microsoftのサイトの『SQL Server Browserサービス』を参照。

  • SQL Serverが稼働しているマシンのポート開放の確認

SQL Serverへの接続に必要なポートが開放されているか、(主にSQL Serverが稼働しているリモートマシンの)ファイアウォールなどの設定を確認する。
JDBCの接続URLの記法により、必要な開放ポートが変わるので注意。

	// この部分
	Connection con = DriverManager.getConnection("jdbc:sqlserver://192.168.1.XXX:55378;instanceName=SQLEXPRESS;databaseName=データベース名", "ユーザ名", "パスワード";
“jdbc:sqlserver://192.168.1.XXX\\インスタンス名;databaseName=データベース名”の場合

ポート番号 1434とインスタンスがリッスンしているポート番号が開放されているか確認

“jdbc:sqlserver://192.168.1.XXX;instanceName=インスタンス名;databaseName=データベース名”の場合

ポート番号 1434とインスタンスがリッスンしているポート番号が開放されているか確認

“jdbc:sqlserver://192.168.1.XXX:インスタンスがリッスンしているポート番号;instanceName=インスタンス名;databaseName=データベース名”

インスタンスがリッスンしているポート番号が開放されているか確認

URL記法の詳細は『接続URLの構築』、開放ポートの詳細は『インターネットを介しての SQL Server への接続』を参照

URLの中にユーザ名やパスワードを持たせた場合、java.sql.DriverManager#getConnection(String url)も使える。

PostgreSQLなどをJDBCを介して使っていると、つい接続URLに”jdbc:sqlserver://192.168.1.XXX:インスタンスがリッスンしているポート番号\\インスタンス名;databaseName=データベース名”みたいな書き方をしてしまいますが、これが受け付けてもらえず例外が発生しLocalizedMessageプロパティには「ポート番号 1433\SQLEXPRESS は無効です。」というようなメッセージが設定されます。(ErrorCodeプロパティには0、SqlStateプロパティにはnullが設定されていました)

  • インスタンスがリッスンしているポートの確認

SQL Server構成マネージャーを起動し、『SQL Serverネットワークの構成』からSQL Serverのインスタンスを選択し、一覧表示されたプロトコルから『TCP/IP』を選択。
プロパティを表示し、『IP All』の『TCP動的ポート』に設定されている値を確認する。

SQL Server構成マネージャーは、スタートメニューの検索ボックスで「sqlservermanager12.msc」とか「sqlservermanager13.msc」とか打ち込むと表示される。
されない場合は、\Windows\SysWOW64のフォルダあたりを探す。

そもそもSQL Serverがリモート接続可能な状態で動いているか、TCP/IPで接続可能になっているかなども確認が必要です。

詳細は『構成マネージャー』を参照

2017-08-17 (2)

確認に用いたプログラム

Windows 10上で稼働しているSQL Server 2014 Expressに、リモートのMacから接続して検証。
Mac側のJava環境はJava SE 7(1.7.0_51)、JDBCドライバは『Microsoft JDBC Driver 6.0 for SQL Server』のJDBC 4.1版を使用。

// This software is distributed under the license of NYSL.
// ( http://www.kmonos.net/nysl/ )

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;

import com.microsoft.sqlserver.jdbc.SQLServerException;

public class Test {

	public static void main(String[] args) {

		// 名前付きインスタンス"SQLEXPRESS"上のデータベースに接続する際のJDBCの接続URL

		// ポート1434とTCP動的ポートの開放が必要
		String url = "jdbc:sqlserver://192.168.1.XXX\\SQLEXPRESS;databaseName=データベース名";

		// ポート1434とTCP動的ポートの開放が必要
		// String url = "jdbc:sqlserver://192.168.1.XXX;instanceName=SQLEXPRESS;databaseName=データベース名";

		// TCP動的ポートの開放が必要
		// String url = "jdbc:sqlserver://192.168.1.XXX:55378;instanceName=SQLEXPRESS;databaseName=データベース名";

		// SQL Serverのユーザ名とパスワード
		String username = "ユーザ名";
		String password = "パスワード";

		try {
			// JDBCドライバをロード
			Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
			// SQL Serverに接続
			Connection con = DriverManager.getConnection(url, username, password);

			// SQL Serverに問い合わせ
			Statement stmt = con.createStatement();
			ResultSet rs = stmt.executeQuery("select * from 適当なテーブル");
			while (rs.next()) {
				System.out.println(rs.getString("テーブルの適当な項目"));
			}

			// 後処理
			rs.close();
			stmt.close();
			con.close();

		} catch (SQLServerException e) {
			// SQL Server固有の例外
			System.out.println("ERROR-CODE:\t" + e.getErrorCode());
			System.out.println("SQL-STATE:\t" + e.getSQLState());
			System.out.println("MESSAGE:\t" + e.getLocalizedMessage());
			e.printStackTrace();
		} catch (Exception e) {
			// その他の例外
			e.printStackTrace();
		}
	}
}

カテゴリー: Java, Windows, コードスニペット, データベース, 技術的なメモ | タグ: , , , | コメントする

Apache Hive上のテーブルをAccessのリンクテーブルとして登録する方法 [VBA]

Apache Hive上に登録されているすべてのテーブルをAccessのリンクテーブルとして一括登録するプログラムです。
前提として、Apache HiveのODBCドライバが導入済み、ODBC接続設定が完了している必要があります。

プログラム実行中に既にAccess上に登録済みのテーブルが出現した場合、リンクテーブルの更新は行わず、処理をスキップします。
更新を必要とする場合は、処理前に該当するAccess上のテーブルを削除してから処理を行ってください。

プログラム中のコメントとしても記述していますが、連続してリンクテーブルの追加処理を行っていると、1802回目の追加処理(TableDefs.Appendの呼び出し)で「実行エラー ‘3146’: ODBC–呼び出しが失敗しました。」というエラーが発生します。
復旧させるためには、いったんAccessを終了させ、Accessを再起動、データベースを開きなおすのがもっとも早いと思います。

Hive用としていますが、ODBC接続できるPostgreSQLやMySQLなどのRDBMSであれば、接続文字列部分のDriver部分に適切な値を変更することでHive以外のデータベースのリンクテーブル登録プログラムとしても使えると思います。

動作確認を行った環境:

Access/Windows

Access 2013 / Windows 10 Pro バージョン:1703 ビルド:15063

ODBCドライバ

Microsoft Hive ODBC Driver 2.01.05.1006 / Hortonworks Hive ODBC Driver 2.01.07.1010

Apache Hadoop

2.8.1

Apache Hive

2.3.0

メタストア

PostgreSQL 9.6.3

ライセンス

NYSL

'

' This software is distributed under the license of NYSL.
' ( http://www.kmonos.net/nysl/ )

Option Explicit
Option Compare Database

' ========================================================================
'
' 指定されたHiveデータベース上のテーブルをAccessのリンクテーブルとして
' 登録するAccess VBAモジュール
'
'
' ※注意
'   実行前に[ツール]-[参照設定]メニューで「Microsoft Active X Data Object 6.0 Library」などの
'   ライブラリを参照できるようにチェックすること。
'
'   Hive側のテーブル数が1802以上の場合、1802回目のCurrentDb.TableDefs.Appendの
'   処理で「実行エラー '3146': ODBC--呼び出しが失敗しました。」が発生
'   する。(Office 2013で確認)
'
' ========================================================================
Sub ConnectHiveTables()

Dim hiveCs As String

Dim adoCon As ADODB.Connection
Dim adoRs As ADODB.Recordset

Dim accessTableName As String
Dim daoTableDef As DAO.TableDef
Dim sourceTableName As String

    ' ODBC経由でHiveに接続するための接続文字列を設定
    hiveCs = "DSN=ODBC_HIVE;Driver=Microsoft Hive ODBC Driver;UID=;PWD=;"
    
    ' Hiveに接続
    Set adoCon = New ADODB.Connection
    adoCon.Open (hiveCs)

    ' Hive上のテーブル情報を取得
    Set adoRs = adoCon.OpenSchema(adSchemaTables)
    
    Do While Not adoRs.EOF
    
        ' Access上のテーブル名を決定
        accessTableName = adoRs.Fields("TABLE_SCHEMA") & "_" & adoRs.Fields("TABLE_NAME")
        
        ' Access上にテーブルが存在するかチェックし、存在する場合は処理を飛ばす
        If DCount("[Name]", "MSysObjects", "[Name] = '" & accessTableName & "'") = 1 Then
            Debug.Print "Skip" & vbTab & accessTableName
            GoTo NextTable
        End If
        
        ' リンクテーブルの接続先となるHive上のテーブル名を決定
        sourceTableName = adoRs.Fields("TABLE_SCHEMA") & "." & adoRs.Fields("TABLE_NAME")
        
        ' リンクテーブルを作成
        Set daoTableDef = CurrentDb.CreateTableDef(accessTableName)
        With daoTableDef
            .Connect = "ODBC;" & hiveCs
            .sourceTableName = sourceTableName
        End With
        
        CurrentDb.TableDefs.Append daoTableDef
        
        Debug.Print "Append" & vbTab & accessTableName & vbTab & "(source='" & sourceTableName & "')"
        
NextTable:
        adoRs.MoveNext
        
        DoEvents
    Loop
    
    adoRs.Close
    adoCon.Close
    

End Sub


カテゴリー: Windows, コードスニペット, 技術的なメモ | タグ: , , , , , | コメントする

IchigoJam BASIC向けサンプルプログラム

『こどもパソコン IchigoJam』を使ったプログラムワークショップで使用した簡単なゲームプログラムです。

BASICで記述されていますが、IchigoJam BASIC以外の環境で実行する場合、なんらかの変換・移植処理が必要になると思います。
IchigoJam BASICのリファレンス(説明書)は、 http://ichigojam.net/IchigoJam.html から参照できます。

プログラム入力後、RUNコマンドで実行します。

■ ゲームルール ■

  • 画面の上からおちてくるアルファベットとおなじキーをおすと、あたり!
  • 上の方であたるほど、こうとくてん!
  • アルファベットが画面の下までいくとミス!
  • 3かいミスをするとゲームオーバー。
ライセンス
NYSL ( http://www.kmonos.net/nysl/ )
10 CLS:LOCATE 10,10:PRINT "PUSH KURO-BUTTON"
20 IF BTN()=0 THEN GOTO 10
30 S=0:M=0
40 C=65+RND(26)
50 X=RND(32):Y=0
60 CLS
70 LOCATE 0,23:PRINT "SCORE=",S,"MISS=",M
80 LOCATE X,Y
90 PRINT CHR$(C);
100 IF INKEY()=C THEN BEEP 1:S=S+25-Y:GOTO 40
110 WAIT 10
120 Y=Y+1
130 IF Y<23 THEN GOTO 60
140 M=M+1
150 IF M<3 THEN PLAY "A16G16A2R8G16F16E16D16D-8D4":WAIT 150:GOTO 40
160 PLAY "C4C8.C16C4E-8.D16D8.C16C8.<B16>C4"
170 I=0
180 SCROLL 0:WAIT 5
190 I=I+1:IF I<24 THEN GOTO 180
200 LOCATE 10,10:PRINT "GAME OVER"
210 LOCATE 10,12:PRINT "SCORE=", S
220 END

ステップアップ・改造のヒントとして、以下の項目を提示しています。

かんたんな改造(かいぞう)

  • プレイヤーがボタンをおすとブザーがなるようにしてみよう
  • てきキャラにヒットしたら、LEDをつけて、ブザーをならすようにしてみよう
  • 音楽(おんがく)のしゅるいをふやしてみよう

ちょっとむずかしい改造(かいぞう)

  • 「レベル」をついかして、てきを10こやっつけるごとにレベルがあがるようにしてみよう
  • レベルが10あがるごとに、めちゃくちゃはやくおちてくるようにしてみよう
  • 自分(じぶん)がそうさするキャラクターをついかしてみよう
  • 自分がそうさするキャラクターからげいげきミサイルをはっしゃできるようにしてみよう

むずかしい改造(かいぞう)

  • てきキャラクターを2ついじょうにふやしてみよう
  • てきキャラクターのおちるそくどに、じゅうりょくがあるようにしてみよう
    (じゅうりょくは、理科(りか)や物理(ぶつり)の教科書(きょうかしょ)でしらべてみよう。理科(りか)・物理(ぶつり)と算数(さんすう)・数学(すうがく)のルールをゲームにくみこむとリアルさがぐんとあがるぞ!!)
カテゴリー: コードスニペット, 技術的なメモ | タグ: , , | コメントする

DB2のデータベースをPostgreSQLに移行する方法

IBMのRDBMS『DB2』のデータを、オープンソースRDBMS『PostgreSQL』に移行したときの作業メモです。

作業環境

移行元の環境
  • Windows 10 Pro(64ビット版)
  • DB2 Express-C v10.5
移行先の環境
  • CentOS 6.9(64ビット版)
  • PostgreSQL 9.6.3
  • DB2クライアント v10.5
PostgreSQLはpostgresql.orgよりyumコマンドでインストールしています。

wget http://download.postgresql.org/pub/repos/yum/9.6/redhat/rhel-6-x86_64/pgdg-centos96-9.6-3.noarch.rpm
rpm -ivh pgdg-centos96-9.6-3.noarch.rpm
yum install postgresql96-server

事前準備

移行先環境では、下記の項目が完了している必要があります。

  • Perlが使える状態になっている。
  • Perl ::CSV::XSが使える状態になっている。
  • DB2クライアントが使える状態になっている。
  • PostgreSQLのユーザ(データベースの作成権限、ロールの作成権限付き)が登録されている。
  • PostgreSQLの移行先データベースが作成されている。

DB2クライアントからDB2サーバに接続できるようにする

移行先の環境で、既にDB2サーバへの接続設定済み、または設定が不要の場合は、この部分は読み飛ばしてください。

Qiitでhitさんが公開されている『db2クライアントからdb2サーバに接続する方法』を参考に、DB2クライアントからDB2サーバに接続できるように設定します。

/opt/ibm/db2/V10.5_01/bin/db2 CATALOG TCPIP NODE RMTNODE REMOTE 192.168.1.160リモートサーバのアドレス SERVER リモートサーバのポート番号 OSTYPE リモートサーバのOS種別
/opt/ibm/db2/V10.5_01/bin/db2 CATALOG DATABASE リモートサーバのデータベース名 AS クライアントで参照する際の別名 AT NODE カタログしたリモートノード名 AUTHENTICATION 認証種別

私の環境では下記のように実行しました。

/opt/ibm/db2/V10.5_01/bin/db2 CATALOG TCPIP NODE ノード名 REMOTE 192.168.1.160 SERVER 50000 OSTYPE WIN
/opt/ibm/db2/V10.5_01/bin/db2 CATALOG DATABASE DB2NODB AS RMTDB AT NODE RMTNODE AUTHENTICATION SERVER

DB2移行ツールの導入

Dalibo社がGitHub上で公開している『db2topg』をダウンロードし、移行先環境の適当な場所に展開します。

公開されているソースを実行するとdb2topg.plに2点ほど問題が見つかったので、あらかじめ修正します。

  1. 「iso-8859-15 or utf8 at ./db2topg.pl line 383.」と出力される。

文字コードの推測に失敗すると発生するようです。
使用しない文字コードを対象としないよう、db2topg.plの383行目付近の条件を修正します。

  • 修正前
    my $decoder = guess_encoding($data_guess, qw/iso8859-15 utf8 utf16-le utf16-be/);
    
  • 修正後
    my $decoder = guess_encoding($data_guess, qw/utf8 utf16-le utf16-be/);
    
  1. I don’t understand </****** ※ 「DB2ADMIN」は適切なスキーマ名を設定してください。>

「DB2ADMIN」などは環境によって変化すると思います。
DB2のコメント形式が出現すると発生するようです。
コメント出現時に無視するよう、db2topg.plの1081行目付近に条件を追加します。

  • 修正後
    elsif ($line =~ /^\/\*\*\*\*\*\*/)
    {
        next;
    }

    db2topgに含まれる.plファイルに対して実行権限を付与しています。
    実行権限を付与しない場合、以下の手順では適宜お使いの環境に合わせて読み替えてください。

DB2からDDLなどを出力

DB2から移行対象となるデータベースのDDLなどを出力します。
私の場合、-zオプションを付けて対象スキーマを絞ってしまいましたが、省略するとデータベースをまるごと移行できるようです。

/opt/ibm/db2/V10.5_01/bin/db2look -d DB2のデータベース名 -z DB2の対象スキーマ名 -i DB2のユーザ名 -w DB2のログインパスワード -e -l -xd -o DB2からの出力スクリプトファイル名.sql

/opt/ibm/db2/V10.5_01/bin/db2look -d rmtdb -z db2admin -i db2admin -w db2admin -e -l -xd -o my_db2_sql_script.sql

PostgreSQL変換用のスクリプトを出力

./db2topg.pl -f my_db2_sql_script.sql -o DB2データの出力ディレクトリ -d DB2のデータベース名 -u DB2のユーザ名 -p DB2のログインパスワード

./db2topg.pl -f my_db2_sql_script.sql -o db2pg_migration -d rmtdb -u db2admin -p db2admin

作業ディレクトリの変更

cd DB2データの出力ディレクトリ

cd db2pg_migration

DB2固有の表現の変更または削除

使用している環境によって変化してくると予想しています。
私の環境では「 OCTETS」と 「DEFAULT “SYSIBM”.”DATE(‘9999-12-31’)」がさしあたって不要だったので、sedコマンドで除去しました。

sed -e 's/ OCTETS//g' -e 's/ DEFAULT "SYSIBM"."DATE"('\''9999-12-31'\'')//g' before.sql > _before.sql

変換用スクリプト実行(before – テーブルなどの作成)

psql -e –set=ON_ERROR_STOP=1 –single-transaction -f _before.sql PostgreSQLのデータベース名

psql -e --set=ON_ERROR_STOP=1 --single-transaction -f _before.sql pgdb_migration

before.sqlを実行するとロールが作成されます。
このため、2回目以降の実行エラーが発生し以後の処理が行われません。
回避するため、before.sqlの実行前に作成されたロールの削除が必要になるケースがあります。

psql -e –set=ON_ERROR_STOP=1 –single-transaction -c ‘drop role PostgreSQLのロール名’ -U PostgreSQLのログインユーザ名 -d postgres

psql -e --set=ON_ERROR_STOP=1 --single-transaction  -c 'drop role db2admin' -U postgres -d postgres

DB2からデータをエクスポート

/opt/ibm/db2/V10.5_01/bin/db2 -f export.db2

DB2のデータをPostgreSQLにインサート

../deltocopy.pl -d . | psql -e –set=ON_ERROR_STOP=1 PostgreSQLのデータベース名

../deltocopy.pl -d . | psql -e --set=ON_ERROR_STOP=1 pgdb_migration

変換用スクリプト実行(after)

psql -e –set=ON_ERROR_STOP=1 –single-transaction -f after.sql PostgreSQLのデータベース名

psql -e --set=ON_ERROR_STOP=1 --single-transaction -f after.sql pgdb_migration

変換用スクリプト実行(unsure)

psql -e -f unsure.sql PostgreSQLのデータベース名

psql -e -f unsure.sql pgdb_migration

このステップでは、ビューに対するコメントづけが失敗するケースがあるようです。
ビューそのものの移行は行われるので、特に必要が無ければ無視しても大丈夫だと思います。


ユーザ名やパスワードの設定などが相当ゆるゆるの環境で手順の検証を行いましたが、上記の手順でDB2のデータがPostgreSQLに移行できました。
文字化け等も発生していないように見受けられますが、現在確認を進めている最中です。

PostgreSQLに移行したデータは、publicスキーマではなくDB2で使用していたスキーマ名を引き継いで移行されることに注意が必要です。

検証はしていませんが、DB2サーバにアクセスできPerlが動く環境があれば、Linux環境以外でも実行できそうな感じがします。

#
# DB2サーバが別のサーバで実行されていて、DB2クライアントからDB2サーバに接続する場合に実行
#   RMTNODE 		ノード名
#   192.168.1.xxx	リモートサーバのアドレス
#	50000			リモートサーバのポート番号
#	WIN				リモートサーバのOS種別
# /opt/ibm/db2/V10.5_01/bin/db2 CATALOG TCPIP NODE RMTNODE REMOTE 192.168.1.xxx SERVER 50000 OSTYPE WIN
#
# ----
#	DB2NODB		リモートサーバのデータベース名
#	RMTDB			クライアントで参照する際の別名
#	RMTNODE			カタログしたリモートノード名
#	SERVER			認証種別
# /opt/ibm/db2/V10.5_01/bin/db2 CATALOG DATABASE DB2NODB AS RMTDB AT NODE RMTNODE AUTHENTICATION SERVER

#############################################################################################



# ----
# DB2からDDLなどを出力
# ----
# /opt/ibm/db2/V10.5_01/bin/db2look -d DB2のデータベース名 -z DB2の対象スキーマ名 -i DB2のユーザ名 -w DB2のログインパスワード -e -l -xd -o DB2からの出力スクリプトファイル名.sql
/opt/ibm/db2/V10.5_01/bin/db2look -d RMTDB -z db2admin -i db2admin -w db2admin -e -l -xd -o my_db2_sql_script

# ----
# 念のためUTF8変換
# ----
# nkf --utf8 DB2からの出力スクリプトファイル名.sql > db2.utf8.sql
nkf --utf8 my_db2_sql_script.sql > db2.utf8.sql

# ----
# PostgreSQL変換用のスクリプトを出力
# ----
# ./db2topg.pl -f db2.utf8.sql -o DB2データの出力ディレクトリ -d DB2のデータベース名 -u DB2のユーザ名 -p DB2のログインパスワード
./db2topg.pl -f db2.utf8.sql -o db2pg_migration -d RMTDB -u db2admin -p db2admin

# ----
# PostgreSQL変換用のスクリプトを出力
# ----
# cd DB2データの出力ディレクトリ
cd db2pg_migration

# ----
# 2回目以降の実行の場合、すでに既存ロールdb2adminが存在しているので、これを削除
# ----
# psql -e --set=ON_ERROR_STOP=1 --single-transaction  -c 'drop role PostgreSQLのロール名' -U PostgreSQLのログインユーザ名 -d postgres
psql -e --set=ON_ERROR_STOP=1 --single-transaction  -c 'drop role db2admin' -U postgres -d postgres

# ----
# before.sqlにはDB2固有の表記が含まれているので実行前に" OCTETS"や"SYSIBM"で始まるデフォルト値設定を取り除く
# ----
sed -e 's/ OCTETS//g' -e 's/ DEFAULT "SYSIBM"."DATE"('\''9999-12-31'\'')//g' before.sql > _before.sql

# ----
# 変換用スクリプト実行(before - テーブルなどの作成)
# ----
# psql -e --set=ON_ERROR_STOP=1 --single-transaction -f _before.sql PostgreSQLのデータベース名
psql -e --set=ON_ERROR_STOP=1 --single-transaction -f _before.sql pgdb_migration

# ----
# DB2からデータをエクスポート
# ----
/opt/ibm/db2/V10.5_01/bin/db2 -f export.db2

# ----
# DB2のデータをPostgreSQLにインサート
# ----
# ../deltocopy.pl -d . | psql -e --set=ON_ERROR_STOP=1 PostgreSQLのデータベース名
../deltocopy.pl -d . | psql -e --set=ON_ERROR_STOP=1 pgdb_migration

# ----
# 変換用スクリプト実行(after)
# ----
# psql -e --set=ON_ERROR_STOP=1 --single-transaction -f after.sql PostgreSQLのデータベース名
psql -e --set=ON_ERROR_STOP=1 --single-transaction -f after.sql pgdb_migration

# ----
# 変換用スクリプト実行(unsure)
# Viewのコメント付けが失敗する
# ----
# psql -e -f unsure.sql PostgreSQLのデータベース名
psql -e -f unsure.sql pgdb_migration

カテゴリー: システム開発, データベース, 技術的なメモ | タグ: , , , | コメントする

JavaCVを使って動画ファイルから静止画ファイルを生成する方法

OpenCVやFFmpegなど画像処理関連のライブラリをJavaで使えるようにしたJavaCV( https://github.com/bytedeco/javacv )を使い、動画ファイルを静止画ファイル群に変換するサンプルプログラムです。

サンプル中では動画ファイルとしてMP4形式を指定していますが、AVI形式やWMV形式なども使用できます。
また、出力する形式についてもJPEG形式以外にもPNG形式やMBP形式なども指定できます。

JavaCVのorg.bytedeco.javacv.FFmpegFrameGrabberクラスには、動画ファイルのフレームレートやサイズ、総フレーム数などを取得するプロパティが用意されているのですが、org.bytedeco.javacv.FFmpegFrameGrabber#startを呼び出すまでは、期待する値が返ってきません。

ループ内でorg.bytedeco.javacv.Java2DFrameConverter#convertを用いて動画からフレームごとの画像を切り出す部分も、かなりの頻度でnullが返ってきます。
これが動画ファイルに起因するものなのか、JavaCVまたはOpenCV/FFmpegなどに起因するものなのか、使用方法の誤りか現在のところ特定できていません。

また、org.bytedeco.javacv.FrameGrabber#getFrameNumberを用いてフレーム番号を取得する処理でも重複したフレーム番号が返ってきたり、番号が飛ぶなどの現象が起きましたが、こちらも原因の特定ができていません。

処理を実行すると、かなりの枚数の画像ファイルが生成されディスク容量を消費します。
実行の際にはディスクの空き容量にご注意ください。

動作確認を行った環境:

Java

Java SE Development Kit 8

JavaCV

1.3.2

ライセンス

NYSL

// This software is distributed under the license of NYSL.
// ( http://www.kmonos.net/nysl/ )

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FrameGrabber;
import org.bytedeco.javacv.Java2DFrameConverter;

public class 動画ファイルから静止画を切り出すプログラム {

	public static void main(String[] args) {
		new 動画ファイルから静止画を切り出すプログラム().slice(new File("./mp4/sample.mp4"), new File("./jpeg"));
	}


	/**
	 * 	動画ファイルをフレームごとに切り出し静止画に変換する
	 *	
	 *	@param srcMoviePath	動画ファイルのパス
	 *	@param destJpegPath	静止画ファイルの出力ディレクトリのパス
	 */
	public void slice(final File srcMoviePath, final File destJpegPath) {
		
		try {
			
			FFmpegFrameGrabber frameGrabber= new FFmpegFrameGrabber(srcMoviePath);
			Java2DFrameConverter frameConverter = new Java2DFrameConverter();
			
			frameGrabber.start();
			
			// FFmpegFrameGrabber#startを実行した後から、getFrameRateやgetLengthInFramesなどのプロパティで値が取得できるようになる 
			System.out.println("format:" + frameGrabber.getFormat() + ", size:" + frameGrabber.getImageWidth() + "x"
					+ frameGrabber.getImageHeight() + ", frame-rate:" + frameGrabber.getFrameRate()
					+ ", length-in-frame:" + frameGrabber.getLengthInFrames());

			int count = 0;
			
			// 現在のフレーム番号が動画ファイルの総フレーム数に到達するまでループ
			while (frameGrabber.getFrameNumber() < frameGrabber.getLengthInFrames()) {
				
				// フレームを取りだして
				BufferedImage img = frameConverter.convert(frameGrabber.grab());
				if (img == null) {
					// BufferedImageが取得できないことがあるので、そのときはスキップ
					continue;
				}
				
				// 静止画で出力
				// フレーム番号が重複することがある
			    ImageIO.write(img, "jpg", new File(destJpegPath, "frame-" + frameGrabber.getFrameNumber() + "-" + count + ".jpg"));
			    
			    count++;
			}
			
			frameGrabber.stop();
			frameGrabber.close();
				
		} catch (FrameGrabber.Exception e) {
			e.printStackTrace();;
		} catch (IOException e) {
			e.printStackTrace();;
		}
	}
}
カテゴリー: Java, コードスニペット, 技術的なメモ | タグ: , , | コメントする

Visual Studio for MacのインストールとProfessionalエディション以上にする方法

macOS上で動くVisual Studio、『Visual Studio for Mac』が正式にリリースされました。

前身の統合開発環境『Xamarin Studio』を使っており、Microsoftによるブランド統合によりWindows版Visual Studioと操作感や機能が統一されていくのではないかと期待していたのでこのリリースは楽しみでした。
さっそくインストールしてみます。

まず、Visual Studio for Macのwebサイト( https://www.visualstudio.com/ja/vs/visual-studio-mac/ )からインストーラをダウンロードします。

0

VisualStudioInstaller.dmgがダウンロードされます。
2017/05/10にリリースされたバージョンのインストーラの大きさは27MB弱なので、全部入りの環境ではなく、環境やユーザの選択に応じて必要なソフトウェアをダウンロードしていく方式のようです。

0-1

ダブルクリックするとフォルダが開きます。
フォルダの中に配置されたアイコンをダブルクリックして作業を続けます。

1

インストーラが起動します。
『続行』ボタンをクリックして作業を続行します。

2

インストールを行うソフトウェアを選択します。
このときVisual Studio for Macをインストールしようとしているマシンに、既にインストールされているソフトウェア(Android SDKなど)の確認を行い、重複してインストールされないようになっています。
自分の場合、Xamarin Studioが導入済みなので、iOSとAndoroid向けのXamarin.Formsは更新インストールとなりました。

インストールしたいソフトウェアを選択し、『インストールと更新』ボタンをクリックします。

3

ダウンロードがはじまります。

4-1

ウィザードの下部に表示されているように、インストール中に何度かmacOSのアカウント確認のダイアログが表示されます。
ほったらかしにしておくという訳にはいかないようです。

5

しばらくするとインストールが完了します。

『Visual Studioの開始』ボタンをクリックして、Visual Studioを起動します。

6

Visual Studio for Macが起動されました。

当たり前のことですが、メニューなどのメッセージが日本語で表示されています。
UIで使用する言語は、ユーザー設定のダイアログから変更できます。

7

『新しいソリューションの作成』からVisual Studio for Macで作成できるアプリケーションの種類をチェックします。
「.NET Core」の項目がまぶしいですね。Microsoft純正という安心感は大きいです。

8

しかし、ここで気づきました。

ダウンロードしてインストールすると、Communityエディションとしてインストールされます。
自分はVisual Studio Professionalサブスクリプションを契約しているので、Professionalエディションにあげられないものか気になります。

9

バージョン情報を確認すると、やはりCommunityエディションと表示されます。

10

インストーラの選択を間違えたのではないかと思い、Visual Studioサブスクリプションのポータル画面( http://my.visualstudio.com )を開きます。

使用できるツールの一覧に、Visual Studio for Macが増えています。

20

アイコンをクリックしてみると、ダウンロードやキーの入手の画面が!
さっそくダウンロードします。

21

ダウンロードしたインストーラを開くと… あれ!? さっきと同じ!?
よく見るとダウンロードしたインストーラのファイルサイズも1バイトたりと違わず同じです。

22

当然、インストール対象のソフトの選択画面も同じ。
しかも「インストールや更新の対象はありません。」の注釈付きです。
インストーラではなくアクティベーションで変わりそうな予感。

 23

Visual Studio for Macを再度起動し、『Account』メニューをクリックします。

24

登録されているアカウントが表示されます。
よく見ると「!」が付いていて、『Re-enter credentials…』ボタンがあります。

ボタンをクリックし、Microsoftアカウントの認証を済ませると

25

無事、Professionalエディションになりました!!

26-1

バージョン情報を見てもProfessionalエディションです。
無事解決。

27

そういえば、MicrosoftがXamarinを買収し、Xamarin Studioのアカウント設定でMicrosoftアカウントが使えるようになった頃、同じようなことがあったような…(^-^;

カテゴリー: Mac, 技術的なメモ | タグ: , , , | コメントする

ブラウザのコンソールに「net::ERR_CONNECTION_RESET」「Failed to load resource: ネットワーク接続が切れました。」と表示された場合

webシステムの開発をしていて、ブラウザから比較的大きなファイルをサーバに送信しようとしたときに正常に終了しないケースを発見しました。
具体的には、2MB程度までは複数個のファイルを指定しても問題なく正常終了するのですが、5MB、10MB、15MB…と増やしていくと処理が失敗するようになります。

ブラウザや環境によって表示されるメッセージは異なるようなのですが、開発者向けのコンソールを見ると、Chromeでは「net::ERR_CONNECTION_RESET」、Safariでは「Failed to load resource: ネットワーク接続が切れました。」と表示されます。
その他のブラウザではなにも表示されないケースもあるようです。名称未設定

この原因についてネットで検索してみると、「IPv6を切れ」や「ブラウザのキャッシュを削除しろ」などいろいろな意見がありますが、これだ!という決定打はないようです。

ネットで見つかった情報をもとに対応をしてみたのですが解決できず、いろいろと試してみたところ、私の環境では───

アンチウィルスソフトが原因

になっていることがわかりました。

ウィルス対策として導入している『ESET Cyber Security Pro』(mac用)、『ESET SMART SECURITY』(Windows用)のWebアクセス保護機能が悪さをしていました。
設定画面からこの機能をオフにすると正常に動作するようになりました。

お悩みの方は、一度アンチウィルスソフトやファイアウォールなどの設定を確認してみると良いかもしれません。


今回、フロント側のJavaScript/jQueryからサーバ側のJava/JEEにファイルを送信するという機能を実装していたのですが、問題を発見してから挙動を確かめていると「100%失敗するのではなく、ときどき正常に終了することもある」という開発者泣かせの厄介なケースであることが判明しました。

FirefoxのFirebugアドオンの『ネットワーク』タブでリクエストの状況を見ると、失敗しつつときどき成功という状態が確認できます。
というか、失敗しているときはHTTPステータスコードも返ってきていないっぽい。

スクリーンショット 2017-05-10 19.07.15

とりあえず、手間がかからなく問題を捕まえやすそうなJavaScriptのデバッガを使って処理を呼び出しているところにブレイクポイントをしかけてみます。
空値が返ってきてヒントになりそうな情報がつかめません。ブラウザによってはあらぬところに処理が飛んでいきます。

次に、JEEサーバにデプロイしているwebアプリケーションの適当な場所にブレイクポイントをしかけたところ、「Error uploading: Processing of multipart/form-data request failed.」というメッセージが生じていることを補足できました。
そのメッセージが生成されている場所を追いかけていくと、内部で使用しているApache Commons FileUploadでなにかが起きているところまで辿り着くことができました。

org.apache.commons.fileupload.FileUploadBaseクラスのparseRequestメソッド内で、Streams#copyを使ってHTTPリクエストからの入力ストリームをテンポラリファイル書き出すところで、java.io.IOExceptionが発生し処理が止まります。
例外処理の中ではキャッチしたIOExceptionからgetMessageメソッドで具体的な内容を取得してメッセージ化し、それをorg.apache.commons.fileupload.FileUploadBase.IOFileUploadExceptionに込めてスローさせるので、意図からすると呼び出し側ではスローされた例外を見れば原因がわかりそうです。
しかし、IOFileUploadExceptionのメッセージを見るとnullが設定されており原因は把握できません。(他のケースでは実装者の意図通り原因が表示されるようです。)
そこで、ここにブレイクポイントをしかけると、IOExceptionの具体的な中身はEOFExceptionであることがわかります。
入力ストリームの内容を出力ストリームに送る機能でEOFExceptionが出るということは、ブラウザ側がきちんとデータを送っていないか、間に挟まっているフレームワークが悪さをしているかがまず思い浮かびます。
処理の都合上jQuery.ajax()を使って実装していることも原因として充分に怪しいのですが、直前に対処した問題がまさに後者のパターンであったため、これなのではないかと推測、そして深みへとはまります。

補足したメッセージをネットで検索をしてみるとApache Commons FileUploadのトラッキングサイトに「FILEUPLOAD-262 Processing of multipart/form-data request failed. null」という今回の事象まんまで2014年11月にバグレポートが上がっているものの、未解決状態のまま放置されています。プライオリティもMinor扱い。

とりあえず、事象とキーワードがわかったので、適当な語句を選んで検索を仕掛けてみると、類似する事象はいくつか見つかるのですが、どれもこれも原因と対策がセットになった結論まではでていません。
みんなお手上げ状態なのでしょうか…

普段使わないような設定を変えてみたり、ライブラリに依存しないよう自前のルーチンに置き換えてみたりいろいろと試してみたのですが、原因はもっと単純なところにあった、というしょうもないオチでした。


2017/05/15 追記

Subversionをweb経由で使用していて、Apacheのログに「Could not get next bucket brigade」という記載があった場合、これもESETが原因である可能性があります。
ESETのWebアクセス保護機能を止めたところ問題なく処理が完了するようになりました。

また、きちんと再現テストはしていないのですが「ModSecurity: Error reading request body: Partial results are valid but processing is incomplete」「ModSecurity: Request body (Content-Length) is larger than the configured limit」といった内容が記載がされた場合もESETに起因する可能性がありそうな。

カテゴリー: Java, システム開発, セキュリティ, 技術的なメモ | タグ: , , , , | コメントする