ソフラボの技術ブログ

仕事で使ったプログラミング、サーバー周りで役に立つこと、Webサービス開発に必要な技術情報、モバイル情報を書いてます。わかりやすく見やすくをモットーにしています。

MacBook Pro 2019 でログイン時に画面が乱れる現象

2019年末に6年使ったMacBook Proを2019年モデルに買い替えました。

バタフライキーボードに違和感を覚えながらも、使い続けるとタイピングが心地よくなってきて慣れてきました。

そんな中、使い出してから数回目の起動時に、画面が乱れる現象が発生するようになりました。

画面が乱れる現象とは?

本体起動時にまずパスワード入力画面が表示されます。
そこでパスワードを入力しエンターキーを押すと、データ読み込みの進捗バーが表示されます。
そのバーが半分を少し超えた所で一瞬画面が乱れます。
乱れた後は、何事もなかったかのようにデスクトップが表示されます。

画面が乱れたときの画像は次のような感じです。

f:id:shinsuke789:20200215194138p:plain
MacBook Proの画面の乱れ

ログイン時だけなのであまり気にしてなかったのですが、普通に使っている時も極稀に違うタイプの画面のチラつきが発生します。

対処方法は?

Macでは定番のメンテナンスツール「OnyX」でメンテナンスを行うと画面のチラつきは発生しなくなります。
しかし、数回起動を行うとまた同じような現象が発生します。
特に外部モニターに繋げた後の起動は必ず発生しているような感じです。

www.titanium-software.fr

f:id:shinsuke789:20200215194654p:plain
OnyXのメンテナンス画面


2020/05/16 追記
ディスクの暗号化(FileVault)をオフにすると発生しなくなりました。
f:id:shinsuke789:20200516114819p:plain

まとめ

対処方法を書きましたが、一時的な対処であり根本的な解決には至っていません。

ネットで調べるとMacのハードウェアリセットを試したけど効果がなかったとあったので、リセットはまだ試してません。

何かご存じの方いらっしゃいましたら、コメント頂けたら幸いです。

Gradle4.xから6.xで変更する依存関係の定義

Graldeを4.10.2から6.0.1にバージョンアップしたときに警告やエラーが出たので、そのときに変更した依存関係周りを簡単にまとめました。

個人的にGradleはあまり詳しくないので、今回影響があった部分のみの記載です。

さらに詳細を知りたい方は、参考サイトをご覧ください。

バージョンアップ時のエラー

Gradle6にしてビルド等を走らせると、lombokを使用している場合、認識されずコンパイルが通りません。
いつの間にかGradleの設定方法が変わっていたので、その辺を変更します。

依存関係

ライブラリの依存定義に使う設定名が以下のように変わっています。

compile -> implementation
compile files -> implementation files
runtime -> runtimeOnly
providedCompile -> compileOnly

注釈処理

annotationProcessor」というものを使う必要があります。
以下、Domalombokの場合です。

// Doma
annotationProcessor 'org.seasar.doma:doma:2.19.3' 
implementation 'org.seasar.doma:doma:2.19.3' 

// lombok
annotationProcessor 'org.projectlombok:lombok:1.18.8'
compileOnly 'org.projectlombok:lombok:1.18.8'

Domaの場合、追加で以下も必要です。書き方が変わっています。

task copyDomaResources(type: Sync)  {
    from sourceSets.main.resources.srcDirs
    into compileJava.destinationDir
    include 'doma.compile.config'
    include 'META-INF/**/*.sql'
    include 'META-INF/**/*.script'
}

compileJava {
	dependsOn copyDomaResources
}

警告の確認

古い定義でもそのまま使えますが、バージョンアップ毎に非推奨の定義があり、将来的に削除されます。
スムーズにバージョンアップするためにも、非推奨定義は別の定義に変更しておいた方が良いです。
非推奨や警告定義の確認は、Gradleの各コマンドで「--warning-mode all」を付与すると出力されます。

gradle build --warning-mode all

MW WP Formで登録後に自動で投稿する方法

WordPressのフォーム登録プラグイン「MW WP Form」を使って、登録後に自動で投稿する方法を紹介します。
この記事では、登録フォームの設定方法は割愛し、登録後の自動登録の部分のみ説明します。

環境

WordPress 5.2.4
MW WP Form 4.2.0
任意のテーマ

変更箇所

以下に記載のコードを使用中のテーマにある「function.php」に追加します。

フォームで使用するカスタムフィールドを定義する

管理画面の投稿登録画面に登録フォームで使用するカスタムフィールドの入力欄を追加します。
これを設定することで、投稿時に毎回プルダウンからカスタムフィールドを選択する手間が省かれます。

/**
 * 投稿画面で表示するカスタムフィールドのHTMLを定義します。
 * 第2引数で独自の引数を設定することで、textareaまたはtextを生成するようにしています。
 */
function write_html($post, $args) {
    $field_nm = $args['args'][0];
    $type = $args['args'][1];
	
    if ($type == 'textarea') {
        echo '<textarea name="' . $field_nm . '" style="width: 100%; height: 200px;">' . get_post_meta($post->ID, $field_nm, true) . '</textarea>';	
    }
    if ($type == 'text') {
        echo '<input type="text" name="' . $field_nm . '" style="width: 100%;" value="' . get_post_meta($post->ID, $field_nm, true) . '"/>';
    }

    wp_nonce_field( 'send_field1' , 'nonce_for_field_1' );
}

/**
 * 投稿画面にカスタムフィールドを表示します。
 * 最後の引数に配列で独自の引数を設定することができます。
 */
function add_custom_meta_box() {
    // 第1引数:項目ごとのdivタグにつくid名(被らないように任意の値)
    // 第2引数:登録フォームの各入力項目のname
    // 第3引数:HTMLを生成するコールバック名
    // 第4引数:適用するページの種別(postで)
    // 第5引数:メタボックスの種別(初期値:advanced)
    // 第6引数:メタボックスの優先度(初期値:default)
    // 第7引数:第3引数のコールバックで使う任意の引数(必要であればarrayで定義)
    add_meta_box('custom_field_1','氏名','write_html','post','advanced','default',array('氏名','text'));
    add_meta_box('custom_field_2','メールアドレス','write_html','post','advanced','default',array('メールアドレス','text'));
}
add_action('admin_menu', 'add_custom_meta_box');

フォーム登録後に投稿する

MW WP Formで作成した登録フォームからデータが登録されたら、投稿するようにします。
処理内容は、投稿を新規登録し、カスタムフィールドを別テーブルに登録します。

function update_meta_posts($Mail_admin, $Data) {

    // 登録フォームから入力データを取得
    $form_data = $Data->getInstance();

    // 投稿新規データを作成(タイトルと公開状態の2つの最低限)	
    $post = array(
        'post_title' => $form_data->get('募集タイトル'),
        'post_status' => 'publish'
    );
    // 上記データで投稿を新規登録
    $post_id = wp_insert_post($post, true);

    // 登録フォームのカスタムフィールドを別テーブルに登録
    update_post_meta($post_id, '氏名', $form_data->get('氏名'));
    update_post_meta($post_id, 'メールアドレス', $form_data->get('メールアドレス'));	
}
// アクションフックを追加
//   第1引数:末尾の数字は、登録フォーム作成時のショートコードを設定
//   第2引数:フック実行時の処理
//   第3・4引数:説明がないので用途は不明(固定値??)
add_action('mwform_before_send_admin_mail_mw-wp-form-8', 'update_meta_posts', 10, 2);

参考サイト

mwform_before_send_admin_mail_mw-wp-form-xxx | MW WP Form


エンジニアのためのWordPress開発入門

エンジニアのためのWordPress開発入門

LINQでグルーピングした最大の要素を取得する

C#でグルーピングして最大の要素を取得したかったので、試行錯誤して調べた結果、MaxではなくOrderByDescendingを使うことで実現できました。

まだまだLINQは、使い慣れてないのでちょっと特殊なことをしようとするとハマって時間が取られてしまいました…。そんなんでお困りの方、ご参考に。

少し解説すると、以下のようになります。
GroupBy
・第1引数:元の値を何でグループ化するかを指定する
・第2引数:元の値をグループ化したものに対して、キーと値の2つの引数が使え、値に対しての処理を指定する

OrderByDescending
元の値を第1引数で指定したもので昇順に並べ、Firstを指定することで先頭を取得する

サンプルコード

// 値を持つクラス
public class Hoge
{
    public int Key { get; set; }

    public string Dt { get; set; }
}

// グルーピングして最大を取得する処理
public void Main(string[] args)
{
    // 値の設定
    List<Hoge> list = new List<Hoge> {
	new Hoge() { Key = 1, Dt = "20190101" },
	new Hoge() { Key = 1, Dt = "20190102" },
	new Hoge() { Key = 2, Dt = "20190101" },
	new Hoge() { Key = 3, Dt = "20190310" },
	new Hoge() { Key = 3, Dt = "20190305" },
	new Hoge() { Key = 3, Dt = "20190301" },
	new Hoge() { Key = 4, Dt = "20190101" },
	new Hoge() { Key = 4, Dt = "20190102" },
	new Hoge() { Key = 5, Dt = "20190103" },
    };

    // Key毎にDtが最大の要素を取得
    List<Hoge> retList = list
	.GroupBy(
	    h => h.Key,
	    (k, v) => v.OrderByDescending(o => o.Dt).First())
	.ToList();

    // 結果
    retList.ForEach(f => Console.WriteLine("{0} {1}", f.Key, f.Dt));
}

結果

1 20190102
2 20190101
3 20190310
4 20190102
5 20190103

Key毎にDtが最大の要素がListに格納されています。

WildFlyでgetResourceするとパスが異なる事例と対処方法

GlassFishで動作していたSpringBootアプリを、サーバー移管に伴いWildFlyに切り替えました。

動作確認をしていくと、リソースの取得で404が返ってきて困っていました。

いろいろと調査すると、パスの取得方法がアプリケーション・サーバーによって異なることがあるらしく、その場合の対処方法をまとめました。

環境

WildFly 15.0.1 Final
・Spring Boot 1.3.6 RELEASE
CentOS 7
Eclipse 4.8
・アプリはWARでデプロイ

WilfFly上での問題の挙動

GlassFishでは、アプリをWAR形式でデプロイし動作させていました。
WildFlyでも同じようにして動作させていたのですが、Excelを出力する処理で、404が発生し原因を調査することにしました。

GlassFishでは、次のコードで正常に動作していました。

new File(this.getClass().getResource(srcFullPath).getPath());


このコードのままWildFlyで処理を行うと変なパスが返ってきます。
(ローカルでWARデプロイしたとき)

# getResourceで引数に「"."」を指定した場合
/Users/user_name/dev/eclipse/server/wildfly-15/modules/system/layers/base/org/jboss/as/ejb3/main/timers/

# getResourceで引数に「"report"」のパスを指定した場合
/content/Hoge.war/WEB-INF/classes/com/test/report/  

いろいろと調べてみる

解決しないとマズイ問題なのでめちゃくちゃ調べました。

How to put an external file in the classpath |JBoss Developer
java - WildFly - getting resource from WAR - Stack Overflow
How to load a file in war file |JBoss Developer
How to load external property file in JBoss 7 classpath? - JBoss 7 Configurations

基本、英語のサイトしかヒットしないのでGoogle翻訳を駆使したり、コードから読み取ったりしてました。

サイトには、「それは解決できないよ」と絶望的な回答があったり、「この方法はどう?」と試すものの的外れだったりで、困り果ててました。

そんな中、「解決方法はこれ!」というのがあり、試すと問題なく動作するものがありました!!

解決方法

java - How to load resource files in jboss war - wildfly9.xV - Stack Overflow

getResource()でFileを取得するのではなく、getResourceAsStream()でStreamを取得して使えとのこと。

this.getClass().getResourceAsStream(path);

これですんなりExcelを出力できるようになりました。

Postfixで独自ドメイン用の送信メールサーバーを構築する

Webサービス等の開発でメールを送信することがあると思います。

リリースにあたり独自ドメインも取得してメールを送信すると思いますが、メールサーバーを構築したり、メールボックスを契約するのは手間や費用がかかり大変です。

それらのことをやらなくても、メールを受信したら転送するだけの仕組みがPostfixで可能だったので、それらについてまとめました。

メール送信するにあたり、キャリアメール(docomoausoftbank等)の送信制限対策の設定も記載しましたので、ご活用下さい。

環境

CentOS 7
Postfix 2.10.1

前提条件

独自ドメインを取得していること

今回構築するPostfixの仕様

独自ドメイン宛のメールは、Postfixで受信し、それを指定のメールアドレスにそのまま転送する
・プログラムからのメール送信は、Postfixを経由してダイレクトに送信される
・プログラムからのキャリアへのメール送信は、Postfixで送信間隔を考慮して送信される
・プログラムからのメールは、暗号化して送信される(TLS設定をした場合)

独自ドメインSPFレコードを設定する

SPFレコードとは?

独自ドメインのよるメール送信が、迷惑メール扱いとならないようにドメインの設定でSPFレコードを追加します。

SPFの詳細については以下のリンクを参考にして下さい。
salt.iajapan.org

ドメイン設定でSPFレコードを登録する

SPFレコードは、ドメインを取得したサイトにいって「DNSレコード設定」等のメニューで行います。

設定する値は、以下の表のようになります。
独自ドメインhoge.com」メール送信するサーバーIP「192.168.0.1」の場合)

ホスト名 TYPE TTL VALUE 補足
mail.hoge.com A 3600 192.168.0.1 すでにあれば不要
hoge.com TXT 3600 v=spf1 a:mail.hoge.com ~all 上記のホスト名を指定

TTLは任意の値で大丈夫です。

SPFレコードが有効か確認する

設定が終わったら、しばらく時間をおいて次のサイトでSPFレコードが有効になったか確認します。グリーンの表示ならOKです。
Sender Policy Framework (SPF) Record Lookup - SPF Check - MxToolBox

送信メールサーバーの設定

/etc/postfix/main.cf

# メール受信許可範囲(変更)
inet_interfaces = all
# 接続時に使うプロトコル(変更)
net_protocols = ipv4

## 配送設定
# 遅延メッセージの配送を試行する最小時間間隔
minimal_backoff_time=1m
# 遅延メッセージの配送を試行する最大時間間隔
maximal_backoff_time=10m
# 配送できないものとして送り返すまでに、メッセージがキューに入っている最大時間
maximal_queue_lifetime=1h
# 配送できないと見なすまでに、バウンスメッセージがキューに入っている最大時間
bounce_queue_lifetime=1h
# 遅延されたキューがキューマネージャによってスキャンされる時間間隔
queue_run_delay=30s
# 配送経路ファイルパス
transport_maps = hash:/etc/postfix/transport

## バーチャルドメイン
# バーチャルドメイン
virtual_alias_domains = hoge.com
# バーチャルドメイン設定ファイルパス
virtual_alias_maps = hash:/etc/postfix/virtual

## STARTTLSの設定(メールが暗号化される)
# SMTP TLSセキュリティレベル
smtp_tls_security_level = may
# CAファイルのパス(Let's Encryptを使った場合)
smtp_tls_CAfile = /etc/letsencrypt/live/hoge.com/cert.pem
# TLS情報をログに出力
smtp_tls_loglevel = 1

/etc/postfix/virtual

hoge.com anything # 必須
# 独自ドメイン宛メールを別アドレスに転送する
info@hoge.com hoge@gmail.com
noreplay@hoge.com hoge@gmail.com

ハッシュ化する

postmap /etc/postfix/virtual

キャリアメールの送信制限対策の設定

キャリアメールの送信制限

キャリアメールの送信制限について調べたところ、表のような感じになりました。
内容が古いかもしれないので、間違っている場合はご指摘頂ければうれしいです。

1セッション同時送信 1日最大送信
docomo 100件 1,000通
au 30件 1,000通
softbank 20件 500通
Y!mobile ???件 1,000通

キャリアにメールを送信する場合、独自ドメインSPFレコードを設定することが必須となります。
キャリア毎の制限に引っかかった場合、迷惑メール扱いとなり、メールが届かなくなるので注意して下さい。

Postfixの設定

/etc/postfix/master.cf
docomo-smtp unix -    -    n    -    1    smtp
    -o smtp_destination_concurrency_limit=1
    -o smtp_destination_recipient_limit=1
au-smtp unix -    -    n    -    1    smtp
    -o smtp_destination_concurrency_limit=1
    -o smtp_destination_recipient_limit=1
softbank-smtp unix -    -    n    -    1    smtp
    -o smtp_destination_concurrency_limit=1
    -o smtp_destination_recipient_limit=1
ymobile-smtp unix -    -    n    -    1    smtp
    -o smtp_destination_concurrency_limit=1
    -o smtp_destination_recipient_limit=1
/etc/postfix/transport
docomo.ne.jp    docomo-stmp:
ezweb.ne.jp     au-smtp:
softbank.ne.jp  softbank-smtp:
.softbank.ne.jp softbank-smtp:
.vodafone.ne.jp softbank-smtp:
ymobile.ne.jp   ymobile-smtp:
willcom.com     ymobile-smtp:

ハッシュ化する

postmap /etc/postfix/transport

ポートを開ける

SMTPポート(25)を開けます。

sudo firewall-cmd --add-service=smtp --zone=public --permanent

上記設定がおわったらPostfixを再起動します。

sudo systemctl restart postfix

まとめ

今回、Webサービスをリリースするときに、独自ドメインをお名前.comで取得したところ、メールボックスは別で契約しないといけないことが分かり、メール送信をどうするか悩んでいました。

メールボックスを用意するのは、管理が面倒くさいので何か方法はないかと思って調べていると、Postfixだけでメール処理ができることが分かりました。

構築は簡単にできたのですが、できればこれらもしない方が手間も省けるので、独自ドメインを取得するなら、メールサーバー付きのサービスで取得する方が良いと思いました。

お名前.com以外にスタードメインドメインを管理しているので、スタードメインだとメールサーバー付いてるので、そちらでメール転送設定をすれば、任意のアドレスでメールを受け取れるので手間いらずです。

宣伝

今回リリースしたサービス

mincrew.net

ドメインを契約したところ

おすすめのドメイン契約先



PostgreSQLでDB間でデータをコピーする

PostgreSQLでDB間またはスキーマー間でpd_dumpを使ってコピーを試みたものの、うまくいきませんでした。

調べているとやり方はあるらしいですが、結局手間がかかり\copyの方が早いらしいのでこれで試してみました。

環境

PostgreSQL 9.4
CentOS 6
Mac 11

コピーの流れ

「コピー元DB:A、コピー先DB:B」とします。

1.AでCSVエクスポート
2.Bでデータ削除
3.BでCSVインポート
4.Bでシーケンス初期化

各項番の説明をしていきます。

1.AでCSVエクスポート

ターミナルで次を実行します。

# 通常接続
psql -h Aのホスト -U Aのユーザー名 -d AのDB名 -c "\copy テーブル名 to '/出力先フルパス/テーブル名.csv'  (format csv, delimiter ',', header true);"

# SSL接続
psql "sslmode=require host=Aのホスト user=Aのユーザー名 dbname=AのDB名" -c "\copy テーブル名 to '/出力先フルパス/テーブル名.csv'  (format csv, delimiter ',', header true);"

2.Bでデータ削除

ターミナルでpsqlコマンドでDBに接続し、次のSQLを流してデータを削除します。
psql -c "コマンド"で接続しなくてもできます)

TRUNCATE TABLE テーブル名;

3.BでCSVインポート

エクスポートの「to」を「from」に変えるだけです。
ターミナルで次を実行します。

# 通常接続
psql -h Bのホスト -U Bのユーザー名 -d BのDB名 -c "\copy テーブル名 from '/出力先フルパス/テーブル名.csv'  (format csv, delimiter ',', header true);"

# SSL接続
psql "sslmode=require host=Bのホスト user=Bのユーザー名 dbname=BのDB名" -c "\copy テーブル名 from '/出力先フルパス/テーブル名.csv'  (format csv, delimiter ',', header true);"

4.Bでシーケンス初期化

ターミナルでpsqlコマンドでDBに接続し、次のSQLを流してシーケンスを設定します。
「\copy」でデータをインポートしても、シーケンスは設定されません。
「テーブルの件数+1」とすることで、次のシーケンス値になります。

SELECT SETVAL('シーケンス名',(SELECT COUNT(*)+1 FROM テーブル名),FALSE);

補足

パスワードの省略

psqlを流すとパスワードを聞かれるので毎回入力するのは面倒です。
それを省くため、ターミナル毎に次の環境変数を設定しておく便利です。

export PGPASSWORD=接続先DBのパスワード
各一覧の取得

テーブル一覧やシーケンス一覧を取得するには、ターミナルでDB接続後次のコマンドを入力します。

-- テーブル一覧
\dt

-- シーケンス一覧
\ds

ターミナルだと出力された結果で、Altキーを押しながらマウスで選択すると矩形選択になるります。
テキストエディタ等に貼り付け、置換を駆使してコマンド・SQLを作成すれば作業が捗ります。

超絶おすすめするPostgreSQL書籍!!

[改訂新版]内部構造から学ぶPostgreSQL 設計・運用計画の鉄則 (Software Design plus)

[改訂新版]内部構造から学ぶPostgreSQL 設計・運用計画の鉄則 (Software Design plus)

PostgreSQL全機能バイブル

PostgreSQL全機能バイブル