20201216のJavaに関する記事は10件です。

逆コンパイルして理解する、Java視点からのScala入門(Class~Tuple編)

この記事は株式会社富士通システムズウェブテクノロジーの社内技術コミュニティで、「イノベーション推進コミュニティ」
略して「いのべこ」が企画する、いのべこ Advent Calendar 2020の16日目の記事です。
本記事の掲載内容は私自身の見解であり、所属する組織を代表するものではありません。

ここまでお約束 :wink:

はじめに

この記事は、Scalaを始めたJavaプログラマが、Scalaをコンパイルして生成されたclassファイルを逆コンパイルし、
Java的な観点からScalaを理解したいなと思いついて始めたものです。

今回は、Class~Tuple編の内容を主に振り返っていきたいと思います。

Class

何の面白みもない基本的なクラス
User.scala
class User
User.java
import scala.reflect.ScalaSignature;

@ScalaSignature(bytes = "\006\005]1AAA\002\001\031!)1\003\001C\001)\t!Qk]3s\025\t!Q!\001\btG\006d\027\rZ3d_6\004\030\016\\3\013\005\0319\021A\0034jg\"L'-Y:iS*\021\001\"C\001\007O&$\b.\0362\013\003)\t1aY8n\007\001\031\"\001A\007\021\0059\tR\"A\b\013\003A\tQa]2bY\006L!AE\b\003\r\005s\027PU3g\003\031a\024N\\5u}Q\tQ\003\005\002\027\0015\t1\001")
public class User {}

特にいうことはない・・・・

何の面白みもない基本クラス2
Point.scala
class Point(var x: Int, var y: Int) {

  def move(dx: Int, dy: Int): Unit = {
    x = x + dx
    y = y + dy
  }

  override def toString: String =
    s"($x, $y)"
}
Point.java
@ScalaSignature(bytes = "\006\005\0313AAC\006\001)!A1\004\001BA\002\023\005A\004\003\005!\001\t\005\r\021\"\001\"\021!9\003A!A!B\023i\002\002\003\025\001\005\003\007I\021\001\017\t\021%\002!\0211A\005\002)B\001\002\f\001\003\002\003\006K!\b\005\006[\001!\tA\f\005\006g\001!\t\001\016\005\006s\001!\tE\017\002\006!>Lg\016\036\006\003\0315\tab]2bY\006$WmY8na&dWM\003\002\017\037\005Qa-[:iS\n\f7\017[5\013\005A\t\022AB4ji\",(MC\001\023\003\r\031w.\\\002\001'\t\001Q\003\005\002\02735\tqCC\001\031\003\025\0318-\0317b\023\tQrC\001\004B]f\024VMZ\001\002qV\tQ\004\005\002\027=%\021qd\006\002\004\023:$\030!\002=`I\025\fHC\001\022&!\t12%\003\002%/\t!QK\\5u\021\0351#!!AA\002u\t1\001\037\0232\003\tA\b%A\001z\003\025Ix\fJ3r)\t\0213\006C\004'\013\005\005\t\031A\017\002\005e\004\023A\002\037j]&$h\bF\0020cI\002\"\001\r\001\016\003-AQaG\004A\002uAQ\001K\004A\002u\tA!\\8wKR\031!%N\034\t\013YB\001\031A\017\002\005\021D\b\"\002\035\t\001\004i\022A\0013z\003!!xn\025;sS:<G#A\036\021\005q\032eBA\037B!\tqt#D\001@\025\t\0015#\001\004=e>|GOP\005\003\005^\ta\001\025:fI\0264\027B\001#F\005\031\031FO]5oO*\021!i\006")
public class Point {
  private int x;

  private int y;

  public int x() {
    return this.x;
  }

  public void x_$eq(int x$1) {
    this.x = x$1;
  }

  public int y() {
    return this.y;
  }

  public void y_$eq(int x$1) {
    this.y = x$1;
  }

  public Point(int x, int y) {}

  public void move(int dx, int dy) {
    x_$eq(x() + dx);
    y_$eq(y() + dy);
  }

  public String toString() {
    return (new StringBuilder(4)).append("(").append(x()).append(", ").append(y()).append(")").toString();
  }
}

、getter/setter、各プロパティに対する_$eqメソッドが生えてます。
toStringメソッドのoverrideキーワードは消え去ってますね。

デフォルト引数を持つコンストラクタ

Point.scala
class Point(var x: Int = 0, var y: Int = 0)
Point.java
@ScalaSignature(bytes = "\006\005\0353A!\004\b\001/!Aa\004\001BA\002\023\005q\004\003\005$\001\t\005\r\021\"\001%\021!Q\003A!A!B\023\001\003\002C\026\001\005\003\007I\021A\020\t\0211\002!\0211A\005\0025B\001b\f\001\003\002\003\006K\001\t\005\006a\001!\t!M\004\bm9\t\t\021#\0018\r\035ia\"!A\t\002aBQ\001M\005\005\002eBqAO\005\022\002\023\0051\bC\004G\023E\005I\021A\036\003\013A{\027N\034;\013\005=\001\022AD:dC2\fG-Z2p[BLG.\032\006\003#I\t!BZ5tQ&\024\027m\0355j\025\t\031B#\001\004hSRDWO\031\006\002+\005\0311m\\7\004\001M\021\001\001\007\t\0033qi\021A\007\006\0027\005)1oY1mC&\021QD\007\002\007\003:L(+\0324\002\003a,\022\001\t\t\0033\005J!A\t\016\003\007%sG/A\003y?\022*\027\017\006\002&QA\021\021DJ\005\003Oi\021A!\0268ji\"9\021FAA\001\002\004\001\023a\001=%c\005\021\001\020I\001\002s\006)\021p\030\023fcR\021QE\f\005\bS\025\t\t\0211\001!\003\tI\b%\001\004=S:LGO\020\013\004eQ*\004CA\032\001\033\005q\001b\002\020\b!\003\005\r\001\t\005\bW\035\001\n\0211\001!\003\025\001v.\0338u!\t\031\024b\005\002\n1Q\tq'A\016%Y\026\0348/\0338ji\022:'/Z1uKJ$C-\0324bk2$H%M\013\002y)\022\001%P\026\002}A\021q\bR\007\002\001*\021\021IQ\001\nk:\034\007.Z2lK\022T!a\021\016\002\025\005tgn\034;bi&|g.\003\002F\001\n\tRO\\2iK\016\\W\r\032,be&\fgnY3\0027\021bWm]:j]&$He\032:fCR,'\017\n3fM\006,H\016\036\0233\001")
public class Point {
  private int x;

  private int y;

  public static int $lessinit$greater$default$2() {
    return Point$.MODULE$.$lessinit$greater$default$2();
  }

  public static int $lessinit$greater$default$1() {
    return Point$.MODULE$.$lessinit$greater$default$1();
  }

  public int x() {
    return this.x;
  }

  public void x_$eq(int x$1) {
    this.x = x$1;
  }

  public int y() {
    return this.y;
  }

  public void y_$eq(int x$1) {
    this.y = x$1;
  }

  public Point(int x, int y) {}
}
Point$.java
public final class Point$ {
  public static final Point$ MODULE$ = new Point$();

  public int $lessinit$greater$default$1() {
    return 0;
  }

  public int $lessinit$greater$default$2() {
    return 0;
  }
}

なんと、PointクラスとPoint\$クラスに分離してしまいました。
デフォルト値のほうは、Point\$クラスのstaticメソッドとして実装されています(\$lessinit\$greater\$default\$?から呼ばないのはなぜなのだろうか・・・)

Javaにはデフォルト引数の概念はなく、オーバーロードやBuilderパターンなどを用いて実装されることが多いです。

にしても、staticな定数を呼び出しているメソッドと値を分離するメリットもよくわからないし、
\$lessinit\$greater\$default\$?はいつ呼び出されているのだろうか・・・

試しにjshellに上記のjavaコードを張り付けて実行してみたところ、以下のようなエラーになったのでScalaのRuntime Reflectionが何らかの解決をしてくれているのだろうな・・・となった。

var point = new Point()
|  エラー:
|  クラス Pointのコンストラクタ Pointは指定された型に適用できません。
|    期待値: int,int
|    検出値: 引数がありません
|    理由: 実引数リストと仮引数リストの長さが異なります
|  var point = new Point();
|              ^---------^

ちょうど次の章でデフォルト引数に触れる予定だったらしく、「Scalaで定義したデフォルトパラメータはJavaのコードから呼び出される時はオプショナルではありません。」と書かれていた。

名前付き引数

メソッドを呼び出すとき、引数に名前を付けることができます。

  def printName(first: String, last: String): Unit = {
    println(first + " " + last)
  }

  // 呼び出し側
  printName("John", "Smith")  // ① Prints "John Smith" 
  printName(first = "John", last = "Smith")  // ② Prints "John Smith"
  printName(last = "Smith", first = "John")  // ③ Prints "John Smith"
  public void printName(String first, String last) {
    Predef$.MODULE$.println((new StringBuilder(1)).append(first).append(" ").append(last).toString());
  }

  // 呼び出し側
  printName("John", "Smith"); // ①
  printName("John", "Smith"); // ②
  String x$1 = "Smith", x$2 = "John";
  printName("John", "Smith"); // ③

なんだかモヤっとする結果になりましたね・・・

Javaには名前付き引数がないので、①, ②の結果については想定通りです。
しかしながらなぜx$1, x$2の引数を用意しておきながら、printNameに代入するときには使用していないのか不可解です。
おそらくJVMの最適化の影響なのかなと思ってますが。。。

とりあえず、一度変数化⇒代入のようにコンパイルされるのかな?という知見が得られたことにします。。。

Tuple

TupleSample.java
class TupleSample {
  def getIngredient: (String, Int) = {
    ("Sugar", 25)
  }

  def useIngredient(): Unit = {
    val ingredient = getIngredient;
    println(ingredient._1)
    println(ingredient._2)
  }
}
TupleSample.java
import scala.Predef$;
import scala.Tuple2;
import scala.reflect.ScalaSignature;
import scala.runtime.BoxesRunTime;

@ScalaSignature(bytes = "\006\005E2A\001B\003\001\035!)Q\003\001C\001-!)\021\004\001C\0015!)A\006\001C\001[\tYA+\0369mKN\013W\016\0357f\025\t1q!\001\btG\006d\027\rZ3d_6\004\030\016\\3\013\005!I\021A\0034jg\"L'-Y:iS*\021!bC\001\007O&$\b.\0362\013\0031\t1aY8n\007\001\031\"\001A\b\021\005A\031R\"A\t\013\003I\tQa]2bY\006L!\001F\t\003\r\005s\027PU3g\003\031a\024N\\5u}Q\tq\003\005\002\031\0015\tQ!A\007hKRLen\032:fI&,g\016^\013\0027A!\001\003\b\020*\023\ti\022C\001\004UkBdWM\r\t\003?\031r!\001\t\023\021\005\005\nR\"\001\022\013\005\rj\021A\002\037s_>$h(\003\002&#\0051\001K]3eK\032L!a\n\025\003\rM#(/\0338h\025\t)\023\003\005\002\021U%\0211&\005\002\004\023:$\030!D;tK&swM]3eS\026tG\017F\001/!\t\001r&\003\0021#\t!QK\\5u\001")
public class TupleSample {
  public Tuple2<String, Object> getIngredient() {
    return new Tuple2("Sugar", BoxesRunTime.boxToInteger(25));
  }

  public void useIngredient() {
    Tuple2<String, Object> ingredient = getIngredient();
    Predef$.MODULE$.println(ingredient._1());
    Predef$.MODULE$.println(BoxesRunTime.boxToInteger(ingredient._2$mcI$sp()));
  }
}

ここで注目すべきは、scala.TupleNscala.runtime.BoxesRunTimeでしょうか。。。

scala.TupleN

scala.TupleNはN = 2~22まで定義されたScalaの組み込みTuple型です。

scala.runtime.BoxesRunTime

scalaのコードでTupleの2番目の要素に指定されているIntはScalaの組み込み型であるscala.Intです。
Stringはscala.Predefにaliasとしてjava.lang.StringがStringとして定義されているため、実質ただのStringです。

そこで、JVMの世界で利用するために、オートボクシング(?)と言っていいのかはわかりませんが、
Java側の型に戻してあげる処理をBoxexRunTimeが行っているように見えます。

BoxesRunTime.java
    public static java.lang.Integer boxToInteger(int i) {
        return java.lang.Integer.valueOf(i);
    }

タプルでのパターンマッチング

パターンマッチングで要素を分解できます。

    val (name, quantity) = getIngredient
    println(name)
    println(quantity)
    Tuple2 tuple21;
    Tuple2<String, Object> tuple2 = getIngredient();
    if (tuple2 != null) {
      String str = (String)tuple2._1();
      int i = tuple2._2$mcI$sp();
      tuple21 = new Tuple2(str, BoxesRunTime.boxToInteger(i));
    } else {
      throw new MatchError(tuple2);
    } 
    Tuple2 tuple22 = tuple21;
    String name = (String)tuple22._1();
    int quantity = tuple22._2$mcI$sp();
    Predef$.MODULE$.println(name);
    Predef$.MODULE$.println(BoxesRunTime.boxToInteger(quantity));

なんだかたった3行だったコードが超面倒なことになりましたね。。。
ポイントはこの辺ですよね

    if (tuple2 != null) {                                         // tupleがnullかどうかチェック
      String str = (String)tuple2._1();                           
      int i = tuple2._2$mcI$sp();
      tuple21 = new Tuple2(str, BoxesRunTime.boxToInteger(i));    // なぜかここで再度Tupleを作成
    } else {
      throw new MatchError(tuple2);                               // まぁnullだったらエラーをthrowするのはわかる
    } 
    Tuple2 tuple22 = tuple21;                                     // なぜかここで新しいTupleに代入
    String name = (String)tuple22._1();
    int quantity = tuple22._2$mcI$sp();

黙ってこんなコードではダメなのか・・・

try {
  String name = (String)tuple2._1();
  int quantity = tuple2._2$mcI$sp();
} catch (XXXException e) { // ClassCastExceptionとか
  throw new MatchError(tuple2);
}

上記のデコンパイル結果のコードのほうが良い理由が知りたい。

for内包表記

for内包表記はscalaではよく使う思います

val numPairs = List((2, 5), (3, -7), (20, 56))
for ((a, b) <- numPairs) {
  println(a * b)
}
    List numPairs = (List)new .colon.colon(new Tuple2.mcII.sp(2, 5), (List)new .colon.colon(new Tuple2.mcII.sp(3, -7), (List)new .colon.colon(new Tuple2.mcII.sp(20, 56), (List)Nil$.MODULE$)));
    numPairs.withFilter(TupleSample::$anonfun$forInclusion$1$adapted).foreach(TupleSample::$anonfun$forInclusion$2$adapted);

これまたすごいコードになってしまいましたね。。。
調べてみたところこんな記事が。。。
https://qtamaki.hatenablog.com/entry/20120125/1327500931

見たところ、内部クラスとかにforの中身を展開して云々のようだけど、gradleのビルド結果を見る限りそのような内部クラスは見つからなかったし、jarにも含まれていなかった。。。
image.png

まず、scalaのforはmap/flatMap/withFilterのシンタックスシュガーであるというのは有名な話ですが、
numPairs.withFilterで処理している部分、まずここいらなくね?と思うのだが、numPairsに(List)Nil$.MODULE$が含まれていたりするし、変数a,bの作成などをしているのではないかと思うので、ここでおそらくfilterをかましている?
で、$anonfun$forInclusion$2$adaptedがおそらくforの中に書いていたprintlnの部分か?

なんにせよここまで見ただけではscalaのことがわかったような気になるだけで何もわかってないので、よく調査する必要がある気がする。

最後に

今回はClassとTupleについての記事でしたが、デコンパイルしただけではわからないことが多く、
もう少し深堀が必要な内容となってしまいました。

次回の記事(いつになるかは未定)では、以降のTOUR OF SCALAをやっていくのもいいですが、
Scalaのコンパイラ・ランタイムが何をしてくれているのかについて学んでいこうかなと思っています。

ちょっと中身が薄い気がするけど。

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

[Java]HttpServletクラスの継承と構造

目次

1. はじめに
2. Servlet(サーブレット)とは?
3. HttpServletクラスの継承
4. HttpServletクラスの構造
5. サンプル
6. さいごに

1. はじめに

本記事は以下の様な人向けに作成しました。
・「初めてJavaを使ってみよう」
・「Java使ってWebアプリを作ってみたいな」
・「サーブレットって言葉が出てきたけど・・・」
皆様の学習の何かのお役に立てば幸いです。

以下を参考に記述しています。
引用サイト:【Java】サーブレットの作成(基本的なコーディング)

2. Servlet(サーブレット)とは?

Servlet(サーブレット)とは、サーバー上でウェブページやウェブアプリなどを動的に処理・生成したりするために、Javaで作成されたプログラムです。JavaEEの一機能でもあります。

3. HttpServletクラスの継承

一般的にサーブレットを作成する場合は、javax.servlet.http.HttpServletクラスを継承します。

servlet.java
import javax.servlet.http.HttpServlet;

public class SampleServlet extends HttpServlet {

}

Javaのライブラリで、サーブレットに必要な機能(HTTP通信に特化した機能)を実装したクラスが提供されているので、開発者はHttpServletを継承し、メソッドをオーバーライドすることで、 自分たちのサーブレットを作成することができます。

4. HttpServletクラスの構造

HttpServletクラスの構造は以下の様に表現する。
HttpServletクラスの構造図

・HttpServletクラスの主なメソッド

init()メソッド
サーブレットの初期化処理を行うメソッド。
初期化パラメータの取得。
サーブレットのインスタンス生成時に最初に呼び出される。

service ()メソッド
リクエストを受け付け、レスポンスを返すメソッド。
クライアントからリクエストのたびに実行される。
HttpServletクラスのservice()メソッドは、クライアントのリクエストの種類に応じて、以下の分岐をおこなう。
・http GET method → doGet ()メソッドが実行される
・http POST method → doPost ()メソッドが実行される

doGet()メソッド
http GET methodでリクエストを受け付け、レスポンスを返すメソッド。
service()メソッドから分岐して実行される。

doPost()メソッド
http POST methodでリクエストを受け付け、レスポンスを 返すメソッド。
service()メソッドから分岐して実行される。

destroy()メソッド
サーブレットの終了時に必要な処理を記述するメソッド。
サーブレットのインスタンスが破棄されるときに呼び出される。

5. サンプル

doGetメソッドを使用したサンプルです。
HttpServletを継承したクラスを作成し、doGet()メソッドをオーバーライドします。

doGetSample.java
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class HelloServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
        // 処理を記入
        // 例)request の内容を解析し使用する
        // ・・・
        // 例)response に返却したい内容を入れる
    }
}

上記の処理記入のところを自由にコーディングし使用してみてください。

6. さいごに

本記事はServlet(サーブレット)のほんの入り口部分を簡単に紹介したものです。
詳しく調べるともっと深い知識や実装方法がありますので、お試しください。
この記事を作成するにあたり、参考にさせて頂いた記事を以下に記載します。
以上、何かしら皆様のお役に立てば幸いです。

参考記事:サーブレットの作成(基本的なコーディング)

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

ArchUnit 実践:同一パッケージからのみ呼び出されるメソッドの可視性をパッケージプライベートまたはプライベートに強制する

// 実行環境
* AdoptOpenJDK 11.0.9.1+1
* JUnit 5.7.0
* ArchUnit 0.14.1

アーキテクチャテストのモチベーション

15 日目の ArchUnit 実践:同一パッケージからのみ依存されるクラスの可視性をパッケージプライベートに強制する のメソッドへの応用。

アーキテクチャテストの実装

package com.example;
 
import com.tngtech.archunit.base.DescribedPredicate;
import com.tngtech.archunit.core.domain.JavaAccess;
import com.tngtech.archunit.core.domain.JavaClasses;
import com.tngtech.archunit.core.domain.JavaMethod;
import com.tngtech.archunit.core.importer.ClassFileImporter;
import com.tngtech.archunit.core.importer.ImportOption;
import org.junit.jupiter.api.Test;

import static com.tngtech.archunit.lang.syntax.ArchRuleDefinition.methods;

class ArchitectureTest {

    // 検査対象のクラス
    private static final JavaClasses CLASSES =
            new ClassFileImporter()
                    .withImportOption(ImportOption.Predefined.DO_NOT_INCLUDE_TESTS)
                    .importPackages("com.example");

    @Test
    void 同一パッケージからのみ呼び出されるメソッドはパッケージプライベートまたはプライベートにする() {
        methods()
            .that()
            .arePublic()
            .and(new DescribedPredicate<>("are only called from classes that reside in same package") {
                @Override
                public boolean apply(final JavaMethod method) {
                    return method.getAccessesToSelf()
                        .stream()
                        .map(JavaAccess::getOriginOwner)
                        .allMatch(callerClass
                            -> callerClass.getPackageName().equals(method.getOwner().getPackageName()));
                }
            })
            .should()
            .notBePublic()
            .check(CLASSES);
    }
}
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Herokuにてgit push heroku master実行時のエラー

Java初学者です。

Eclipseで作成したプログラムをHerokuでデプロイをする際に、
発生したエラーが解決出来ず困っております。

エラー発生時のコマンドは、

git push heroku master

※バージョンは以下を使用しております。
java -version "15.0.1"
mvn -version Apache Maven 3.6.3

エラーは内容はこちらです。

remote:        [INFO] BUILD FAILURE
remote:        [INFO] ------------------------------------------------------------------------
remote:        [INFO] Total time:  8.308 s
remote:        [INFO] Finished at: 2020-12
remote:        [INFO] ------------------------------------------------------------------------
remote:        [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project プロジェクト名: Fatal error compiling: invalid flag: --release -> [Help 1]
remote:        [ERROR] 
remote:        [ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
remote:        [ERROR] Re-run Maven using the -X switch to enable full debug logging.
remote:        [ERROR] 
remote:        [ERROR] For more information about the errors and possible solutions, please read the following articles:
remote:        [ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoExecutionException
remote: 
remote:  !     ERROR: Failed to build app with Maven
remote:        We're sorry this build is failing! If you can't find the issue in application code,
remote:        please submit a ticket so we can help: https://help.heroku.com/
remote: 
remote:  !     Push rejected, failed to compile Java app.
remote: 
remote:  !     Push failed
remote: Verifying deploy...
remote: 
remote: !   Push rejected to createしたサイトURL名.
remote: 
To https://git.heroku.com/createしたサイトURL名.
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'https://git.heroku.com/createしたサイトURL名.'

初めてのアプリ公開でつまずき、
3日程javaのバージョンを変更したり、。
グーグルの翻訳ではこの文がエラーの原因かと考えMavenのpom.xmlのファイルを見直したりしましたがエラー解決にはなりませんでした。

グーグル翻訳

リモート:[エラー]プロジェクトbmitoolでゴールorg.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile(default-compile)の実行に失敗しました:致命的なエラーコンパイル:無効なフラグ:-

記事はこちらを参考にさせて頂いております。
https://qiita.com/YJ2222/items/caa0a21215e9d24288d4

初めての投稿の為至らぬ点があると思いますが、ご指導ご鞭撻の程よろしくお願い致します。

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

放射線被ばく線量が記録されたRDSRを閲覧できる簡易アプリケーション(CT編)

RDSR(Radiation Dose Structured Report)

放射線を利用する医療機器は、例えばCT装置であればCTDIvolなど、標準化された規格で、被ばく線量を記録しています。
昨今では、医療施設において、被ばく線量管理についての法整備も進められています。
これまで集められなかった大規模な医療被ばくデータを収集し、人体への影響を調べるためのコホート研究などの実現に向けて、確実に前進しているようです。
画像検査に関する医療被ばくの記録は主に、DICOMデータとして管理されます。
具体的には、DICOMデータの規格のうち、RDSRというオブジェクトとして管理されます。
RDSRとは、放射線被ばく構造化レポートです。

CTを対象としたRDSRビューワ

RDSRは様々な放射線医療機器で作成されますが、今回はCT装置を対象に、RDSRを取り扱ってみたいと思います。
RDSRは、たくさんのメタデータで情報オブジェクトがまとめられているので、被ばく線量を調べたいときに、直接、DICOMタグを調べるなどをしているとかなり大変です。
なので、簡易的なツールを開発してみました。
ここで紹介するツールは、RDSRに記録されている情報を表示する、CTDIvol・DLPをcsvで出力する、CT検査ごとにグラフに表示する、機能を持っています。

KMII2020_RDSRViewer.PNG

ソース(java)

即席コードのため、細かなところはお許しください。
Answer packageが真です。
教育・研究用に自由にお使いください。
共同研究などご希望の方は、筆者まで直接ご連絡ください。
Eclipse + Java JDK 1.8 above
https://www.dropbox.com/sh/5clpuwvmo0uxnze/AAA7XcUbWh1k6lSfwIuHa-5oa?dl=0

Note

本アプリケーションは医用画像情報実習の教材として本記事の作者によって開発されました。

References

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

Java HTMLをPDFで保存

HTMLをPDFで保存するには、様々な方法がありますが、試しにネット上で検索をかけたところ、複雑でしょうがないでしょう。

さあ、本文ではSpire.Doc for Javaを通してHTML StringとHTML ファイルというHTML形式をPDFで保存する方法を紹介します。

下準備

1.E-iceblueの公式サイトからFree Spire.doc for Java無料版をダウンロードしてください。

f:id:lendoris:20201216121839p:plain

2.IDEを起動して新規プロジェクトを作成してから、インストールされたファイルにあった相応しいSpire.doc.jarを参照に追加してください。

f:id:lendoris:20201216121952p:plain

HTML String PDFで保存

import com.spire.doc.*;
import java.io.*;

public class htmlStringToWord {

    public static void main(String[] args) throws Exception {

        String inputHtml = "InputHtml.txt";
        //documentを作成します。
        Document document = new Document();
        // sectionを追加します。
        Section sec = document.addSection();

        String htmlText = readTextFromFile(inputHtml);
        //段落とhtml stringを追加します。
        sec.addParagraph().appendHTML(htmlText);

        // PDFで保存します。
        document.saveToFile("HTMLstringToPDF.pdf", FileFormat.PDF);
    }
    public static String readTextFromFile(String fileName) throws IOException{
        StringBuffer sb = new StringBuffer();
        BufferedReader br = new BufferedReader(new FileReader(fileName));
        String content = null;
        while ((content = br.readLine()) != null) {
            sb.append(content);
        }
        return sb.toString();
    }
}

HTML ファイルをPDFで保存

import com.spire.doc.*;
import com.spire.doc.documents.XHTMLValidationType;

public class htmlFileToWord {

    public static void main(String[] args) throws Exception {
        //  HTML ファイルをロードします。
        Document document = new Document();
        document.loadFromFile("InputHtmlFile.html", FileFormat.Html, XHTMLValidationType.None);

        //保存します。
        document.saveToFile("Result.pdf",FileFormat.PDF);
    }
}

 

f:id:lendoris:20201216123643p:plain

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

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

Java Word文書を印刷する方法

今日は、JavaでWord文書を印刷する方法を紹介していただきます。Spire.doc for Javaで三つのやり方があって、すなわち:

  1. PrinterJobクラスで印刷
  2. 物理プリンターに印刷
  3. 仮想プリンターに印

というものになります。

では、今それぞれを紹介させていただきましょう。

 下準備

1.E-iceblueの公式サイトからFree Spire.doc for Java無料版をダウンロードしてください。

f:id:lendoris:20201216121839p:plain

2.IDEを起動して新規プロジェクトを作成してから、インストールされたファイルにあった相応しいSpire.doc.jarを参照に追加してください。

f:id:lendoris:20201216121911p:plain

PrinterJobクラスで印刷

import com.spire.doc.*;
import java.awt.print.*;
public class WordPrint {

    public static void main(String[] args) throws Exception {
        // Documentをロードします。
        Document doc = new Document();
        doc.loadFromFile("Sample.docx");

        PrinterJob loPrinterJob = PrinterJob.getPrinterJob();
        PageFormat loPageFormat = loPrinterJob.defaultPage();

        //用紙サイズを設定します。 
        Paper loPaper = loPageFormat.getPaper();
        loPaper.setSize(600, 500);
        loPageFormat.setPaper(loPaper);

        //デフォルトのマージンを削除します。
        loPaper.setImageableArea(0, 0, loPageFormat.getWidth(), loPageFormat.getHeight());
        //印刷部数を設定します。
        loPrinterJob.setCopies(1);
        loPrinterJob.setPrintable(doc, loPageFormat);
        //ダイアログボックスを設定します。
        if (loPrinterJob.printDialog()) {
            //印刷します。
            try {
                loPrinterJob.print();
            } catch (PrinterException e)

            {
                e.printStackTrace();
            }
        }
    }
}

物理プリンターに印刷

import com.spire.doc.Document;
import com.spire.ms.System.Drawing.Printing.PrinterSettings;

public class PrintWord {

    public static void main(String[] args) {

        //Wordをロードします。
        Document document = new Document();
        document.loadFromFile("C:\\Users\\Administrator\\Desktop\\DocoumentToPrint.docx");

        //PrinterSettings objectを作成します。
        PrinterSettings printerSettings = new PrinterSettings();

        //物理プリンターの名前を設定します。
        printerSettings.setPrinterName("\\\\192.168.1.104\\HP LaserJet P1007");

        //印刷部数を設定します。
        printerSettings.setCopies((short) 1);

        //印刷範囲を設定します。
        printerSettings.setFromPage(2);
        printerSettings.setToPage(4);

        //設定を適用します。
        document.getPrintDocument().setPrinterSettings(printerSettings);

        //印刷します。
        document.getPrintDocument().print();
    }
}

仮想プリンターに印刷

import com.spire.doc.Document;
import com.spire.ms.System.Drawing.Printing.PrinterSettings;

public class PrintWord {

    public static void main(String[] args) {

        //Wordをロードします。
        Document document = new Document();
        document.loadFromFile("C:\\Users\\Administrator\\Desktop\\DocumentToPrint.docx");

        //PrinterSettingsオブジェクトを作成します。
        PrinterSettings printerSettings = new PrinterSettings();

        //仮想プリンターを設定します。
        printerSettings.setPrinterName("Microsoft Print to PDF");

        //ファイルに印刷します。
        printerSettings.setPrintToFile(true);

        //ファイルの保存場所と名前を設定します。
        printerSettings.setPrintFileName("output/PrintToPDF.pdf");

        //設定を適用します。
        document.getPrintDocument().setPrinterSettings(printerSettings);

        //印刷します。
        document.getPrintDocument().print();
    }
}

f:id:lendoris:20201216122015p:plain



 

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

Eclipseで`ERROR: JDWP Transport dt_socket failed`エラーが出てデバッグ起動できない

以下のようなエラーがコンソールに出力されて起動できなくなる

ERROR: transport error 202: handshake failed - connection prematurally closed
ERROR: JDWP Transport dt_socket failed to initialize, TRANSPORT_INIT(510)
JDWP exit error AGENT_ERROR_TRANSPORT_INIT(197): No transports initialized [./open/src/jdk.jdwp.agent/share/native/libjdwp/debugInit.c:734]

原因はいろいろあるようだが、自分の場合はESETというウィルス対策ソフトのファイヤーウォールが原因。

ESET上に許可するルールを追加してあげたら起動できるようになった。
自分の場合は内向きの一定のポート範囲(50000-65000)を許可してあげた。
(ルールの追加は自己責任でお願いします!)

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

Eclipseデバッグ起動時に`ERROR: JDWP Transport dt_socket failed`エラーが出て起動できない

以下のようなエラーがコンソールに出力されて起動できなくなる

ERROR: transport error 202: handshake failed - connection prematurally closed
ERROR: JDWP Transport dt_socket failed to initialize, TRANSPORT_INIT(510)
JDWP exit error AGENT_ERROR_TRANSPORT_INIT(197): No transports initialized [./open/src/jdk.jdwp.agent/share/native/libjdwp/debugInit.c:734]

原因はいろいろあるようだが、自分の場合はESETというウィルス対策ソフトのファイヤーウォールが原因。

ESET上に許可するルールを追加してあげたら起動できるようになった。
自分の場合は内向きの一定のポート範囲(50000-65000)を許可してあげた。
(ルールの追加は自己責任でお願いします!)

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

【Java・SpringBoot】Springセキュリティ⑥ - CSRF

CSRFとは

  • クロスサイトリクエストフォージェリ(CrossSiteRequestForgeries)
  • ユーザーが意図しないリクエストをWebサイトに送るという攻撃
    • ex:例えば、抽選で2億円が当たるというサイトを見つけました
      • そのサイトには個人情報の入力が必要と書いていて。。
      • 個人情報を入力してしまって、"応募"のボタンを押すと、
      • そのユーザー情報で、別のWebサイトにアクセスさせて、高い物を購入させる
    • といった攻撃です。。??

トークン

  • これを防ぐため、Springで作ったWebサイトからは、トークン(ランダムな文字列)をパラメーターに加えてサーバーにリクエストするようにします
  • トークン無しでリクエストを送ると、外部からのリクエストと判断できるため、Springはリクエストを拒否します
  • 一方GETメソッドで画面を表示する場合には、トークンをパラメーターに含める必要はありません。
  • Springセキュリティでは、CSRF対策がデフォルトで有効になっています

ログイン画面にトークンをパラメーターに含める

  • ログイン画面のhtmlで、CSRF対策用トークンをパラメーターに含める
login.html
 <!-- CSRF対策用トークン -->
   <input type="hidden"
    th:name="${_csrf.parameterName}"
    th:value="${_csrf.token}" />

CSRF対策用トークン

  • 上記のようにhiddenでCSRF対策用のトークンをサーバーに送ります
  • CSRF対策を有効にしている場合、フォームを使っている画面では上記のコードを追加しないと、Springが何も応答しないです
  • formタグを使用している画面は他にもありますが、CSRF対策用のトークンを送らなくても動作する。
  • 実は、CSRF対策用のトークンを送らなくても、タイムリーフが自動でトークンを追加してくれるのです?
    • th:action属性を使っていると、タイムリーフが自動追加してくれるが、
      • 以下のログイン画面ではformタグ内にaction属性を使っているので、自分でトークン追加しました!
  • 基本はth:action属性を使えばOK!
login.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8"></meta>

    <!-- Bootstrapの設定 -->
    <link th:href="@{/webjars/bootstrap/3.3.7-1/css/bootstrap.min.css}" rel="stylesheet"></link>
    <script th:src="@{/webjars/jquery/1.11.1/jquery.min.js}"></script>
    <script th:src="@{/webjars/bootstrap/3.3.7-1/js/bootstrap.min.js}"></script>

    <title>Login</title>
</head>
<body class="text-center">
    <h1>Login</h1>
    <form method="post" action="/login">
        <!-- エラーメッセージ -->
        <p th:if="${session['SPRING_SECURITY_LAST_EXCEPTION']} != null"
            th:text="${session['SPRING_SECURITY_LAST_EXCEPTION'].message}"
            class="text-danger">
            ログインエラーメッセージ
        </p>
        <!-- ユーザーID -->
        <label>ユーザーID</label>
        <input type="text" name="userId" /><br/>
        <br />
        <!-- パスワード -->
        <label>パスワード</label>
        <input type="password" name="password" /><br/>
        <br />
        <!-- ログインボタン -->
        <button class="btn btn-primary" type="submit">ログイン</button>
        <!-- CSRF対策用トークン -->
        <input type="hidden"
            th:name="${_csrf.parameterName}"
            th:value="${_csrf.token}" />
    </form>
    <br />
    <!-- ユーザー登録画面へのリンク -->
    <a th:href="@{'/signup'}">ユーザー新規登録はこちら</a>
</body>
</html>

ログインできることを確認!

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