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