Quantcast
Channel: プログラム の個人的なメモ
Viewing all 860 articles
Browse latest View live

【SQL】複数の集計を1回のSQL文で行う方法を考える

$
0
0

■ はじめに

 * 例えば、全体数とその母数からさらに条件を付けてカウントしたい場合に
   それをサブクエリを使わずに、一回で行う方法を考える

解決案

COUNT (条件 OR null)
でカウント可能。

■ 実行環境

 * DB : MySQL5.7

■ サンプルデータ

テーブル

-- 学生
CREATE TABLE IF NOT EXISTS `student` (
	`id` INT(11) NOT NULL AUTO_INCREMENT,
	`name` VARCHAR(50) NULL DEFAULT NULL,
	PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=1
;

-- 科目
CREATE TABLE IF NOT EXISTS `subject` (
	`id` INT(11) NOT NULL AUTO_INCREMENT,
	`name` VARCHAR(10) NOT NULL,
	PRIMARY KEY (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=1
;

-- 点数
CREATE TABLE IF NOT EXISTS `score` (
	`id` INT(11) NOT NULL AUTO_INCREMENT,
	`date` DATE NOT NULL,
	`student_id` INT(11) NOT NULL,
	`subject_id` INT(11) NOT NULL,
	`score` INT(11) NOT NULL DEFAULT '0',
	PRIMARY KEY (`id`),
	INDEX `FK_score_student` (`student_id`),
	INDEX `FK_score_subject` (`subject_id`),
	INDEX `date` (`date`),
	CONSTRAINT `FK_score_student` FOREIGN KEY (`student_id`) REFERENCES `student` (`id`),
	CONSTRAINT `FK_score_subject` FOREIGN KEY (`subject_id`) REFERENCES `subject` (`id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=1
;

データ

-- 学生
REPLACE INTO `student` (`id`, `name`) VALUES
	(1, 'Mike'),
	(2, 'Tom'),
	(3, 'Sam');

-- 科目
REPLACE INTO `subject` (`id`, `name`) VALUES
	(1, 'Japanese'),
	(2, 'Math'),
	(3, 'Science'),
	(4, 'Social'),
	(5, 'English');

-- 点数
REPLACE INTO `score` (`id`, `date`, `student_id`, `subject_id`, `score`) VALUES
	(1, '2018-04-12', 1, 1, 40),
	(2, '2018-04-12', 2, 1, 43),
	(3, '2018-04-12', 3, 1, 86),
	(4, '2018-04-12', 1, 2, 21),
	(5, '2018-04-12', 2, 2, 43),
	(6, '2018-04-12', 3, 2, 32),
	(7, '2018-04-12', 1, 3, 54),
	(8, '2018-04-12', 2, 3, 87),
	(9, '2018-04-12', 3, 3, 76),
	(10, '2018-04-13', 1, 4, 65),
	(11, '2018-04-13', 2, 4, 98),
	(12, '2018-04-13', 3, 4, 87),
	(13, '2018-04-13', 1, 5, 76),
	(14, '2018-04-13', 2, 5, 45),
	(15, '2018-04-13', 3, 5, 84);

データ表示

SELECT
 sb.name AS subject_name,
 st.name AS student_name,
 sc.score AS score
FROM
 score AS sc
INNER JOIN
 student AS st
ON
 st.id = sc.student_id
INNER JOIN
 subject AS sb
ON
 sb.id = sc.subject_id
ORDER BY
 sb.id, st.id
データ表示
subject_name | student_name | score
-------------+--------------+--------
Japanese     |         Mike |    40
Japanese     |          Tom |    43
Japanese     |          Sam |    86
-------------------------------------
Math         |         Mike |    21
Math         |          Tom |    43
Math         |          Sam |    32
-------------------------------------
Science      |         Mike |    54
Science      |          Tom |    87
Science      |          Sam |    76
-------------------------------------
Social       |         Mike |    65
Social       |          Tom |    98
Social       |          Sam |    87
-------------------------------------
English      |         Mike |    76
English      |          Tom |    45
English      |          Sam |    84

■ サンプル

 * 全体数(count_all_student)とその内80点以上のデータ数を数える

SQL文

SELECT
 sb.name AS subject_name,
 COUNT(*) AS count_all_student,
 COUNT(sc.score >= 80 OR null) AS count_high_score
FROM
 score AS sc
INNER JOIN
 student AS st
ON
 st.id = sc.student_id
INNER JOIN
 subject AS sb
ON
 sb.id = sc.subject_id
GROUP BY
 sc.subject_id

出力結果

subject_name | count_all_student | count_high_score
-------------+-------------------+------------------
Japanese     |                 3 |               1
Math         |                 3 |               0
Science      |                 3 |               1
Social       |                 3 |               2
English      |                 3 |               1


関連記事

【SQL】GROUP BY句

https://blogs.yahoo.co.jp/dk521123/16360238.html

【Java】【log4j2】log4j 2 ~ 入門編 ~

$
0
0

■ はじめに

 AWS/Lambda の Java のロギングについて、調べたところ
https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/java-logging.html
~~~~~~~~~
Log4j™ 2 のカスタム Appender
AWS Lambda は、Log4j 2 を使用してカスタム Appender を提供することをお勧めします。。
~~~~~~~~~
とのことだった。

Log4jは、以下の関連記事でも扱ったが、
Log4j 2 について、触ったことなかったので、調べてみた。

■ Log4j との違い

 * propertiesファイル が使えない
  => XML(log4j2.xml)だけでなく、代わりに(?)、
     YAML(log4j2.yml)、JSON(log4j2.json)が使えるようになった

■ 設定

モジュールを落とす

 * 今回は、Gradle を使う
https://logging.apache.org/log4j/2.x/maven-artifacts.html
build.gradle
dependencies {
    // Log4j 2
    compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.11.0'
    compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.11.0'
    // For AWS
    //compile group: 'com.amazonaws', name: 'aws-lambda-java-log4j2', version: '1.1.0'
}

■ サンプル

log4j2.xml

src/main/resources配下に置く
<?xml version="1.0" encoding="UTF-8"?>
<configuration status="off">
  <Properties>
    <Property name="default_format">%d{yyyy/MM/dd HH:mm:ss.SSS} [%t] %-5level %C{1}:%L - %msg%n</Property>
    <Property name="log_file">C:/temp/sample_log4j.log</Property>
    <Property name="log_file_pattern">C:/temp/sample_log4j%d{yyyy-MM-dd}-%i.log</Property>
  </Properties>
  <appenders>
    <Console name="Console" target="SYSTEM_OUT">
      <PatternLayout>
        <pattern>${default_format}</pattern>
      </PatternLayout>
    </Console>
    <RollingFile name="LogOutput" append="true" fileName="${log_file}"
      filePattern="${log_file_pattern}">
      <PatternLayout>
        <pattern>${default_format}</pattern>
      </PatternLayout>
      <Policies>
        <SizeBasedTriggeringPolicy size="1KB"/>
      </Policies>
      <DefaultRolloverStrategy max="5"/>
    </RollingFile>
  </appenders>
  <loggers>
    <root level="trace">
      <AppenderRef ref="Console" />
      <AppenderRef ref="LogOutput" />
    </root>
  </loggers>
</configuration>

LogDemo.java

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class LogDemo {
  private static final Logger logger = LogManager.getLogger(LogDemo.class);
  
  public static void main(String[] args) {
    logger.trace("!! trace !! {}", "Hello, World");
    logger.debug("!! debug !! {}", "Hello, World");
    logger.info("!! info !! {}", "Hello, World");
    logger.warn("!! warn !! {}", "Hello, World");
    logger.error("!! error !! {}", "Hello, World");
    logger.fatal("!! fatal !! {}", ClassLoader.getSystemResource("log4j2.xml"));
  }
}
出力結果(コンソールと「C:\temp\sample_log4j.log」)
2018-04-16 21:24:01.592 [main] TRACE LogDemo:10 - !! trace !! Hello, World
2018-04-16 21:24:01.596 [main] DEBUG LogDemo:11 - !! debug !! Hello, World
2018-04-16 21:24:01.596 [main] INFO  LogDemo:12 - !! info !! Hello, World
2018-04-16 21:24:01.596 [main] WARN  LogDemo:13 - !! warn !! Hello, World
2018-04-16 21:24:01.596 [main] ERROR LogDemo:14 - !! error !! Hello, World
2018-04-16 21:24:01.596 [main] FATAL LogDemo:15 - !! fatal !! file:/C:/workspace/SampleDemo/bin/log4j2.xml

■ log4j 2 あれこれ

String.format( %s ) みたいな使い方をしたい

// {} 部分に「Hello, World」が出力される
logger.trace("... {}", "Hello, World");


関連記事

log4j ~入門編~

https://blogs.yahoo.co.jp/dk521123/32020042.html

log4j のローテーションについて

https://blogs.yahoo.co.jp/dk521123/32113555.html

Java で イベントログ を出力するには ~Log4j編~

https://blogs.yahoo.co.jp/dk521123/33210162.html

【Java】 Java での リソース の扱いについて

$
0
0

■ はじめに

Javaアプリを作成する際に、
log4j 2の設定ファイル「log4j2.xml」(以下の関連記事を参照)
https://blogs.yahoo.co.jp/dk521123/37495975.html
や Velocity 等のテンプレートファイル XXXXX.vm (以下の関連記事を参照)
https://blogs.yahoo.co.jp/dk521123/34456704.html
を格納する必要がある。
そこで、これらの「リソース」の扱いについて調べてみた。

■ リソースとは?

https://docs.oracle.com/javase/jp/1.5.0/guide/lang/resources.html
より
~~~~~~
プログラムのコードの位置とは無関係な方法で
プログラムがアクセスする必要のあるデータ (イメージ、オーディオ、テキストなど) 
~~~~~~

■ リソース格納場所(デフォルト?)

 * 「src/main/resources」 配下に置く

■ Java からリソース格納場所を取得する

 * ClassLoader.getSystemResource() を使う
https://docs.oracle.com/javase/jp/8/docs/api/java/lang/ClassLoader.html

構文

URL url = ClassLoader.getSystemResource("【ファイル名 or ディレクトリ名】");

使用上の注意

[1] 戻り値が URLである
  => 例えば、「file:/C:/【パス】」のような形になる(以下の「サンプル」を参照)

[2] ファイル名 or ディレクトリ名 が存在しない場合は、 null を返却する
  => ClassLoader.getSystemResource(【パス】).toString() ってすると NullPointerで落ちる

[3] 「src/main/resources」 配下に、更にディレクトリを切ってリソースファイルを置く場合
     
  => 

補足:URL => Path への変換

URL => URI => Path で変換可能(もっといい方法があるかもしれないが)
URL url = ClassLoader.getSystemResource("sample.vm");
URI uri = urlForLog4j2xml.toURI();
Path path = Paths.get(uri);

■ サンプル

ディレクトリ構成

C:
 + workspace
    + SampleDemo
       + src
          + main
             + resources
                + log4j2.xml << ファイル
                + mail
                  + templates
                    + sample.vm << ファイル

Demo.java

import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Demo {

  public static void main(String[] args) {
    // 「src/main/resources」 配下に「log4j2.xml」が存在
    URL urlForLog4j2xml = ClassLoader.getSystemResource("log4j2.xml");
    System.out.println("URL for log4j2.xml : " + urlForLog4j2xml);

    // 存在しないファイル
    URL urlForDummyxml = ClassLoader.getSystemResource("dummy.xml");
    System.out.println("URL for Dummy.xml : " + urlForDummyxml);

    // 「src/main/resources」 配下にディレクトリ「mail/templates」が存在
    URL urlForMailTemplates = ClassLoader.getSystemResource("mail/templates");
    System.out.println("URL for mail/templates : " + urlForMailTemplates);

    // 「src/main/resources」 配下にファイル「mail/templates/sample.vm」が存在
    URL urlForMailTemplatesSampleVm = ClassLoader.getSystemResource("mail/templates/sample.vm");
    System.out.println("URL for mail/templates/sample.vm : " + urlForMailTemplatesSampleVm );

    System.out.println("*****************");

    // URL「file:/C:/【パス】」 => Path 「C:/【パス】」に変換
    try {
      URI uriForLog4j2xml = urlForLog4j2xml.toURI();
      Path pathForLog4j2xml = Paths.get(uriForLog4j2xml);
      System.out.println("Path for log4j2.xml : " + pathForLog4j2xml);
    } catch (URISyntaxException ex) {
      ex.printStackTrace();
    }
  }
}

出力結果

URL for log4j2.xml : file:/C:/workspace/SampleDemo/bin/log4j2.xml
URL for Dummy.xml : null
URL for mail/templates : file:/C:/workspace/SampleDemo/bin/mail/templates
URL for mail/templates/sample.vm : file:/C:/workspace/SampleDemo/bin/mail/templates/sample.vm
*****************
Path for log4j2.xml : C:\workspace\SampleDemo\bin\log4j2.xml

関連記事

log4j 2 ~ 入門編 ~

https://blogs.yahoo.co.jp/dk521123/37495975.html

Apache Velocity ~入門編~

https://blogs.yahoo.co.jp/dk521123/34456704.html

【JUnit】 AssertJ / AssertJ-DB ~ 基本編 [1] / DBデータの比較について ~

$
0
0

■ はじめに

https://blogs.yahoo.co.jp/dk521123/36157721.html
の続き。
今回は、時間以外のDBデータの比較について扱う。

なお、時間に関する比較は、以下の関連記事を参照。
AssertJ / AssertJ-DB ~ 基本編 [3] / 時間に関する比較 ~
https://blogs.yahoo.co.jp/dk521123/37145996.html

■ 比較について

 (知ってる限りだと)大きく分けて以下の2点。

【1】テーブル・インスタンスを取得して比較する
【2】DBの変更に関わるインスタンスを取得して比較する

【1】テーブル・インスタンスを取得して比較する

 * テーブルのインスタンスを取得して、比較することもできる

構文

Source source = new Source("jdbc:mysql://【DB Host】:【ポート】/【DB名】", "【ID】", "【パスワード】");
Table table = new Table(source, "【テーブル名】");

assertThat(table).row(0).value("id").isEqualTo("X0001");
詳細なサンプルは、以下の関連記事を参照のこと。
https://blogs.yahoo.co.jp/dk521123/36164701.html

【2】DBの変更に関わるインスタンスを取得して比較する

http://joel-costigliola.github.io/assertj/assertj-db-concepts.html#changes
の図が分かりやすいかも。

構文

Source source = new Source("jdbc:mysql://【DB Host】:【ポート】/【DB名】", "【ID】", "【パスワード】");

// DB変更を追跡するための Changesインスタンス
Changes changes = new Changes(source);

// スタートポイント(変更前の状態を記憶)
changes.setStartPointNow();

// テスト対象メソッド実行...
// ... 略 ...

// エンドポイント(変更後の状態を記憶)
changes.setEndPointNow();

// 項目を明示的に指定して比較可能
assertThat(changes)
    .hasNumberOfChanges(【変更したレコード数】)
    .change()
    .isOnTable("【テーブル名】")
    .rowAtEndPoint()
    .value("【項目1】").isEqualTo("【期待値1】")
    .value("【項目2】").isEqualTo("【期待値2】")
    .value("【項目3】").isEqualTo("【期待値3】");
詳細なサンプルは、以下の関連記事を参照のこと。
https://blogs.yahoo.co.jp/dk521123/36157721.html


関連記事

AssertJ / AssertJ-DB ~ 入門編 ~

https://blogs.yahoo.co.jp/dk521123/36157721.html

AssertJ / AssertJ-DB ~ 基本編 [3] / 時間に関する比較 ~

https://blogs.yahoo.co.jp/dk521123/37145996.html

【Raspberry PI】ラズパイの Wi-Fi に関するあれこれ

$
0
0

■ ラズパイの Wi-Fi

 * Wifi通信規格:IEEE 802.11 b/g/n 2.4 GHz
  => Raspberry Pi 3 Model B+ (2018年3月14日)だと「2.4GHz and 5GHz IEEE 802.11.b/g/n/ac」
https://ja.wikipedia.org/wiki/Raspberry_Pi

Wifi通信規格

 * 「2.4GHz帯」と「5GHz帯」がある
  => ラズパイのほとんどは前者。
http://trendy.nikkeibp.co.jp/article/column/20130910/1052041/03_spx400.jpg


関連記事

ラズパイを無線LANルータにする

https://blogs.yahoo.co.jp/dk521123/37489264.html

【トラブル】【Java】MySQLのJDBCドライバを v5.1.X から v8.0.X に上げたらエラー「The server time zone value」になる

$
0
0

■ 現象詳細

MySQLのJDBCドライバを v5.1.46 から v8.0.11 にあげたとこと、
以下の「エラー内容」になる

build.gradle

修正前 (現象発生なし)
dependencies {    
    // MySQL
    compile group: 'mysql', name: 'mysql-connector-java', version: '5.1.46'
}
修正後 (現象発生あり)
dependencies {    
    // MySQL
    compile group: 'mysql', name: 'mysql-connector-java', version: '8.0.11'
}

エラー内容

java.sql.SQLException: The server time zone value '???? (?W????)' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:127)
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:95)
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:87)
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:61)
	at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:71)
	at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:76)
	at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:862)
	at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:444)
	at com.mysql.cj.jdbc.ConnectionImpl.getInstance(ConnectionImpl.java:230)
	at com.mysql.cj.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:226)
	at java.sql.DriverManager.getConnection(Unknown Source)
	at java.sql.DriverManager.getConnection(Unknown Source)
	at com.sample.db.SelectDemo.main(SelectDemo.java:15)
Caused by: com.mysql.cj.exceptions.InvalidConnectionAttributeException: The server time zone value '???? (?W????)' is unrecognized or represents more than one time zone. You must configure either the server or JDBC driver (via the serverTimezone configuration property) to use a more specifc time zone value if you want to utilize time zone support.
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
	at java.lang.reflect.Constructor.newInstance(Unknown Source)
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:59)
	at com.mysql.cj.exceptions.ExceptionFactory.createException(ExceptionFactory.java:83)
	at com.mysql.cj.util.TimeUtil.getCanonicalTimezone(TimeUtil.java:128)
	at com.mysql.cj.protocol.a.NativeProtocol.configureTimezone(NativeProtocol.java:2201)
	at com.mysql.cj.protocol.a.NativeProtocol.initServerSession(NativeProtocol.java:2225)
	at com.mysql.cj.jdbc.ConnectionImpl.initializePropsFromServer(ConnectionImpl.java:1391)
	at com.mysql.cj.jdbc.ConnectionImpl.connectOneTryOnly(ConnectionImpl.java:993)
	at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:852)
	... 6 more

サンプル

修正前
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class SelectDemo {

  public static void main(String[] args) throws ClassNotFoundException {
    Class.forName("com.mysql.jdbc.Driver");

    try (
        Connection connection = DriverManager.getConnection(
            "jdbc:mysql://localhost:3306/sampledb", "root", "password");
        PreparedStatement preparedStatement = connection
            .prepareStatement("SELECT * FROM person");) {
      ResultSet resultSet = preparedStatement.executeQuery();
      while (resultSet.next()) {
        System.out.println(String.format("id=%s, name=%s",
            resultSet.getString("id"), resultSet.getString("name")));
      }
    } catch (SQLException ex) {
      ex.printStackTrace();
    }
  }
}

■ 解決案

 * 「serverTimezone」を指定する
  => 「serverTimezone=JST」みたいな。

サンプル

修正後
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class SelectDemo {

  public static void main(String[] args) throws ClassNotFoundException {
    Class.forName("com.mysql.jdbc.Driver");

    try (
        Connection connection = DriverManager.getConnection(
            "jdbc:mysql://localhost:3306/sampledb?serverTimezone=JST", "root", "password"); // ★ここ★
        PreparedStatement preparedStatement = connection
            .prepareStatement("SELECT * FROM person");) {
      ResultSet resultSet = preparedStatement.executeQuery();
      while (resultSet.next()) {
        System.out.println(String.format("id=%s, name=%s",
            resultSet.getString("id"), resultSet.getString("name")));
      }
    } catch (SQLException ex) {
      ex.printStackTrace();
    }
  }
}


関連記事

JDBC ドライバのunregister 絡みのトラブル

https://blogs.yahoo.co.jp/dk521123/37476585.html

【Gradle】Gradle ~あれこれ編~

$
0
0

■ ビルド時にテストを実行しないようにするには

サンプル

gradle build -x test 

参考文献

http://anton0825.hatenablog.com/entry/2014/11/19/000000

■ サブプロジェクトを含めてビルドするには

前提条件

 * サブプロジェクトも Gradle でビルドできること

フォルダ構成

workspace
 + RootProject
 |  + build.gradle
 |  + settings.gradle
 |
 + SubProject
    + build.gradle
    + settings.gradle

サンプル

RootProject/settings.gradle
rootProject.name = 'RootProject'
includeFlat ':SubProject'
RootProject/build.gradle
dependencies {
   compile project(':SubProject')
}

■ 文字化け対策

「Gradle: エラー: この文字は、エンコーディングMS932にマップできません」
がでるので、その対策。

サンプル

build.gradle
def defaultEncoding = 'UTF-8'

tasks.withType(Compile) {
    options.encoding = defaultEncoding
}

参考文献

http://anton0825.hatenablog.com/entry/2015/03/30/000000

■ 外部ファイルから値を取得するには

 * ant でいう「build.properties」的なもの
https://blogs.yahoo.co.jp/dk521123/33989901.html
 * 「gradle.properties」を用意し「getProperty("【キー】")」で取得する

サンプル

gradle.properties
hello.world=This is hello world!
build.gradle
def hello = getProperty("hello.world")
println "Result : " + hello

参考文献

https://qiita.com/hatimiti/items/a127311d739c9d3e0045

関連記事

Gradle ~入門編~

https://blogs.yahoo.co.jp/dk521123/35915819.html

【Java】DB Connection Pool ~ HikariCP / 基本編 ~

$
0
0

■ 使用上の注意

 * HikariDataSource#close() は、システム終了後にコールすること
★ 重要 ★
 * HikariDataSource#getConnection()から得たコネクションについては、使用後即 close() をコールしておいた方がいい
  => これをしていなかったせいで、maximumPoolSize の デフォルト 10 を使い切ってしまい、
     その結果、コネクションタイムアウトして落ちていた
  => システム終了していないのに、close() を呼んでしまっていいの?って思ったが、
     以下の「参考文献」が参考になった

参考文献

https://stackoverflow.com/questions/25367261/best-approach-for-returning-connection-objects-to-hikaricp-pool?rq=1
As with most connection pools, Hikari doesn't give you an actual JDBC Connection when you ask for one.
# 多くのコネクションプールと同様に、Hikari は実際のJDBCのコネクションを提供している訳じゃありません。

What it does instead is give you a proxy that implements the Connection interface.
# コネクション何がその代わりに

In the case of Hikari - it's a ConnectionProxy object.
# Hikariの場合、それはConnectionProxy オブジェクトです。

This proxy serves a few purposes, the main of which is
 - take the control of opening/closing connections and statements away from you and into the connection pool.
This happens automagically and you should be using your connections as usual.
This includes closing them after use.

If you look at the source code for Hikari, at the ConnectionProxy class in particular,
 you will see that the close() method is very different from the standard one. The code reads as:
# Hikariのソースコード(特にConnectionProxy クラス)をみてみれば
# close() が標準のものとは、とても異なっていることが分かるでしょう
https://github.com/brettwooldridge/HikariCP/blob/dev/src/main/java/com/zaxxer/hikari/pool/ProxyConnection.java
Mark the connection as closed, do cleanup, reset underlying connection state and params.
# コネクションが閉じたということは、クリーンアップ、下層のconnection 状態とパラメータをリセットすることを
# 覚えておいてください

Hence, simply calling close() will just clean and return the connection to the pool.
# そのため、単純にclose()をよぶことは、ただクリーンにし、コネクションをプールに返します

■ 設定値

公式サイト
https://github.com/brettwooldridge/HikariCP

maximumPoolSize

デフォルト:10
使用する最大のconnection数を指定

connectionTimeout

デフォルト:30000 (30 seconds)
connectionが接続される時のタイムアウト時間を設定

idleTimeout

デフォルト:600000 (10 minutes)
本設定値の間、Pool内のconnectionが使われなかった場合、connectionをDBに返却

参考文献

http://time-complexity.blogspot.jp/2015/03/db-connection-pool-hikaricp.html

関連記事

【Java】DB Connection Pool ~ HikariCP / 導入編 ~

https://blogs.yahoo.co.jp/dk521123/37397210.html

【Java】DB Connection Pool ~ HikariCP / 複数DB編 ~

https://blogs.yahoo.co.jp/dk521123/37452804.html

【シェル】 Javaを実行するシェルスクリプト

$
0
0

【1】Javaのclassファイルを実行する

サンプル

#!/bin/bash

# 【オプション】環境変数
export JAVA_HOME=/usr/local/java

# カレントディレクトリを取得する
APP_HOME="`pwd -P`"

# クラスパスを設定(CLASSPATH="【カレントディレクトリパス】:【依存するJar1】:【依存するJar2】)
CLASSPATH="$APP_HOME:$APP_HOME/lib/XXX1.jar:$APP_HOME/lib/XXX2.jar"

# classファイルを実行(java -cp 【クラスパス】 【実行するクラス】)
java -cp $CLASSPATH com.sample.Main

【2】実行可能JARファイルを実行する

サンプル

#!/bin/bash

java -jar sample.jar

関連記事

【Linux】【シェル】 シェルスクリプトあれこれ

https://blogs.yahoo.co.jp/dk521123/37422743.html

【Java】【Velocity】JARファイル内のリソースファイルを開いてApache Velocityを使って文字列取得

$
0
0

■ はじめに

https://blogs.yahoo.co.jp/dk521123/37497699.html
で、JARファイル内のリソースファイルのパスは
「jar:file:/C:/XXXX/SampleDemo-1.0.0.jar!/Sample.vm」のようになる。
しかし、直接ストリームとしてオープンすることは可能なので
そのサンプルを書く

■ サンプル

JarOpenDemo.java

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;

import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;

public class JarOpenDemo {
  public static void main(String[] args) {
    try (InputStream inputStream = JarOpenDemo.class.getClass()
        .getResourceAsStream("/mail/templates/sample.vm");) {
      String fileText = toString(inputStream);
      System.out.println("File Content : " + fileText);

      // Using Velocity
      Velocity.init();
      VelocityContext context = new VelocityContext();
      context.put("name", "Mike");
      try (StringWriter stringWriter = new StringWriter();) {
        Velocity.evaluate(context, stringWriter, "Demo", fileText);
        System.out.println("Result : " + stringWriter.toString());
      }
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }

  public static String toString(InputStream inputStream) throws IOException {
    if (inputStream == null) {
      return null;
    }

    try (BufferedReader bufferedReader = new BufferedReader(
        new InputStreamReader(inputStream));) {
      StringBuilder stringBuilder = new StringBuilder();

      String line;
      while ((line = bufferedReader.readLine()) != null) {
        stringBuilder.append(line);
      }
      return stringBuilder.toString();
    }
  }
}

/mail/templates/sample.vm

Hello World, $name

出力結果

File Content : Hello World, $name
Result : Hello World, Mike


関連記事

Java での リソース の扱いについて

https://blogs.yahoo.co.jp/dk521123/37497699.html

Apache Velocity ~入門編~

https://blogs.yahoo.co.jp/dk521123/34456704.html

【SQL】 自己相関サブクエリ でパフォーマンスが悪かった話とその解決策

$
0
0

■ はじめに

 自己相関サブクエリ でパフォーマンスが悪かったので
その際の解決策などを記録する

■ 現象

 * 50万レコードほどのテーブルで相関サブクエリを使った結果、数十分掛かってしまった

動作環境

 * OS : CentOS7
 * DB : MySQL

パフォーマンスが悪かったSQL文 (イメージ)

SELECT
 (中略)
 main_ol.remarks AS remarks
FROM
 user AS u
 (中略)
INNER JOIN
  operation AS o
ON
  (中略)
LEFT OUTER JOIN
 operation_log AS main_ol
ON
 u.id = main_ol.user_id
 AND o.operation_type = main_ol.operation_type
 AND main_ol.operation_level IN ('1', '2') -- '1':ERROR, '2':WARN
 AND main_ol.operation_datetime =
  -- ★最新のoperation_datetimeを取得
  (SELECT
     MAX(sub_ol.operation_datetime)
   FROM
     operation_log AS sub_ol
   WHERE
      sub_ol.user_id = main_ol.user_id
      AND sub_ol.operation_type = main_ol.operation_type
      AND sub_ol.operation_level IN ('1', '2') -- '1':ERROR, '2':WARN
      AND sub_ol.operation_datetime < CURRENT_TIMESTAMP
   GROUP BY
     sub_ol.user_id,
     sub_ol.operation_type

■ 原因

 * 最新のoperation_datetimeを取得するための自己相関サブクエリ(★)が
   毎行動作してしまい、使いまわしができていない

■ 解決案

 * 「インデックス追加」および「SQL文変更」で解決できた

インデックス追加

 * インデックス追加については、条件に関わる項目に対して行う
 * 複数の場合、複合インデックスで追加する
  => 上記のSQL文だと「user_id」「operation_type」「operation_level」「operation_datetime」
 * どの位、どの順番で行うかは、実際のデータを使い、検証して決める

SQL文変更 (イメージ)

処理が使い回しできるようなSQL文に工夫
SELECT
 (中略)
 main_ol.remarks AS remarks
FROM
 user AS u
 (中略)
INNER JOIN
  operation AS o
ON
  (中略)
LEFT OUTER JOIN
 -- (iii) operation IDに紐づく remarks を取得
 (SELECT
   sub_ol.user_id AS user_id,
   sub_ol.operation_type AS operation_type,
   sub_ol.remarks AS remarks
  FROM
   operation_log AS sub_ol
  INNER JOIN
    -- (ii) user_id / operation_type / operation_datetime 単位(※1)で、最大operation IDを取得
    (SELECT
       latest_ol.user_id AS user_id,
       latest_ol.operation_type AS operation_type,
       MAX(latest_ol.id) AS latest_id
     FROM
       operation_log AS latest_ol
     INNER JOIN
       -- (i) user_id / operation_type 単位(※1)で、最新operation_datetimeを取得
       (SELECT
          latest_each_usr_ol.user_id AS user_id,
          latest_each_usr_ol.operation_type AS operation_type,
          MAX(latest_each_usr_ol.operation_datetime) AS latest_operation_datetime
        FROM
          operation_log AS latest_each_usr_ol
        WHERE
          latest_each_usr_ol.operation_datetime < CURRENT_TIMESTAMP
          AND latest_each_usr_ol.operation_level IN ('1', '2') -- '1':ERROR, '2':WARN
        GROUP BY
          latest_each_usr_ol.user_id,
          latest_each_usr_ol.operation_type
        ) AS leu_ol
     ON
       latest_ol.user_id = leu_ol.user_id,
       latest_ol.operation_type = leu_ol.operation_type,
       latest_ol.operation_datetime = leu_ol.operation_datetime
     WHERE
       latest_ol.operation_level IN ('1', '2') -- '1':ERROR, '2':WARN
     GROUP BY
       latest_ol.user_id,
       latest_ol.operation_type
       latest_ol.operation_datetime
    ) AS l_ol
    ON
      sub_ol.id = l_ol.id
 ) AS main_ol
 ON
   u.id = main_ol.user_id
   AND o.operation_type = main_ol.operation_type
※1:XXXX単位について
 * GROUP BY句により、実現
※2:修正したSQL文について
https://blogs.yahoo.co.jp/dk521123/35761987.html
で、相関サブクエリを『集合のカット(分割)』と表現したが
以下のようなイメージで間引いていく

【間引きのイメージ】

+--(i) user_id / operation_type 単位で、最新operation_datetimeを取得 ---------------------------+
|                                                                                               |
|  +--(ii) user_id / operation_type / operation_datetime 単位で、最大operation IDを取得 --+     |
|  |                                                                                      |     |
|  | +--(iii) operation IDに紐づく remarks を取得 --------------------------------+       |     |
|  | |                                                                            |       |     |
|  | |                                                                            |       |     |
|  | |                                                                            |       |     |
|  | +----------------------------------------------------------------------------+       |     |
|  |                                                                                      |     |
|  +--------------------------------------------------------------------------------------+     |
|                                                                                               |
+-----------------------------------------------------------------------------------------------+

関連記事

【SQL】相関サブクエリ / 自己相関サブクエリ

https://blogs.yahoo.co.jp/dk521123/35761987.html

【SQL】パフォーマンスの良いSQLを記述 ~全般編~

https://blogs.yahoo.co.jp/dk521123/17141399.html

【AWS】CloudWatch Logs あれこれ / トラブルシュート

$
0
0

■ はじめに

https://blogs.yahoo.co.jp/dk521123/37405613.html
の続き。

今回は、前回で設定した以外で必要なその他事項やトラブルシュートについては、扱う。

■ CloudWatch Logs あれこれ

【1】ログの設定を追加したい場合

 ログの設定を追加したい場合に、
再度、「sudo python ./awslogs-agent-setup.py --region ap-northeast-1」を実行すると
前回の設定が上書きされてしまう。

 => オプション「--only-generate-config」を指定して実行
コマンド例
# 念のため、設定をバックアップ
sudo cp /var/awslogs/etc/awslogs.conf /var/awslogs/etc/awslogs.conf.backup

sudo python ./awslogs-agent-setup.py --region ap-northeast-1 --only-generate-config

【2】日付付きのログファイル名について

 * ログファイル「file」において、「file=/var/log/system.log*」「file=/var/log/system.*.log」のように
   ワイルドカードを使用する

【3】yyyy/MM/dd hh:mm:ss.SSS でミリ秒まで表示するログへの対応

 * 「Choose Log Event timestamp format」で「4. Custom」を選択し、「%Y/%m/%d %H:%M:%S.%f」を設定する
 * ミリ秒は、以下の公式サイトの「%f: 左ゼロ詰め 10 進数でのマイクロ秒です。000000, ..., 999999」で対応可能
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/AgentReference.html

■ CloudWatch Logs トラブルシュート

【1】 CloudWatch Logsにアップされない

 日付付きのログファイル名でワイルドカード「*」を使用した際に、
ロググループ名にも「log_group_name=Sample-/var/log/system.*.log」のように指定したら
CloudWatch Logsにアップされなかった。

なお、ワイルドカード「*」を使用しなかった時は、CloudWatch Logs画面で確認できた。
原因
 * ロググループ名「log_group_name」には、「*」が名前として使用できない
  => 詳細は、以下「ロググループ名「log_group_name」の制約」を参照
【公式サイトより】ロググループ名「log_group_name」の制約
https://docs.aws.amazon.com/ja_jp/AmazonCloudWatch/latest/logs/AgentReference.html
より、抜粋

 * 1~512 文字で指定
 * a~z、A~Z、0~9、"_" (アンダーバー)、"-" (ハイフン)、"/" (スラッシュ) および "." (ピリオド)

関連記事

【AWS】EC2内のログを CloudWatch Logs で管理する

https://blogs.yahoo.co.jp/dk521123/37405613.html

【Gradle】Gradle ~基本編 / Task ~

$
0
0

■ はじめに

https://blogs.yahoo.co.jp/dk521123/35915819.html
の続き。

以下「公式サイト(翻訳)より」から、Gradle を使うには、タクスが重要そうなので、
今回は、タスク(task) について、調べてみた。

公式サイト(翻訳)より

http://gradle.monochromeroad.com/docs/userguide/tutorial_using_tasks.html
Gradleの根本にあるのは、プロジェクトとタスクという二つの基本的な概念です。

・・・略・・・

それぞれのプロジェクトは、一つ以上のタスクから構成されます。
タスクとは、分割不能な何らかの作業単位を表す概念です。

■ 構文

【1】基本形

task 【タスク名】 {
  // 実行処理
}
補足1:説明文
task 【タスク名】 {
  description = '【説明文】'
  ... 略 ...
補足2:タクスの有効/無効
// 無効にする
【タスク名】.enabled = false

【2】doFirst/doLast

task 【タスク名】 {
  // 最初に実行
  doFirst {
    // 実行処理
  }
  // 最後に実行
  doLast {
    // 実行処理
  }
}
doLast の別の書き方
task << 【タスク名】 {
  // 実行処理
}
http://gradle.monochromeroad.com/docs/userguide/tutorial_using_tasks.html
より抜粋

<<はdoLastの単なるエイリアスです
補足:<< なし と あり の違い
 * << なし : タスクのインスタンス生成時に呼び出される
 * << あり : タスクの実行時に呼び出される
http://d.hatena.ne.jp/bluepapa32/20110208/1297178119

【3】依存するタスクを指定

単一
task 【タスク名】 (dependsOn:'【依存するタスク名】') {
  // 実行処理
}
複数
task 【タスク名】 (dependsOn:['【依存するタスク名1】', '【依存するタスク名2】', ...]) {
  // 実行処理
}
補足:グループ化
task 【タスク名】 (dependsOn:'【依存するタスク名】') {
  group = '【グループ名】'
  ... 略 ...

■ サンプル

【2】doFirst/doLast

task helloWorld {
    println 'helloWorld1'
    
    doFirst {
        println 'doFirst'
    }
    doLast {
        println 'doLast'
    }
    
    println 'helloWorld2'
}
出力結果
helloWorld1
helloWorld2
:helloWorld
doFirst
doLast

【3】依存するタスクを指定

task helloWorld(dependsOn:['hello', 'world']) {
    description = 'main task - helloWorld'
    group = 'Group for Hello World'
    doLast {
        println 'Hello World'
    }
}
task hello {
    description = 'sub task - hello'
    group = 'Group for Hello World'
    doLast {
        println 'hello'
    }
}
task world {
    description = 'sub task - world'
    group = 'Group for Hello World'
    doLast {
        println 'world'
    }
}
出力結果
:hello
hello
:world
world
:helloWorld
Hello World


【Android】 アラームを設定するには...

$
0
0

■ サンプル

AndroidManifest.xml

パーミッションの追加
<?xml version="1.0" encoding="utf-8"?>
    ... 略 ...
    </application>
    <uses-permission android:name="com.android.alarm.permission.SET_ALARM"/>
</manifest>

MainActivity.java

import android.content.Intent;
import android.provider.AlarmClock;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;


public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    protected  void onClickButton(View view) {
        try {
            // アラームを設定するインテントを取得する
            Intent intent = new Intent(AlarmClock.ACTION_SET_ALARM);

            /* アラーム時刻を設定する */
            intent.putExtra(AlarmClock.EXTRA_HOUR, 23);
            intent.putExtra(AlarmClock.EXTRA_MINUTES, 35);
            intent.putExtra(AlarmClock.EXTRA_MESSAGE, "Set Alarm!");

            // インテントを発行する
            startActivity(intent);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        Toast.makeText(this, "Setting!!", Toast.LENGTH_LONG).show();
    }
}

【Java】iCalendar ライブラリ ~ biweekly ~

$
0
0

■ iCalendar

アイカレンダ
 * スケジュールの標準フォーマット

仕様

 * RFC 5545(旧版 RFC 2445)で規定
 * 以下のサイトが詳しい
http://www.asahi-net.or.jp/~CI5M-NMR/iCal/ref.html
https://www.kanzaki.com/docs/sw/rdf-calendar.html

■ biweekly

「バイウィークリィ」。日本語で「隔週」
 * iCalendar 形式を扱うためのJavaライブラリ

公式サイト

https://github.com/mangstadt/biweekly

要件

 * Java 1.5 以上

■ 設定

 * Gradle で行う

build.gradle

dependencies {
    // biweekly
    compile 'net.sf.biweekly:biweekly:0.6.2'
}

■ サンプル

【1】アイカレンダの読み込み
【2】 ファイルに書き出す
【3】アイカレンダ形式の検証

Main.java

import java.io.File;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import biweekly.Biweekly;
import biweekly.ICalVersion;
import biweekly.ICalendar;
import biweekly.ValidationWarnings;
import biweekly.component.VEvent;
import biweekly.io.chain.ChainingTextStringParser;
import biweekly.io.chain.ChainingTextWriter;
import biweekly.property.DateStart;
import biweekly.property.Summary;
import biweekly.util.Duration;
import biweekly.util.Frequency;
import biweekly.util.Recurrence;

public class Main {

  public static void main(String[] args) {
    // 【1】アイカレンダの読み込み
    String sampleData =
        "BEGIN:VCALENDAR\r\n" +
          "VERSION:2.0\r\n" +
          "PRODID:-//Microsoft Corporation//Outlook 14.0 MIMEDIR//EN\r\n" +
          "BEGIN:VEVENT\r\n" +
            "UID:0123\r\n" +
            "DTSTAMP:20130601T080000Z\r\n" +
            "SUMMARY;LANGUAGE=jp:ハローワールド\r\n" +
            "DTSTART:20130610T120000Z\r\n" +
            "RRULE:FREQ=WEEKLY;INTERVAL=2\r\n" +
          "END:VEVENT\r\n" +
        "END:VCALENDAR\r\n";
    
    ChainingTextStringParser parser = Biweekly.parse(sampleData);
    for (ICalendar ical : parser.all()) {
      for (VEvent event : ical.getEvents()) {
        String summary = event.getSummary().getValue();
        System.out.println("summary = " + summary);
      }
    }
    
    // 【2】 ファイルに書き出す
    try {
      ICalendar ical = new ICalendar();
      VEvent event = new VEvent();
      Summary summary = event.setSummary("ハローワールド!!");
      summary.setLanguage("jp");

      Date start = new Date();
      event.setDateStart(new DateStart(start, false));
      event.setDuration(new Duration.Builder().days(1).build());
      Recurrence recurrence = new Recurrence.Builder(Frequency.YEARLY).build();
      event.setRecurrenceRule(recurrence);
      ical.addEvent(event);

      File file = new File("helloworld.ics");
      ChainingTextWriter writer = Biweekly.write(ical);
      writer.go(file);
    } catch (Exception ex) {
      ex.printStackTrace();
    }
    
    // 【3】アイカレンダ形式の検証
    try {
      DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm");

      ICalendar ical = new ICalendar();
      VEvent event = new VEvent();
      // 終了「2018-05-15 11:00」より開始「2018-05-15 13:00」の方が後にしている
      event.setDateStart(dateFormat.parse("2018-05-15 13:00"));
      event.setDateEnd(dateFormat.parse("2018-05-15 11:00"));
      ical.addEvent(event);
      ValidationWarnings warnings = ical.validate(ICalVersion.V2_0);
      
      System.out.println("Case1");
      System.out.println(warnings.toString());
      
      // 正しいデータ
      ICalendar icalCase2 = new ICalendar();
      VEvent eventCase2 = new VEvent();
      eventCase2.setDateStart(dateFormat.parse("2018-05-15 13:00"));
      eventCase2.setDateEnd(dateFormat.parse("2018-05-15 15:00"));
      icalCase2.addEvent(eventCase2);
      ValidationWarnings warningsCase2 = icalCase2.validate(ICalVersion.V2_0);
      
      System.out.println("Case2");
      System.out.println(warningsCase2.toString());
    } catch (Exception ex) {
      ex.printStackTrace();
    }
    
    System.out.println("Done!!");
  }
}

出力結果

コンソール
summary = ハローワールド
Case1
[ICalendar > VEvent]: (16) The start date must come before the end date.
Case2

Done!!
helloworld.ics
BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Michael Angstadt//biweekly 0.6.2//EN
BEGIN:VEVENT
UID:58b66720-bf95-40d3-a4e4-da99306717b9
DTSTAMP:20180508T125230Z
SUMMARY;LANGUAGE=jp:ハローワールド!!
DTSTART;VALUE=DATE:20180508
DURATION:P1D
RRULE:FREQ=YEARLY
END:VEVENT
END:VCALENDAR


【Java】Java で Google Calendar API を使う

$
0
0

■ はじめに

https://developers.google.com/calendar/quickstart/java
を参考にすればできた

■ 設定

ポイント

【1】 Gradle を使う(以下の「build.gradle」参照)
【2】「src/main/resources」に「client_secret.json」を置く

build.gradle

// Apply the java-library plugin to add support for Java Library
apply plugin: 'java-library'
apply plugin: 'application'

repositories {
    mavenCentral()
}

dependencies {
    compile 'com.google.api-client:google-api-client:1.23.0'
    compile 'com.google.oauth-client:google-oauth-client-jetty:1.23.0'
    compile 'com.google.apis:google-api-services-calendar:v3-rev305-1.23.0'
}

■ サンプル

CalendarQuickstart.java

import com.google.api.client.auth.oauth2.Credential;
import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp;
import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver;
import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow;
import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets;
import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport;
import com.google.api.client.http.javanet.NetHttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.DateTime;
import com.google.api.client.util.store.FileDataStoreFactory;
import com.google.api.services.calendar.Calendar;
import com.google.api.services.calendar.CalendarScopes;
import com.google.api.services.calendar.model.Event;
import com.google.api.services.calendar.model.Events;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.security.GeneralSecurityException;
import java.util.Collections;
import java.util.List;

public class CalendarQuickstart {
  private static final String APPLICATION_NAME = "Google Calendar API Java Quickstart";
  private static final JsonFactory JSON_FACTORY = JacksonFactory
      .getDefaultInstance();
  private static final String CREDENTIALS_FOLDER = "credentials";

  /**
   * Global instance of the scopes required by this quickstart. If modifying
   * these scopes, delete your previously saved credentials/ folder.
   */
  private static final List<String> SCOPES = Collections
      .singletonList(CalendarScopes.CALENDAR_READONLY);
  private static final String CLIENT_SECRET_DIR = "client_secret.json";

  /**
   * Creates an authorized Credential object.
   * 
   * @param HTTP_TRANSPORT
   *          The network HTTP Transport.
   * @return An authorized Credential object.
   * @throws IOException
   *           If there is no client_secret.
   */
  private static Credential getCredentials(
      final NetHttpTransport HTTP_TRANSPORT) throws IOException {
    // Load client secrets.
    URL url = ClassLoader.getSystemResource(CLIENT_SECRET_DIR);
    URI uri = URI.create(url.toString());
    Path path = Paths.get(uri);
    GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY,
        new InputStreamReader(new FileInputStream(path.toString())));

    // Build flow and trigger user authorization request.
    GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(
        HTTP_TRANSPORT, JSON_FACTORY, clientSecrets, SCOPES)
            .setDataStoreFactory(
                new FileDataStoreFactory(new java.io.File(CREDENTIALS_FOLDER)))
            .setAccessType("offline").build();
    return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver())
        .authorize("user");
  }

  public static void main(String... args)
      throws IOException, GeneralSecurityException {
    // Build a new authorized API client service.
    final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport
        .newTrustedTransport();
    Calendar service = new Calendar.Builder(HTTP_TRANSPORT, JSON_FACTORY,
        getCredentials(HTTP_TRANSPORT)).setApplicationName(APPLICATION_NAME)
            .build();

    // List the next 10 events from the primary calendar.
    DateTime now = new DateTime(System.currentTimeMillis());
    Events events = service.events().list("primary").setMaxResults(10)
        .setTimeMin(now).setOrderBy("startTime").setSingleEvents(true)
        .execute();
    List<Event> items = events.getItems();
    if (items.isEmpty()) {
      System.out.println("No upcoming events found.");
    } else {
      System.out.println("Upcoming events");
      for (Event event : items) {
        DateTime start = event.getStart().getDateTime();
        if (start == null) {
          start = event.getStart().getDate();
        }
        System.out.printf("%s (%s)\n", event.getSummary(), start);
      }
    }
  }
}

【Java】Google/Outlook Calendar の iCal形式のURLからデータを取得する

$
0
0

■ はじめに

https://blogs.yahoo.co.jp/dk521123/37534228.html
だと、Google Calendar専用の処理になり、
例えば、Outlookのような他のカレンダーシステムも対応する場合は
別の処理を実装しなおさなければならない。
また、APIの仕様がいつ変更されるのか?どのように変更されるのか?などの懸念もある。

そこで、以下の関連記事で扱った標準のiCal形式をURLから取得できるので
そのサンプルを作成してみた
https://blogs.yahoo.co.jp/dk521123/37532480.html

■ サンプル

import java.io.InputStream;
import java.net.URL;

import biweekly.Biweekly;
import biweekly.ICalendar;
import biweekly.component.VEvent;
import biweekly.io.chain.ChainingTextParser;

public class GoogleCalendarDemo {
  /** Google Calendar : https://calendar.google.com/calendar/ical/【ユーザ名】%40gmail.com/public/basic.ics */
  private static final String TARGET_URL_FOR_GOOGLE_CALENDAR = "https://calendar.google.com/calendar/ical/yourusername%40gmail.com/public/basic.ics";
  /** Outlook Calendar */
  private static final String TARGET_URL_FOR_OUTLOOK_CALENDAR = "https://outlook.live.com/owa//calendar/XXXXXXXXXXXXX-XXXX-XXXXXXXXXXXXXXX/cid-XXXXXXXXXXXX/calendar.ics";
  
    
  public static void main(String[] args) {
    System.out.println("Start!");
    
    System.out.println("From Google");
    printICal(TARGET_URL_FOR_GOOGLE_CALENDAR);
    
    System.out.println("From Outlook");
    printICal(TARGET_URL_FOR_OUTLOOK_CALENDAR);
    
    System.out.println("Done!!");
  }

  private static void printICal(String targetUrl) {
    try {
      URL url = new URL(targetUrl);
      try (InputStream inputStream = url.openStream();) {
        ChainingTextParser<ChainingTextParser<?>> parser = Biweekly
            .parse(inputStream);
        for (ICalendar ical : parser.all()) {
          System.out.println("event List size = " + ical.getEvents().size());
          for (VEvent event : ical.getEvents()) {
            String summary = event.getSummary().getValue();
            System.out.println("summary = " + summary);
          }
        }
      }
    } catch (Exception ex) {
      ex.printStackTrace();
    }
  }
}

出力結果例

Start!
From Google
event List size = 2
summary = テストのために
summary = サンプル
From Outlook
event List size = 2
summary = Sample
summary = This is a sample. Hello World!!
Done!!

関連記事

Java で Google Calendar API を使う

https://blogs.yahoo.co.jp/dk521123/37534228.html

【Java】iCalendar ライブラリ ~ biweekly ~

https://blogs.yahoo.co.jp/dk521123/37532480.html

【Android】 テキスト読み上げ (TTS : Text To Speech) を行う

$
0
0

■ サンプル

import android.speech.tts.TextToSpeech;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

import java.util.Locale;

public class MainActivity extends AppCompatActivity implements TextToSpeech.OnInitListener {
    private TextToSpeech tts;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // TextToSpeechオブジェクトの生成
        tts = new TextToSpeech(this, this);
    }


    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (null != tts) {
            // TextToSpeechのリソースを解放する
            tts.shutdown();
        }
    }

    @Override
    public void onInit(int status) {
        if (TextToSpeech.SUCCESS == status) {
            Locale locale = Locale.ENGLISH;
            if (tts.isLanguageAvailable(locale) >= TextToSpeech.LANG_AVAILABLE) {
                tts.setLanguage(locale);
            } else {
                Log.d("", "Error SetLocale");
            }
        } else {
            Log.d("", "Error Init");
        }
    }

    protected  void onClickButton(View view) {
        try {
            String value = ((EditText)findViewById(R.id.inputEditText)).getText().toString();
            this.speechText(value);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        Toast.makeText(this, "Done!!", Toast.LENGTH_LONG).show();
    }

    private void speechText(String value) {
        if (0 < value.length()) {
            if (tts.isSpeaking()) {
                // 読み上げ中なら止める
                tts.stop();
            }

            // 読み上げ開始
            tts.speak(value, TextToSpeech.QUEUE_FLUSH, null);
        }
    }
}

【Eclipse】【Gradle】 Eclipse での Gradle に関するあれこれ

$
0
0

■ Eclipse でGradleのタスクを簡単に実行するには

便利!!
 * 「Gradle Tasks」ビューから実行可能

「Gradle Tasks」ビューを表示する

 * Eclipse の [Window]-[Show View]-[Gardle]-[Gradle Tasks] で表示

タスクを簡単に実行する

 * プロジェクト配下の各フォルダ内のタスクを右クリックし、[Run Gradle Tasks]を選択

使用上の注意

 * Defaultでは、独自で作成したタスクが表示されない
  => 表示するには、「Gradle Tasks」ビューの[▽アイコン(View Menu)]-[Show All Tasks]で
     プロジェクト配下にフォルダ「other」が表示され、その配下にタクスが表示されるはず

関連記事

Gradle ~入門編~

https://blogs.yahoo.co.jp/dk521123/35915819.html

マテリアル デザイン フレームワーク

$
0
0
Viewing all 860 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>