ソフラボの技術ブログ

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

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全機能バイブル

Gradle 4.x で「Could not resolve all artifacts for configuration」のエラーが出たときの対処方法

個人的にGradleはよく分かってないので突っ込まれると回答できません。ご了承下さい。

※ 一概に「Could not resolve all artifacts for configuration」のエラーが出たら、ここの対処をすれば良いと言うわけではない点にご注意下さい。

環境

Mac
Spring Boot 1.3.6
Gradle 4.9 (Homebrew)

エラー内容

gradle eclipse等を実行すると以下のエラーが発生する。

* What went wrong:
A problem occurred configuring root project 'Test'.
> Could not resolve all artifacts for configuration ':classpath'.
   > Could not find org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}.
     Required by:
         project :

build.gradle(一部抜粋)

buildscript {
    ext {
        springBootVersion = '1.3.6.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}'
        // Gradle4対応
        classpath 'io.spring.gradle:dependency-management-plugin:0.6.0.RELEASE'
    }
}

対処方法

build.gradle(一部抜粋)

buildscript {
    ext {
        springBootVersion = '1.3.6.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        // 変数ではなく固定値にする
        classpath 'org.springframework.boot:spring-boot-gradle-plugin:1.3.6.RELEASE'
        // Gradle4対応
        classpath 'io.spring.gradle:dependency-management-plugin:0.6.0.RELEASE'
    }
}

Gradle徹底入門 次世代ビルドツールによる自動化基盤の構築

Gradle徹底入門 次世代ビルドツールによる自動化基盤の構築

Spring Boot 1.3.x で Gradle 4.x に更新してビルドするとエラーが出る場合の対処法

Gradleのバーションを久々に更新したら、一部のプロジェクトでエラーが出てました。
その時の対処を紹介します。

環境

Mac
Eclipse 4.5
Gradle 4.9(Homebrew)
Spring Boot 1.3.6

エラー内容

gradle eclipseを実行するとbuild.gradleの15行目でエラーが発生と表示されます。

$ gradle eclipse
FAILURE: Build failed with an exception.

* Where:
Build file '/Users/Applications/eclipse/workspace/Test/build.gradle' line: 15

* What went wrong:
A problem occurred evaluating root project 'Test'.
> Failed to apply plugin [class 'io.spring.gradle.dependencymanagement.DependencyManagementPlugin']
   > Could not create task of type 'DependencyManagementReportTask'.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.

* Get more help at https://help.gradle.org

BUILD FAILED in 0s

build.gradle

問題の15行目は、apply 'spring-boot'の宣言です。

buildscript {
	ext {
		springBootVersion = '1.3.6.RELEASE'
	}
	repositories {
		mavenCentral()
	}
	dependencies {
		classpath 'org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}'
		// Gradle4対応
		classpath 'io.spring.gradle:dependency-management-plugin:0.6.0.RELEASE'
	}
}

apply plugin: 'java'
apply plugin: 'eclipse-wtp'
apply plugin: 'spring-boot'  // line: 15

以下略

対処法

buildscript {
	ext {
		springBootVersion = '1.3.6.RELEASE'
	}
	repositories {
		mavenCentral()
	}
	dependencies {
		classpath 'org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}'
		//  追加
		classpath 'io.spring.gradle:dependency-management-plugin:0.6.0.RELEASE'
	}
}

apply plugin: 'java'
apply plugin: 'eclipse-wtp'
apply plugin: 'spring-boot' 
// 追加
apply plugin: 'io.spring.dependency-management'

WildFlyにJerseyアプリをデプロイするとエラーが発生するときの対処方法

WildFlyにSpring Bootで作成したJerseyを含むWebアプリをデプロイしようとすると、エラーが発生しデプロイができない現象に遭遇しました。
その時の対処方法をまとめました。

環境

CentOS 6
WildFly 11
Spring Boot 2.0.4
Jersey 2.26

原因

デプロイできない原因は、WildFly側にJAX-RSのライブラリがあり競合するためです。

対処方法

Webアプリ側にjboss-deployment-structure.xmlを用意し以下のように定義する。

jboss-deployment-structure.xml

<?xml version="1.0"?>
<jboss-deployment-structure xmlns="urn:jboss:deployment-structure:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <deployment>
        <exclude-subsystems>
            <subsystem name="jaxrs" /> <!-- 追記する -->
        </exclude-subsystems>
    </deployment>
</jboss-deployment-structure>

jboss-deployment-structure.xmlをwarに含める場合、WEB-INF配下に置くようにします。

Gradleで以下のように定義しwarビルドすると、自動的にWEB-INF配下にコピーされます。

war {
    from('src') {
        include 'jboss-deployment-structure.xml'
        into 'WEB-INF'
    }
}

開発環境では、xmlファイルをsrcディレクトリ直下に置いておきます。

注意事項

すでにデプロイできない事象に遭遇している場合、上記対応を行ってもデプロイ時にエラーが発生します。
エラーなのにデプロイ時のゴミは残ったままなので、それらを削除する必要があります。

standalone.xmlを修正する

ファイルの場所:wildfly-root/standalone/configuration/standalone.xml

タグがある所を探し(ファイルの最後あたり)、デプロイに失敗したアプリ名の箇所を削除します。

<deployments>
    <deployment name="postgresql-9.4.1212.jar" runtime-name="postgresql-9.4.1212.jar">
        <content sha1="38931d70811d9bfcecf9c06f7222973c038a12de"/>
    </deployment>

    <!-- 対象のアプリ名部分を削除する -->
    <deployment name="Test.war" runtime-name="Test.war">
        <content sha1="fcb6ff1bf0777ce6b276f5067903db03c1addc3d"/>
    </deployment>
    <!-- ここまで -->
</deployments>

関連ファイルを削除する

WildFlyインストールディレクトリからfindコマンドで対象のアプリ名で検索をかけます。
それでヒットしたファイル、ディレクトリを削除します。
忘れましたが、warファイルとそれらを展開したクラスファイル等があるディレクトリを削除すれば良いです。

MacでAndroid Studioを初回起動、新規プロジェクトを作成するまで

初めてAndroid Studioをインストールしてみました。
同じような方向けにどんな画面なのか、画像メインで記事を作成してみました。

アプリ自体開発したことないので、突っ込まれても回答できないのでご了承下さい。

セットアップ・ウィザード

1.ようこそ
f:id:shinsuke789:20180827131900p:plain

2.インストールタイプの選択。分からなければ標準を選択しておけばOK。
f:id:shinsuke789:20180827131907p:plain

3.表示色の設定
f:id:shinsuke789:20180827131920p:plain

4.設定の確認。SDKのダウンロードなど必要なファイル群をさらにダウンロードされます。
f:id:shinsuke789:20180827132504p:plain

5.各種ダウンロード中
f:id:shinsuke789:20180827132518p:plain

6.ダウンロード完了
f:id:shinsuke789:20180827132533p:plain

新規プロジェクト作成

1.やりたいことを選択。ここでは新規プロジェクト作成を選択。
f:id:shinsuke789:20180827122210p:plain

2.必要事項を編集。kotlinを選択してみた。
f:id:shinsuke789:20180827132603p:plain

3.開発する対象デバイスを設定する
f:id:shinsuke789:20180827132614p:plain

4.アクティビティーの追加
f:id:shinsuke789:20180827132626p:plain

5.アクティビティーの設定
f:id:shinsuke789:20180827132637p:plain

6.コンポーネントインストーラ
f:id:shinsuke789:20180827132650p:plain

7.ビルド中
f:id:shinsuke789:20180827132702p:plain

8.コーディング画面表示
f:id:shinsuke789:20180827132714p:plain

※ プロジェクトは「~/AndroidStudioProjects」に作成されます。