001/* 002 * Copyright (c) 2009 The openGion Project. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, 013 * either express or implied. See the License for the specific language 014 * governing permissions and limitations under the License. 015 */ 016package org.opengion.plugin.report; 017 018import java.io.BufferedWriter; 019import java.io.File; 020import java.io.FileNotFoundException; 021import java.io.FileOutputStream; 022import java.io.OutputStreamWriter; 023import java.io.UnsupportedEncodingException; // 024// import java.nio.channels.FileChannel; 025// import java.nio.channels.FileLock; 026 027import org.opengion.hayabusa.common.HybsSystemException; 028import org.opengion.hayabusa.common.HybsSystem; 029import org.opengion.hayabusa.report.AbstractCSVPrintPointService; 030import org.opengion.fukurou.util.StringUtil; 031 032import static org.opengion.fukurou.system.HybsConst.BUFFER_MIDDLE ; // 7.2.9.4 (2020/11/20) 033 034/** 035 * ユニリタ「Report & Form Warehouse」に対応したCSV形式でデータを作成します。 036 * Linuxから出力する際に標準ではファイルロックされないため、リネーム(拡張子変換)処理を追加しています。 037 * それ以外は通常の_RFWと同じです。 038 * 039 * CSVはシステムリソースRFW_CSV_OUTPUTDIRで指定した場所に[LISTID]_[GRPID]_[YKNO].csvで出力されます。 040 * 又、RFWはNASに出力する場合はJOB単位にNASサーバを指定する必要があるため、出力先ディレクトリの先頭文字が「\\」 041 * となっていた際には「_NASサーバ名」を出力先ディレクトリとします。 042 * 特殊な動作として、デーモングループに"BIG"の文字が入っている場合はCSV出力先ディレクトリ末尾に"_BIG"を付加します。 043 * 2つのフォルダは予め作成しておきます。 044 * 045 * PDF等の最終的な出力先、つまりCSVのコントロールヘッダのRDSetOutputFileNameはGE50で指定します。 046 * (Defaultのプラグインと出力が異なるので注意が必要です) 047 * 048 * データに関しては、全てダブルクウォートで囲って出力されます。 049 * ダブルクウォートそのものは二重化でエスケープします。 050 * ヘッダ、フッタが存在する場合、ボディ、ヘッダ、フッタの順番に連結して出力し、カラム名はヘッダはH_、フッタはF_を先頭に追加します。 051 * 052 * 区分Excelの場合にどの文字列でヘッダーを出すかはシステムリソースRFW_EXCEL_TYPEで決めます。 053 * 指定なしの場合はXLSとなります。 054 * 区分Excel(XLSX)の場合はXLSX固定です。 055 * 056 * なお、デーモングループ名の先頭文字が*の場合には最後に約7秒待ってから終了します。 057 * (プリンタによっては並列処理に対応していない場合があるため、Excel帳票と同等まで発行速度を落とす) 058 * 059 * @og.group 帳票システム 060 * 061 * @version 5.10.9.2 062 * @author Masakazu Takahashi 063 * @since JDK6.0, 064 */ 065public class CSVPrintPointService_RFW3 extends AbstractCSVPrintPointService { 066 067 private static final String CR = System.getProperty("line.separator"); 068// private final StringBuilder strCSV = new StringBuilder(); // CSVはこれに吐く 069 private final StringBuilder strCSV = new StringBuilder( BUFFER_MIDDLE ); // CSVはこれに吐く 7.2.9.4 (2020/11/20) 070 071 // 7.2.9.4 (2020/11/20) PMD:Variables that are final and static should be all capitals, 'csvEncode' is not all capitals. 072// private static final String csvEncode = HybsSystem.sys("REPORT_CSV_TEXT_ENCODE"); 073 private static final String CSV_ENCODE = HybsSystem.sys("REPORT_CSV_TEXT_ENCODE"); 074 075 private static final String RFW_CSV_OUTPUTDIR = HybsSystem.sys("RFW_CSV_OUTPUTDIR"); 076 077 private static final String RFW_EXCEL_TYPE = StringUtil.nval( HybsSystem.sys("RFW_EXCEL_TYPE"), "XLS" ) ; 078 079 private static final String FILENAME_SUFIX = "pre"; 080 081 /** 082 * デフォルトコンストラクター 083 * 084 * @og.rev 7.2.9.4 (2020/11/20) PMD:Each class should declare at least one constructor. 085 */ 086 public CSVPrintPointService_RFW3() { super(); } // これも、自動的に呼ばれるが、空のメソッドを作成すると警告されるので、明示的にしておきます。 087 088 /** 089 * 発行処理。 090 * ファイル出力 091 * 092 * @og.rev 5.10.9.2 (2018/03/15) 書き込み中に処理されないようにロックする+拡張子変更処理 093 * @og.rev 5.10.10.0 (2019/03/29) リネームのみで問題なさそうなのでロックはやめる 094 * 095 * @return 結果 [true:正常/false:異常] 096 */ 097 @Override 098 public boolean execute(){ 099 System.out.print( "CSV create ... " ); 100// FileOutputStream fos = null; // 5.10.9.2 (2019/03/15) 101// FileChannel channel = null; // 5.10.9.2 (2019/03/15) 102 BufferedWriter bw = null; 103 boolean flg = false; 104 String filename = null; 105 106 try { 107 // 5.9.6.2 (2016/03/11) RFWのNAS出力対応に伴う修正 108 // outdirが\\から開始される場合に、次の\もしくは/までの文字列を出力フォルダに付け足す 109 // 5.9.6.3 (2016/03/18) かつ、outdirからはサーバ名は削除する 110 String nasName = ""; 111 if( outdir != null && outdir.startsWith( "\\\\" ) ){ 112 int spl = outdir.indexOf( "\\", 2 ); 113 int spl2 = outdir.indexOf( "/", 2 ); 114 spl = spl<0 ? outdir.length() : spl; 115 spl2 = spl2<0 ? outdir.length() : spl2; 116 spl = spl < spl2 ? spl : spl2; 117 nasName = "_" + outdir.substring( 2, spl ); 118 outdir = outdir.substring(spl+1); // 5.9.6.3 119 } 120 121 makeheader(); 122 makebody(); 123 124// bw = getWriter( RFW_CSV_OUTPUTDIR + File.separator + listid + "_" + ykno + ".csv" ,false,CSV_ENCODE); 125 126 // 汎用化も考えたが、予期せぬ出力があると困るのでBIG決め打ち。フォルダ存在しない場合はエラー 127 if( dmngrp != null && dmngrp.indexOf( "BIG" ) >= 0 ){ // 5.9.2.2 128// bw = getWriter( RFW_CSV_OUTPUTDIR + "_BIG" + File.separator + listid + "_" + grpid + "_" + ykno + ".csv" ,false,CSV_ENCODE); 129// bw = getWriter( RFW_CSV_OUTPUTDIR + nasName + "_BIG" + File.separator + listid + "_" + grpid + "_" + ykno + ".csv" ,false,CSV_ENCODE); // 5.9.6.2 130 filename = RFW_CSV_OUTPUTDIR + nasName + "_BIG" + File.separator + listid + "_" + grpid + "_" + ykno + ".csv"; 131 } 132 else{ 133// bw = getWriter( RFW_CSV_OUTPUTDIR + File.separator + listid + "_" + grpid + "_" + ykno + ".csv" ,false,CSV_ENCODE); 134// bw = getWriter( RFW_CSV_OUTPUTDIR + File.separator + listid + "_" + grpid + "_" + ykno + ".csv" ,false,CSV_ENCODE); 135// bw = getWriter( RFW_CSV_OUTPUTDIR + nasName + File.separator + listid + "_" + grpid + "_" + ykno + ".csv" ,false,CSV_ENCODE); // 5.9.6.2 136 filename = RFW_CSV_OUTPUTDIR + nasName + File.separator + listid + "_" + grpid + "_" + ykno + ".csv"; 137 } 138// fos = getStream( filename + FILENAME_SUFIX ,false ); // 5.10.9.2 (2019/03/15) 139// channel = fos.getChannel(); 140// 141// bw = new BufferedWriter( new OutputStreamWriter( fos , CSV_ENCODE)); 142// 143// // ファイルロックする 5.10.9.2 (2019/03/15) 144// final FileLock lock = channel.tryLock(); 145// 146// try { 147// bw.write( strCSV.toString() ); 148// bw.flush(); 149// } 150// finally { 151// 152// if(lock != null) { 153// lock.release(); 154// } 155// bw.close(); 156// fos.close(); 157// } 158 159// // リネームを行う 160// boolean rnm = org.opengion.fukurou.util.FileUtil.renameTo( new File(filename + FILENAME_SUFIX) , new File(filename), false ); 161// if( !rnm ) { throw new RuntimeException( "RENAME FAILED" ); } 162 163 final File file1 = new File(filename + FILENAME_SUFIX); 164 final File file2 = new File(filename); 165 166 bw = getWriter( file1, false, CSV_ENCODE); 167 168 bw.write( strCSV.toString() ); 169 bw.flush(); 170 bw.close(); 171 172 // リネームを行う 173 // Ver7 では、FileUtil.renameTo の引数、戻り値が変更されている。 174 if( file2.exists() && !file2.delete() || !file1.renameTo( file2 ) ) { 175 throw new RuntimeException( "RENAME FAILED" ); 176 } 177 178 flg = true; 179 180// if( prgfile != null && prgfile.length() > 0){ 181// makeShellCommand(); 182// flg = programRun(); 183// } 184 185 // 5.9.17.3 (2017/02/24) 先頭が*のデーモングループの場合は約7秒スリープさせる=このスレッドでの連続処理をわざと遅延させる 186 // 特殊対応なので決め打ち 187 if( dmngrp != null && dmngrp.indexOf( "*" ) == 0 ){ 188 Thread.sleep(7000); 189 } 190 } 191 catch ( Throwable ex ) { 192 errMsg.append( "CSV Print Request Execution Error. " ).append( CR ); 193 errMsg.append( "==============================" ).append( CR ); 194 errMsg.append( "SYSTEM_ID=[" ).append( systemId ).append( "] , " ); 195 errMsg.append( "YKNO=[" ).append( ykno ).append( "] , " ); 196 errMsg.append( ex.toString() ); 197 errMsg.append( CR ); 198// throw new RuntimeException( errMsg.toString() ); 199 throw new RuntimeException( errMsg.toString(), ex ); 200 } 201 202 return flg; 203 } 204 205 /** 206 * ヘッダの出力。 207 * 208 * @og.rev 7.2.9.4 (2020/11/20) PMD:Avoid appending characters as strings in StringBuffer.append. 209 */ 210 private void makeheader(){ 211 //ヘッダデータを出力する場合はここで指定する。 212 strCSV.append( "<rdstart>" ).append( CR ) 213 .append( "RDSetForm=\"" ).append(modelname).append('"').append( CR ) 214 215 //5.9.3.1 (2015/12/16) 216 .append( "RDSetUserName=\"" ).append(systemId).append('"').append( CR ) 217 .append( "RDSetComputer=\"" ).append( listid + "_" + grpid + "_" + ykno ).append('"').append( CR ) 218 .append( "RDSetDocName=\"" ).append(listid).append('"').append( CR ); 219 220 String suffix = ""; // 5.9.6.0 221 222 // 5.9.6.0 拡張子を自動で付ける対応を入れておく 223 // PDFの場合 224 if( FGRUN_PDF.equals( fgrun ) ){ 225 if( outdir != null && outdir.indexOf(".") < 0 ){ 226 suffix = ".pdf"; 227 } 228 229 strCSV.append( "RDSetOutputMode=PDF" ).append( CR ) 230 .append( "RDSetOutputFileName=\"" ).append( outdir ).append( suffix ).append('"').append( CR ); 231 } 232 // Excel(XLS) 233 else if( FGRUN_EXCEL.equals(fgrun) ){ 234 if( outdir != null && outdir.indexOf(".") < 0 ){ 235 suffix = ".xls"; 236 } 237 strCSV.append( "RDSetOutputMode=" + RFW_EXCEL_TYPE ).append( CR ) 238 .append( "RDSetOutputFileName=\"" ).append( outdir ).append( suffix ).append('"').append( CR ); 239 } 240 // Excel(XLSX) 5.9.4.2 (2016/01/13) 241 else if( FGRUN_EXCEL2.equals(fgrun) ){ 242 if( outdir != null && outdir.indexOf(".") < 0 ){ 243 suffix = ".xlsx"; 244 } 245 strCSV.append( "RDSetOutputMode=XLSX" ).append( CR ) 246 .append( "RDSetOutputFileName=\"" ).append( outdir ).append( suffix ).append('"').append( CR ); 247 } 248 // 印刷 249 else{ 250 strCSV.append( "RDSetOutputMode=SPOOL" ).append( CR ) 251 // .append( "RDSetOutputPrinter=\"" ).append(prtName).append('"').append( CR ); 252 // プリンタ名ではなく、プリンタIDを出力するように変更 253 .append( "RDSetOutputPrinter=\"" ).append(prtid).append('"').append( CR ); 254 } 255 256 if( option != null && option.length() > 0 ){ 257 strCSV.append( option ).append( CR ); // 5.9.3.0 (2015/12/04) 258 } 259 260 strCSV.append( "<rdend>" ).append( CR ); 261 262 //1行目にカラム名を出力します。クウォートで囲わない。 263 // メインテーブルはNULLではない 264 for( int clmNo=0; clmNo<table.getColumnCount(); clmNo++ ) { 265 // 先頭以外はカンマを付ける 266 if( clmNo > 0 ){ strCSV.append( ',' ); } 267 strCSV.append( table.getColumnName( clmNo )); 268 } 269 if( tableH != null){ 270 for( int clmNo=0; clmNo<tableH.getColumnCount(); clmNo++ ) { 271 strCSV.append( ',' ) 272 .append("H_").append( tableH.getColumnName( clmNo )); 273 } 274 } 275 if( tableF != null){ 276 for( int clmNo=0; clmNo<tableF.getColumnCount(); clmNo++ ) { 277 strCSV.append( ',' ) 278 .append("F_").append( tableF.getColumnName( clmNo )); 279 } 280 } 281 strCSV.append( CR ); 282 } 283 284 /** 285 * 本体の出力を行います。 286 * HTMLエスケープされている場合は戻します 287 * 288 * @og.rev 7.2.9.4 (2020/11/20) PMD:Avoid appending characters as strings in StringBuffer.append. 289 */ 290 private void makebody(){ 291 292 for( int rowNo=0; rowNo<table.getRowCount(); rowNo++ ) { 293 // カラム単位の処理 294 for( int clmNo=0; clmNo<table.getColumnCount(); clmNo++ ) { 295 // 先頭以外はカンマを付ける 296 if( clmNo > 0 ){ strCSV.append( ',' ); } 297 // 原則全てダブルクウォートで囲う 298 // 5.9.8.2 (2016/05/16) 但し、先頭カラムが制御コードである//EOR//の場合のみ囲わない 299 if( clmNo == 0 && "//EOR//".equals( table.getValue( rowNo, clmNo )) ){ 300 strCSV.append( table.getValue( rowNo, clmNo ) ); 301 } 302 else{ 303 strCSV.append('"').append( StringUtil.replace( StringUtil.getReplaceEscape( table.getValue( rowNo, clmNo )) ,"\"","\"\"" ) ).append('"'); 304 } 305 } 306 307 //ヘッダ、フッタは毎行に必ず付加します。 308 //例え複数行あったとしても先頭行のみ有効です 309 //ヘッダ 310 if( tableH != null){ 311 final int rowNoH=0; // 先頭行のみ有効 312 for( int clmNo=0; clmNo<tableH.getColumnCount(); clmNo++ ) { 313 // 必ずカンマを付ける 314 strCSV.append( ',' ) 315 // 全てダブルクウォートで囲う 316 .append('"').append( StringUtil.replace( StringUtil.getReplaceEscape( tableH.getValue( rowNoH, clmNo )) ,"\"","\"\"" ) ).append('"'); 317 } 318 } 319 320 //フッタ 321 if( tableF != null ){ 322 final int rowNoF=0; // 先頭行のみ有効 323 for( int clmNo=0; clmNo<tableF.getColumnCount(); clmNo++ ) { 324 // 必ずカンマを付ける 325 strCSV.append( ',' ) 326 // 全てダブルクウォートで囲う 327 .append('"').append( StringUtil.replace( StringUtil.getReplaceEscape( tableF.getValue( rowNoF, clmNo )) ,"\"","\"\"" ) ).append('"'); 328 } 329 } 330 331 strCSV.append( CR ); 332 } 333 } 334 335 /** 336 * ファイル書き込み用のライターを返します。 337 * 338 * @param file ファイルオブジェクト 339 * @param append アベンドするか 340 * @param encode エンコード 341 * 342 * @return ライター 343 */ 344 private BufferedWriter getWriter( final File file, final boolean append, final String encode) { 345// File file = new File ( fileName ); 346 BufferedWriter bw; 347 348 try { 349 bw = new BufferedWriter( new OutputStreamWriter( new FileOutputStream( file, append ), encode ) ); 350 } 351 catch ( UnsupportedEncodingException ex ) { 352 errMsg.append( "[ERROR] Input File is written by Unsupported Encoding" ); 353 throw new HybsSystemException( ex ); 354 } 355 catch ( FileNotFoundException ex ) { 356 errMsg.append( "[ERROR] File not Found" ); 357 throw new HybsSystemException( ex ); 358 } 359 return bw; 360 } 361 362// /** 363// * ファイル書き込み用のStreamを返します。 364// * 365// * @og.rev 5.10.9.2 (2019/03/15) 新規追加 366// * 367// * @param fileName ファイル名 368// * @param append アペンドするかどうか 369// * 370// * @return ストリーム 371// */ 372// private FileOutputStream getStream( final String fileName, final boolean append) { 373// final File file = new File ( fileName ); 374// FileOutputStream fos; 375// 376// try { 377// fos = new FileOutputStream( file, append ); 378// } 379// catch ( FileNotFoundException ex ) { 380// errMsg.append( "[ERROR] File not Found" ); 381// throw new HybsSystemException( ex ); 382// } 383// return fos; 384// } 385}