m_shige1979のときどきITブログ

プログラムの勉強をしながら学習したことや経験したことをぼそぼそと書いていきます

Github(変なおっさんの顔でるので気をつけてね)

https://github.com/mshige1979

Spring Bootでログを出力する

デバッグ中の場合とかにデータの動きを少しずつ確認したい場合など

ログを出力して確認します。
System.out.printlnではよくわからない場合など…

サンプル

Sample01Controller
package com.example.controller;

import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.example.dto.sample01.Sample01ReqDto;
import com.example.dto.sample01.Sample01ResDto;

@RestController
public class Sample01Controller {
	
	private static final Logger logger = LoggerFactory.getLogger(Sample01Controller.class);
	
	@ResponseBody
	@RequestMapping(value = "/sample01/test1", method = {RequestMethod.POST}, consumes = MediaType.APPLICATION_JSON_VALUE)
	public Sample01ResDto test1(@RequestBody Sample01ReqDto req) {
		Sample01ResDto res = new Sample01ResDto();
		
		logger.info("log sample1");
		logger.debug("log sample2");
		logger.warn("log sample3");
		logger.error("log sample4");
		
		return res;
	}
	
}

2017-01-15 16:14:58.197  INFO 35008 --- [nio-8080-exec-1] c.example.controller.Sample01Controller  : log sample1
2017-01-15 16:14:58.197  WARN 35008 --- [nio-8080-exec-1] c.example.controller.Sample01Controller  : log sample3
2017-01-15 16:14:58.197 ERROR 35008 --- [nio-8080-exec-1] c.example.controller.Sample01Controller  : log sample4

※debug以外は確認できる。おそらくなんか表示条件が違うのでしょう…

今回はとりあえずここまで…

JavaのFreeMakerでテンプレート処理を行う2

インクルード処理を使う

1つのテンプレートの中に他のテンプレートファイルを読み込みます。

pom.xml

  <dependencies>
    
    <!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
    <dependency>
        <groupId>org.freemarker</groupId>
        <artifactId>freemarker</artifactId>
        <version>2.3.23</version>
    </dependency>

  </dependencies>
ml|

テンプレートファイル

sample2.ftl
<#-- header -->
<#include "parts/header.ftl">

<#-- contents -->
${contents}

<#-- footer -->
<#include "parts/footer.ftl">
parts/header.ftl
----------------------
header情報です。
----------------------
${header}
parts/footer.ftl
----------------------
footer情報です。
----------------------
${footer}

実装

Sample02.java
package com.example;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

public class Sample02 {

	public static void main(String[] args) throws IOException, TemplateException {
		
		// 初期化
		Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
		
		// ディレクトリを指定
		cfg.setDirectoryForTemplateLoading(new File("src/main/resources/template"));
		
		// テンプレートファイルを指定
		Template temp = cfg.getTemplate("sample2.ftl");
		
		// データモデル
		Map<String, String> root = new HashMap<String, String>();
		root.put("header", "ヘッダーのデータを表示中");
		root.put("contents", "テストデータです。");
		root.put("footer", "フッターのデータを表示中");
		
		// テンプレート処理
		Writer out = new StringWriter();
		temp.process(root, out);
		out.flush();
		
		// 結果出力
		String displayStr = out.toString();
		System.out.print(displayStr);
	}
}

※ヘッダー、フッターのものも一緒に定義する必要がある。

header情報です。
----------------------
ヘッダーのデータを表示中

テストデータです。

----------------------
footer情報です。
----------------------
フッターのデータを表示中

うん、想定どおりで安心しました。

今回はここまで…

JavaのFreeMakerでテンプレート処理を行う

テンプレート処理

HTMLなどでヘッダー部分やフッター部分など
ある程度共通の部分は別ファイルで管理して読み込みたいことがありますのでテンプレートとして使用します。

FreeMaker

Javaのテンプレートエンジンとのこと
仕事でJava使うのはほとんどなかったので調べてみる。

設定

pom.xml
  <dependencies>
    
    <!-- https://mvnrepository.com/artifact/org.freemarker/freemarker -->
    <dependency>
        <groupId>org.freemarker</groupId>
        <artifactId>freemarker</artifactId>
        <version>2.3.23</version>
    </dependency>

  </dependencies>

テンプレートファイル

sampl1.ftl
<#-- name -->
name: ${name}
<#-- age -->
age: ${age}

<#-- biko -->
<#if biko?has_content>
biko: ${biko}
</#if>

うん、まあ意味は分かる。変数を割り当てるような感じ

実装

Sample01.java
package com.example;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.Map;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

public class Sample01 {

	public static void main(String[] args) throws IOException, TemplateException {
		
		// 初期化
		Configuration cfg = new Configuration(Configuration.VERSION_2_3_23);
		
		// ディレクトリを指定
		cfg.setDirectoryForTemplateLoading(new File("src/main/resources/template"));
		
		// テンプレートファイルを指定
		Template temp = cfg.getTemplate("sample1.ftl");
		
		// データモデル
		Map<String, String> root = new HashMap<String, String>();
		root.put("name", "hoge");
		root.put("age", "100");
		root.put("vvvv", "ddddd");	// 未定義のものは設定してもエラーにならない
		
		// テンプレート処理
		Writer out = new StringWriter();
		temp.process(root, out);
		out.flush();
		
		// 結果出力
		String displayStr = out.toString();
		System.out.print(displayStr);
	}
}

※標準出力に返すサンプルが多かったけどあんまり好きじゃないので文字列に設定してみました。

name: hoge
age: 100

できました(^^)

所感

とりあえず、こんな感じ
これだけじゃああまり有用ではないので他のファイルを読み込めることを調べてみる

AWSのSESでメールを送信2

SESでメールを送信するもう1つの方法

を試してみる

サンプル

Sample02.java
import java.util.Properties;

import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.simpleemail.AWSJavaMailTransport;
import com.amazonaws.services.simpleemail.AmazonSimpleEmailService;
import com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClient;
import com.amazonaws.services.simpleemail.model.ListVerifiedEmailAddressesResult;
import com.amazonaws.services.simpleemail.model.VerifyEmailAddressRequest;

import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

public class Sample02 {
	
    private static final String ACCESS_KEY = "アクセスキー";
    private static final String SECRET_ACCESS_KEY = "シークレットアクセスキー";
	
    public static void main(String[] args) throws AddressException, MessagingException {
		
        String TO = "送信先のメール";
        String FROM = "送信元のメール";
        String SUBJECT = "テストメール";
        String BODY = "テストメールです。";
		
        // 認証
        AWSCredentials credentials = new BasicAWSCredentials(ACCESS_KEY, SECRET_ACCESS_KEY);
        AmazonSimpleEmailService ses = new AmazonSimpleEmailServiceClient(credentials);
	ses.setRegion(Region.getRegion(Regions.US_WEST_2));

        // 送信元認証
        verifyEmailAddress(ses, FROM);
        verifyEmailAddress(ses, TO);
	    
        // 設定
        Properties props = new Properties();
        props.setProperty("mail.transport.protocol", "aws");
        props.setProperty("mail.aws.user", credentials.getAWSAccessKeyId());
        props.setProperty("mail.aws.password", credentials.getAWSSecretKey());
        props.setProperty("mail.aws.host", "email.us-west-2.amazonaws.com");
        Session session = Session.getDefaultInstance(props);
	 
        // メッセージ作成
        Message msg = new MimeMessage(session);
        msg.setFrom(new InternetAddress(FROM));
        msg.addRecipient(Message.RecipientType.TO, new InternetAddress(TO));
        msg.setSubject(SUBJECT);
        msg.setText(BODY);
        msg.saveChanges();
	 
        // 送信
        Transport t = new AWSJavaMailTransport(session, null);
        try {
            t.connect();
            t.sendMessage(msg, null);
        } finally {
            t.close();
        }
    }
    
    // 認証
    private static void verifyEmailAddress(AmazonSimpleEmailService ses, String address) {
        ListVerifiedEmailAddressesResult verifiedEmails = ses.listVerifiedEmailAddresses();
        if (verifiedEmails.getVerifiedEmailAddresses().contains(address)) return; 
        ses.verifyEmailAddress(new VerifyEmailAddressRequest().withEmailAddress(address));
    }
}

認証していない場合

認証メールを送信して認証することで対応可能とのこと

hostでリージョンのエンドポイントを指定する。

何も指定していないとus-east-1でしか送信していない感じに見える。

AWSのSESでメール送信

SESはメール送信のサービスだった

忘れてた…
受信できるように設定できる以上、送信できるはず…

やること

  • SES用のアクセスキーを生成
  • メール送信制限を解除
  • リージョンを指定

実装

Sample01.java
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClient;
import com.amazonaws.services.simpleemail.model.Body;
import com.amazonaws.services.simpleemail.model.Content;
import com.amazonaws.services.simpleemail.model.Destination;
import com.amazonaws.services.simpleemail.model.Message;
import com.amazonaws.services.simpleemail.model.SendEmailRequest;

public class Sample01 {
	
	private static final String ACCESS_KEY = "アクセスキー";
	private static final String SECRET_ACCESS_KEY = "シークレットアクセスキー";
	
	public static void main(String[] args) {
		
		String TO = "送信先のメールアドレス";
		String FROM = "SESで認証したメールアドレス";
		String SUBJECT = "テストメール";
		String BODY = "テストメールです。";
		
		// 宛先
		Destination destination = new Destination().withToAddresses(new String[]{TO});
	    // 件名
	    Content subject = new Content().withData(SUBJECT);
		// 本文
	    Body body = new Body().withText(new Content().withData(BODY));
	    
	    // メッセージ
	    Message message = new Message()
	        .withSubject(subject)
	        .withBody(body);
	    
	    // リクエストを作成
	    SendEmailRequest request = new SendEmailRequest()
	        .withSource(FROM)
	        .withDestination(destination)
	        .withMessage(message);

	    // 認証
	    AmazonSimpleEmailServiceClient client = new AmazonSimpleEmailServiceClient(
	    		new  BasicAWSCredentials(ACCESS_KEY, SECRET_ACCESS_KEY));
	    
	    // リージョンを設定
	    client.setRegion(Region.getRegion(Regions.US_WEST_2));
	    
	    // メール送信
	    client.sendEmail(request);
		
	}

}

リージョンで送信できない場合がある

送信解除をしても対象のリージョンを指定していない場合は送信できないかも…

Exception in thread "main" com.amazonaws.services.simpleemail.model.MessageRejectedException: Email address is not verified. The following identities failed the check in region US-EAST-1: 送信先のメールアドレス,解除したメールアドレス (Service: AmazonSimpleEmailService; Status Code: 400; Error Code: MessageRejected; Request ID: f61b711c-da49-11e6-b2e2-ff93b2bc0127)
	at com.amazonaws.http.AmazonHttpClient$RequestExecutor.handleErrorResponse(AmazonHttpClient.java:1586)
	at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeOneRequest(AmazonHttpClient.java:1254)
	at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeHelper(AmazonHttpClient.java:1035)
	at com.amazonaws.http.AmazonHttpClient$RequestExecutor.doExecute(AmazonHttpClient.java:747)
	at com.amazonaws.http.AmazonHttpClient$RequestExecutor.executeWithTimer(AmazonHttpClient.java:721)
	at com.amazonaws.http.AmazonHttpClient$RequestExecutor.execute(AmazonHttpClient.java:704)
	at com.amazonaws.http.AmazonHttpClient$RequestExecutor.access$500(AmazonHttpClient.java:672)
	at com.amazonaws.http.AmazonHttpClient$RequestExecutionBuilderImpl.execute(AmazonHttpClient.java:654)
	at com.amazonaws.http.AmazonHttpClient.execute(AmazonHttpClient.java:518)
	at com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClient.doInvoke(AmazonSimpleEmailServiceClient.java:3340)
	at com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClient.invoke(AmazonSimpleEmailServiceClient.java:3316)
	at com.amazonaws.services.simpleemail.AmazonSimpleEmailServiceClient.sendEmail(AmazonSimpleEmailServiceClient.java:2345)
	at Sample01.main(Sample01.java:50)

ちょっとむずかしいね

(´・ω・`)

Javaで2つの期間より差を検出する。

2つの日時より日数を取得したい

ので簡単なやつを調査

年月日、時分秒の差を取得するサンプル

Sample02.java
package sample02;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class Sample02 {
	
	static final String FORMAT = "yyyy-MM-dd HH:mm:ss.SSS";
	
	public static void main(String[] args) {
		// 2つのLocalDateTime
		LocalDateTime dt1;
		LocalDateTime dt2;
		
		dt1 = parse("2017-12-10 09:10:30.432");
		dt2 = parse("2022-03-16 12:45:12.102");
		
		System.out.println("期間:" + dt1 + "〜" + dt2);
		System.out.println();
		
		// 日数計算
		long localDiffDays1 = ChronoUnit.DAYS.between(dt1, dt2);
		System.out.println("日数:" + localDiffDays1);
		
		// 月数計算
		long localDiffDays2 = ChronoUnit.MONTHS.between(dt1, dt2);
		System.out.println("月数:" + localDiffDays2);
		
		// 年数計算
		long localDiffDays3 = ChronoUnit.YEARS.between(dt1, dt2);
		System.out.println("年数:" + localDiffDays3);
		
		// 秒計算
		long localDiffDays4 = ChronoUnit.SECONDS.between(dt1, dt2);
		System.out.println("秒 :" + localDiffDays4);
		
		// 分計算
		long localDiffDays5 = ChronoUnit.MINUTES.between(dt1, dt2);
		System.out.println("分 :" + localDiffDays5);
		
		// 時間計算
		long localDiffDays6 = ChronoUnit.HOURS.between(dt1, dt2);
		System.out.println("時 :" + localDiffDays6);
	}
	
	public static LocalDateTime parse(String date) {
		return LocalDateTime.parse(date, DateTimeFormatter.ofPattern(FORMAT));
	}
	
}

期間:2017-12-10T09:10:30.432〜2022-03-16T12:45:12.102

日数:1557
月数:51
年数:4
秒 :134537681
分 :2242294
時 :37371

ChronoUnit

こいつでそれぞれのデータ型に合わせてLocalDateTimeのデータを設定することで2つの日時の差を取得できるようです。
結構シンプル(^^)

参考

Javaで日時を扱う(Java8) - Qiita

今回はここまで

Javaで暗号化処理

なんか一括でやるのないの?

ライブラリとしてはありそうですけどJava共通ではなさそう

参考資料

qiita.com
基本↑見て

やりたいこと

指定の文字列にソルト文字列とかを設定して変な文字列にしたい…

実装

Sample01.java
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.UUID;

public class Sample01 {
	
//	private static final String ALGORITHM = "SHA-256";
//	private static final String ALGORITHM = "SHA-384";
	private static final String ALGORITHM = "MD5";
	
	public static void main(String[] args) {
		
		for(int i=0;i<10;i++) {
			String res = null;
			String id = UUID.randomUUID().toString();
			DateTimeFormatter f = DateTimeFormatter.ofPattern("yyyyMMddHHmmssSSS");
			String time = String.valueOf(LocalDateTime.now().format(f));
			String value = id + time;
			
			//
			res = value + " -> " + hashValue(value);
			System.out.println(res);
		}

	}
	
	public static String hashValue(String id) {
		String res = null;
		StringBuilder sb = null;
		
		try {
			MessageDigest md = MessageDigest.getInstance(ALGORITHM);
			md.update(id.getBytes());
			sb = new StringBuilder();
		    for (byte b : md.digest()) {
		        String hex = String.format("%02x", b);
		        sb.append(hex);
		    }
		    res = sb.toString();
		    
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		}
		
		return res;
	}

}

e1242272-9bf8-442d-8117-9e3ad4b3647720170111232904271 -> 521ef152cb07f806660753e403095556
c30a9dfa-e253-4972-b3aa-4a76b6471faa20170111232904288 -> c0463468c4615db586e57a1453b3c1fe
a2f03642-2ac3-4b37-b02a-fd0722224abc20170111232904289 -> 0a6a0cfafed084796377e6047884422e
411000d1-b143-4742-bf05-6531ba1c16a520170111232904291 -> 1e21f51ec30dc0d8f17f17018b87855b
fbac2ec4-8502-48b7-a551-20209346272120170111232904292 -> f34c894ee82d26761773b7be4e32e3e8
cf5c4f1c-d3f3-4648-aaf6-7cbeb1cdbe5720170111232904293 -> 38ad47a89b2f9407fee73605245a85ac
0656800a-1ec1-4ba7-9260-195a769d620820170111232904294 -> 1ce74a1b8862d761d538ec280b8adac3
1f9c7c16-58ad-4991-8ba8-f9fa3d0564ca20170111232904295 -> d0b63e2b96bbfedf16d41aa3142261ba
05f11ff3-be44-4a76-ad46-31534d466e9f20170111232904296 -> 62beae12ce3b618c842971995c4737d0
06534f7f-c6e1-4c52-be91-233bb6477c2320170111232904297 -> 140ba0d50f0a32ae5631a34e77aae581

今回は乱数文字に日時を付与して暗号化してみた
使い捨ての暗号ならこのレベルで良いはず…