20191009のJavaに関する記事は9件です。

PDFBoxを使って既存のPDF文書に線を引く

PDFBox を使って既存のPDF文書に対して線を書き込みたいというニッチなニーズがあったので、その方法を紹介します。例えば、行政に提出する様式で、申請内容に応じて関係のない部分に取り消し線を引くといったケースを想定しています。

image.png

確認環境

  • PDFBox 2.0.15
  • AdoptOpenJDK 11.0.3+7
  • macOS 10.14.6

実現方法

  • PDPageContentStream を初期化するときに AppendMode.APPEND を指定してあげるのがポイント
    • デフォルトだと AppendMode.OVERWRITE なので、元の内容がクリアされて真っ白な背景になってしまう
  • PDF の座標系はページの左下が (0, 0) で右上に向かって大きくなる
    • 描画位置の特定方法は後述
void line() throws IOException {
    try (InputStream is = getInputStream("sample.pdf");
         PDDocument doc = PDDocument.load(is)) {

        PDPage page = doc.getPage(0);
        PDPageContentStream contents = new PDPageContentStream(doc, page, AppendMode.APPEND, false);

        contents.moveTo(290f, 808f); // 始点座標
        contents.lineTo(365f, 808f); // 終点座標
        contents.setLineWidth(1f);   // 線の太さ
        contents.stroke();           // 描画

        contents.moveTo(290f, 805f); // 始点座標
        contents.lineTo(365f, 805f); // 終点座標
        contents.setLineWidth(1f);   // 線の太さ
        contents.stroke();           // 描画

        contents.close();
        doc.save("/tmp/output.pdf");
    }
}

描画位置の特定

適切なツールを使うとかもっと賢い方法がありそうですが、分からなかったので泥臭く自分で座標系を描画してあげることにします。

void grid() throws IOException {
    try (InputStream is = getInputStream("sample.pdf");
         PDDocument doc = PDDocument.load(is)) {
        PDPage page = doc.getPage(0);

        // ページ左下から右上に向かって格子線を引く
        PDPageContentStream contents = new PDPageContentStream(doc, page, AppendMode.APPEND, false);
        PDRectangle bbox = page.getBBox();
        for (float x = 0; x < bbox.getWidth(); x += 10) {
            contents.moveTo(bbox.getLowerLeftX() + x, bbox.getLowerLeftY());
            contents.lineTo(bbox.getLowerLeftX() + x, bbox.getUpperRightY());
            contents.setLineWidth((x % 100 == 0) ? 2f : 0.5f);
            contents.stroke();
        }
        for (float y = 0; y < bbox.getHeight(); y += 10) {
            contents.moveTo(bbox.getLowerLeftX(), bbox.getLowerLeftY() + y);
            contents.lineTo(bbox.getUpperRightX(), bbox.getLowerLeftY() + y);
            contents.setLineWidth((y % 100 == 0) ? 2f : 0.5f);
            contents.stroke();
        }
        contents.close();

        doc.save("/tmp/output.pdf");
    }
}

あとは左下から目盛りを数えてやれば、描画したい位置の大まかな座標を特定できるというわけです。

image.png

もっと良い方法があれば、ぜひ教えて下さい :sweat_smile:

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【NetBeans】起動しなくなってしまった時の話

NetBeansをいつものように起動しようとしたら、何も出ないままいつまで経っても起動しない…。前回は普通に起動していたのに…。このようになった時のことをここに書き残しておく。
*環境
OS: Windows 10
NetBeans: Apache NetBeans IDE 11.1
Java: JDK 13

*netbeans.conf(下の方でも記述あり)を編集したら同様に起動しなくなってしまうことがあるので、もしnetbeans.confを直近で編集したのであればまず元に戻してみよう。それでもダメだった時の話が以下のとおり。

エラーの内容

 デスクトップ上とかのショートカットで起動しようとすると、何も起こらない。エラーメッセージなど何も出ない。ここで、コマンドプロンプトで起動してみる。
C:\Program Files\NetBeans-11.1\netbeans\bin>netbeans.exe
こちらでもやはり起動に失敗するが、エラーメッセージが出る。
(再現しようと思ったが、できなかったのでどのようなエラーメッセージかは出せませんでした。すみません。)
そのエラーメッセージの中で、ログを出している場所を教えてくれるので、そのログを開いてみよう。場所の例としては、
C:\Users\【ユーザー名】\AppData\Local\Temp
で、ログファイルの名称はhs_err_pid18004.logというような感じ。(数字はプロセスIDなので、きっと違う番号になっている。)中身の最初の方は以下のような感じ。

#
# A fatal error has been detected by the Java Runtime Environment:
#
# EXCEPTION_ACCESS_VIOLATION (0xc0000005) at pc=0x00007fff2a50f98a, pid=18004, tid=12052
#
# JRE version: Java(TM) SE Runtime Environment (13.0+33) (build 13+33)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (13+33, mixed mode, sharing, tiered, >compressed oops, g1 gc, windows-amd64)
# Problematic frame:
# C [awt.dll+0x8f98a]
#
# No core dump will be written. Minidumps are not enabled by default on client versions of >Windows
#
# If you would like to submit a bug report, please visit:
# http://bugreport.java.com/bugreport/crash.jsp
# The crash happened outside the Java Virtual Machine in native code.
# See problematic frame for where to report the bug.

EXCEPTION_ACCESS_VIOLATIONがエラーの内容であるわけだが、メモリの読み取り違反ではないかと推測される。どこで問題が起こったかというと、

# C [awt.dll+0x8f98a]

の所に書かれている。
 awt.dllというのは、java\jdk-13\binの中にあるファイル。これはいじりようがないファイル…。

今回の解決方法

 最終的には起動に成功したわけだが、どうしたかというと、

JDKをバージョンダウンした

ことである。JDKを13から12.0.2にバージョンダウンして、
C:\Program Files\NetBeans-11.1\netbeans\etc
にあるnetbeans.confの中身の一部を以下のように書き換える。(数字はバージョンに合わせる)
netbeans_jdkhome="C:\Program Files\Java\jdk-12.0.2"
*Windowsでは直に書き換えられない(保存できない)と思うので、そのファイルをどこかにコピーして書き換えて、その書き換えたファイルをペーストしよう。

これで起動に成功!

同じような目に遭った方がいたら、参考にしてくれると幸いです。

余談

 起動に成功したから、この記事を書くために再現しようとnetbeans.confを戻したらなぜかそれでも起動できた。一体何だったんだ…。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

\tの使い方 macとWindowsで異なる-java

文字列内で使う時

    System.out.println("美味しい\tみかん");

コンソール: 美味しい みかん

変数を使う時

    String sample1 = "ふつうの";
    String sample2 = "いちご";
    System.out.println(sample1 + "\t" + sample2);

コンソール: ふつうの いちご

Windows

    System.out.println(sample1 + "\t" + sample2);
    System.out.println(sample1 + "¥t" + sample2);

\と¥両方が使える

mac

    System.out.println(sample1 + "\t" + sample2);
    System.out.println(sample1 + "¥t" + sample2); //macではバックスラッシュと円記号が区別される

コンソール:
ふつうの いちご
ふつうの¥tいちご

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Azure】無料でJava アプリを作成してみた〜FTPで接続 編〜【初心者】

概要

この記事ではAzureの「App Service」を使用して、
前回【Azure】無料でJava アプリを作成してみた〜Web App作成 編〜【初心者】で作成した、
WebアプリにFTPで接続してTOPの文言変更します:bangbang::ghost:

Azure サブスクリプションをお持ちでない場合は、
開始する前に無料アカウントを作成してください。

【Azure】無料でJava アプリを作成してみた

【Azure】無料でJava アプリを作成してみた〜Web App作成 編〜【初心者】
・「App Service」を使用して「java」Webアプリを作る
・TOPページに「Hello world!!」まで表示させる

【Azure】無料でJava アプリを作成してみた〜FTPで接続 編〜【初心者】:point_left_tone1: 今ここ:bangbang:
・前回作成した「java」WebアプリにFTPで接続する
・TOPページの「Hello world!!」を変更する

〜 次回以降の予定 〜
【Azure】無料でJava アプリを作成してみた〜Git 編〜【初心者】
【Azure】無料でJava アプリを作成してみた〜SQL Server 編〜【初心者】

FTPで接続する

FTP情報を取得する

  • ブラウザーでポータルを開く

  • [ホーム] > [App Service]でFTPで接続したいアプリを選択する
    ※ 今回は記事は、前回作成した「namari-sampleWebApp-java」を使用していく。

  • [概要]にある[URL]に飛びサイトが表示されているか確認する。(現状の状態を確認!)

  • [デプロイ センター]を選択し、[Manual Deployment (push / sync)] > [FTP]を選択すると下に[ダッシュボード]が表示されすのでそれをクリックする

  • 表示されている情報をFTPに入力して接続する(今回はAtomで接続します)
    [ユーザーの資格情報]を設定している場合は、設定したユーザー名・パスワードを使用することも可能

Screenshot_2019-10-09 Microsoft Azure.png

- サンプル -

FTP情報 接続できない場合
(Atom...etc)
FTPS エンドポイント(ホスト) ftps://waws-prod-dm1-161.ftp.azurewebsites.windows.net/site/wwwroot waws-prod-dm1-161.ftp.azurewebsites.windows.net
ユーザー名 namari-sampleWebApp-java\$namari-sampleWebApp-java namari-sampleWebApp-java
パスワード ●●●●●●●●●● ●●●●●●●●●●
ポート 21 21

TOPページの内容を変更する(接続確認)

  • TOPページの文言を「Hello world!!」→「FTP!OK!」に変更する。
    ※ 前回作成したアプリだと編集するファイルは、
    /site/wwwroot/webapps/ROOT/index.jspになる
  • 文言が変更されているかサイトを確認する(https://< appName >.azurewebsites.net/)

  • 変更されていたらOK!!

!!! 完了 !!!

関連記事

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

基本型-java

基本型

・整数 int -2147483648~2147483647  int i =100; 小数点なし
・整数 long -9223372036854775808~9223372036854775807 long lo = 300000L; 最後にL付ける 小数点なし
・整数 short -32768~32767
・整数 byte -128~127

整数は普通int型使うし byteとshortの存在意義がわからない
でもオラクルの問題に出たりする

・真偽値 boolean 真偽値,trueまたはfalse boolean Win = false;
・文字型 char Unicode文字 ¥u0000~¥uFFFF char A ='A';

        char uni =  ‘¤’; //OK
        char uni2 = U+00A4; //NG ユニコード入れてもだめ
        char uni3 = ‘U+00A4’; //NG ''で囲んでも無駄無駄

・浮動小数型 float 単精度浮動小数点数 小数点付きで、末尾にFまたはf float fo = 30.5f;
・浮動小数型 double 倍精度浮動小数点数 double do = 30.5;

整数型 最小値 最大値
byte Byte.MIN_VALUE Byte.MAX_VALUE
short Short.MIN_VALUE Short.MAX_VALUE
int Integer.MIN_VALUE Integer.MAX_VALUE
long Long.MIN_VALUE Long.MAX_VALUE

で最大値と最小値を取得できる

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

引っ掛け問題-java

引っ掛かった数だけ更新

        public class Sample {
        public static void main(String args) {
        int i = 10;
        double d = 3.14;
        boolean b = true;
        System.out.print(i + " " + d + " " + b);
    }
}

10 3.14 trueが表示されるかと思いきや実行時に起動エラー。
エラーメッセージ:エディターにはメイン型が含まれていません。

mainメソッドの引数の表記が正しくない []

        public static void main(String args) ❌
        public static void main(String[] args) ⭕️

文法的には正しいのでコンパイルは通ってしまう。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

識別式のルール-java

識別子

プログラミング言語において、パッケージ、クラス、インターフェイス、メソッド、フィールドや変数などに付ける名前のこと。言語の種類によって命名規則が異なる。

識別子は数字で始めることはできない
使うことができる記号は$と_のみ
長さの制限は無し

    int $$$ = 100; //OK
    int no_32 = 32; //OK
    int _Max = 5; //OK
    int 12s = 12; //エラー
    int var-1 = 1; //エラー
    int if = 1; //エラー

・使用できる文字 AからZ、aからz、0から9、_、$
(例) animal, Max, No_1
・大文字と小文字は区別される
(例) animal と Animalは別の変数
・予約語は使用できない
(例) if,classなどの予約語は使用できない

典型的な予約語・キーワード
・流れ制御を表す単語(if、while など)
・プログラムの構成要素を表す単語(function、const など)
・組み込み関数(open、readなど)
・組み込みの型(int、stringなど)
・他の言語などと混同して、誤用される可能性のある語(Javaのgoto、constなど)
・将来キーワードとして利用するかも知れない語(JavaScriptのlet、super、C++11のexport (以前は使われていた))
過去にキーワードだったため意味が無くなった後も残してあるもの

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Spring-boot+Heroku+PostgreSQL+Flywayでアプリ作成してみた

はじめに

Spring-bootとLINEMessagingAPIで電車遅延通知botを作る の続編?です。
ここから新しい機能を追加した感じです。
LINE Messaging API でもう少し色々やってみたいなぁと思い、とりあえずDBを使える感じにしてみました。

ターゲット

こんな人に役立つかと思います。

  • 「Spring-boot + PostgreSQL + Flyway」 の連携周りではまっている人。
  • 「Heroku + Spring-boot」で簡単にアプリを作りたい人。

ソース(差分)

先にどんな修正をしたか記載しています。
少し余計な修正も入っているので、そっと読み飛ばしてください。
https://github.com/ken-araki/line_bot/commit/324662adcff85cd7fd2aa1445c258be1b03602cd

本題

ここから本題です。

1. Spring-bootとPostgreSQL+Flywayを連携する

build.gradle

build.gradle
plugins {
    id 'org.springframework.boot' version '2.1.8.RELEASE'
    id 'java'
    id 'eclipse'
    id "org.flywaydb.flyway" version "5.2.4"
}
// 省略...

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter'
    implementation 'com.linecorp.bot:line-bot-spring-boot:2.7.0'
    implementation 'org.projectlombok:lombok:1.16.18'
    implementation 'org.flywaydb:flyway-core:5.2.4'
    implementation 'org.postgresql:postgresql:42.2.8'
    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.0.1'

// 省略...

pluginsにflywayを追加し、dependenciesにflyway-corepostgresqlmybatis-spring-boot-starterを追加します。

ちなみに、version6系だと以下エラーで正しく動きませんでした(SrpingFramework2.2から解消される?)

Caused by: java.lang.ClassNotFoundException: org.flywaydb.core.api.callback.FlywayCallback

Mapper(TobuyMapper.java, TobuyMapper.xml)

build.gradleからもわかりますが、ORマッパーとしてiBtatisを利用しています。
Mapperアノテーションをつけて、メソッドを定義するだけです。
メソッド名をSQL IDと合わせておけばよしなにマッピングされます。

TobuyMapper.java
package com.linebot.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import com.linebot.domain.Tobuy;

@Mapper
public interface TobuyMapper {
    List<Tobuy> findByIsCompleted(@Param("isCompleted") String isCompleted);
    int insert(Tobuy tobuy);
    int updateAllCompleted();
}

SQLはsrc/main/resources/配下にMapperクラスとパスを合わせて用意します。
※ 今回ならsrc/main/resources/com/linebot/mapper/TobuyMapper.xml になります。

TobuyMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.linebot.mapper.TobuyMapper">
    <insert id="insert" parameterType="com.linebot.domain.Tobuy">
        insert into tobuy (
            goods,
            is_completed,
            created_date,
            updated_date
        ) values (
            #{goods},
            #{isCompleted},
            current_timestamp,
            current_timestamp
        )
    </insert>

    <update id="updateAllCompleted">
        update tobuy
        set
            is_completed = '1',
            updated_date = current_timestamp
        where
            is_completed = '0'
    </update>

    <select id="findByIsCompleted" resultType="com.linebot.domain.Tobuy">
        select
            id,
            goods,
            is_completed,
            created_date,
            updated_date
        from
            tobuy
        where
            is_completed = #{isCompleted}
        order by
            id
    </select>
</mapper>

細かい部分は、mybatis-springドキュメント が参考になると思います。

MigrationSQL(V1__CreateShopping.sql)

DDLはこちらで定義できます。
V${バージョン}__${SQL名}.sqlというファイルをsrc/main/resources/db/migration
配置すればアプリケーション起動時にMigrateしてくれます。
今回はtobuyというテーブルを新規作成するSQLを用意しました。

V1__CreateShopping.sql
CREATE TABLE tobuy (
    id SERIAL NOT NULL,
    goods VARCHAR(255) NOT NULL,
    is_completed VARCHAR(1) NOT NULL,
    created_date TIMESTAMP,
    updated_date TIMESTAMP,
    PRIMARY KEY (id)
);

2. ローカルでPostgreSQL を利用する

マイグレーションとsqlの準備ができたので、正しく動作するかローカルで確認していきます。
dockerを利用してローカルDBを作成するので、docker-compose.ymlを用意します。

docker/docker-compose.yml
version: "3"

services:
  db:
    image: postgres:11.5-alpine
    container_name: postgres-db 
    ports: 
      - "5432:5432"
    environment:
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD=postgres1234
      - PGPASSWORD=postgres1234
      - POSTGRES_DB=testdb
      - DATABASE_HOST=localhost
    volumes:
      - ./db/init:/docker-entrypoint-initdb.d

以下コマンドで起動させます。

docker-compose up -d

application.propertiesdocker-compose.ymlに合わせてDB接続情報を追加します。
※ ローカル、Herokuで設定を分岐させるのはここでは書きません。

application.properties
spring.datasource.url = jdbc:postgresql://localhost:5432/testdb
spring.datasource.username = postgres
spring.datasource.password = postgres1234
spring.datasource.driver-class-name = org.postgresql.Driver

flyway.url = jdbc:postgresql://localhost:5432/testdb
flyway.user = postgres
flyway.password = postgres1234
flyway.driver = org.postgresql.Driver

./gradlew bootRun でアプリケーションを起動させます。

起動ログにもマイグレーションしていることが出ていますが、実際にテーブルが作られていることを確認します。

docker-compose exec db psql -U postgres testdb

testdb=# \dt
                 List of relations
 Schema |         Name          | Type  |  Owner   
--------+-----------------------+-------+----------
 public | flyway_schema_history | table | postgres
 public | tobuy                 | table | postgres

3. 本番(Heroku)とローカルでDB接続先を変えられるようにする

そこまで難しくないので、軽く記載しておきます。

Herokuとローカルで読み込む設定を切り替えられるようにします。
application.properties をこんな感じで修正します。

application.properties
spring.profiles.active = local
spring.profiles.include = common, ${spring.profiles.active}

ミスで本番環境へ接続しないように基本localにしてます。
ただ、このままだとローカル設定値しか読み込めないので、Procfileというファイルを修正してHerokuの設定値を見にいくようにします。

web: java -Dserver.port=$PORT -jar build/libs/line_bot-0.0.1-SNAPSHOT.jar --spring.profiles.active=production

アプリケーションを起動させるコマンドを記載しています。
--spring.profiles.active=productionapplication.propertiesの設定を上書きしています。
※ ちなみに、デプロイするコマンドを指定したいときはrelease: XXXXとします。

これで切り分けができたので、共通、ローカル、Heroku、の設定値を以下のように作成していきます。

application-common.properties
line.bot.channelSecret = ${LINE_BOT_CHANNEL_SECRET}
line.bot.channelToken = ${LINE_BOT_CHANNEL_TOKEN}
line.bot.handler.path = /callback

ark.line.id = ${LINE_ID}
ark.line.host = ${LINE_HOST}
ark.line.port = ${LINE_PORT}

ローカル、Herokuどちらでも利用する設定値は、commonに定義します。
※ 現状使っていないので消そうと思っている設定値ですが・・・

application-production.properties
spring.datasource.url = ${DATABASE_JDBC_URL}
spring.datasource.username = ${DATABASE_USER}
spring.datasource.password = ${DATABASE_PASSWORD}
spring.datasource.driver-class-name = ${DATABASE_DRIVER_CLASS_NAME}

flyway.url = ${DATABASE_JDBC_URL}
flyway.user = ${DATABASE_USER}
flyway.password = ${DATABASE_PASSWORD}
flyway.driver = ${DATABASE_DRIVER_CLASS_NAME}

Herokuで読み込む設定値はproductionに定義します。

おわりに

これでいい感じにHeroku+PostgreSQLでアプリ起動ができたと思います。
ホントはちゃんとした状態にしてから書きたかったのですが、いつになるかわからなかったのでコツコツ記事にしていきます。

次はこんなことをやっていこうと思います。

  • LINE Message API 部分の作り込み
    • 少なくてもローカルで動作確認取れるようにしたい
  • MyBatis -> JPA へ書き換え
  • Java -> Kotlin へ書き換え
  • 普通にリファクタ。とりあえず動くもので進めすぎた
  • JUnit作成。常識ですよね・・・
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

JavaとJavaScript(平均の求め方での違い)

単純ではありますがこちらもアウトプット

Java

import java.util.*;
public class Test12{
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
int sum = 0; //合計を保存するための変数
int a=0;
while (true) {
    System.out.println("数値を繰り返し入力して下さい(0で合計と平均をだして終了)"); //メッセージに終了条件を追加
    int num = scan.nextInt();
    if (num == 0) {
        break;
    } else {
        sum += num;
        a++;//おわるまで加算していく
        }
    }
//System.out.println("合計は"+sum);//
System.out.println("平均は"+sum/a);
 }
}

一例です。

JavaScript

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8"/>
<title>jsave</title>
</head>
<body>
  <h1>平均の計算</h1>
  <input type="number" id="num" value="" placeholder="数値を入力"/>
  <button onclick="add()">Add</button>
  <button onclick="btCalc()">Calc!</button>
  <div id="msg"></div>
<script>
var nums=[];
var str="";
var eleNum=document.getElementById("num");
var eleMsg=document.getElementById("msg");
function add(){
//intに変換だがJavaと違いIntegerではない

  var num=parseInt(eleNum.value);
  nums.push(num);
  eleMsg.textContent=num+"を配列に追加";
}
function btCalc(){
    var sum=0;
    for(var i=0;i<nums.length;i++){
    str +=nums[i]+(i==nums.length-1?"":",");
    sum +=nums[i];
  }
    document.getElementById("data").textContent=str;
    var msg='[{${nums.join(",")}]の平均は${sum/nums.length}です。';
    eleMsg.textContent=msg;
}
</script>
</body>
</html>

とりあえずvarはブロックスコープ(let,const)に変えるべきなんでしょうな。

躓いた所で言えば

<script>
'use strict';
window.onload=function(){
  //dom取得
  let ele=document.getElementById("bt");
  let result=document.getElementById("result");
  //特定のイベントが対象に配信されるたびに呼び出される関数を設定します。 
  ele.addEventListener("click",function(){
    result.textContent="Clicked!";
  });
  ele.addEventListener("click",function(){
    window.alert("Clicked!!!");
  });
};
</script>

のようにHTMLを読み込んだ後にメソッドを実行?とか。。意味や意義を考えてたら理解が難しかったですね。。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む