システム開発を好きになる。

2018年1月14日日曜日

HighSierraでHomebrewのbrew install、brew updateに失敗する場合の対処

1月 14, 2018

オフィス狛 技術部です。

High Sierra(macOS 10.13.2)を搭載したmacで、
久々に Homebrew の brew install を行なったらエラーになりました。
officekoma:~ hogehoge$ brew install python3
Error: /usr/local is not writable. You should change the ownership
and permissions of /usr/local back to your user account:
  sudo chown -R $(whoami) /usr/local
似たような事が前にもありました。
※CocoaPodsでAbort trap: 6が発生した場合の対処方法。

先程のエラーメッセージに「sudo chown -R $(whoami) /usr/local」を実行するように記載があるのでやってみます。
officekoma:~ hogehoge$ sudo chown -R $(whoami) /usr/local
Password:
chown: /usr/local: Operation not permitted
・・・・ダメでした。

こういう時は、再インストールした方が早いです。下記のコマンドでインストールを行います。
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
※詳細は本家サイトもご覧ください。

インストール出来たら、brewを試してみます。
officekoma:~ hogehoge$ brew install python3
(中略)
officekoma:~ hogehoge$ which python3
/usr/local/bin/python3
officekoma:~ hogehoge$ python3 --version
Python 3.6.4
うまく行きました。

macOSのアップデート後に、久しく使っていないツールを使うと、今回のような事が発生しやすいですね。

2018年1月6日土曜日

PHP7 における date.timezone の設定について

1月 06, 2018

オフィス狛 技術部です。

今更感はあるのですが、PHP7から変わった事の内、
date.timezone の設定について記載しようと思います。

以前、『PHPで Fatal error: Uncaught exception 'ErrorException' with message date() が発生した場合の対処方法』
という記事を書いたのですが、
PHP7から、タイムゾーンが指定されていない場合でもエラーにならないようになりました。

お手軽に実行できるのがPHPの良さであると思うので、
デフォルト設定でエラーになるのはちょっと嫌ですよね。
そういう意味だと、良い変更だと思うのですが、日本だと大抵「Asia/Tokyo」なので、
結局設定する事になりますね・・・・。(逆に忘れそう)

新年一発目なので、軽い記事にしました。今年も宜しくお願い致します。

2017年12月16日土曜日

C# で SQL Server 接続時に "SqlCommand.Prepare requires all variable length parameters to have an explicitly set non-zero Size" が発生した場合の対処

12月 16, 2017
オフィス狛 技術部です。

とあるプロジェクト(C#)で、今まで Oracle だけに接続していたのが、SQL Server への接続も追加になりました。
Oracle への接続は「Oracle.ManagedDataAccess.Client」を使っていましたが、
SQL Server への接続は、「System.Data.SqlClient」を使う事になりました。

※本当ならデータベースに依存しないように「System.Data.Common」を使って、
プロバイダファクトリ的に作るべきなのでしょうが、途中から変更するのが大人の事情で難しかったです・・・。

と言う事で、Oracleへの接続クラスをコピーして、使用するクラスなどを変えていざ実行してみると、

    "Message": "An error has occurred.",
    "ExceptionMessage": "SqlCommand.Prepare requires all variable length parameters to have an explicitly set non-zero Size",
    "ExceptionType": "System.ApplicationException",

「可変長パラメータには、必ずゼロ以外のサイズを指定しろ」と言っていますね。
確かに、プログラム的には、
string sql =
        "SELECT login_id" 
  + ",user_name "
  + "FROM authentication_info "
                + "WHERE user_identifier = :user_identifier "
                + "AND hoge_code = :hoge_code ";
〜中略〜
SqlParameter parameter1 = this._cmd.CreateParameter();
parameter1.Value = "123";
parameter1.ParameterName = ":user_identifier";
parameter1.DbType = DbType.String;
SqlParameter parameter2 = this._cmd.CreateParameter();
parameter2.Value = "123";
parameter2.ParameterName = ":hoge_code";
parameter2.DbType = DbType.String;
this._cmd.Parameters.Add(parameter1);
this._cmd.Parameters.Add(parameter2);
となっていて、サイズを指定していません。

「Oracle.ManagedDataAccess.Client」では不要でも、
「System.Data.SqlClient」では必要なんですね。

と言うわけで、プログラムも以下の様に変更します。
string sql =
        "SELECT login_id" 
  + ",user_name "
  + "FROM authentication_info "
                + "WHERE user_identifier = @user_identifier "
                + "AND hoge_code = @hoge_code ";
〜中略〜
SqlParameter parameter1 = this._cmd.CreateParameter();
parameter1.Value = "123";
parameter1.ParameterName = ":user_identifier";
parameter1.DbType = DbType.String;
parameter1.Size = parameter1.Value.ToString().Length;   // 追加
SqlParameter parameter2 = this._cmd.CreateParameter();
parameter2.Value = "123";
parameter2.ParameterName = ":hoge_code";
parameter2.DbType = DbType.String;
parameter2.Size = parameter2.Value.ToString().Length;  // 追加
this._cmd.Parameters.Add(parameter1);
this._cmd.Parameters.Add(parameter2);
これで問題なく実行出来ました。

ちなみに、「Oracle.ManagedDataAccess.Client」でのプリペアドステートメントには「:(コロン)」を使いますが、「System.Data.SqlClient」では「@(アットマーク)」を使います。

今回の話は大した話ではないのですが、
「Oracle.ManagedDataAccess.Client」では問題無かった、と言う部分がハマりどころなので、
わざわざ記事にしてみました。

2017年12月9日土曜日

Android Studio で DexArchiveMergerException: Unable to merge dex が発生した場合の対処

12月 09, 2017
オフィス狛 技術部です。

あるAndroidアプリの改修を行う事になり、Android Studio で gradleの設定を変更しました。
(対応のsdkversionを変更したり、ライブラリの追加など)

いざ、ビルドをすると・・・
Error:Execution failed for task ':app:transformDexArchiveWithExternalLibsDexMergerForDebug'.
> java.lang.RuntimeException: com.android.builder.dexing.DexArchiveMergerException: Unable to merge dex

これだけではちょっと分からないですね・・・。
まずは、エラーの詳細を知る為にビルドの設定を変えましょう。

Preferences...」->「Build, Execution, Deployment」->「Compiler」を選択して、
Command-line Options:」に「--stacktrace」と入力します。

この状態で再度ビルドしてみます。すると・・・
先程より情報が多く表示されました。その中でも注目すべきは、下記の部分です。
Error:com.android.dex.DexException: Multiple dex files define Lorg/apache/http/conn/HttpClientConnectionManager;
これは、Gradleにおける依存関係のエラーで、
dependenciesで指定しているライブラリが別のライブラリから使用されている場合などに出てきます。

結論から言ってしまうと、この時は単純なミスで、dependenciesに同じライブラリを2つ指定していた事がエラーの原因でした。
(記述が離れた場所にあったから気付かなかった・・・)

ただ、実際はそんな単純に解決できない場合もあります。
その場合は、各ライブラリの依存関係を調べてみましょう。

Android Studio のターミナルから下記のコマンドで依存関係を見ます。
./gradlew app:dependencies
この時、
./gradlew: Permission denied
となった場合、「gradlew」に実行権限が付いて可能性があるので、
下記コマンドで実行権限を付けます。
chmod +x gradlew

gradlewの出力は以下の様になります。(抜粋)
+--- com.android.support:appcompat-v7:26.1.0 (*)
+--- com.google.android.gms:play-services-gcm:11.6.2
|    +--- com.google.android.gms:play-services-base:11.6.2
|    |    +--- com.google.android.gms:play-services-basement:11.6.2
|    |    |    +--- com.android.support:support-v4:25.2.0 -> 26.1.0 (*)
|    |    |    \--- com.google.android.gms:play-services-basement-license:11.6.2
|    |    +--- com.google.android.gms:play-services-tasks:11.6.2
|    |    |    +--- com.google.android.gms:play-services-basement:11.6.2 (*)
|    |    |    \--- com.google.android.gms:play-services-tasks-license:11.6.2
|    |    \--- com.google.android.gms:play-services-base-license:11.6.2
|    +--- com.google.android.gms:play-services-basement:11.6.2 (*)
|    +--- com.google.android.gms:play-services-iid:11.6.2
|    |    +--- com.google.android.gms:play-services-base:11.6.2 (*)
|    |    +--- com.google.android.gms:play-services-basement:11.6.2 (*)
|    |    \--- com.google.android.gms:play-services-iid-license:11.6.2
|    \--- com.google.android.gms:play-services-gcm-license:11.6.2
+--- com.android.support:cardview-v7:26.1.0
|    \--- com.android.support:support-annotations:26.1.0
この情報から、各ライブラリの依存関係を確認します。

私の感覚だと、
しばらくメンテしてなかったアプリなどの、
ライブラリのバージョン上げる作業をしている時によく発生する気がします。

メンテは計画的に、ですね。


2017年11月23日木曜日

Oracle Data Provider for .NET (ODP.NET) で ORA-12154 が発生した場合の対処

11月 23, 2017

オフィス狛 技術部です。

弊社では、C# を使ってOracleに接続する際は、
Oracle Data Provider for .NET (ODP.NET) を使用する機会が多いです。
Oracle Clientのインストールが不要で、Oracleに接続出来るのが、やっぱり強みですね。

ただ、Oracle Clientと全く関係ないかと言うと、そうでもないので、トラブルが発生する事があります。

今回、とあるWebサーバーのOracleへの接続設定を変更した際、以下のようなエラーが発生しました。

ORA-12154:TNS: 指定された接続識別子を解決できませんでした

接続情報はプログラム側とWeb.config側で、以下のように設定しています。

[プログラム側]
private OracleConnection _con;
private string _constr = "User Id=komauser;"
           + "Password=komapass;"
           + "Data Source=SRC_TEST";

public void Connect()
{
    try
    {
        this._con = new OracleConnection(this._constr);
        this._con.Open();
    }
    catch (Exception ex)
    {
        throw ex;
    }
}

[Web.config側]
<configuration>
  --(中略)--
  <oracle.manageddataaccess.client>
    <version number="*">
      <dataSources>
        <dataSource alias="SRC_DEV" descriptor="(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=192.168.1.111)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=komadb))) " />
      </dataSources>
    </version>
  </oracle.manageddataaccess.client>
</configuration>

Data Sourceの指定が間違えている」とすぐ気付くかもしません。
ただ、今回はそれに気付かず、数時間悩んでしまいました。

気付かなかった理由として、
今回、変更したのは接続先のIPアドレスだけで、変更前は確かに接続出来ていたからです。
そうなると、もうネットワークとかその辺の設定ミスを疑ってしまいます。

まあ、結局数時間後に気付いたのですが、
では何故変更前はこの状態で接続出来たのか・・・。

実は、ODP.NET(パッケージはOracle.ManagedDataAccess.Client ) が Data Source を判断するのは、優先順位があります。
Oracle Data Provider for .NET, Managed Driver Configuration dataSources Section
によると、最優先は「 Web.config の oracle.manageddataaccess.client タグ」なのですが、
そこに合致しない場合は、「tnsnames.ora」を参照するようです。

と言う訳で、該当のWebサーバーを調べたら・・・「tnsnames.ora」ありました。
そこにしっかりと、
SRC_TEST =
  (DESCRIPTION =
    (ADDRESS_LIST =
      (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.1.110)(PORT = 1521))
    )
    (CONNECT_DATA =
      (SERVICE_NAME = komadb)
    )
  )
と記載されていました。

と言う訳で、
ODP.NET で 接続がうまくいかない場合、Data Source の指定を疑ってみましょう、と言う話でした。


2017年5月27日土曜日

AWS EC2 で Amazon Linux を使用する場合に最初にやること

5月 27, 2017

オフィス狛 技術部です。

AWSでEC2インスタンスを立ち上げる度に、設定すべき事を忘れてしまうので、
備忘録の為にブログに残しておこうと思います。

「aws ec2 やる事」でググると、もっと細くやる事を記載している方も居るので、
しっかりやりたい方は、それらを参考にして下さい。

ここに記載するのは、本当に最低限の設定です。

何はともあれ yum update

まずは yum update です。
$ sudo yum update -y

タイムゾーンの変更

Amazon Linux はデフォルトのタイムスタンプが UTC になっているので、日本時間に変更します。
$ date
2017年  5月  20日 土曜日 02:33:43 UTC
$ sudo ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
$ date
2017年  5月  20日 土曜日 11:35:33 JST

このままでは、サーバーの再起動を行うと UTC に戻ってしまうので、設定を変更します。
$ sudo vim /etc/sysconfig/clock

※「ZONE="UTC"」となっている部分を「ZONE="Asia/Tokyo"」に変更します。

ec2-user の 削除

ec2-user はデフォルトのユーザーなので、セキュリティ上、削除しておいた方が良いです。
(もちろん変わりのユーザーも作成します。)

まずは、新規ユーザー(ofkomauser)を追加します。
$ sudo useradd ofkomauser

鍵などをec2-userからコピーし、権限設定を行います。
$ sudo cp -arp /home/ec2-user/.ssh /home/ofkomauser/
$ sudo chown -R ofkomauser /home/ofkomauser/.ssh

新しいユーザーのパスワードを設定します
$ sudo passwd ofkomauser

sudoersファイルを編集し、新しいユーザーで sudo が使えるようにします。
$ sudo visudo -f /etc/sudoers.d/cloud-init
※「ec2-user」の部分をコメント化、もしくは削除し、新しいユーザーを追加します。
#ec2-user ALL = NOPASSWD: ALL
ofkomauser ALL = NOPASSWD: ALL

# User rules for ec2-user
#ec2-user ALL=(ALL) NOPASSWD:ALL

そして、最後に「ec2-user」を削除します。
$ sudo userdel -r ec2-user

以上が、最低限の設定になります。

EC2の用途によっては、もっと細かい設定は必要ですが、
それはまった追って記事にしようと思います。


2017年5月6日土曜日

Codeigniter で他システムとセッション共有する場合に、$_SESSION が消えてしまう

5月 06, 2017
オフィス狛 技術部です。

同じドメイン配下に存在する別システム(Codeigniter ではないPHPを使ったシステム)と、
セッションを共有する・・・つまり、$_SESSION で値をやりとりする事になったのですが、
Codeigniter 側で $_SESSION から値を取ろうとすると、空になっている現象が発生しました。
単純には行かないのですね、やっぱり。

※ちなみに、Codeigniter のバージョンは 3.1.2 です。

1)セッションIDを調べてみる

セッションIDが変わってしまって、違うセッションを見ているのかと思い、
$_COOKIE を調べたところ、session_id は同じでした。
(同じドメインですし、敢えてセッションを変えるような設定もしていないので)
セッションIDが同じで値が取れないのであれば、どこかで値を消している、という事になります。

2)Codeigniter を使用していないPHPシステムで試してみる

Codeigniter を使用していない、Webサーバー上に置いただけのPHPファイルで試してみたところ、
$_SESSION から値が取れる事が確認出来ました。
という事は、Codeigniter でセッションを消している事になります。

3)Codeigniter の Session.php を調べてみる

というわけで、Codeigniter の System/libraries/Session に存在する、
「Session.php」を調べてみました。

105行目付近に、
$class = $this->_ci_load_classes($this->_driver);
という記述があり、このタイミングで driver を変更しているため、
この処理以降に設定した driver のセッションを参照しているのではないかと予想できます。

※実際、この処理の直前では $_SESSION の値は取得できたが、
直後には値が消えていた事を確認しています。

4)対応方法(暫定)

とりあえず、先程の記述の直前で、
// 追加ここから
session_start();
$session = $_SESSION // 値を一時変数に格納
session_write_close();
// 追加ここまで
$class = $this->_ci_load_classes($this->_driver);
と記載します。実は、session_start 自体はもっと後で行うのですが、
ここで一旦スタートさせて、$_SESSION の値を退避しておきます。
(closeも忘れずに)
そして、今度は、元々あった「session_start」(150行目近辺)の後に、
session_start();
//追加ここから
$_SESSION = $session; // 値を一時変数から再設定
// 追加ここまで
と追加する事で、他システムで設定された $_SESSION を利用する事ができます。

5)本当はどう対応するべきなのか

実は、弊社側のシステムは、セッションをDBに保存する設定にしています。
そしておそらく、他システム側は、セッションはファイル保存していると思います。
(弊社保有のシステムでは無いので、実際は分からないですが・・・)
ですので、$_SESSION が消えてしまうのも、当然と言えば当然なのです。
だとするならば、
こちら側の driver を「files」にして、保存パスを共有した場合、うまくいきそうな気がします。

※ちなみに、設定出来る driver は、本家サイト(翻訳版)に記載してありますが、
  • files(デフォルト; ファイルシステムベース)
  • database
  • redis
  • memcached
となっています。(自分でカスタムセッションドライバも作成可能です。)

今回は、テスト環境含め、弊社が触れる環境では無いので、
色々試せないですが、いずれ社内に環境作って試してみようと思います。