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