ブログトップ 記事一覧 ログイン 無料ブログ開設

水まんじゅう

 

2016-12-02

どこからも使用されてないクラスを列挙する

これはJava Advent Calendarの2日目の記事です。

さて、Javaで開発をしているといつの間にかどこからも使用されていないクラスというものが出てきてしまいます。
リファクタリングや仕様変更の結果、呼び出されてなくなったクラスです。

それら、どこからも使用されてないクラスを一覧化してみます。

まず、使用されているクラスを一覧化します。
一覧化にはjdepsを使用します。

jdepsの概要についてはCLOVERの記事を参照してください。

CLOVERの記事ではclassもしくはjarを指定していますが、実はパッケージ(フォルダ)を指定することで、その下のクラスの依存先をすべて出力してくれます。

使用されているクラスの一覧が出来たら、全体のクラスの一覧から引き算してあげます。
package 配下のクラス一覧を取得する方法はいろいろあるようですが、今回はGuavaのClassPathを使用しました。

ということで、以下のような感じです。


import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.Charset;
import java.nio.file.Paths;
import java.util.Set;
import java.util.stream.Collectors;

import com.google.common.reflect.ClassPath;

public class SelectNotUsedClass {

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

    // buildはコンパイルされたクラスの出力先
    Process process = new ProcessBuilder("jdeps", "-v", Paths.get("build").toAbsolutePath().toString()).start();

    //Charsetは各環境に合わせてください 
    Set<String> dependencies = new BufferedReader(new InputStreamReader(process.getInputStream(), Charset.forName("Windows-31J"))).lines().map(s -> s.trim())
        .filter(s -> s.contains("->")).map(s -> s.substring(s.indexOf("->"))).map(s -> s.replace("->", "").trim().replaceAll("\\s+.*$", ""))
        .collect(Collectors.toSet());

    ClassLoader loader = Thread.currentThread().getContextClassLoader();

    // jp.hoge.xxxxは依存関係を調べたいパッケージの名前
    ClassPath.from(loader).getTopLevelClassesRecursive("jp.hoge.xxxx").stream().map(info -> info.getName()).filter(c -> !dependencies.contains(c))
        .forEach(System.out::println);
  }
}

jdepsが出るまでは.javaファイルのimport行を解析して出してたりしましたが、ずいぶんと楽になりましたね。

2016-12-01

Go言語を完全にマスターする

これはさすかめアドベントカレンダーの一日目です。

○○を完全にマスターしたという言葉はチョットデキルに対抗してさすかめ先生が提案した概念です。
各言語でhello worldが出力できた状態を示します。

ということで、Go言語を完全にマスターしました。

package main

import "fmt"

func main() {
	fmt.Printf("hello, world\n")
}

実行は

C:\gowork\src\hello>go run hello.go
hello, world

ということで、完全にマスターできました。

2016-09-21

JDBCでOracleのchar型のカラムを検索する。

char型は桁数に足りない場合、スペースで勝手に埋められます。*1

検索する場合は、空白スペースを埋めた状態で検索してあげる必要があります。

ここで、OracleJDBCドライバに入っているPreparedStatementの実装、OraclePreparedStatementのメソッドsetFixedCharを使用することで、空白で埋めるのを適切に行ってくれます。

Oracleの公式ドキュメントを見るとPreparedStatementをOraclePreparedStatementにキャストして使用してくださいと記載してあります。


データベース内のCHARデータは、列幅まで埋め込まれます。このため、SELECT文のWHERE句に文字データをバインドするためのsetCHARメソッドの使用に関して、制限が生じます。WHERE句の文字データも、SELECT文で合致させるために、列幅まで埋め込む必要があります。これは特に列幅がわからない場合に問題になります。

これを修正するために、OracleはOraclePreparedStatementクラスにsetFixedCHARメソッドを追加しました。このメソッドは埋込みなしの比較を実行します。

注意:
setFixedCHARメソッドを使用するには、必ずプリコンパイルされたSQLオブジェクトをOraclePreparedStatementにキャストしてください。
INSERT文で、setFixedCHARを使用する必要はありません。データベースは挿入時に、常にそのデータを列幅まで自動的に埋め込みます。


https://docs.oracle.com/cd/E16338_01/java.112/b56281/datacc.htm#BABCHGCH

これには問題があります。
それはPreparedStatementが何かの理由でProxyされている場合にOraclePreparedStatementにキャストが出来ないことがあることです。
アプリケーションサーバー組込のDataSource実装を使用しているとよくあります。

そこで次のようなコードを考えます。

OraclePreparedStatement orapstmt = (OraclePreparedStatement)pstmt.unwrap(PreparedStatement.class);

unwrapはJava 6から増えたメソッドJavaDocには次のような記載があります。

標準以外のメソッド、またはプロキシによって公開されない標準メソッドにアクセスできるようにするために、指定されたインタフェースを実装しているオブジェクトを返します。レシーバがこのインタフェースを実装している場合、結果はレシーバ、またはレシーバのプロキシになります。レシーバがラッパーであり、ラップされたオブジェクトインタフェースを実装している場合、結果はラップされたオブジェクト、またはそのプロキシです。それ以外の場合は、ラップされたオブジェクト、またはその結果のプロキシに対してunwrapを再帰的に呼び出した結果が返されます。レシーバがインタフェースを実装しておらず、ラッパーでもない場合は、SQLExceptionがスローされます。

https://docs.oracle.com/javase/jp/8/docs/api/java/sql/Wrapper.html


これでOKかと思ったら、別の問題がありました。
それは、Wrapper自体がJava 6から実装されたもので、古くからあるDataSourceでは正しく実装されてない場合があるということです。(例えばうぇぶなんとかというアプリケーションサーバーのDataSource実装とか)
そもそも、何らかの理由でラップされているものを直接使ってしまってよいのかという問題もあります。

さて、どうするか

PreparedStatementには値として直接文字列をバインドするsetStringメソッド以外にsetObjectメソッドがあります。
そのsetObjectメソッドは第三引数java.sql.Typesで定義された値を渡すことで、型が何かを指定することができます。
例えば、setStringと同じ挙動をさせるためには、java.sql.Typesで定義されたVARCHARもしくはNVARCHARあたりを渡してあげればよいです。

これでsetFixedCharの挙動をさせたい場合は、999を渡してあげます。

pstmt.setObject(index, "string", 999);

しかしながら、この999自体はjava.sql.Typesには定義されていません。

定数フィールド値一覧
https://docs.oracle.com/javase/jp/8/docs/api/constant-values.html#java.sql.Types.ARRAY


どこで定義されていると、Oracleドライバ内のoracle.jdbc.OracleTypesで定義されています。
https://docs.oracle.com/cd/E16338_01/appdev.112/e13995/oracle/jdbc/OracleTypes.html#FIXED_CHAR

Use this type when binding to a CHAR column in the where clause of a Select statement. A non padded comparision will be done unlike in CHAR and VARCHAR case. Not particularly needed for an insert as the database will pad it. This type is used for bind only. It cannot be used for define and registerOutParameter.

ということで、これを使ってあげるのが一番確実のようです。
もちろんOracle以外では使えませんのでご注意を。

*1:例えば、char(8)で定義したカラムに"abc"を入れると"abc "になる

2016-08-16

Class#forNameはファイルシステムが大文字小文字の区別をしない場合にNoClassDefFoundErrorを投げることがある

タイトルのとおり。
通常、Class#forNameでクラスが見つからない場合はClassNotFoundExceptionが発生するが、
Windows上では以下のJUnitのテストコードのような挙動を示す。

package test;

import org.junit.Test;

public class TestMain {

  @Test
  public void test1() throws ClassNotFoundException {
    Class.forName("test.TestMain");// このクラスを呼び出す
  }

  @Test
  public void test2() throws ClassNotFoundException {
    Class.forName("java.lang.Thread");// rt.jarに入っているクラスを呼び出す
  }

  @Test(expected = ClassNotFoundException.class)
  public void test3() throws ClassNotFoundException {
    Class.forName("java.lang.thread");// rt.jarに入ってるクラスの大文字小文字の間違い
  }

  @Test(expected = NoClassDefFoundError.class)
  public void test4() throws ClassNotFoundException {
    Class.forName("test.testmain");// このクラスの大文字小文字の間違い
  }

}

まーじーかー・・・・・・
JVMバグじゃないの?

Java 1.8.0_92

2016-08-03

パーフェクトJava EEを読み始めた(パラ見した感想)

ポケモンに飽きてきたので

さて、こそこそっとパーフェクトJava EEを読み始めてみました。
とはいってもまだ1章のみ。

以前からパーフェクトJava EEが発売されるという話は聞いていて、JPAの章についてはレビューにも参加しているので、
JPAの章については安心してみてください的な感じはあります。*1

そのうち全部読むのですが、1章と全体をパラ見した感想としては、
この本のターゲットはこうですと記載されているものと、実際に読める人間には随分と隔たりがありそうだなぁという印象でした。

JSPどこいった

この本ではJSPについての説明はありません。ただ、JSPを使った説明はたくさんあります。
そのため、JSPを理解していることが前提となっており、少なくとも、これからJava EEを始めたい人が読むべき本ではないのではないかなぁと思いました。

そういえば、パーフェクトJavaのおまけにJava EEのボーナスPDFがあったなぁと思って、そちらも見直してみましたが、
そちらでもJSPの説明はなく。
http://gihyo.jp/book/2014/978-4-7741-6685-8/support

まあ、厳しいねぇ。

よくよく見てみると、第1章については上のPDFの内容を抜粋した形式となっており、さすがにちょっとさぼりすぎなのではーという印象を受けました。
一度校了をした文章を構成しなおしたからか?確認が甘かったのか、mavenダウンロードサイトの説明はあるのに、GlassFishダウンロードサイトの説明はなく、これ1冊でJava EEを始められるのか?というのは本当に疑問。
GlassFishって何よで終わる気がする。

例えば、NetBeansであればGlassFishバンドルしたインストーラーがあるので、そういったものを使用してここら辺の説明がなくても大丈夫にする方法もあるけれども、今回はそういった方法も取られておらず、asadminというのがあるよとだけ書かれており、結構きつい。

Java EE 7徹底入門ではNetBeansを使用する方法でちゃんと書かれていたので、あちらはあちらで各章の内容がちぐはぐということもあるのですが、少なくともこの本が対象としている読者層にとってはJava EE 7徹底入門のほうが良い本ではないかなぁと思いました。

しかし、巷のJava EEというか、Servlet/JSP入門書のほとんどはTomcatを前提としているので、本当はそういった本の次として置きたいのだろうけれども、どうしたもんかねぇ。

*1:さすが槙さん