- 投稿日:2019-12-15T23:14:57+09:00
JavaでExcelデータの取り込み
はじめに
今回はApachePOIです。
Javaの処理の中にExcelで作られたデータ群を取り込む為に使いました。ApachePOIとは
正式名称ApachePOI
WordやExcelといったMicrosoft Office形式のファイルを読み書きできるJavaライブラリ
※Wiki参照
https://ja.wikipedia.org/wiki/Apache_POIここから以下のjarファイルをダウンロードしました。
https://poi.apache.org/download.htmlpoi-4.1.1.jar
poi-ooxml-4.1.1.jar
poi-ooxml-schemas-4.1.1.jar
commons-collections4-4.4.jar
commons-compress-1.19.jar
xmlbeans-3.1.0.jar作った処理
対象のExcelファイルの1シート目、A列をJavaに取り込む処理を作ってみました。
SamplePOI.javapublic class SamplePOI { /* * 本処理 */ public static void main(String[] args) { //ここに取り込みたいExcelファイルのフルパスを入れる String ExcelPath = ""; //Excel用オブジェクト Workbook wb; Sheet sh; Row row; Cell cell; //取得データを保持するリスト List<String> columnA_List = new ArrayList<>(); try (InputStream is = new FileInputStream(ExcelPath)) { //対象のExcelファイルをJavaに取り込み wb = WorkbookFactory.create(is); //対象ファイルの1枚目のシートを指定 sh = wb.getSheetAt(0); //シート内の最大行を取得 int rowMaxA = sh.getLastRowNum(); //最大行分のループを回してA列のセルをString型として取得 for (int i = 0; i <= rowMaxA; i++) { row = sh.getRow(i); cell = row.getCell(0); String cellValue = getCellStringValue(cell); columnA_List.add(cellValue); } //コンソールに出力 System.out.print("["); for (String outStr : columnA_List) { System.out.print(outStr); } System.out.print("]"); } catch (Exception e) { e.printStackTrace(); } } /* * セルの状態を判別してString型で返す。 */ private static String getCellStringValue(Cell cell) { String retStr; CellType cellType = cell.getCellType(); switch (cellType) { case STRING: retStr = cell.getStringCellValue(); break; case NUMERIC: retStr = String.valueOf(cell.getNumericCellValue()); break; case BOOLEAN: retStr = String.valueOf(cell.getBooleanCellValue()); break; case FORMULA: retStr = String.valueOf(cell.getCellFormula()); break; case ERROR: retStr = String.valueOf(cell.getErrorCellValue()); break; default: retStr = ""; break; } return retStr; } }実行してみる
次のひらがなを入れたExcelファイルを読み込ませて実行してみると、
「あかさたなはまやらわ」が出力される。
samplePOI.result[あかさたなはまやらわ]まとめ
簡単な処理しか行っていないが感覚的にはVBAと同じだなと感じました。
セルの中身を取得する際にはセルタイプを考慮しないといけないという所は少し理解に苦戦を強いられた。Excelで関数を入れて取り込もうとすると、計算結果じゃなくて関数そのものが値として取得されるっぽい?
ので結果が欲しい場合は少し大変そう。
- 投稿日:2019-12-15T20:19:22+09:00
Getting Started with Gradle on Heroku をやってみた
動機
私は現在、TitterやLINEでbotを作り、特定の界隈向けに情報を発信しています。これらは既存のプラットフォームにのっかるだけなので比較的簡単に公開まで持って行くことができます。
しかし、これらの方法での発信では物足りなくなってきました。そこで、webサービスを作ろうと考えました。
完全に趣味としてやっているので、可能な限り無料でやろうということでHerokuに目を付けました。
というわけで今回はHeroku公式にあるチュートリアルをやっていこうと思います。
この記事はほぼチュートリアルの和訳みたいになってしまいましたがご了承ください。準備
最初に見つけたのはGetting Started on Heroku with Javaというページだったのですが、こちらはMavenを使用するようです。
このページの中で「MavenではなくGradleを使う人はこっちをみてね。」とあったので、今回はそちらをやっていきます。
※私個人がGradleの方が馴染みがあるため。環境
今回使用するPCの環境をざっくりと。
- OS Windows10 Home 64bit
- Java 11
- Git Bashは導入済み
チュートリアル目次
- Introduction
- Set up
- Prepare the app
- Deploy the app
- View logs
- Define a Procfile
- Scale the app
- Declare app dependencies
- Run the app locally
- Push local changes
- Provision add-ons
- Start a one-off dyno
- Define config vars
- Use a detabase
- Next steps
Introduction
こちらには今回のチュートリアルの前提条件が記載してあります。
- Herokuのアカウントを持っていること
- Java8が導入されていること
Javaのバージョン指定が8となっていましたが、私の環境では11でも問題ありませんでした。
Set up
ここではHeroku Command Line Interface (CLI)を導入します。
(以前はHeroku Toolbeltという名前だったようです。)
CLIはアプリケーションの管理とスケーリング、アドオンのプロビジョニング、Herokuで実行されるアプリケーションのログの表示、アプリケーションのローカル実行に使用するようです。
自分の環境にあったインストーラを選択してダウンロードします。インストールが終わると
heroku
コマンドが使えるようになります。heroku login
コマンドでHeroku CLIへログインしましょう。herokuへのログイン$ heroku login heroku: Press any key to open up the browser to login or q to exit # ここでキーを押すとブラウザで画面が立ち上がる # 画面にあるログインボタンを押すとログインできる › Warning: If browser does not open, visit › https://cli-auth.heroku.com/auth/browser/*** heroku: Waiting for login... Logging in... done Logged in as me@example.comproxy設定ができるようですが、今回は必要ないので省略します。
参考:CLI Usage / Using an HTTP proxyPrepare the app
用意されているサンプル用プロジェクトを任意のディレクトリにクローンします。
プロジェクトをクローン$ git clone https://github.com/heroku/gradle-getting-started.git $ cd gradle-getting-startedDeploy the app
先ほどクローンしたプロジェクトをデプロイしていきます。
まず、Herokuにアプリケーションを作成しHerokuでソースコードを受け取れるようにします。アプリケーションを作成$ heroku create Creating app... done, arcane-inlet-19935 https://arcane-inlet-19935.herokuapp.com/ | https://git.heroku.com/arcane-inlet-19935.git
arcane-inlet-19935
というのはアプリケーション名でランダムに命名されたものです。アプリケーション名は指定することもできるようです。このアプリケーション名はURLの一部になります。今回はチュートリアルなので特に指定しませんでした。
heroku
というリポジトリへpushします。$ git push heroku masterアプリケーションが動いていることを確認します。
$ heroku ps:scale web=1 Scaling dynos... done, now running web at 1:Free下記コマンドをを打つとブラウザが立ち上がり、ページを開いてくれます。
$ heroku openURLを確認すると、
https://{アプリケーション名}.herokuapp.com
となっています。View logs
Herokuのログはすべてのアカウント、Herokuコンポーネント共通で単一の出力ストリームに時系列順でログを出力しています。
ログ出力$ heroku logs --tail 2019-12-15T06:22:53.476464+00:00 app[web.1]: 2019-12-15 06:22:53.476 INFO 4 --- [ main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index 2019-12-15T06:22:54.441609+00:00 heroku[web.1]: State changed from starting to up 2019-12-15T06:22:54.408814+00:00 app[web.1]: 2019-12-15 06:22:54.405 INFO 4 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 37878 (http) with context path '' 2019-12-15T06:22:54.452349+00:00 app[web.1]: 2019-12-15 06:22:54.452 INFO 4 --- [ main] com.example.heroku.HerokuApplication : Started HerokuApplication in 12.317 seconds (JVM running for 14.742) 2019-12-15T06:32:15.384939+00:00 app[web.1]: 2019-12-15 06:32:15.382 INFO 4 --- [io-37878-exec-8] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet' 2019-12-15T06:32:15.384963+00:00 app[web.1]: 2019-12-15 06:32:15.382 INFO 4 --- [io-37878-exec-8] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet' 2019-12-15T06:32:15.412236+00:00 app[web.1]: 2019-12-15 06:32:15.412 INFO 4 --- [io-37878-exec-8] o.s.web.servlet.DispatcherServlet : Completed initialization in 29 ms
--tail
はリアルタイムで表示するというオプションです。起動確認時に訪れたページに再度アクセスすると、そのログが流れていく様子を見ることができます。
Ctrl + C
でリアルタイム表示を終了できます。
ログの出し方のオプションは他にもいろいろ用意されています。
ログの出し方:https://devcenter.heroku.com/articles/logging#view-logsDefine a Procfile
アプリケーションのルートディレクトリにあるProcfileでアプリケーションを起動するコマンドを明示的に宣言します。
サンプルプロジェクトのProcfileは次のようになっています。Procfileweb: java -jar build/libs/gradle-getting-started-1.0.jar単一のプロセスタイプ
web
とをれを実行するために必要なコマンドが宣言されています。ここではweb
という名前が重要な意味を持ちます。
プロセスタイプやProcfileについては下記を読んでください。
Procfileについて:https://devcenter.heroku.com/articles/procfileProcfileに書かれたコマンドがそのままUNIX環境で実行されるので、Syntaxが重要になります。
Scale the app
現在、アプリケーションは単一のWeb dynoで実行されています。dynoというのはProcfileで指定されたコマンドを実行する小さなコンテナだと考えることができます。
psコマンドで実行されているdynoの数を確認できます。
$ heroku ps Free dyno hours quota remaining this month: 949h 54m (94%) Free dyno usage for this app: 0h 0m (0%) For more information on dyno sleeping and how to upgrade, see: https://devcenter.heroku.com/articles/dyno-sleeping === web (Free): java -jar build/libs/gradle-getting-started-1.0.jar (1) web.1: up 2019/02/13 11:10:03 -0600 (~ 13s ago)この例では
web.1
が動いていることを確認できます。この辺りは課金の話になりますが、dynoについてはこのように書かれています。
- デフォルトではFreeのdynoにデプロイされる。
- Freeのdynoは30分間操作しない(トラフィックを受信しない)とスリープ状態になる。
- スリープ時にリクエストを受けると起動のために数秒の遅延が起きる。
- アカウント単位で月ごとにFreeのdynoの起動時間の無料枠があり、それを消費しきるとFreeのdynoにデプロイされているアプリケーションは月末までスリープ状態のままになってしまう。
詳細はこちらに書いてあります。
Declare app dependencies
Herokuはルートディレクトリに
gradlew
またはbuild.gradle
があることでGradleアプリであることを認識しています。サンプルプロジェクトには
build.gradle
が入っています。build.gradle(抜粋)dependencies { implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-jdbc' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' runtimeOnly 'org.postgresql:postgresql' runtimeOnly 'org.webjars:jquery:3.3.1-1' runtimeOnly 'org.webjars:jquery-ui:1.12.1' runtimeOnly 'org.webjars:bootstrap:4.1.3' testImplementation 'org.springframework.boot:spring-boot-starter-test' }Javaのバージョンはルートディレクトリにある
system.properties
に記載します。system.propertiesjava.runtime.version=1.8Introductionで書きましたが、このチュートリアルはJava8を前提としているため1.8となっています。
それでは
build
コマンドを実行しましょう。$ ./gradlew buildRun the app locally
続いてローカル環境での実行です。
$ heroku local webHeroku上で起動するのと同様にProcfileで実行するものを決定します。ポートの指定は
src/main/resources/application.properties
に記載します。application.propertiesserver.port=${PORT:5000}
heroku local
コマンドはアプリケーションの実行だけでなくconfig vars
のセットもできます。(後述)Push local changes
このセクションではローカルの変更をHerokuへ反映する手順を説明します。
例として以下のようにサンプルに変更を加えていきます。build.gradleの30行目compile "org.jscience:jscience:4.3.1"src/main/java/com/example/heroku/HerokuApplication.java// 19行目 import static javax.measure.unit.SI.KILOGRAM; import javax.measure.quantity.Mass; import org.jscience.physics.model.RelativisticModel; import org.jscience.physics.amount.Amount; // 中略 // 59行目 @RequestMapping("/hello") String hello(Map<String, Object> model) { RelativisticModel.select(); Amount<Mass> m = Amount.valueOf("12 GeV").to(KILOGRAM); model.put("science", "E=mc^2: 12 GeV = " + m.toString()); return "hello"; }src/main/resources/templates/hello.htmlを作成<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org" th:replace="~{fragments/layout :: layout (~{::body},'hello')}"> <body> <div class="container"> <p th:text="${science}"/> </div> </body> </html>変更を終えたら再度ビルドし、アプリケーションを実行します。
$ ./gradlew build ... BUILD SUCCESSFUL in 41s 5 actionable tasks: 5 executed $ heroku local web ... 17:48:20 web.1 | 2019-12-15 17:48:20.865 INFO 14244 --- [ main] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 9409 ms 17:48:23 web.1 | 2019-12-15 17:48:23.462 INFO 14244 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor' 17:48:25 web.1 | 2019-12-15 17:48:25.429 INFO 14244 --- [ main] o.s.b.a.w.s.WelcomePageHandlerMapping : Adding welcome page template: index 17:48:26 web.1 | 2019-12-15 17:48:26.397 INFO 14244 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 5000 (http) with context path ''起動したら
http://localhost:5000/hello
にアクセスします。E=mc^2: 12 GeV = (2.139194076302506E-26 ± 1.4E-42) kgという何か物理に関する数式のようなものが出ればOKです。この計算をするために
dependencies
やimport
で追加したライブラリを使っているようです。
(jscienceなんてライブラリがあるんですね…)確認出来たらデプロイしていきます。
$ git add . $ git commit -m "Demo" $ git push heroku masterデプロイできたことを確認します。
$ heroku openデモのトップ画面が開いたらURLの末尾に
/hello
を付けると、先ほどの数式のが表示されるはずです。Provision add-ons
Herokuには様々なサードパーティアドオンがあります。今回はPapertrailというログ用アドオンを追加します。
Pepertrailを入れるとブラウザ上でログを確認することができます。$ heroku addons:create papertrail Creating papertrail on mysterious-wildwood-86066... ! ! Please verify your account to install this add-on plan (please enter a ! credit card) For more information, see ! https://devcenter.heroku.com/categories/billing Verify now at ! https://heroku.com/verifyどうやらアドオンを追加するにはクレジットカードの登録が必要なようです。クレジットカードを登録するとFree dyno時間(いわゆる無料枠)が400h/月から1000h/月に拡大されるので私は登録しました。
では改めて
$ heroku addons:create papertrail Creating papertrail on mysterious-wildwood-86066... free Welcome to Papertrail. Questions and ideas are welcome (support@papertrailapp.com). Happy logging! Created papertrail-curly-70913 as PAPERTRAIL_API_TOKEN Use heroku addons:docs papertrail to view documentation無事に追加できました。追加されているアドオンは以下のようにして確認することができます。
$ heroku addons Add-on Plan Price State ─────────────────────────────────── ─────── ───── ─────── papertrail (papertrail-curly-70913) choklad free created └─ as PAPERTRAIL The table above shows add-ons and the attachments to the current app (mysterious-wildwood-86066) or other apps.それでは下記のコマンドでログを見てみましょう。
$ heroku addons:open papertrailブラウザでログが表示されたページが立ち上がります。
Start a one-off dyno
one-off dynoというのはHeroku上でコマンドを一度限りで実行するもののようです。one-off dynoでは
heroku run
というコマンドを用います。例)Javaのバージョンを確認する$ heroku run java -version Running java -version on arcane-inlet-19935... up, run.6561 (Free) openjdk version "1.8.0_201-heroku" OpenJDK Runtime Environment (build 1.8.0_201-heroku-b09) OpenJDK 64-Bit Server VM (build 25.201-b09, mixed mode)ほかにもローカル端末に接続されたREPLプロセスを起動してアプリの環境で実験したり、アプリケーションと共にデプロイしたコードを起動したりすることもできます。
Define config vars
Herokuでは、暗号化キーや外部リソースアドレスなどのデータをconfig varsに保存して、構成を外部化できます。config varsは実行時に環境変数としてアプリケーションに公開されます。
例として先ほどサンプルに追加したHerokuApplication.java
のhello
メソッドがENERGY
環境変数からエネルギー値を取得するように変更してみます。HerokuApplication.java@RequestMapping("/hello") String hello(Map<String, Object> model) { RelativisticModel.select(); // ここから追加 String energy = System.getenv().get("ENERGY"); // ここで環境変数ENERGYを使用 if (energy == null) { energy = "12 GeV"; } // ここまで Amount<Mass> m = Amount.valueOf(energy).to(KILOGRAM); // 変数energyを使用するように変更 model.put("science", "E=mc^2: " + energy + " = " + m.toString()); // 変数energyを使用するように変更 return "hello"; }最上位ディレクトリの
.env
ファイルに環境変数を記述します。このファイルはheroku local
コマンドの時に読み込まれるようです。.envENERGY=20 GeVビルドし直してlocalで起動後
http://localhost:5000/hello
へアクセスすると数値が変わっていることが確認できます。変更後の数式E=mc^2: 20 GeV = (3.5653234605041761E-26 ± 2.9E-42) kg確認できたところでHerokuにも同じ環境変数を設定しましょう。
$ heroku config:set ENERGY="20 GeV" 'C:\Program' は、内部コマンドまたは外部コマンド、 操作可能なプログラムまたはバッチ ファイルとして認識されていません。???
とりあえずチュートリアルをさかのぼっていたらDefine a Procfileの項にこのように書いてありました。When running locally under Windows, you may receive an error because the Unix way of passing in environment variables ($JAVA_OPTS) and of concatenating paths (:) is incompatible.
Unix環境では文字列結合の環境変数の渡し方とpathの連結の仕方に互換性がないことが原因のようです。
仕方がないのでHeroku上での確認はいったんここはスルーします。Use a database
add-on maketplaceにはRedisやMongoDBからPostgres、MySQLまで数多くのデータストアがあります。まずはPostgreSQLアドオンの新しいインスタンスをアプリにアタッチしてみましょう。
$ heroku addons:create heroku-postgresql Creating heroku-postgresql on mysterious-wildwood-86066... free Database has been created and is available ! This database is empty. If upgrading, you can transfer ! data from another database with pg:copy Created postgresql-amorphous-60925 as DATABASE_URL Use heroku addons:docs heroku-postgresql to view documentation
heroku addons
コマンドで見てみましょう。$ heroku addons Add-on Plan Price State ────────────────────────────────────────────── ───────── ───── ─────── heroku-postgresql (postgresql-amorphous-60925) hobby-dev free created └─ as DATABASE papertrail (papertrail-curly-70913) choklad free created └─ as PAPERTRAIL The table above shows add-ons and the attachments to the current app (mysterious-wildwood-86066) or other apps.
heroku-postgresql
が追加されています。続いて、環境変数を見てみましょう。
$ heroku config === arcane-inlet-19935 Config Vars DATABASE_URL: postgres://pccigjsjmqxlvk:b3aedb1e099aa729d8037c0be6454e1028dc0c7c4dc16f8b366471486a8a7014@ec2-54-235-159-101.compute-1.amazonaws.com:5432/db2b5rodiinvv4 PAPERTRAIL_API_TOKEN: duMLEm2E5WHtf7mC163g
DATABASE_URL
という変数が追加されています。
heroku にはpg
というコマンドも用意されています。$ heroku pg === DATABASE_URL Plan: Hobby-dev Status: Available Connections: 10/20 PG Version: 11.6 Created: 2019-12-15 10:26 UTC Data Size: 7.7 MB Tables: 0 Rows: 0/10000 (In compliance) - refreshing Fork/Follow: Unsupported Rollback: Unsupported Continuous Protection: Off Add-on: postgresql-amorphous-60925これをみると
Hobby-dev(=無料プラン)
であり、PGのバージョンが11.6であることがわかりました。
これまでの手順でデプロイ済みのアプリケーションには既にデータベースの機能が含まれています。
https://{アプリケーション名}.herokuapp.com/db
にアクセスするとアクセスした日時が表示されます。(北米リージョンなので日本時間の9時間前)HerokuApplication.java(抜粋)@Value("${spring.datasource.url}") private String dbUrl; @Autowired private DataSource dataSource; @RequestMapping("/db") String db(Map<String, Object> model) { try (Connection connection = dataSource.getConnection()) { Statement stmt = connection.createStatement(); stmt.executeUpdate("CREATE TABLE IF NOT EXISTS ticks (tick timestamp)"); stmt.executeUpdate("INSERT INTO ticks VALUES (now())"); ResultSet rs = stmt.executeQuery("SELECT tick FROM ticks"); ArrayList<String> output = new ArrayList<String>(); while (rs.next()) { output.add("Read from DB: " + rs.getTimestamp("tick")); } model.put("records", output); return "db"; } catch (Exception e) { model.put("message", e.getMessage()); return "error"; } } @Bean public DataSource dataSource() throws SQLException { if (dbUrl == null || dbUrl.isEmpty()) { return new HikariDataSource(); } else { HikariConfig config = new HikariConfig(); config.setJdbcUrl(dbUrl); return new HikariDataSource(config); } }これにより、
/db
にアクセスするたびにtick
テーブルに現在時刻のレコードが追加され、これまでのアクセス日時が一覧表示されます。Next steps
ここまででデプロイの仕方、設定変更、ログチェック、アドオンの追加ができるようになりました。
次に学習するのにおすすめの記事がいくつか挙げられています。終わりに
Herokuの基本的なところはこれで押さえられたかなと思います。Herokuはアクセスしなければ30分でスリープ状態になり無料枠を消費しなくなるので、お試しでいくつもアプリケーションを作った後にわざわざ消したりしなくてもよいのが地味にうれしいところかなと思います。
参考
- 投稿日:2019-12-15T15:43:50+09:00
特定の日付との差が何年何か月かを表示
社内システムである特定の日付と現在の日付との差を何年何か月で表示する必要があったので、調べてみた。
- Java
あまり使ってこなかった java.time を使用。
今回は時間までは必要ないため、LocalDateTimeではなく、LocalDateを使用。LocalDate sampleDate = LocalDate.parse("2019/12/09", DateTimeFormatter.ofPattern("yyyy/MM/dd")); LocalDate currentDate = LocalDate.now(); // 特定の日付と本日の差を年形式で取得 //long year = ChronoUnit.YEARS.between(sampleDate , currentDate); // 特定の日付と本日の差を月形式で取得 ※間違い。トータル月数が取得される //long month = ChronoUnit.MONTHS.between(sampleDate , currentDate); // コメントいただいた方法で修正 Period period = Period.between(sampleDate , currentDate); // 特定の日付と本日の差を年形式で取得 long year = period.getYears(); // 特定の日付と本日の差を月形式で取得 long month = period.getMonths();上記で取得した年と月を文字列にして画面に表示。
- PHP
ついでにPHPも最近使ったので覚書。
PHPは独学なのでやり方間違っているかも。$sampleDate = new DateTime('2019/12/09'); $currentDate = new DateTime('now'); $diffDate = $currentDate ->diff($sampleDate); $year = $diffDate->format('%y'); $month = $diffDate->format('%m');終わり!
- 投稿日:2019-12-15T14:39:07+09:00
フォームへSLEEP値を入力してタイムアウトをテストするアプリ
はじめに
Webフォームへ値を入力し送信ボタンを押すと、その秒数SLEEPして応答を返すという単純なJavaのサンプルコードです。タイムアウト値をテストする際に使ったものです。
サーブレット配置
servletservlet +WEB-INF | +classes | | +RequestSample1.class | +web.xml +formsample.htmlformsample.html
formsample.html<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> <html lang="ja"> <head> <meta http-equiv="Content-Type" Content="text/html;charset=Shift_JIS"> <title>sleep_sample</title> </head> <body> <p>sleep_sample</p> <form action="/sample/RequestSample1" method="get"> <table> <tr> <td>SLEEP(秒)</td> <td><input type="text" size="3" value="" name="sleep"></td> </tr> </table> <input type="submit" name="button1" value="送信"> </form> </body> </html>web.xml
web.xml<?xml version="1.0" encoding="ISO-8859-1"?> <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4"> <servlet> <servlet-name>RequestSample1</servlet-name> <servlet-class>RequestSample1</servlet-class> </servlet> <servlet-mapping> <servlet-name>RequestSample1</servlet-name> <url-pattern>/RequestSample1</url-pattern> </servlet-mapping> </web-app>RequestSample1.java
RequestSample1.javaimport java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class RequestSample1 extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException{ response.setContentType("text/html;charset=Shift_JIS"); PrintWriter out = response.getWriter(); String sleep_sec = request.getParameter("sleep"); try { Thread.sleep(Long.parseLong(sleep_sec) * 1000); } catch (InterruptedException e) { } StringBuffer sb = new StringBuffer(); sb.append("<html>"); sb.append("<head>"); sb.append("<title>sleep_sample</title>"); sb.append("</head>"); sb.append("<body>"); sb.append("<p>"); sb.append(sleep_sec); sb.append("秒SLEEPしました。</p>"); sb.append("</body>"); sb.append("</html>"); out.println(new String(sb)); out.close(); } }
- 投稿日:2019-12-15T14:10:39+09:00
Androidアプリ開発で国民の祝日を表示させるのに試行錯誤。前編
どうも!ヨースケです。日記アプリもようやく1画面が終わったので、約10時間に及ぶ日帰りミニ旅をしていました。一眼レフでたくさん撮るぞーと意気込んでいましたが、ほとんど撮れませんでした...ただ疲れただけで、帰り道に前方後方不注意の車に二度轢かれかけるなどさんざんな一日でした(笑)。
今回は日記アプリに取り入れたその日が国民の祝日ならば表示しタップするとその詳細をダイアログで表示する。というのを前編後編にわたって紹介します。
- 国民の祝日
- 日付が固定されている祝日
- 第〇月曜日の祝日
- 春分と秋分
- 振替休日
国民の祝日
まず、日本にある国民の休日を記してみます。
元日、成人の日、建国記念の日、天皇誕生日、春分の日、昭和の日、憲法記念日、みどりの日、こどもの日、海の日、山の日、敬老の日、秋分の日、スポーツの日、文化の日、勤労感謝の日の16個あります。「体育の日」は2020年に「スポーツの日」と改められました。次はプログラムで重要になる日付の話をします。日付が固定されている祝日
プログラム的には楽な日付が初めから決められている祝日の一覧です。
祝日名 日付 備考 元旦 1月1日 - 建国記念の日 2月11日 - 天皇誕生日 2月23日 - 昭和の日 4月29日 - 憲法記念日 5月3日 - みどりの日 5月4日 - こどもの日 5月5日 - 山の日 8月11日 2020年は8月10日 文化の日 11月3日 - 勤労感謝の日 11月23日 - 第〇月曜日の祝日
祝日名 日付 備考 成人の日 1月の第2月曜日 - 海の日 7月の第3月曜日 2020年は7月23日 敬老の日 9月の第3月曜日 - スポーツの日 10月の第2月曜日 2020年は7月24日 以上の表のようにオリンピック・パラリンピックがある2020年は祝日が動いてしまいますが、月と週と曜日が分かるので何とか判定できそうです。
春分と秋分
この2つの祝日は厄介でした。まず、春分の日は3月の19日~22日うちの1日に、秋分の日は9月の22~24日のうちの1日になります。なぜ年によって日にが異なるのかと言うと地球の公転周期と関係があるようで詳しくはこちらをご覧ください(投げやり)。
春分の日や秋分の日は逐一カレンダーを見ながら入れていかなければならないのか?と最初は肩を落としましたが、Wikipediaによれば西暦年を4で割ったあまりでその年の春分・秋分の日が決められるそうです。
春分の日:https://ja.wikipedia.org/wiki/%E6%98%A5%E5%88%86%E3%81%AE%E6%97%A5
秋分の日:https://ja.wikipedia.org/wiki/%E7%A7%8B%E5%88%86%E3%81%AE%E6%97%A5
振替休日
振替休日は「国民の祝日の日曜日の翌日の月曜日以降の国民の祝日でない祝日の翌日」とちょっとややこしいですがそのように祝日法ではそう記されています。簡単に言えば日曜日が祝日なら次の平日が振替休日に充てられると考えた方がいいかもです。
後編では考え方や実際書いたソースコードを載せて解説とは言い難いですが、説明をしていこうかなと思います。今週中には上げるのでよろしくお願い致します。
参考文献
祝日法:https://www8.cao.go.jp/chosei/shukujitsu/gaiyou.html
国民の祝日:https://ja.wikipedia.org/wiki/%E5%9B%BD%E6%B0%91%E3%81%AE%E7%A5%9D%E6%97%A5
- 投稿日:2019-12-15T13:21:02+09:00
Redmine REST APIでハマったことリスト
背景
今年
Redmine REST API
を使って下記業務を実装する機会がありました。
- 進捗管理をExcelからRedmineにmigration
- CIのリグレッションテストでFailしたテストケースをRedmineに自動起票
目的
今まで
Redmine REST API
は使ったことがなく、実装しながら学んでいきました。その中でハマったことを中心に皆さんに共有して、僕のはハマった罠を回避して頂きたいと思います。前提
- javaをそこそこ理解している
- Redmineの機能をそこそこ理解している
環境
Redmine VersionEnvironment: Redmine version 3.4.6.stablejava Versionjava version "1.8.0_221" Java(TM) SE Runtime Environment (build 1.8.0_221-b11) Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)pom.xml<dependency> <groupId>com.taskadapter</groupId> <artifactId>redmine-java-api</artifactId> <version>4.0.0.preview.1</version> </dependency>ハマったこと
1. ユーザーに付与された権限にAPIの機能も制限される
Redmine REST APIを使うために最初に
RedmineManager
クラスを生成します。このときにAPI KEY
というものを使用します。RedmineManager manager = RedmineManagerFactory.createWithApiKey(REDMINE_URL, API_KEY);この
API KEY
はRedmineのユーザー一人一人に付与されます。当時、自分のユーザー権限は管理者権限ではありませんでした。APIのメソッドによってはRedmineのシステム管理者権限が必要なものがあります。全ユーザーを取得/** * Load list of users from the server. * <p><strong>This operation requires "Redmine Administrator" permission.</strong> * <p> * This method calls Redmine with "include = memberships,groups" parameter. * * @return list of User objects * @throws RedmineAuthenticationException invalid or no API access key is used with the server, which * requires authorization. Check the constructor arguments. * @throws NotFoundException * @throws RedmineException */ public List<User> getUsers() throws RedmineException { return transport.getObjectsList(User.class, new BasicNameValuePair( "include", "memberships,groups")); }柔軟に利用したい場合、システム管理者権限になることをお勧めします。
2.
Transport
が必要
redmine-java-api
のversion4以降から取り込まれた概念のようです。RedmineManager
クラスから取得できます。RedmineManager redmineManager = RedmineManagerFactory.createWithApiKey(REDMINE_URL, API_KEY); Transport transport = redmineManager.getTransport();
Transport
がないとチケット作成・更新時に「Transportが未設定だぞ!」という例外がスローされます。なので作成・更新前にIssue
のインスタンスにTransport
を設定してあげてください。新規チケット作成// Transportをセット Issue issue = new Issue(manager.getTransport()); // (中略) issue = issue.create();既存チケット更新Issue issue = manager.getIssueManager().getIssueById(fetchedIssue.getTicketId()); // Transportをセット issue.setTransport(manager.getTransport()); // (中略) issue.update();3.
Params
の使い方既存チケットに更新をかけるとき、「この条件にあったチケット」を取得したいときがあります。そのときは
Params
を使うことで「この条件」を表現できます。チケットの題名が"actual.getFileName"に一致するという条件Params().add("set_filter", "1") .add("f[]", "subject") .add("op[subject]", "=") .add("v[subject][]", actual.getFileName());ハマったことは下記3点です。
題名以外(例:担当者)ときのキー値は?
ドキュメントを見つけられなかったので、力技で特定してました。チケット一覧のフィルタで希望の条件を設定するとURLのパラメータとしてフィルタ条件が表示されます。パラメータをデコードするとキーの値を特定できます。
一致以外(例:一致しない、含む)のときは?
上の方法で解析。
チケット全部取得したいから条件なしで取得したら500件しか取得できない
現状不明。方法をご存じの方がいたらご教示ください。
4. 既存チケットのカスタムフィールドを更新するときは一度clearする
一度既存チケットのカスタムフィールドを取得して、値を更新する。そのあとに既存チケットのカスタムフィールドを
clearCustomFields
してから追加しないとうまく更新されませんでした。// この処理で既存チケットからカスタムフィールドを取得して更新して、返却している Collection<CustomField> customFieldCollection = setEachCustomField(issue, fetchedIssue); issue.clearCustomFields(); issue.addCustomFields(customFieldCollection);5. 日付型のカスタムフィールドに値を設定するときのフォーマット
yyyy-MM-dd
一択です。これ以外はparseエラーになります。以上(思いついたら追加していきます。)
参考
- 投稿日:2019-12-15T13:21:02+09:00
Redmine REST APIでハマったこと
背景
今年
Redmine REST API
を使って下記のようなことをする機会がありました。
- 進捗管理をExcelからRedmineにmigration
- CIのリグレッションテストでFailしたテストケースをRedmineに自動起票
目的
今まで
Redmine REST API
は使ったことがなく、実装しながら学んでいきました。その中でハマったことを中心に皆さんに共有して、僕のはハマった罠を回避して頂きたいと思います。前提
- javaをそこそこ理解している
- Redmineの機能をそこそこ理解している
環境
Redmine VersionEnvironment: Redmine version 3.4.6.stablejava Versionjava version "1.8.0_221" Java(TM) SE Runtime Environment (build 1.8.0_221-b11) Java HotSpot(TM) 64-Bit Server VM (build 25.221-b11, mixed mode)pom.xml<dependency> <groupId>com.taskadapter</groupId> <artifactId>redmine-java-api</artifactId> <version>4.0.0.preview.1</version> </dependency>ハマったこと
1. ユーザーに付与された権限にAPIの機能も制限される
Redmine REST APIを使うために最初に
RedmineManager
クラスを生成します。このときにAPI KEY
というものを使用します。RedmineManager manager = RedmineManagerFactory.createWithApiKey(REDMINE_URL, API_KEY);この
API KEY
はRedmineのユーザー一人一人に付与されます。当時、自分のユーザー権限は管理者権限ではありませんでした。APIのメソッドによってはRedmineのシステム管理者権限が必要なものがあります。全ユーザーを取得/** * Load list of users from the server. * <p><strong>This operation requires "Redmine Administrator" permission.</strong> * <p> * This method calls Redmine with "include = memberships,groups" parameter. * * @return list of User objects * @throws RedmineAuthenticationException invalid or no API access key is used with the server, which * requires authorization. Check the constructor arguments. * @throws NotFoundException * @throws RedmineException */ public List<User> getUsers() throws RedmineException { return transport.getObjectsList(User.class, new BasicNameValuePair( "include", "memberships,groups")); }柔軟に利用したい場合、システム管理者権限になることをお勧めします。
2.
Transport
が必要
redmine-java-api
のversion4以降から取り込まれた概念のようです。RedmineManager
クラスから取得できます。RedmineManager redmineManager = RedmineManagerFactory.createWithApiKey(REDMINE_URL, API_KEY); Transport transport = redmineManager.getTransport();
Transport
がないとチケット作成・更新時に「Transportが未設定だぞ!」という例外がスローされます。なので作成・更新前にIssue
のインスタンスにTransport
を設定してあげてください。新規チケット作成// Transportをセット Issue issue = new Issue(manager.getTransport()); // (中略) issue = issue.create();既存チケット更新Issue issue = manager.getIssueManager().getIssueById(fetchedIssue.getTicketId()); // Transportをセット issue.setTransport(manager.getTransport()); // (中略) issue.update();3.
Params
の使い方既存チケットに更新をかけるとき、「この条件にあったチケット」を取得したいときがあります。そのときは
Params
を使うことで「この条件」を表現できます。チケットの題名が"actual.getFileName"に一致するという条件Params().add("set_filter", "1") .add("f[]", "subject") .add("op[subject]", "=") .add("v[subject][]", actual.getFileName());ハマったことは下記3点です。
題名以外(例:担当者)ときのキー値は?
ドキュメントを見つけられなかったので、力技で特定してました。チケット一覧のフィルタで希望の条件を設定するとURLのパラメータとしてフィルタ条件が表示されます。パラメータをデコードするとキーの値を特定できます。
一致以外(例:一致しない、含む)のときは?
上の方法で解析。
チケット全部取得したいから条件なしで取得したら500件しか取得できない
現状不明。方法をご存じの方がいたらご教示ください。
4. 既存チケットのカスタムフィールドを更新するときは一度clearする
一度既存チケットのカスタムフィールドを取得して、値を更新する。そのあとに既存チケットのカスタムフィールドを
clearCustomFields
してから追加しないとうまく更新されませんでした。// この処理で既存チケットからカスタムフィールドを取得して更新して、返却している Collection<CustomField> customFieldCollection = setEachCustomField(issue, fetchedIssue); issue.clearCustomFields(); issue.addCustomFields(customFieldCollection);5. 日付型のカスタムフィールドに値を設定するときのフォーマット
yyyy-MM-dd
一択です。これ以外はparseエラーになります。以上(思いついたら追加していきます。)
参考
- 投稿日:2019-12-15T12:54:56+09:00
Catalinaにjavaをインストールできない場合
javaをmacにインストールできない、、
macを買い替えたので、新たにjavaをインストールしようとしたときに問題が起こりました。
MacのOSはCatalina(10.15.1)です。brew cask install javaこのスクリプトを実行してバージョンを確認しようとしたところ思わぬエラーが、、
MacのOSがcatalinaになってからこのようなエラーが出ている人がちらほらいるみたいです。解決策
解決策としては、oracle公式のインストーラを使ってインストールすることで事なきを得ました。
oracle公式にあるインストーラ(拡張子がdmgのほう)を使うことでこの問題を解決することができます。
バージョンも確認できました。$ java -version java version "13.0.1" 2019-10-15 Java(TM) SE Runtime Environment (build 13.0.1+9) Java HotSpot(TM) 64-Bit Server VM (build 13.0.1+9, mixed mode, sharing)注意点
oracle公式からmac用のインストーラが2つあるのですが、拡張子がtar.gzのほうを使うと同様のエラーが出てしまう可能性があります。
自分の環境ではエラーが出ました。
- 投稿日:2019-12-15T09:07:10+09:00
Javaのenumにabstractなメソッドを定義してそれぞれの振る舞いを書く
概要
このエントリでは、Javaのenumにabstractなメソッドを定義してそれぞれの振る舞いを書くことについて取り扱います。
(EffectiveJava 3rd editionに書いてあるやつ、だいたいそのまんまです。自分でやってみた内容についてメモしておきます。)Javaのenum
Javaのenumでは、値だけでなくメソッドを定義することができます。
値だけ定義したパターン
enum Operator { PLUS, SUBTRACT, MULTIPLY, DIVIDE, NONE }これに対し、値ごとにメソッドを定義し、下図のような書き方をすることができます。
enum Operator { PLUS { @Override BigDecimal apply(@NonNull BigDecimal lhs, @NonNull BigDecimal rhs) { return lhs.add(rhs); } }, SUBTRACT{ @Override BigDecimal apply(@NonNull BigDecimal lhs, @NonNull BigDecimal rhs) { return lhs.subtract(rhs); } }, MULTIPLY { @Override BigDecimal apply(@NonNull BigDecimal lhs, @NonNull BigDecimal rhs) { return lhs.multiply(rhs); } }, DIVIDE { @Override BigDecimal apply(@NonNull BigDecimal lhs, @NonNull BigDecimal rhs) { return lhs.divide(rhs, BigDecimal.ROUND_UNNECESSARY);//; } }, NONE { // allow null for rhs @Override BigDecimal apply(@NonNull BigDecimal lhs, BigDecimal rhs) { return lhs; } }; abstract BigDecimal apply(BigDecimal lhs, BigDecimal rhs); }enum内の「abstract BigDecimal apply(BigDecimal lhs, BigDecimal rhs);」ですべての値で定義すべきメソッドを指定しておき、それぞれの値の宣言時にメソッドをOverrideしています。
(補足)上のソースで、「@NonNull」は、lombokで提供されているアノテーションの一つで、Nullチェックを実装してくれるものです。
使用例
前述の形でenumを定義しておくと、例えば以下のようなメソッドが、
public synchronized BigDecimal pushEvalButton() { var v = new BigDecimal(sb.toString()); switch(currentOperator) { case PLUS: { v = stack.add(getCurrentValue()); break; } case SUBTRACT: { v = stack.subtract(getCurrentValue()); break; } case MULTIPLY: { v = stack.multiply(getCurrentValue()); break; } case DIVIDE: { v = stack.divide(getCurrentValue(), BigDecimal.ROUND_UNNECESSARY);// break; } case NONE: { return v; } default: { throw new RuntimeException("Not defined."); } } currentOperator = Operator.NONE; replaceBuffer(v.toPlainString()); clearStack(); return v; }下記のようにスッキリ書けます。
public synchronized BigDecimal pushEvalButton() { var v = new BigDecimal(sb.toString()); if(Operator.NONE == currentOperator) { return v; } v = currentOperator.apply(stack, getCurrentValue()); currentOperator = Operator.NONE; replaceBuffer(v.toPlainString()); clearStack(); return v; }enumのメソッドの方に内容が移動したので元のメソッドからコードが減るのは当然として、以下がメリットと感じました。
- switch内のdefaultを追い出せた
- ほかの場所でもenumのメソッドを使うことにより、処理が一か所にまとめられたまとめ
このエントリでは、Javaのenumにabstractなメソッドを定義してそれぞれの振る舞いを書くことについて、例をあげて説明しました。
上記の差分例は、GitHubのこのcommitを参照ください。
- 投稿日:2019-12-15T08:52:44+09:00
可変長引数
個人的な勉強のためまとめました
可変長引数
・引数の数を自由に変更できる引数
・引数の型の直後にピリオドを3つ「...」つけて宣言
・理論上は引数をいくつでも渡しても呼び出すことができる
・渡された複数の値は、JVMによって配列に置き換えられる。そのため、値を使うときは配列と同じように「[]」を使う2つの注意点
①同じ型の数が可変な引数をまとめられるだけで、異なる型はまとめられない
②可変長引数以外の引数を受け取る必要がある場合、可変長引数は最後の引数にすること①は、可変長引数が配列に置き換えられていることを考えれば当然
②は、例えば次のようなメソッドの宣言では、どこまでが第一引数で、どこからが第二引数かわからず、コンパイルエラーになる例)可変長引数を最初に記述したメソッド(コンパイルエラー)
sample.javavoid sample(int... num,int value) { //do something }次のように順番を入れ替えるとコンパイルエラーが発生しない
例)可変長引数を最後に記述したメソッド
sample.javavoid sample(int value,int... num) { //do something }