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.hayabusa.resource;
017
018import java.util.Map;
019import java.util.WeakHashMap;
020
021import org.opengion.hayabusa.common.HybsSystem;
022import org.opengion.hayabusa.common.SystemManager;
023import org.opengion.fukurou.db.DBUtil;
024import org.opengion.fukurou.util.Cleanable;
025import org.opengion.fukurou.db.ApplicationInfo;
026
027/**
028 * 事業所(CDJGS) , 年月(YYYYMM) に対応した休日カレンダデータを作成するファクトリクラスです。
029 *
030 * カレンダデータは、1日(DY1)~31日(DY31)までの日付け欄に対して、0:平日 1:休日 という
031 * データを持っています。実際には、内部では true:平日 false:休日 という持ち方をします。
032 * (カレンダDBは、文字列ですが、内部で持つ場合は、数字で管理しています。)
033 *
034 * 通常のカレンダでは、月毎に、(2月は、年によって)最大日付けが変わります。
035 * これは、カレンダデータクラスとしては、-1 を設定しておきます。
036 *
037 * カレンダデータには、3つのレベルのオブジェクト作成方法が適用されます。
038 * 他のリソースの3つのレベルとは異なり、エンジンリソースからの読み取りは
039 * 存在しなく、第4の方法が加わった事により、3つの方法になっています。
040 *
041 * まず、読込フラグ(FGLOAD)='1'のカレンダデータは、このCalendarFactoryオブジェクトが
042 * 構築された時に、すべてキャッシュとして内部メモリに読み取ります。
043 * 読込フラグが、'1' 以外のデータは、初期起動時には、メモリにキャッシュされず
044 * 実際に使用されるまで、オブジェクトが作成されません。
045 * カレンダの場合、過去の使用される可能性が低いデータまで、キャッシュしない様に、
046 * このフラグを使用して、メモリの節約を図ることが可能になります。
047 *
048 * 読込フラグ(FGLOAD)自動設定機能を使用すれば、読み取ったラベルデータに対して、
049 * 読込フラグ(FGLOAD)に '1' を自動的にセットします。この機能により、一度でも
050 * 読み取ったことがあるデータに '1' を付け、次回起動時には、メモリキャッシュさせる
051 * 事と、一度も読み取っていないデータを判別して、データ削除等のメンテナンスに
052 * 使用することが可能です。
053 * カレンダに限定すれば、当面使用されない先のカレンダ(1年分など)を登録する時に、
054 * 読込フラグ(FGLOAD)='0' にしておけば、実際に読み取られるまで、メモリキャッシュ
055 * されないため、さらにメモリ効率が向上します。
056 *
057 * 2つめの方法は、キャッシュに存在しない場合は、DBから読み取ります。
058 *
059 * 3つめは、カレンダ特有の方法で、DBにデータが存在しない場合の設定です。
060 * これは、カレンダテーブル未設定時でも、カレンダとして使用できるように
061 * 自動的にカレンダデータを作成します。日曜日を休日として自動設定します。
062 *
063 * @og.rev 3.6.0.0 (2004/09/17) 新規作成
064 * @og.group リソース管理
065 *
066 * @version  4.0
067 * @author   Kazuhiko Hasegawa
068 * @since    JDK5.0,
069 */
070public final class CalendarFactory {
071        /** 6.4.3.1 (2016/02/12) Collections.synchronizedMap で同期処理するのではなく、LOCKオブジェクトで、同期処理を行います。 */
072        private static final Map<String,CalendarData>  DATA_POOL        = new WeakHashMap<>();          // 6.4.1.1 (2016/01/16) pool           → DATA_POOL  refactoring
073        /** 6.4.3.1 (2016/02/12) Collections.synchronizedMap で同期処理するのではなく、LOCKオブジェクトで、同期処理を行います。 */
074        private static final Map<String,CalendarQuery> QUERY_POOL       = new WeakHashMap<>();          // 6.4.1.1 (2016/01/16) queryClassPool → QUERY_POOL refactoring
075        // 4.0.0.0 PGカレンダーの初期設定をシステムリソースに変更
076        private static CalendarData pgCalData   ;
077        private static final String PG_CALENDAR_DATA_CLASS = HybsSystem.sys( "DEFAULT_CALENDAR_CLASS");
078        private static final Object LOCK = new Object();                                                                                // 6.4.1.1 (2016/01/16) lock → LOCK refactoring
079
080        /** カレンダDBの接続先を、取得します。 */
081        private static final String DBID  = HybsSystem.sys( "RESOURCE_CALENDAR_DBID" );
082
083        /** カレンダDBを使用するかどうかを指定します。(互換モード) */
084        private static boolean useDB = HybsSystem.sysBool( "USE_CALENDAR_DATABASE" );
085
086        /** 3.8.7.0 (2006/12/15) アクセスログ取得の為、ApplicationInfoオブジェクトを設定 */
087        private static final ApplicationInfo APP_INFO;                                                                                  // 6.4.1.1 (2016/01/16) appInfo → APP_INFO refactoring
088
089        /** 4.0.0 (2005/01/31) Cleanable インターフェースによる初期化処理 */
090        static {
091                final Cleanable clr = new Cleanable() {
092                        /**
093                         * 初期化(クリア)します。
094                         * 主に、キャッシュクリアで利用します。
095                         */
096                        public void clear() {
097                                CalendarFactory.clear();
098                        }
099                };
100
101                SystemManager.addCleanable( clr );
102
103                /** コネクションにアプリケーション情報を追記するかどうか指定 */
104                final boolean USE_DB_APPLICATION_INFO  = HybsSystem.sysBool( "USE_DB_APPLICATION_INFO" ) ;
105
106                // 3.8.7.0 (2006/12/15) アクセスログ取得の為、ApplicationInfoオブジェクトを設定
107                if( USE_DB_APPLICATION_INFO ) {
108                        final String SYSTEM_ID = HybsSystem.sys( "SYSTEM_ID" );
109                        APP_INFO = new ApplicationInfo();
110                        // ユーザーID,IPアドレス,ホスト名
111                        APP_INFO.setClientInfo( SYSTEM_ID,HybsSystem.HOST_ADRS,HybsSystem.HOST_NAME );
112                        // 画面ID,操作,プログラムID
113                        APP_INFO.setModuleInfo( "CalendarFactory",null,null );
114                }
115                else {
116                        APP_INFO = null;
117                }
118        }
119
120        /**
121         * デフォルトコンストラクターをprivateにして、
122         * オブジェクトの生成をさせないようにする。
123         */
124        private CalendarFactory() { }
125
126        /**
127         * CalendarData オブジェクトを取得します。
128         * 作成したCalendarDataオブジェクトは、内部にプールしておき、同じリソース要求が
129         * あったときは、プールの CalendarDataを返します。
130         * DBにデータが存在しない場合は、自動でカレンダを作成します。
131         *
132         * @og.rev 3.8.7.0 (2006/12/15) アクセスログ取得の為、ApplicationInfoオブジェクトを設定
133         * @og.rev 4.0.0.0 (2007/08/29) カレンダーテーブルが存在しない場合のデフォルトのカレンダーデータをシステムリソースで設定する
134         * @og.rev 6.0.4.0 (2014/11/28) NullPointerException が発生するので、事前にチェックします。
135         * @og.rev 6.4.3.3 (2016/03/04) Map#computeIfAbsent で対応するのと、HybsSystem#newInstance(String,String)を利用します。
136         * @og.rev 6.9.9.3 (2018/09/25) 可変引数に変更します(PMD Rather than using a lot of String arguments, consider using a container object for those values.)。
137         *
138         * @param       cls     CalendarQueryオブジェクトを名称を指定します。
139         * @param       inArgs  データベース検索時の可変引数
140//       * @param       arg1    データベース検索時の第1引数
141//       * @param       arg2    データベース検索時の第2引数
142//       * @param       arg3    データベース検索時の第3引数
143//       * @param       arg4    データベース検索時の第4引数
144         *
145         * @return      CalendarDataオブジェクト
146         */
147//      public static CalendarData getCalendarData( final String cls,final String arg1,final String arg2,final String arg3,final String arg4 ) {
148        public static CalendarData getCalendarData( final String cls,final String... inArgs ) {
149
150                // 4.0.0.0 PGカレンダーの初期設定をシステムリソースに変更
151                synchronized( LOCK ) {
152                        if( pgCalData == null ) {
153                                pgCalData = HybsSystem.newInstance(PG_CALENDAR_DATA_CLASS);
154                        }
155                }
156
157                if( ! useDB || cls == null ) { return pgCalData; }
158
159                CalendarData calData ;
160
161//              final String key = cls + ":" + arg1 + ":" + arg2 + ":" + arg3 + ":" + arg4 ;
162                final String key = cls + ":" + String.join( ":" , inArgs );                                             // 6.9.9.3 (2018/09/25)
163                synchronized( LOCK ) {
164                        calData = DATA_POOL.get( key ) ;
165                }
166
167                if( calData == null ) {
168                        // 6.4.3.3 (2016/03/04) Map#computeIfAbsent で対応するのと、HybsSystem#newInstance(String,String)を利用します。
169                        // Map#computeIfAbsent : 戻り値は、既存の、または計算された値。追加有り、置換なし、削除なし
170                        final CalendarQuery query =
171                                                QUERY_POOL.computeIfAbsent( cls , k -> HybsSystem.newInstance( "CalendarQuery_" , k ) );
172
173//                      final String[] args   = query.checkArgment( arg1,arg2,arg3,arg4 );
174                        final String[] args   = query.checkArgment( inArgs );                                                           // 6.9.9.3 (2018/09/25)
175                        final String[][] vals = DBUtil.dbExecute( query.getQuery(),args,APP_INFO,DBID );        // 3.8.7.0 (2006/12/15)
176                        final boolean  isFlat = query.isFlatTable();
177
178                        if( vals != null && vals.length > 0 ) {
179                                calData = new CalendarDBData( vals,isFlat );
180                                // 完全同期ではない。DB処理中に別にもDB処理を行い、
181                                // 先に put されたとしても、同一キーに同じ属性のオブジェクトを
182                                // 登録するだけなので、実質的な問題は発生しない。
183                                // これが、List などの順序が関係すると、このコードは使えない。
184                                synchronized( LOCK ) {
185                                        DATA_POOL.put( key,calData );
186                                }
187                        }
188                        else {
189                                // カレンダテーブルにデータが存在しない場合。
190                                // 4.0.0.0 PGカレンダーの初期設定をシステムリソースに変更
191                                calData = pgCalData;
192                        }
193                }
194                return calData ;
195        }
196
197        /**
198         * キャッシュ(プール)から、すべてのオブジェクトをクリアします。
199         *
200         */
201        public static void clear() {
202                synchronized( LOCK ) {
203                        DATA_POOL.clear();
204                        QUERY_POOL.clear();
205                        useDB = HybsSystem.sysBool( "USE_CALENDAR_DATABASE" );
206                }
207        }
208}