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