StreamAPIのおさらい - java.util.stream.Collector
JavaSE8で導入されたStreamAPIを使用してできる処理のうち、java.util.stream.Collectorクラスの機能を利用してできる処理を書いてみた。
【やること】
・java.util.stream.Collector系クラスのメソッドを使ってできる処理をさらう。
・Java Collectorメモ(Hishidama's Java8 Collector Memo) で紹介されているメソッドを全部書いてみる。
・分類分けに関しては蒐集してやんよ java.util.stream.Collectors クラス (1) - Map 以外を返す Collector - - 倭マン's BLOG を参考にさせていただいた。
【環境】
●Java
・jdk1.8.0_25
●IDE
・Eclipse Luna SR1 (4.4.1)
【資産】
具体的な使い方はソースを参照。
1.汎用的なCollector
(1)Collectors#mapping・・・値の変換
(2)Collectors#reducing・・・値の集約
StreamSample/GeneralCollector.java at master · hhhhhskw/StreamSample · GitHub
2.文字列に関するCollector
(1)Collectors#joining・・・文字列の結合
StreamSample/StringCollector.java at master · hhhhhskw/StreamSample · GitHub
3.StreamからCollectionへ変換するCollector
(1)Collectors#toCollection・・・Collectionクラスへ変換
(2)Collection#toList・・・Listクラスへ変換
(3)Collection#toSet・・・Setクラスへ変換
StreamSample/StreamToCollectionCollector.java at master · hhhhhskw/StreamSample · GitHub
4.StreamからMapへ変換するCollectior
(1)Collections#toMap・・・Mapクラスへ変換
(2)Collection#toConcurrentHashMap・・・ConcurrentHashMapへ変換
StreamSample/StreamToMapCollector.java at master · hhhhhskw/StreamSample · GitHub
5.Streamの統計に関するCollector
(1)Collectors#counting・・・Streamの要素数
(2)Collection#min・・・Stream内の最小値
(3)Collectors#max・・・Stream内の最大値
(4)Collectors#summingInt・・・Stream(int)の総和
(5)Collectors#summingLong・・・Stream(long)の総和
(6)Collectors#summingDouble・・・Stream(double)の総和
(7)Collectors#averagingInt・・・Stream(int)の平均
(8)Collectors#averagingLong・・・Stream(long)の平均
(9)Collectors#averagingDouble・・・Stream(double)の平均
(10)Collectors#summarizingInt・・・Stream(int)の統計情報
(11)Collectors#summarizingLong・・・Stream(long)の統計情報
(12)Collectors#summarizingDouble・・・Stream(double)の統計情報
StreamSample/SummaryCollector.java at master · hhhhhskw/StreamSample · GitHub
6.Streamに対して条件判定を適用するCollector
(1)Collectors#partitioningBy・・・指定した条件判定を適用する
(2)Collectors#partitioningByRule・・・指定した条件判定を適用する(返却する値の形式は別に指定する)
StreamSample/PartitioningCollector.java at master · hhhhhskw/StreamSample · GitHub
7.Streamに対してグループ分け
(1)Collectors#groupingBy()・・・グルーピング
(2)Collectors#groupingByConcurrent()・・・グルーピング(戻り値がConcurrentHashMap)
StreamSample/GroupingCollector.java at master · hhhhhskw/StreamSample · GitHub
全てをまとめたクラスは以下
StreamSample/CollectorSample.java at master · hhhhhskw/StreamSample · GitHub
【感想】
・数値系の操作はsummarizingInt等で大体こと足りそう。
→StreamAPI側の実装で詳しく書きすぎると可読性が落ちるので、機能が足りないときは別メソッドに委譲するべき?オレオレCollectorで対応する?
・CollectionsクラスのJavaDoc無いと実装できない・・・
・ラムダ式内での型変換で何回かハマった。
→ラムダ式で使用する関数インタフェースクラス(Predicateクラス、Functionクラス等)のメソッド仕様を理解してないとハマる。
【参考文献・URL】
Collectors (Java Platform SE 8 )
Collector (Java Platform SE 8 )
Java Collectorメモ(Hishidama's Java8 Collector Memo)
蒐集してやんよ java.util.stream.Collectors クラス (1) - Map 以外を返す Collector - - 倭マン's BLOG
Javaによる関数型プログラミング ―Java 8ラムダ式とStream
- 作者: Venkat Subramaniam,株式会社プログラミングシステム社
- 出版社/メーカー: オライリージャパン
- 発売日: 2014/10/24
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (3件) を見る
StreamAPIのおさらい - java.util.stream.Stream
JavaSE8で導入されたStreamAPIを使用してできる処理のうち、java.util.stream.Streamクラスの機能を利用してできる処理を書いてみた。
【やること】
・java.util.stream.Stream系クラスのメソッドを使ってできる処理をさらう。
・Java Streamメモ(Hishidama's Java8 Stream Memo) で紹介されているメソッド+αを書いてみる。
【環境】
●Java
・jdk1.8.0_25
●IDE
・Eclipse Luna SR1 (4.4.1)
【資産】
具体的な使い方はソースを参照。
1.Stream内の単一の値に対する順次処理
(1)Stream#map・・・Streamの要素を取り出して変換を行う
(2)Stream#mapToDouble・・・Streamの要素を取り出してdoubleへの変換を行う
(3)Stream#mapToInt・・・Streamの要素を取り出してintへの変換を行う
(4)Stream#mapToLong・・・Streamの要素を取り出してlongへの変換を行う
(5)Stream#floatMap・・・Streamの要素を取り出してStreamへの変換を行う
(6)Stream#flatMapToDouble・・・Streamの要素を取り出してDoubleStreamへの変換を行う
(7)Stream#flatMapToInt・・・Streamの要素を取り出してIntStreamへの変換を行う
(8)Stream#flatMapToLong・・・Streamの要素を取り出してLongStreamへの変換を行う
(9)Stream#forEach・・・Stream内各要素に対して処理を適用する(※処理順の指定なし)
(10)Stream#forEachOrdered・・・Stream内各要素に対して処理を適用する(※処理順の指定あり)
StreamSample/OperateSingleElementStreams.java at master · hhhhhskw/StreamSample · GitHub
2.Streamの内の複数の値に対する順次処理
(1)Stream#reduce・・・Stream内の複数の要素を取り出して処理を行い、単一の結果(戻り値は入力値による)として出力する
(2)Stream#collect・・・Stream内の複数の要素を取り出して処理を行い、コレクション(CollectionやStringBuilderなど)として出力する
(3)Stream#concat・・・Stream同士の連結を行う
StreamSample/OpetrateMultiElementStreams.java at master · hhhhhskw/StreamSample · GitHub
3.Stream内の値に対するソート処理
(1)Stream#sort・・・Streamに含まれる値をソートする
StreamSample/SortStreams.java at master · hhhhhskw/StreamSample · GitHub
4.Stream内の値に対するフィルタリング処理
(1)Stream#filter・・・Streamに含まれる値のフィルタリング
(2)Stream#min・・・Streamに含まれる値のうち、最小値を返却する
(3)Stream#max・・・Streamに含まれる値のうち、最大値を返却する
(4)Stream#count・・・Streamに含まれる要素の数を返却する
(5)Stream#anyMatch・・・Streamに含まれる要素に対して、条件検索(条件に1要素でも合致すればtrue)を行う
(6)Stream#allMatch・・・Streamに含まれる要素に対して、条件検索(条件に完全に一致する場合はtrue)を行う
(7)Stream#noneMatch・・・Streamに含まれる要素に対して、条件検索(条件一致する値がなけれはtrue)を行う
(8)Stream#findFirst・・・Streamに含まれる要素の先頭の値を返却する
(9)Stream#findAny・・・Streamに含まれる要素のいずれかの値を返却する(※並列処理用)
(10)Stream#distinct・・・Streamに含まれる値の重複を排除する
StreamSample/FilteringStreams.java at master · hhhhhskw/StreamSample · GitHub
5.Streamに対する各種操作
(1)Stream#limit・・・Streamに含まれる値の個数を制限(limit)し、Streamを返却する
(2)Stream#skip・・・Streamに含まれる値の開始位置を変更(skip)し、Streamを返却する
StreamSample/OperateStreams.java at master · hhhhhskw/StreamSample · GitHub
6.Streamを生成する
(1)Stream#of・・・指定された値を含むStreamを返却する。
(2)Stream#empty・・・空のStreamを返却する
(3)Stream#generate・・・第一引数で渡された要素を含むStreamを返却する
(4)Stream#iterate・・・第一引数で渡された初期要素に対し、第二引数で指定されたを関数を適用した要素の集合を含むStreamを返却する
StreamSample/CreateStreams.java at master · hhhhhskw/StreamSample · GitHub
7.Streamの形式変更操作
(1)Stream#toArray・・・Straemを配列に変換する ※終端操作
(2)Stream#builder・・・Streamのbuilderを取得する
StreamSample/ChangeTypeStream.java at master · hhhhhskw/StreamSample · GitHub
8.中間処理全体に対する各種操作
(1)Stream#peek・・・Streamに含まれる値に対する操作中に副作用を含む処理を行う。
StreamSample/DebugStream.java at master · hhhhhskw/StreamSample · GitHub
【参考文献・URL】
Stream (Java Platform SE 8)
DoubleStream (Java Platform SE 8)
IntStream (Java Platform SE 8)
Stream.Builder (Java Platform SE 8)
StreamAPIについて調べてみた flatMap編 その1 - シュンツのつまづき日記
ラムダ式・Stream APIの理解のポイントは「型」 - Java EE 事始め!
Java Advent Calendar 2013 5日目 - Java 8 Stream APIを学ぶ - kagamihogeの日記
Javaによる関数型プログラミング ―Java 8ラムダ式とStream
- 作者: Venkat Subramaniam,株式会社プログラミングシステム社
- 出版社/メーカー: オライリージャパン
- 発売日: 2014/10/24
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (2件) を見る
jBatch(Batchlet & Chunk)+JPA2.1(Entity & JPQL)でデータ読み書き
JavaEE7で追加されたjBatch(JSR-352)とJPA2.1(JSR-338)を使用して、DBのデータを読み書きする。
【やること】
・jBatchのBatchlet方式とChunk方式を両方使ってバッチジョブを実行する。
・JPAのうち、SELECTはJPQL、INSERT,UPDATE,DELETEはEntity方式を使用してデータの読み書きをする。
処理概要は下図の通り。
【環境】
●Java
・jdk1.8.0_25
・Oracle Enterprise Pack for Eclipse (12.1.3.3.1) ※中身はLuna SR1 (4.4.1)
・GlassFish Server Open Source Edition 4.1
●JPA
・Eclipselink 2.5.2-RC1 ※GlassFish組み込み
●DB・JDBC
・PostgreSQL 9.0
・JDBC41 Postgresql Driver, Version 9.3-1102
●ツール
・pgAdminⅢ
【準備】
・JDBCをGlassfishのクラスパスが通る場所(libフォルダ)に置いた。
・srcフォルダ配下にMETA-INFフォルダを作り、その中に「batch-jobs」フォルダを作った。
・GlassfishにてJNDIデータソースを設定済。
[参考]GlassfishでJNDIデータソースの設定方法その1 - しんさんの出張所 はてな編
・データ読み書き用のテーブルを作成済。
CREATE TABLE employees ( empno integer NOT NULL, ename character varying(10), yomi character varying(20), job character varying(8), mgr integer, hiredate date, sal character varying(7), comm character varying(7), deptno integer, CONSTRAINT pk_emp PRIMARY KEY (empno) ) WITH ( OIDS=FALSE );
【資産 - 定義体】
1.emp-batch.xml
・バッチジョブの実行順序を記述
2.persistence.xml
・JPA定義+DB接続先情報
・"eclipselink.logging.level.sql"を"FINE"とすると生成されたSQLが見える
【資産 - ソースファイル】
1.InsertEmpInfoBatchlet.java
・データ入力役
・従業員番号2001番・太郎くんの情報をINSERTする。 ※入力データはハードコーディングした。
2.EmpInfoReader.java
・データ読み込み役
・従業員番号2001番・太郎くんの情報をSELECTする。
・1件取得する場合はTypedQuery#getSingleResultを使用する。
3.EmpInfoProcessor.java
・データ加工役
・SELECTしたデータに数値を設定する。入力データに意味はない
4.EmpInfoWriter.java
・データ書き込み役
・EmpInfoProcessorで加工したデータを適用し、UPDATEする。
5.DeleteEmpInfoBatchlet.java
・データ消去役
・従業員番号2001番・太郎くんの情報をDELETEする。
6.BatchServlet.java
・バッチジョブを実行する。
作成したプロジェクトのトップページは以下。
hhhhhskw/EmpInfoBatch · GitHub
【実行ログ】
2015-02-11T15:07:09.847+0900|情報: EmpInfoBatch was successfully deployed in 5,035 milliseconds. 2015-02-11T15:07:26.098+0900|情報: id = 50 2015-02-11T15:07:26.271+0900|情報: InsertEmpInfoBatchlet#process 2015-02-11T15:07:26.393+0900|普通: INSERT INTO employee (empno, comm, deptno, ename, hiredate, job, mgr, sal, yomi) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) bind => [9 parameters bound] 2015-02-11T15:07:26.493+0900|情報: JTS5014: Recoverable JTS instance, serverId = [100] 2015-02-11T15:07:26.572+0900|情報: EmpInfoReader#open 2015-02-11T15:07:26.574+0900|情報: EmpInfoWriter#open 2015-02-11T15:07:26.578+0900|情報: EmpInfoReader#readItem 2015-02-11T15:07:26.770+0900|情報: EmpInfoProcessor#processItem 2015-02-11T15:07:26.772+0900|情報: EmpInfoWriter#writeItems 2015-02-11T15:07:26.772+0900|情報: persistEmployee 2015-02-11T15:07:26.772+0900|情報: EmpInfoReader#checkpointInfo 2015-02-11T15:07:26.783+0900|情報: EmpInfoWriter#checkpointInfo 2015-02-11T15:07:26.791+0900|普通: UPDATE employee SET comm = ? WHERE (empno = ?) bind => [2 parameters bound] 2015-02-11T15:07:26.823+0900|情報: EmpInfoReader#readItem 2015-02-11T15:07:26.824+0900|情報: EmpInfoReader#checkpointInfo 2015-02-11T15:07:26.835+0900|情報: EmpInfoWriter#checkpointInfo 2015-02-11T15:07:26.842+0900|情報: EmpInfoReader#close 2015-02-11T15:07:26.843+0900|情報: EmpInfoWriter#close 2015-02-11T15:07:26.886+0900|情報: DeleteEmpInfoBatchlet#process#begin 2015-02-11T15:07:26.906+0900|普通: UPDATE employee SET comm = ?, deptno = ?, ename = ?, hiredate = ?, job = ?, mgr = ?, sal = ?, yomi = ? WHERE (empno = ?) bind => [9 parameters bound] 2015-02-11T15:07:26.926+0900|普通: DELETE FROM employee WHERE (empno = ?) bind => [1 parameter bound] 2015-02-11T15:07:26.926+0900|情報: DeleteEmpInfoBatchlet#process#commit
【つまったところなど】
・バッチ処理で無限ループした時はItemReaderの実装をまず疑う。
→readItemメソッドの戻り値にnullが設定されるコードになってるか、チェックポイントをカウントする実装はおかしくないかetc
・JPQL使用時のコードでPSQLExceptionが発生する場合は、9割方文法が間違っている(体感)。
→SELECT句のカラム名に「*」を設定していないか、FROM句にEntityクラス名を指定しているかetc
・EclipseからGlassfishへ資産をデプロイできない場合は、EclipseからGlassfishサーバーを一旦削除して、Glassfishサーバーを再度作成する。もしくはドメインフォルダ配下に生成された一時フォルダ・ファイルを削除する。
・それでもデプロイできないときは、Eclipseと端末の再起動
・Glassfish起動時にポート1527が開いていません的例外が出たら、JDK付属のJavaDBを起動したうえで、Glassfishを起動する。
[2015-01-27T23:52:11.436+0900] [glassfish 4.1] [WARNING] [poolmgr.create_resource_error] [javax.enterprise.resource.resourceadapter.com.sun.enterprise.resource.allocator] [tid: _ThreadID=47 _ThreadName=admin-listener(5)] [timeMillis: 1422370331436] [levelValue: 900] [[ RAR5038:Unexpected exception while creating resource for pool DerbyPool. Exception : javax.resource.spi.ResourceAllocationException: Connection could not be allocated because: java.net.ConnectException: ポート1527のサーバーlocalhostへの接続中にメッセージConnection refused: connectでエラーが発生しました。]]
【参考文献・URL】
The Java EE 7 TutorialのjBatchの章をテキトーに訳した - kagamihogeの日記
jBatchでデータロードしてみる - kagamihogeの日記
jbatch (JSR352) - Chunk方式のStepを使ってみる - @lbtc_xxx lab
Java EE6を使う
GitHubに間違ってあげてしまった過去のコミットを削除してみる|めっとぼ
Beginning Java EE 6 GlassFish 3で始めるエンタープライズJava (Programmer’s SELECTION)
- 作者: Antonio Goncalves,日本オラクル株式会社,株式会社プロシステムエルオーシー
- 出版社/メーカー: 翔泳社
- 発売日: 2012/03/09
- メディア: 大型本
- 購入: 5人 クリック: 147回
- この商品を含むブログ (28件) を見る
aptを使ったアノテーション処理
apt(Annotation Processing Tools)を使用してソースコード内のアノテーションを読み込む。
JSR-175に準拠したcom.sun.*パッケージのMirrorAPIではなく、JSR-269に準拠したaptを使用する。
JEP-117にて、aptが内部で使用するクラスが、JavaSE6より追加されたjavax.annotation.processingもしくはjarax.lang.modelパッケージに含まれるクラスになった。
※MirrorAPIを使用したaptはJavaSE8より削除対象となった。
「参考」
JEP 117: Remove the Annotation-Processing Tool (apt)
Oracle Blogs 日本語のまとめ: [Java] Java 8's new Type Annotations
javax.annotation.processing インタフェース Processor
ツールは、「検出処理」を使って注釈プロセッサを見つけ出し、それらを実行すべきかどうかを決定します。ツールを構成することで、可能性のあるプロセッサのセットを制御できます。たとえば、JavaCompiler の場合、実行候補プロセッサのリストは、直接設定 することも、サービススタイル の検索で使用される検索パス を使って制御することもできます。
実際に注釈プロセッサを作ってみた。
javac呼び出し時に、作成した注釈プロセッサを指定し実行する。
実装自体はアノテーションが設定可能な場所に設定されているアノテーション名を出力するだけ。
【実行環境】
jdk1.7.0_60
【実装】
package annotation; import java.util.Set; import javax.annotation.processing.AbstractProcessor; import javax.annotation.processing.RoundEnvironment; import javax.annotation.processing.SupportedAnnotationTypes; import javax.annotation.processing.SupportedSourceVersion; import javax.lang.model.SourceVersion; import javax.lang.model.element.TypeElement; @SupportedSourceVersion(SourceVersion.RELEASE_7) // Javaのバージョン @SupportedAnnotationTypes({ "*" }) // 抽出対象とするアノテーションクラスの指定 public class SampleProcessor extends AbstractProcessor{ @Override public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { for( TypeElement element : annotations ) { // TypeElementクラスがアノテーションの要素にあたる System.out.println( element.toString() ); } return true; } }
【実行コマンド】
javac -cp (クラスパス) -proc:only -processor (プロセッサのクラス名(完全修飾)) (javaファイル)
D:\workspace\workspace_annotation_20141120\ProcessorProject>set CP=./bin D:\workspace\workspace_annotation_20141120\ProcessorProject>javac -cp %CP% -proc:only -processor processor.SampleProcessor src\annotation\SampleAnnotated.java Sample D:\workspace\workspace_annotation_20141120\ProcessorProject>tree /f D:. │ ├─bin │ ├─annotation │ │ Sample.class │ │ SampleAnnotated.class │ │ │ └─processor │ SampleProcessor.class │ └─src ├─annotation │ Sample.java │ SampleAnnotated.java │ └─processor SampleProcessor.java D:\workspace\workspace_annotation_20141120\ProcessorProject>
今度は取得するアノテーションの種類、取得対象のソースファイルの指定など、入出力の
仕様を柔軟にしてみる。
【参考】
JDK 6 Java Compiler (javac)-related APIs & Developer Guides
javac - Java programming language compiler
Java SE 6 and JWSDP 2.0:6
「追記 2015/01/09」
Java8でも上記プログラムは正常に動作した。
※ただし@SupportedSourceVersionで指定するバージョンは「8」へ変更した。
ゆえにapt自体はJavaSE8であっても動作する。
サーブレット3.0のセキュリティ系アノテーションを試す
色々試してみたのでまとめる。試したのは以下のアノテーション。
@ServletSecurity @HttpConstraint @HttpMethodConstraint
【検証環境】
Tomcat7.0.56
【パターン1】特定ロールのユーザのみアクセスを許す場合
※ロール「admin-role」にアクセス許可を与える
@ServletSecurity(value=@HttpConstraint(rolesAllowed={"admin-role"})) @WebServlet(name="hogehoge", urlPatterns="/hogehoge") public class TestServlet extends HttpServlet { ・・・(中略)・・・ }
2.コンテナ側で認証の設定
「設定内容」
・全てのURLにBASIC認証を適用する。
・ユーザ「admin」と「sub」を用意。
・web.xml
<security-constraint> <web-resource-collection> <web-resource-name>admin page</web-resource-name> <url-pattern>/*</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin-role</role-name> </auth-constraint> <auth-constraint> <role-name>sub-role</role-name> </auth-constraint> <user-data-constraint> <transport-guarantee>NONE</transport-guarantee> </user-data-constraint> </security-constraint> <security-role> <role-name>admin-role</role-name> </security-role> <security-role> <role-name>sub-role</role-name> </security-role> <login-config> <auth-method>BASIC</auth-method> <realm-name>admin page</realm-name> </login-config>
<role rolename="admin-role"/> <role rolename="sub-role"/> <user username="admin" password="password" roles="admin-role"/> <user username="sub" password="password" roles="sub-role"/>
3.デプロイ&起動後、ブラウザで「http://localhost:8080/TestServlet/hogehoge」へアクセス。
結果は以下の通り。
●ユーザ「admin」でアクセス : アクセス可能 (HTTPステータスコード:200)
●ユーザ「sub」でアクセス : アクセス拒否 (HTTPステータスコード:403)
【パターン2】特定メソッドは認証処理なし、その他のメソッドを使ったアクセスは特定ロールのユーザのみ可能
※GETメソッドは認証処理なし、その他のメソッドを使ったアクセスはロール「admin-role」に属するユーザのみ可能、とする。
@ServletSecurity(value=@HttpConstraint(rolesAllowed={"admin-role"}), httpMethodConstraints = {@HttpMethodConstraint("GET")}) @WebServlet(name="hogehoge", urlPatterns="/hogehoge") public class TestServlet extends HttpServlet { ・・・(中略)・・・ }
2.コンテナ側の設定はパターン1の時と同じ
3.デプロイ&起動後、ブラウザで「http://localhost:8080/TestServlet/hogehoge」へアクセス。
結果は以下の通り。
「結果」
●アクセス可能 (HTTPステータスコード:200) ※ユーザ認証なし
4.認証処理の対象をPOSTメソッドへ変更する。
@ServletSecurity(value=@HttpConstraint(rolesAllowed={"admin-role"}), httpMethodConstraints = {@HttpMethodConstraint("POST")}) @WebServlet(name="hogehoge", urlPatterns="/hogehoge") public class TestServlet extends HttpServlet { ・・・(中略)・・・ }
5.デプロイ&起動後、ブラウザで「http://localhost:8080/TestServlet/hogehoge」へアクセス。
結果は以下の通り。
「結果」
●ユーザ「admin」でアクセス : アクセス可能 (HTTPステータスコード:200)
●ユーザ「sub」でアクセス : アクセス拒否 (HTTPステータスコード:403)
【パターン3】特定メソッドは認証処理なし、その他のメソッドを使ったアクセスは特定ロールのユーザのみ可能。ただし特定メソッドはアクセス不可とする
※POSTメソッドは認証処理なし、その他のメソッドを使ったアクセスはロール「admin-role」に属するユーザのみ可能、ただしHEADメソッドはアクセス不可となる。
1.適当なサーブレットクラスに以下のアノテーションを設定
@ServletSecurity(value=@HttpConstraint(rolesAllowed={"admin-role"}), httpMethodConstraints = {@HttpMethodConstraint("POST"), @HttpMethodConstraint(emptyRoleSemantic = javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic.DENY, value = "HEAD")}) @WebServlet(name="hogehoge", urlPatterns="/hogehoge") public class TestServlet extends HttpServlet { ・・・(中略)・・・ }
2.コンテナ側の設定はパターン1の時と同じ
3.デプロイ&起動後、「http://localhost:8080/TestServlet/hogehoge」へアクセス。
結果は以下の通り。
「結果」
●[HEAD]アクセス拒否 (HTTPステータスコード:503 ※エラーメッセージ:Duplicate method name: HEAD)
●[POST]アクセス可能 (HTTPステータスコード:200)※ユーザ認証なし
●[GET・ロール:admin]アクセス可能 (HTTPステータスコード:200)※ユーザ認証あり(認証失敗すると401)
●[GET・ロール:sub] アクセス拒否 (HTTPステータスコード:403)※ユーザ認証あり(認証失敗すると401)
[備考]ちなみに「@HttpMethodConstraint」におけるアクセス許可の設定については、
「javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic」の設定が優先される。
よって、下記の設定においてGETメソッドはアクセス拒否となる。
@ServletSecurity(value=@HttpConstraint(rolesAllowed={"admin-role"}), httpMethodConstraints = {@HttpMethodConstraint("GET"), @HttpMethodConstraint(emptyRoleSemantic = javax.servlet.annotation.ServletSecurity.EmptyRoleSemantic.DENY, value = "GET")}) @WebServlet(name="hogehoge", urlPatterns="/hogehoge") public class TestServlet extends HttpServlet { ・・・(中略)・・・ }
- 作者: 川崎克巳
- 出版社/メーカー: 秀和システム
- 発売日: 2012/03
- メディア: 単行本
- この商品を含むブログを見る
http-method-omissionを試す
サーブレット3.0の仕様から追加された「http-method-omission」について。
詳しくは下記ブログ参照。
New Security Features in Glassfish v3 (Java EE 6) - Part I (Yours Officially...Nithya Subramanian)
<security-constraint> <display-name>WebConstraint</display-name> <web-resource-collection> <web-resource-name>test</web-resource-name> <description/> <url-pattern>/test.jsp</url-pattern> <http-method-omission>GET</http-method-omission> </web-resource-collection> <auth-constraint> <description/> <role-name>dev</role-name> </auth-constraint> </security-constraint>
which means that the auth-constraint for the resource accessible by the url-pattern /test.jsp is applicable for all methods except GET.
認証対象のURLパターン「/test.jsp」に対して実行するHTTPメソッドのうち、
"<http-method-omission>"で指定したメソッド(※上記の例ではGETメソッド)については認証処理の対象外となる。
・・・合ってるか不安なので実際に試してみた。
【前提・目標?】
・全てのJSPファイルに対し、BASIC認証をかける設定とする
・<http-method-omission>の値をGET・POSTの状態にした時、
任意のJSPファイルへブラウザよりアクセスし、表示される内容を確認する。
【環境】
・Tomcat7.0.56
【検証】
1.web.xmlに、全てのJSPに対し、BASIC認証をかける設定を行う。
「web.xml」
※DTD・XMLスキーマ定義
<web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="serv" version="3.0">
※ルートタグ配下の末尾に追記。
<security-constraint> <web-resource-collection> <web-resource-name>admin page</web-resource-name> <url-pattern>*.jsp</url-pattern> </web-resource-collection> <auth-constraint> <role-name>admin</role-name> </auth-constraint> <user-data-constraint> <transport-guarantee>NONE</transport-guarantee> </user-data-constraint> </security-constraint> <security-role> <role-name>admin</role-name> </security-role> <login-config> <auth-method>BASIC</auth-method> <realm-name>admin page</realm-name> </login-config>
2.tomcat-user.xmlにユーザ・ロールの設定を行う。(権限は適当)
<role rolename="admin"/> <user username="admin" password="password" roles="admin"/>
3.適当なプロジェクト・JSPを作る
4.上記の状態で資産デプロイ・サーバ起動を行う
5.JSPへアクセスする。
→自分の場合は「http://localhost:8080/TestServlet/TestJSP.jsp」へアクセスした。
アクセスすると、BASIC認証のダイアログが表示された。
6."<http-method-omission>"にGETメソッドを指定する。
「web.xml」
<security-constraint> <web-resource-collection> <web-resource-name>admin page</web-resource-name> <url-pattern>*.jsp</url-pattern> <http-method-omission>GET</http-method-omission> </web-resource-collection>
7.JSPへアクセスする。
→アクセスすると、作成したJSPファイルの内容が表示された。
8."<http-method-omission>"にPOSTメソッドを指定する。
「web.xml」
<security-constraint> <web-resource-collection> <web-resource-name>admin page</web-resource-name> <url-pattern>*.jsp</url-pattern> <http-method-omission>POST</http-method-omission> </web-resource-collection>
9.JSPへアクセスする。
→アクセスすると、BASIC認証のダイアログが表示された。
【結論】
・"<http-method-omission>"に指定したHTTPメソッドは認証処理の対象外となる、であってた
- 作者: 宮川 幸久,ターゲット編集部
- 出版社/メーカー: 旺文社
- 発売日: 2011/11/23
- メディア: 単行本
- 購入: 3人 クリック: 10回
- この商品を含むブログを見る
LuhnアルゴリズムをJavaで書いた
カドカワ祭りで買った『プログラマの考え方がおもしろいほど身につく本』に載っていたLuhnアルゴリズムをJavaで書いてみた。
Amazon.co.jp: カドカワ祭り: Kindleストア: 【ピックアップ】ライトノベル, 【ピックアップ】文芸, 【ピックアップ】実用・ビジネス・専門書, 【ピックアップ】コミック, 【ピックアップ】新書 など
後でエロい人が書いたコード*1を見ると、自分の糞コードっぷりがよくわかったが、後学のために晒す。
【お題】
元の数字の各桁に対して、1桁おきにその数字を2倍する。そのあとで、各桁の数字を足していく(2倍した結果2桁になる数字があれれば、十の位と一の位をそれぞれ個別に足す)。その合計が10で割り切れる場合、識別記号は妥当なものであるとみなす。
public class SampleLuhn { /** * チェックサム値の取得 * @param total 各桁の総和 * @return チェックサム値 */ private static int getCheckSum(int total) { int checkSum = 0; if(total % 10 != 0){ checkSum = 10 - (total % 10); } return checkSum; } /** * 引数の値を2倍した値を返却する(ただし2桁になった場合は各桁の数値を足した値を返却する) * @param target 処理対象の数値 * @return 第一引数の値を2倍した値 */ private static int doubleDigitValue(int target) { int doubleDigit = target * 2; if(doubleDigit >= 10){ return 1 + doubleDigit % 10; }else { return doubleDigit; } } /** * 第一引数の数値(偶数桁)における各桁の総和を返却する * @param target 処理対象の数値 * @param length 処理対象の数値の桁数 * @param power 処理対象の数値の累乗 * @return 第一引数における各数字の総和 */ private static int evenSum(int target, int length, int power) { // 総和 int total = 0; for (int i = 1; i <= length; i++) { System.out.println(i + "桁目は" + (target / power)); if(i%2 == 0){ // 偶数桁読み込み時 total += target / power; }else { // 奇数桁読み込み時 int doubleVal= doubleDigitValue(target / power); total += doubleVal; } target %= power; power /= 10; } System.out.println("総和 : " + total); return total; } /** * 第一引数の数値(奇数桁)における各桁の総和を返却する * @param target 処理対象の数値 * @param length 処理対象の数値の桁数 * @param power 処理対象の数値の累乗 * @return 第一引数における各数字の総和 */ private static int oddSum(int target, int length, int power) { // 総和 int total = 0; for (int i = 1; i <= length; i++) { System.out.println(i + "桁目は" + (target / power)); if(i%2 == 0){ // 偶数桁読み込み時 int doubleVal= doubleDigitValue(target / power); total += doubleVal; }else { // 奇数桁読み込み時 total += target / power; } target %= power; power /= 10; } System.out.println("総和 : " + total); return total; } /** * チェックサム値の取得 * @param target * @return チェックサム値 */ public static int createLohnCheckSum(int target) { // 桁数 int length = String.valueOf(target).length(); // 桁数に対する累乗 int power = (int)Math.pow(10, length - 1); // 総和 int sum = 0; // 桁数の偶数・奇数で場合分け if(length % 2 == 0){ sum = evenSum(target, length, power); }else { sum = oddSum(target, length, power); } // チェックサム値取得 int checkSum = getCheckSum(sum); return checkSum; } /** * mainメソッド * @param args */ public static void main(String[] args) { // チェック対象の数値 int target = 12345; // チェックサム作成 int checkSum = createLohnCheckSum(target); System.out.println("checksum : " + checkSum); } }
ちなみに参照実装は下記。エレガントだった。
http://www.chriswareham.demon.co.uk/software/Luhn.java
プログラマの考え方がおもしろいほど身につく本 問題解決能力を鍛えよう! (アスキー書籍)
- 作者: V.AntonSpraul,角征典,高木正弘
- 出版社/メーカー: KADOKAWA / アスキー・メディアワークス
- 発売日: 2014/08/14
- メディア: Kindle版
- この商品を含むブログ (3件) を見る