WindowsとMacの両方でローカル配信メールを設定する方法
ローカルからアプリケーションがメール投げようとすると何も設定してないとエラーになっちゃいますよね。 私の職場じゃWindowsとMac使っている人が半々なのですが、どちらの環境でもこのメールの問題を解決したので書いてみます。
Windowsの場合
簡単です。 smtp4devをダウンロードして起動するだけで25ポートをlistenしてくれます。 あとはアプリがメール投げたらこの子がそれを拾ってくれるようになります。
Macの場合
Macの場合は、OSがUnix系なので、postfixを設定します。
設定をすると/Users/{username}/Maildir/
の配下にメールが届くようになります。
こんな感じ。
# command : create setting file for postfix $ sudo vi /etc/postfix/aliases.reg ## discription /(?!^root$|^{username}$)^.*$/ {username} # command : create setting file for postfix $ sudo vi /etc/postfix/transport_maps ## discription /^.*@.*$/ local # command : modify setting file for postfix $ sudo vi /etc/postfix/main.cf ## discription #local_recipient_maps = ↓ #local_recipient_maps = local_recipient_maps = #alias_maps = netinfo:/aliases ↓ #alias_maps = netinfo:/aliases alias_maps = hash:/etc/postfix/aliases,regexp:/etc/postfix/aliases.reg #home_mailbox = Mailbox ↓ #home_mailbox = Mailbox home_mailbox = Maildir/ #luser_relay = admin+$local ↓ #luser_relay = admin+$local luser_relay = {username} # add following description to bottom line transport_maps = pcre:/etc/postfix/transport_maps # command : create aliases's binaly file $ sudo postalias /etc/postfix/aliases # command : reflect $ sudo postfix reload
{username}は各自のユーザ名に置き換えてください。
postfixが起動してないとsudo postalias /etc/postfix/aliases
の箇所でエラーになります。
その場合は、sudo postfix start
してあげてください。
参考にしたサイトはこちら。
https://blog.tagbangers.co.jp/ja/2015/05/22/setting-of-local-e-mail-for-mac
CentOS7でLAMP環境構築(といいつつnginx)
友人のサイトのローカル環境を構築するのにLAMP環境をMacに入れたので、参考にしたサイトをメモ代わりに貼っておく。
CentOS 7 でLAMP(Nginx+MariaDB(MySQL)+PHP)インストールからWordPressを動かすまで(Nginx編) | レンタルサーバー・自宅サーバー設定・構築のヒント
Dockerエキスパート養成読本が出る!
今通ってる現場で、インフラ再構築ってことで今後はドッカー使うらしい。 ってことで勉強しとかないとついていけないので、電子書籍版出たらすぐ買う。
Javaでprivateメソッドをmockに置き換える方法
探していたら、mockitoでは実装してない旨のページを見つけた。
powermockでできるからそっち使ってねって書いてある(ように読める)…○r2”
社内のMさんとOさんに助けを求めたら教えてもらえたのだが、 どうやら、jmockitなるものを使えば、簡単にstaticだろうがfinalだろうがprivateだろうがmockに置き換えられる模様。 ↓教えてもらったサイト↓ qiita.com
やり方を具体例で教えてもらったが、確かに簡単かつ意図したとおりにできた!
ので、今後お世話になりそうなのでメモしておく。
以下、サンプルコード
package test.jmockit; public class JmockitSample { public static void main(String[] args){ System.out.println(new JmockitSample().methodPublic()); System.out.println(methodPublicStatic()); System.out.println(new JmockitSample().callPraivateMethod()); System.out.println(callPrivateStaticMethod()); } public String methodPublic() { return "methodPublic"; } public static String methodPublicStatic() { return "methodPublicStatic"; } public String callPraivateMethod(){ return new JmockitSample().methodPrivate(); } private String methodPrivate() { return "methodPrivate"; } public static String callPrivateStaticMethod(){ return JmockitSample.methodPrivateStatic(); } private static String methodPrivateStatic() { return "methodPrivateStatic"; } }
実行結果
methodPublic methodPublicStatic methodPrivate methodPrivateStatic
テストコード内で4つのメソッドを前もmockに置き換えて、 Mocked{メソッド名}に変更してみる
package test.jmockit; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import mockit.Mock; import mockit.MockUp; import org.junit.Test; public class JmockitSampleTest { @Test public void mockTest(){ new MockUp<JmockitSample>() { @Mock public String methodPublic() { return "mocked methodPublic"; } @Mock public String methodPublicStatic() { return "mocked methodPublicStatic"; } @Mock private String methodPrivate() { return "mocked methodPrivate"; } @Mock private String methodPrivateStatic() { return "mocked methodPrivateStatic"; } }; System.out.println(new JmockitSample().methodPublic() ); System.out.println(JmockitSample.methodPublicStatic() ); System.out.println(new JmockitSample().callPraivateMethod()); System.out.println(JmockitSample.callPrivateStaticMethod() ); assertThat(new JmockitSample().methodPublic(), is("mocked methodPublic")); assertThat(JmockitSample.methodPublicStatic(), is("mocked methodPublicStatic")); assertThat(new JmockitSample().callPraivateMethod(), is("mocked methodPrivate")); assertThat(JmockitSample.callPrivateStaticMethod(), is("mocked methodPrivateStatic")); } }
結果
mocked methodPublic mocked methodPublicStatic mocked methodPrivate mocked methodPrivateStatic
注意点
クラスパスがjunitよりも先に来てないとうまく動かないので、その点だけ注意して下さい。 Eclipseだとこんな感じにすればOK
gradleならこんな感じ
apply plugin: 'java' apply plugin: 'eclipse' sourceCompatibility = '1.7' [compileJava, compileTestJava]*.options*.encoding = 'UTF-8' configurations { jmockit_agent } repositories { mavenCentral() } dependencies { jmockit_agent 'com.googlecode.jmockit:jmockit:1.7' testCompile 'com.googlecode.jmockit:jmockit:1.7' testCompile 'junit:junit:4.11' } test { jvmArgs "-javaagent:${configurations.jmockit_agent.asPath}" }
ちなみにNetBeansの場合は、gradle使ってる場合はgradleに完全にコンパイル任せてるのでbulid.gradleの設定さえしておけば、うまく動きます。
参考にしたサイトその1 knjname.hateblo.jp
参考にしたサイトその2 gist.github.com
Base64でデコード出来ない文字をデコードしようとするとどうなるか?
調べても出てこなかったので、メモ。
何かわかりやすいエラーが出るのかとおもいきや、エラーとわかりやすい挙動はしなかった…orz
答えは『実装による』っぽい。 実際に平文→base64.decode→sha256でhash→base64.encodeの処理をOpensslとJavaの両方で試したら挙動に違いがった。
例えばOpenSSLだとこうなる
$ echo "aaaa" | openssl base64 -d | openssl sha -sha256 -binary | openssl base64 # u6nbXovptodruQ0AGBFeI/x0G6a/IyXn/PiO/tdQxMc= $ echo "aaaa,,,," | openssl base64 -d | openssl sha -sha256 -binary | openssl base64 # 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
aaaa
の場合とaaaa,,,,
の場合で結果が異なる。
,
はbase64で使用していない文字なのでデコード出来ない。
そのため、,
が入るとエラーになって、その結果毎回同じ文字列が返ってきている(ように見える)。
試しにaaaaaaaa,
も試してみる
$ echo "aaaaaaa," | openssl base64 -d | openssl sha -sha256 -binary | openssl base64 # 47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
やっぱり47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
になる。
なぜ47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=
が返ってくるかはOpenSSLの中身を見ていないのでわからない。
Apache Commons Codecだとこうなる
package Base64Test; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import org.apache.commons.codec.binary.Base64; public class Base64TestExecutor { public static void main(String[] agrs) throws NoSuchAlgorithmException{ String target1 = "aaaa"; String target2 = "aaaa,,,,"; String result1 = testDigest(target1); String result2 = testDigest(target2); System.out.println("result1 : " + result1); System.out.println("result2 : " + result2); System.out.println("isEqual : " + result1.equals(result2)); } private static String testDigest(String targetString) throws NoSuchAlgorithmException { byte[] testResult = Base64.decodeBase64(targetString); MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] digestVal = md.digest(testResult); String encPassword = Base64.encodeBase64String(digestVal); return encPassword; } }
実行結果
run: result1 : u6nbXovptodruQ0AGBFeI/x0G6a/IyXn/PiO/tdQxMc= result2 : u6nbXovptodruQ0AGBFeI/x0G6a/IyXn/PiO/tdQxMc= isEqual : true ビルド成功(合計時間: 0秒)
こっちは中身を見てみた。
/** * <p> * Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once * with the data to decode, and once with inAvail set to "-1" to alert decoder that EOF has been reached. The "-1" * call is not necessary when decoding, but it doesn't hurt, either. * </p> * <p> * Ignores all non-base64 characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are * silently ignored, but has implications for other bytes, too. This method subscribes to the garbage-in, * garbage-out philosophy: it will not check the provided data for validity. * </p> * <p> * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach. * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/ * </p> * * @param in * byte[] array of ascii data to base64 decode. * @param inPos * Position to start reading data from. * @param inAvail * Amount of bytes available from input for encoding. * @param context * the context to be used */ @Override void decode(final byte[] in, int inPos, final int inAvail, final Context context) { if (context.eof) { return; } if (inAvail < 0) { context.eof = true; } for (int i = 0; i < inAvail; i++) { final byte[] buffer = ensureBufferSize(decodeSize, context); final byte b = in[inPos++]; if (b == pad) { // We're done. context.eof = true; break; } else { if (b >= 0 && b < DECODE_TABLE.length) { final int result = DECODE_TABLE[b]; if (result >= 0) { context.modulus = (context.modulus+1) % BYTES_PER_ENCODED_BLOCK; context.ibitWorkArea = (context.ibitWorkArea << BITS_PER_ENCODED_BYTE) + result; if (context.modulus == 0) { buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 16) & MASK_8BITS); buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS); buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS); } } } } } // Two forms of EOF as far as base64 decoder is concerned: actual // EOF (-1) and first time '=' character is encountered in stream. // This approach makes the '=' padding characters completely optional. if (context.eof && context.modulus != 0) { final byte[] buffer = ensureBufferSize(decodeSize, context); // We have some spare bits remaining // Output all whole multiples of 8 bits and ignore the rest switch (context.modulus) { // case 0 : // impossible, as excluded above case 1 : // 6 bits - ignore entirely // TODO not currently tested; perhaps it is impossible? break; case 2 : // 12 bits = 8 + 4 context.ibitWorkArea = context.ibitWorkArea >> 4; // dump the extra 4 bits buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS); break; case 3 : // 18 bits = 8 + 8 + 2 context.ibitWorkArea = context.ibitWorkArea >> 2; // dump 2 bits buffer[context.pos++] = (byte) ((context.ibitWorkArea >> 8) & MASK_8BITS); buffer[context.pos++] = (byte) ((context.ibitWorkArea) & MASK_8BITS); break; default: throw new IllegalStateException("Impossible modulus "+context.modulus); } } }
final int result = DECODE_TABLE[b];
の箇所でDECODE出来ない文字の場合は-1
が返される。
そのため、result >= 0
はfalse
になるので、その文字は読み飛ばされる。
その結果、例えばa
とa,,,
の結果はおなじになる。
こういうのは実際に試して動作確認して、中身のソースまで見てみないとわかんないですね!
旧来型IT企業の罪
すごく身に覚えがある。。。 抜け出してよかったなってすごく思う。 http://treeapps.hatenablog.com/entry/2014/04/29/141007