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 125// bw = getWriter( RFW_CSV_OUTPUTDIR + File.separator + listid + "_" + ykno + ".csv" ,false,CSV_ENCODE); 126 127 // 汎用化も考えたが、予期せぬ出力があると困るのでBIG決め打ち。フォルダ存在しない場合はエラー 128 if( dmngrp != null && dmngrp.indexOf( "BIG" ) >= 0 ){ // 5.9.2.2 129// bw = getWriter( RFW_CSV_OUTPUTDIR + "_BIG" + File.separator + listid + "_" + grpid + "_" + ykno + ".csv" ,false,CSV_ENCODE); 130// bw = getWriter( RFW_CSV_OUTPUTDIR + nasName + "_BIG" + File.separator + listid + "_" + grpid + "_" + ykno + ".csv" ,false,CSV_ENCODE); // 5.9.6.2 131 filename = RFW_CSV_OUTPUTDIR + nasName + "_BIG" + File.separator + listid + "_" + grpid + "_" + ykno + ".csv"; 132 } 133 else{ 134// bw = getWriter( RFW_CSV_OUTPUTDIR + File.separator + listid + "_" + grpid + "_" + ykno + ".csv" ,false,CSV_ENCODE); 135// bw = getWriter( RFW_CSV_OUTPUTDIR + File.separator + listid + "_" + grpid + "_" + ykno + ".csv" ,false,CSV_ENCODE); 136// bw = getWriter( RFW_CSV_OUTPUTDIR + nasName + File.separator + listid + "_" + grpid + "_" + ykno + ".csv" ,false,CSV_ENCODE); // 5.9.6.2 137 filename = RFW_CSV_OUTPUTDIR + nasName + File.separator + listid + "_" + grpid + "_" + ykno + ".csv"; 138 } 139// fos = getStream( filename + FILENAME_SUFIX ,false ); // 5.10.9.2 (2019/03/15) 140// channel = fos.getChannel(); 141// 142// bw = new BufferedWriter( new OutputStreamWriter( fos , CSV_ENCODE)); 143// 144// // ファイルロックする 5.10.9.2 (2019/03/15) 145// final FileLock lock = channel.tryLock(); 146// 147// try { 148// bw.write( strCSV.toString() ); 149// bw.flush(); 150// } 151// finally { 152// 153// if(lock != null) { 154// lock.release(); 155// } 156// bw.close(); 157// fos.close(); 158// } 159 160// // リネームを行う 161// boolean rnm = org.opengion.fukurou.util.FileUtil.renameTo( new File(filename + FILENAME_SUFIX) , new File(filename), false ); 162// if( !rnm ) { throw new RuntimeException( "RENAME FAILED" ); } 163 164 final File file1 = new File(filename + FILENAME_SUFIX); 165 final File file2 = new File(filename); 166 167 bw = getWriter( file1, false, CSV_ENCODE); 168 169 bw.write( strCSV.toString() ); 170 bw.flush(); 171 bw.close(); 172 173 // リネームを行う 174 // Ver7 では、FileUtil.renameTo の引数、戻り値が変更されている。 175 if( file2.exists() && !file2.delete() || !file1.renameTo( file2 ) ) { 176 throw new RuntimeException( "RENAME FAILED" ); 177 } 178 179 flg = true; 180 181// if( prgfile != null && prgfile.length() > 0){ 182// makeShellCommand(); 183// flg = programRun(); 184// } 185 186 // 5.9.17.3 (2017/02/24) 先頭が*のデーモングループの場合は約7秒スリープさせる=このスレッドでの連続処理をわざと遅延させる 187 // 特殊対応なので決め打ち 188 if( dmngrp != null && dmngrp.indexOf( "*" ) == 0 ){ 189 Thread.sleep(7000); 190 } 191 } 192 catch ( Throwable ex ) { 193 errMsg.append( "CSV Print Request Execution Error. " ).append( CR ); 194 errMsg.append( "==============================" ).append( CR ); 195 errMsg.append( "SYSTEM_ID=[" ).append( systemId ).append( "] , " ); 196 errMsg.append( "YKNO=[" ).append( ykno ).append( "] , " ); 197 errMsg.append( ex.toString() ); 198 errMsg.append( CR ); 199// throw new RuntimeException( errMsg.toString() ); 200 throw new RuntimeException( errMsg.toString(), ex ); 201 } 202 203 return flg; 204 } 205 206 /** 207 * ヘッダの出力。 208 * 209 * @og.rev 7.2.9.4 (2020/11/20) PMD:Avoid appending characters as strings in StringBuffer.append. 210 */ 211 private void makeheader(){ 212 //ヘッダデータを出力する場合はここで指定する。 213 strCSV.append( "<rdstart>" ).append( CR ) 214 .append( "RDSetForm=\"" ).append(modelname).append('"').append( CR ) 215 216 //5.9.3.1 (2015/12/16) 217 .append( "RDSetUserName=\"" ).append(systemId).append('"').append( CR ) 218 .append( "RDSetComputer=\"" ).append( listid + "_" + grpid + "_" + ykno ).append('"').append( CR ) 219 .append( "RDSetDocName=\"" ).append(listid).append('"').append( CR ); 220 221 String suffix = ""; // 5.9.6.0 222 223 // 5.9.6.0 拡張子を自動で付ける対応を入れておく 224 // PDFの場合 225 if( FGRUN_PDF.equals( fgrun ) ){ 226 if( outdir != null && outdir.indexOf(".") < 0 ){ 227 suffix = ".pdf"; 228 } 229 230 strCSV.append( "RDSetOutputMode=PDF" ).append( CR ) 231 .append( "RDSetOutputFileName=\"" ).append( outdir ).append( suffix ).append('"').append( CR ); 232 } 233 // Excel(XLS) 234 else if( FGRUN_EXCEL.equals(fgrun) ){ 235 if( outdir != null && outdir.indexOf(".") < 0 ){ 236 suffix = ".xls"; 237 } 238 strCSV.append( "RDSetOutputMode=" + RFW_EXCEL_TYPE ).append( CR ) 239 .append( "RDSetOutputFileName=\"" ).append( outdir ).append( suffix ).append('"').append( CR ); 240 } 241 // Excel(XLSX) 5.9.4.2 (2016/01/13) 242 else if( FGRUN_EXCEL2.equals(fgrun) ){ 243 if( outdir != null && outdir.indexOf(".") < 0 ){ 244 suffix = ".xlsx"; 245 } 246 strCSV.append( "RDSetOutputMode=XLSX" ).append( CR ) 247 .append( "RDSetOutputFileName=\"" ).append( outdir ).append( suffix ).append('"').append( CR ); 248 } 249 // 印刷 250 else{ 251 strCSV.append( "RDSetOutputMode=SPOOL" ).append( CR ) 252 // .append( "RDSetOutputPrinter=\"" ).append(prtName).append('"').append( CR ); 253 // プリンタ名ではなく、プリンタIDを出力するように変更 254 .append( "RDSetOutputPrinter=\"" ).append(prtid).append('"').append( CR ); 255 } 256 257 if( option != null && option.length() > 0 ){ 258 strCSV.append( option ).append( CR ); // 5.9.3.0 (2015/12/04) 259 } 260 261 strCSV.append( "<rdend>" ).append( CR ); 262 263 //1行目にカラム名を出力します。クウォートで囲わない。 264 // メインテーブルはNULLではない 265 for( int clmNo=0; clmNo<table.getColumnCount(); clmNo++ ) { 266 // 先頭以外はカンマを付ける 267 if( clmNo > 0 ){ strCSV.append( ',' ); } 268 strCSV.append( table.getColumnName( clmNo )); 269 } 270 if( tableH != null){ 271 for( int clmNo=0; clmNo<tableH.getColumnCount(); clmNo++ ) { 272 strCSV.append( ',' ) 273 .append("H_").append( tableH.getColumnName( clmNo )); 274 } 275 } 276 if( tableF != null){ 277 for( int clmNo=0; clmNo<tableF.getColumnCount(); clmNo++ ) { 278 strCSV.append( ',' ) 279 .append("F_").append( tableF.getColumnName( clmNo )); 280 } 281 } 282 strCSV.append( CR ); 283 } 284 285 /** 286 * 本体の出力を行います。 287 * HTMLエスケープされている場合は戻します 288 * 289 * @og.rev 7.2.9.4 (2020/11/20) PMD:Avoid appending characters as strings in StringBuffer.append. 290 */ 291 private void makebody(){ 292 293 for( int rowNo=0; rowNo<table.getRowCount(); rowNo++ ) { 294 // カラム単位の処理 295 for( int clmNo=0; clmNo<table.getColumnCount(); clmNo++ ) { 296 // 先頭以外はカンマを付ける 297 if( clmNo > 0 ){ strCSV.append( ',' ); } 298 // 原則全てダブルクウォートで囲う 299 // 5.9.8.2 (2016/05/16) 但し、先頭カラムが制御コードである//EOR//の場合のみ囲わない 300 if( clmNo == 0 && "//EOR//".equals( table.getValue( rowNo, clmNo )) ){ 301 strCSV.append( table.getValue( rowNo, clmNo ) ); 302 } 303 else{ 304 strCSV.append('"').append( StringUtil.replace( StringUtil.getReplaceEscape( table.getValue( rowNo, clmNo )) ,"\"","\"\"" ) ).append('"'); 305 } 306 } 307 308 //ヘッダ、フッタは毎行に必ず付加します。 309 //例え複数行あったとしても先頭行のみ有効です 310 //ヘッダ 311 if( tableH != null){ 312 final int rowNoH=0; // 先頭行のみ有効 313 for( int clmNo=0; clmNo<tableH.getColumnCount(); clmNo++ ) { 314 // 必ずカンマを付ける 315 strCSV.append( ',' ) 316 // 全てダブルクウォートで囲う 317 .append('"').append( StringUtil.replace( StringUtil.getReplaceEscape( tableH.getValue( rowNoH, clmNo )) ,"\"","\"\"" ) ).append('"'); 318 } 319 } 320 321 //フッタ 322 if( tableF != null ){ 323 final int rowNoF=0; // 先頭行のみ有効 324 for( int clmNo=0; clmNo<tableF.getColumnCount(); clmNo++ ) { 325 // 必ずカンマを付ける 326 strCSV.append( ',' ) 327 // 全てダブルクウォートで囲う 328 .append('"').append( StringUtil.replace( StringUtil.getReplaceEscape( tableF.getValue( rowNoF, clmNo )) ,"\"","\"\"" ) ).append('"'); 329 } 330 } 331 332 strCSV.append( CR ); 333 } 334 } 335 336 337 /** 338 * ファイル書き込み用のライターを返します。 339 * 340 * @param file ファイルオブジェクト 341 * @param append アベンドするか 342 * @param encode エンコード 343 * 344 * @return ライター 345 */ 346 private BufferedWriter getWriter( final File file, final boolean append, final String encode) { 347// File file = new File ( fileName ); 348 BufferedWriter bw; 349 350 try { 351 bw = new BufferedWriter( new OutputStreamWriter( new FileOutputStream( file, append ), encode ) ); 352 } 353 catch ( UnsupportedEncodingException ex ) { 354 errMsg.append( "[ERROR] Input File is written by Unsupported Encoding" ); 355 throw new HybsSystemException( ex ); 356 } 357 catch ( FileNotFoundException ex ) { 358 errMsg.append( "[ERROR] File not Found" ); 359 throw new HybsSystemException( ex ); 360 } 361 return bw; 362 } 363 364// /** 365// * ファイル書き込み用のStreamを返します。 366// * 367// * @og.rev 5.10.9.2 (2019/03/15) 新規追加 368// * 369// * @param fileName ファイル名 370// * @param append アペンドするかどうか 371// * 372// * @return ストリーム 373// */ 374// private FileOutputStream getStream( final String fileName, final boolean append) { 375// final File file = new File ( fileName ); 376// FileOutputStream fos; 377// 378// try { 379// fos = new FileOutputStream( file, append ); 380// } 381// catch ( FileNotFoundException ex ) { 382// errMsg.append( "[ERROR] File not Found" ); 383// throw new HybsSystemException( ex ); 384// } 385// return fos; 386// } 387}