001package org.opengion.plugin.cloud;
002
003        // import java.io.IOException;                  // 5.9.26.0 (2017/11/02) SendGridApiを利用して、メール送信を行う
004import java.text.SimpleDateFormat;
005import java.util.ArrayList;
006import java.util.Calendar;
007import java.util.Date;
008import java.util.HashMap;
009import java.util.List;
010import java.util.Map;
011import java.util.concurrent.ConcurrentMap;                                              // 5.9.26.0 (2017/11/02) Ver6
012
013import org.opengion.fukurou.system.DateSet;                                             // 5.9.26.0 (2017/11/02)
014import org.opengion.fukurou.db.DBUtil;
015import org.opengion.hayabusa.common.HybsSystem;
016        // import org.opengion.hayabusa.common.HybsSystemException;             // 5.9.26.0 (2017/11/02) SendGridApi
017import org.opengion.hayabusa.mail.MailManager_DB;
018import org.opengion.hayabusa.mail.MailPattern;
019
020        // 5.9.26.0 (2017/11/02) とりあえず、拡張jar は入れていません。
021        // つまり、動きません。
022        // import com.fasterxml.jackson.core.JsonProcessingException;
023        // import com.fasterxml.jackson.databind.ObjectMapper;
024        // import com.sendgrid.Method;
025        // import com.sendgrid.Request;
026        // import com.sendgrid.SendGrid;
027
028/**
029 * パッチによるメール送信の実装クラスです。
030 * 送信デーモンはパラメータテーブル(GE30)を監視して、新規のデータが登録されたら、
031 * そのデータをパラメータとしてメール合成処理メソッドに渡して合成を行って送信します。
032 * 最後に、処理結果を受取って、パラメータテーブルの状況フラグを送信済/送信エラーに更新します。
033 * エラーが発生した場合、エラーテーブルにエラーメッセージを書き込みます。
034 * 
035 * hayabusa.mailの標準クラスを継承して作成しています。
036 * 基本的な動作は同じですが、メール送信にSMTPではなくsendGridのAPIを利用します。
037 * MAIL_SENDGRID_APIKEYをシステムリソースとして登録する必要があります。
038 * 
039 * 一時的に利用できなくなる事を想定して、
040 * 一定時間の間(ハードコーディングで10分としている)はエラーが発生しても再送を試みるようにします。
041 * 
042 * このクラスをコンパイルするためにはsendgrid-java-4.1.1.jar,java-http-client-4.1.0.jarが必要です。
043 * 実行にはhamcrest-core-1.1.jar,httpclient-4.5.2.jar,httpcore-4.4.4.jar,mockito-core-1.10.19.jar,objenesis-2.1.jar
044 * ,jackson-annotations-2.5.3.jar,jackson-core-2.5.3.jar,jackson-databind-2.5.3.jarが必要です。
045 *
046 * @og.group    メールモジュール
047 *
048 * @og.rev 5.9.26.0 (2017/11/02) 新規作成
049 * @author              T.OTA
050 * @sinse               JDK1.7
051 *
052 */
053public class MailManager_DB_SendGridAPI extends MailManager_DB {
054        // 6.8.5.0 (2018/01/09) PMD Variables that are final and static should be all capitals。selGE30DYSET → SQL_GE30
055        private static final String     SQL_GE30        = "SELECT DYSET FROM GE30 WHERE UNIQ = ?";      // 2017/10/27 ADD 登録時刻の取得
056        // SendGridのAPIキー
057        private static final String SENDGRID_APIKEY = HybsSystem.sys("MAIL_SENDGRID_APIKEY");
058        // メール送信先のtoリスト
059        private final List<String> toList = new ArrayList<String>();
060        // メール送信先のccリスト
061        private final List<String> ccList = new ArrayList<String>();
062        // メール送信先のbccリスト
063        private final List<String> bccList = new ArrayList<String>();
064
065        /**
066         * バッチより呼出のメインメソッドです。
067         * パラメータテーブル(GE30)を監視します。
068         * 新規のデータが登録されたら、メール文を合成して送信を行います。
069         * エラーが発生した場合、エラーテーブルにエラーメッセージを書き込みます。
070         *
071         * @og.rev 6.8.5.0 (2018/01/09) PMD Variables that are final and static should be all capitals。selGE30DYSET → SQL_GE30
072         *
073         * @param systemId システムID
074         */
075        @Override
076        public void sendDBMail( final String systemId ){
077                // パラメータテーブルよりバッチでセットしたデータを取得します。
078                final String[][] ge30datas = DBUtil.dbExecute( SEL_GE30, new String[]{ systemId, DateSet.getDate( "yyyyMMddHHmmss" ) }, APP_INFO, DBID );               // 5.9.18.0 (2017/03/02)
079
080                // 2017/10/27 ADD SendGrid利用の追加対応
081                String timePre1Hour = "";
082                // タイムスタンプの設定
083                timePre1Hour = getTimePre1Hour();
084
085                final int ge30Len = ge30datas.length;
086
087                for( int i=0; i < ge30Len; i++ ) {
088                        String fgj = SNED_OK;
089                        try {
090                                final ConcurrentMap<String, String> initParam = makeParamMap( systemId, ge30datas[i] );                 // 5.9.26.0 (2017/11/02) Ver6
091                                create( initParam );
092                                send();                                                         // 合成されたメール文書、宛先で送信処理を行います。
093                                errMsgList.addAll( getErrList() );
094                        }
095                        catch( RuntimeException rex ) {
096                                fgj = SNED_NG;
097                                errMsgList.add( "メール送信失敗しました。パラメータキー:" + ge30datas[i][GE30_UNIQ] + " " + rex.getMessage() );
098                        }
099                        finally {
100                                if(fgj != SNED_NG){
101                                        commitParamTable( ge30datas[i][GE30_UNIQ], fgj );
102                                }else{
103                                        // エラーレコードの登録日時を取得
104                                        final String[][] rec = DBUtil.dbExecute( SQL_GE30, new String[]{ge30datas[i][GE30_UNIQ]}, APP_INFO, DBID);
105                                        final String DYSET = rec[0][0];
106
107                                        if(DYSET.compareTo(timePre1Hour) < 0){
108                                                // 登録から一定時間以上のエラーをエラーに更新
109                                                commitParamTable( ge30datas[i][GE30_UNIQ], fgj );
110                                        }
111                                        else {
112                                                // それ以外は再送を試みる
113                                                commitParamTable( ge30datas[i][GE30_UNIQ], "1" );
114                                                
115                                        }
116                                }
117
118                                if ( ! errMsgList.isEmpty() ) {
119                                        writeErrorTable( ge30datas[i][GE30_UNIQ], systemId, errMsgList );
120                                        errMsgList.clear();
121                                }
122                        }
123                }
124        }
125
126        /**
127         * 1時間前のタイムスタンプを取得。
128         *
129         * @return タイムスタンプ(1時間前)
130         */
131        private String getTimePre1Hour(){
132                final Date date = new Date();
133                final Calendar call = Calendar.getInstance();
134                final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
135                call.setTime(date);
136                // sendGridが一時的に使えなくなる場合を考慮
137                // 10分間は再送を試みる
138                call.add(Calendar.MINUTE, -10);
139
140                return sdf.format(call.getTime());
141        }
142
143        // 5.9.26.0 (2017/11/02) とりあえず、拡張jar は入れていません。
144        //      /**
145        //       * SendGridApiを利用して、メール送信を行うメソッドです。
146        //       *
147        //       */
148        //      @Override
149        //      public void send(){
150        //              // 宛先
151        //              List<String> invalidAddrBuf     = new ArrayList<String>();
152        //              setMailDst(invalidAddrBuf);
153        //
154        //              try{
155        //                      SendGrid sg = new SendGrid(SENDGRID_APIKEY);
156        //
157        //                      Request request = new Request();
158        //                      request.setMethod(Method.POST);
159        //                      request.setEndpoint("mail/send");
160        //
161        //                      // SengGrid向けJsonの設定
162        //                      request.setBody(makeJson());
163        //
164        //                      // メール送信要求
165        //                      sg.api(request);
166        //
167        //                      // 送信結果を履歴テーブル、宛先テーブルにセットします。
168        //                      commitMailDB();
169        //
170        //              }catch(IOException e){
171        //                      String errMsg = "送信時にエラー発生しました。" + e.getMessage();
172        //                      throw new RuntimeException( errMsg,e );
173        //              }
174        //      }
175
176        /**
177         * SendGrid向けのJsonを生成します。
178         * @return JSONデータ
179         */
180        @SuppressWarnings(value={"rawtypes"})
181        private String makeJson(){
182                String rtnJson = "";
183                final Map<Object,Object> jsonMap = new HashMap<Object, Object>();
184                // 送信先の設定
185                final Map<String,List<Map<String,String>>> sendMap = new HashMap<String,List<Map<String,String>>>();
186                sendMap.put("to", setSendList(toList));
187                if(!ccList.isEmpty()){
188                        sendMap.put("cc", setSendList(ccList));
189                }
190                if(!bccList.isEmpty()){
191                        sendMap.put("bcc",  setSendList(bccList));
192                }
193                jsonMap.put("personalizations", new Map[]{sendMap});                    // 警告: [rawtypes] raw型が見つかりました: Map
194                // タイトル
195                jsonMap.put("subject",getTitle());
196                // 送信元
197                jsonMap.put("from", setMap("email",getFromAddr()));
198                // 内容
199                final Map<String,String> contentMap = new HashMap<String,String>();
200                contentMap.put("type","text/plain");
201                contentMap.put("value",getContent());
202                jsonMap.put("content", new Map[]{contentMap});                                  // 警告: [rawtypes] raw型が見つかりました: Map
203
204                // 5.9.26.0 (2017/11/02) とりあえず、拡張jar は入れていません。
205/***********
206                ObjectMapper mapper = new ObjectMapper();
207
208                try{
209                        rtnJson = mapper.writeValueAsString(jsonMap);
210                }catch(JsonProcessingException e){
211                        String errMsg = "JSONの生成に失敗しました。" + e;
212                        throw new HybsSystemException(errMsg);
213                }
214*************/
215                return rtnJson;
216        }
217
218        /**
219         * Map格納用メソッド。
220         *
221         * @param val1 Mapにセットするキー
222         * @param val2 Mapにセットする値
223         * @return マップ
224         */
225        private Map<Object,Object> setMap(final Object val1, final Object val2){
226                final Map<Object,Object> rtnMap = new HashMap<Object,Object>();
227                rtnMap.put(val1,val2);
228                return rtnMap;
229        }
230
231        /**
232         * メール送信先リストをJSON用リストに設定。
233         *
234         * @param list メール送信先リスト
235         * @return JSON用リスト
236         */
237        private List<Map<String,String>> setSendList(final List<String> list){
238                // toリスト
239                final List<Map<String,String>> rtnList = new ArrayList<Map<String,String>>();
240                for(final String str: list){
241                        final Map<String,String> map = new HashMap<String,String>();
242                        map.put("email", str);
243                        rtnList.add(map);
244                }
245                return rtnList;
246        }
247
248        /**
249         * 宛先マップを元に、送信オブジェクトに宛先をセットします。
250         * セットする際に、アカウントエラーとなっているアドレスを除外します。
251         * 宛先が存在しない場合、例外を投げます。
252         *
253         * 計算方法は親クラスのprivateメソッドを流用。
254         * 値はクラス変数のリストに格納するように変更しています。
255         *
256         * @param invalidAddr 宛先のリスト
257         */
258        private void setMailDst( final List<String> invalidAddr ){
259
260                final Map<Integer, List<String>> tempMap = new HashMap<Integer, List<String>>();
261                tempMap.put( Integer.valueOf( MailPattern.KBN_TO ),  toList );
262                tempMap.put( Integer.valueOf( MailPattern.KBN_CC ),  ccList );
263                tempMap.put( Integer.valueOf( MailPattern.KBN_BCC ), bccList );
264
265                final ConcurrentMap<String, String[]> tmp = getMailDstMap();
266                for( final String dstId : getMailDstMap().keySet()) {
267                        String[] dstInfo = getMailDstMap().get( dstId );
268                        final Integer kbn = Integer.valueOf( dstInfo[MailPattern.IDX_DST_KBN] );
269                        if( !invalidAddr.contains( dstInfo[MailPattern.IDX_DST_ADDR] )
270                                        && !FGJ_ADDR_ERR.equals( dstInfo[MailPattern.IDX_FGJ] )){
271                                dstInfo[MailPattern.IDX_FGJ] = FGJ_SEND_OVER;
272
273                                final String name = dstInfo[MailPattern.IDX_DST_NAME];
274                                if( name != null && name.length() > 0 ) {
275                                        tempMap.get( kbn ).add( dstInfo[MailPattern.IDX_DST_NAME] +  "<"+ dstInfo[MailPattern.IDX_DST_ADDR] + ">" );
276                                }
277                                else {
278                                        tempMap.get( kbn ).add( dstInfo[MailPattern.IDX_DST_ADDR] );
279                                }
280                        }
281                        else {
282                                if( FGJ_SEND_OVER.equals( dstInfo[MailPattern.IDX_FGJ] ) ) {
283                                        dstInfo[MailPattern.IDX_FGJ] = FGJ_ACNT_ERR;
284                                }
285                        }
286                }
287
288                // 宛先が全部無効の場合、例外を投げます
289                if( toList.isEmpty() && ccList.isEmpty() && bccList.isEmpty()){
290                        final String errMsg = "宛先のメールアドレスが有効ではありません。"
291                                        + "TO , CC , BCC のいづれにもアドレスが設定されていません。";
292                        throw new RuntimeException( errMsg );
293                }
294        }
295
296        /**
297         * エラーテーブルにエラーメッセージを登録します。
298         * 親のprivateメソッドを流用。エラーメールの送信は行いません。
299         *
300         * @param       paraKey         パラメータキー(GE36.PARA_KEY)
301         * @param       systemId        システムID
302         * @param       emList          エラーメッセージリスト
303         *
304         */
305        private void writeErrorTable( final String paraKey, final String systemId, final List<String> emList ){
306                String[] insGE36Args = new String[6];
307                insGE36Args[GE36_PARA_KEY]      = paraKey;
308                insGE36Args[GE36_DYSET]         = DateSet.getDate( "yyyyMMddHHmmss" );
309                insGE36Args[GE36_USRSET]        = "DAEMON";
310                insGE36Args[GE36_PGUPD]         = "DAEMON";
311                insGE36Args[GE36_SYSTEM_ID] = systemId;
312                for( int i=0; i< emList.size(); i++ ){
313                        insGE36Args[GE36_ERRMSG] = trim( emList.get( i ), 4000);
314                        DBUtil.dbExecute( INS_GE36, insGE36Args, APP_INFO, DBID );
315                }
316        }
317}