001package com.killcoding.datasource;
002
003import java.util.Calendar;
004import java.util.TimeZone;
005import java.util.Date;
006import java.sql.Timestamp;
007import java.io.File;
008import com.killcoding.tool.CommonTools;
009import com.killcoding.tool.ConfigProperties;
010import java.io.InputStream;
011import java.io.IOException;
012import java.io.FileInputStream;
013import com.killcoding.datasource.DriverDataSource;
014import java.sql.Time;
015import com.killcoding.datasource.CacheDriverExecutor;
016import java.sql.Connection;
017import java.sql.SQLException;
018import com.killcoding.log.Logger;
019import com.killcoding.log.LoggerFactory;
020import java.util.Map;
021import java.time.LocalDate;
022import java.time.Instant;
023import java.time.ZoneId;
024import java.time.LocalDateTime;
025
026public final class Clock {
027
028        private Calendar calendar = null;
029
030        public static File APP_CLOCK = null;
031        private static boolean enable = false;
032        private static ConfigProperties configProperties = null;
033        private static DriverDataSource dataSource = null;
034        private static String dataSourcePath = null;
035        private Long gap = 0L;
036
037        private Logger log = LoggerFactory.getLogger(Clock.class);
038
039        public Clock() {
040                super();
041                init();
042        }
043
044        private synchronized void init() {
045                if (dataSource == null) {
046                        try {
047                                if (APP_CLOCK == null) {
048                                        APP_CLOCK = CommonTools.getSystemProperties(Clock.class, "APP_CLOCK", "Clock.properties");
049                                }
050                                if (APP_CLOCK != null && APP_CLOCK.exists() && configProperties == null) {
051                                        configProperties = new ConfigProperties();
052                                        Runnable updatedRun = new Runnable(){
053                                @Override
054                                public void run(){
055                                try {
056                                                        reload();
057                                                        Logger.systemMark(Clock.class, "Clock Reloaded");
058                                                } catch (Exception e) {
059                                                        log.error(e.getMessage(), e);
060                                                }
061                                }
062                            };
063                                        configProperties.load(APP_CLOCK,updatedRun);
064                                        enable = configProperties.getBoolean("Enable", false);
065                                        if (enable) {
066                                                dataSourcePath = configProperties.getString("DataSource");
067                                                if (!CommonTools.isBlank(dataSourcePath)) {
068                                                        dataSource = new DriverDataSource(new File(dataSourcePath));
069                                                }
070                                        }
071                                }
072                        } catch (Exception e) {
073                                log.error(e.getMessage(), e);
074                        }
075                }
076        }
077
078        private void reload() {
079                if (configProperties != null) {
080                        enable = configProperties.getBoolean("Enable", false);
081                        String reloadDataSourcePath = configProperties.getString("DataSource");
082                        
083                        boolean isReload = false;
084                        if(!CommonTools.isBlank(reloadDataSourcePath)){
085                            isReload = !dataSourcePath.equals(reloadDataSourcePath);
086                        }
087                        if(dataSource != null && isReload){
088                            // May be referenced when expansion is needed
089                            // dataSource.closeAll();
090                            dataSourcePath = reloadDataSourcePath;
091                        }
092                        // May be referenced when expansion is needed
093                        // if(!enable && dataSource != null){
094                        //    dataSource.closeAll();
095                        // }
096                        if (enable && isReload) {
097                                if (!CommonTools.isBlank(dataSourcePath)) {
098                                        dataSource = new DriverDataSource(new File(dataSourcePath));
099                                }
100                        }
101                }
102        }
103
104        private Calendar getRemoteCalendar() {
105                if (enable && dataSource != null) {
106                        Connection conn = null;
107                        CacheDriverExecutor executor = null;
108                        try {
109                                String getAppClockSql = configProperties.getString("GetAppClockSql");
110
111                                if (CommonTools.isBlank(getAppClockSql))
112                                        getAppClockSql = dataSource.getConfigProperties().getString("GetAppClockSql");
113
114                                if (CommonTools.isBlank(getAppClockSql))
115                                        return null;
116
117                                conn = dataSource.getConnection();
118
119                                if (conn == null)
120                                        return null;
121
122                                executor = new CacheDriverExecutor(conn);
123                                Map<String, Object> result = executor.first(getAppClockSql);
124
125                                if (result == null)
126                                        return null;
127
128                                Object appSystemDateObj = result.get("app_systime_date");
129                                Object appTimeZoneObj = result.get("app_time_zone");
130                                Long appSystemDateTime = null;
131                                if (appSystemDateObj instanceof Timestamp) {
132                                        appSystemDateTime = ((Timestamp) appSystemDateObj).getTime();
133                                }
134                                if (appSystemDateTime == null && appSystemDateObj instanceof java.sql.Date) {
135                                        appSystemDateTime = ((java.sql.Date) appSystemDateObj).getTime();
136                                }
137                                if (appSystemDateTime == null && appSystemDateObj instanceof java.util.Date) {
138                                        appSystemDateTime = ((java.util.Date) appSystemDateObj).getTime();
139                                }
140                                if (appSystemDateTime == null && appSystemDateObj instanceof Long) {
141                                        appSystemDateTime = (Long) appSystemDateObj;
142                                }
143                                if (appSystemDateTime == null)
144                                        return null;
145
146                gap = Calendar.getInstance().getTimeInMillis() - appSystemDateTime;
147                
148                                String timeZone = CommonTools.isBlank(appTimeZoneObj)
149                                                ? configProperties.getString("DefaultTimeZone", "GMT") : (String) appTimeZoneObj;
150
151                                Calendar remoteCalendar = Calendar.getInstance();
152                                TimeZone calTimeZone = TimeZone.getTimeZone(timeZone);
153                                remoteCalendar.setTimeZone(calTimeZone);
154                                remoteCalendar.setTimeInMillis(appSystemDateTime);
155                                return remoteCalendar;
156                        } catch (Exception e) {
157                                log.error(e.getMessage(), e);
158                                return null;
159                        } finally {
160                                try {
161                                        if (executor != null)
162                                                executor.close();
163                                } catch (SQLException e) {
164                                        log.error(e.getMessage(), e);
165                                }
166                        }
167                }
168                return null;
169        }
170
171        public Calendar getCalendar() {
172
173                if (calendar != null)
174                        return calendar;
175
176                calendar = getRemoteCalendar();
177                if (calendar == null && !enable) {
178                        calendar = Calendar.getInstance();
179                }
180                if(calendar == null && enable){
181                    calendar = getDefaultCalendear();
182                }
183                return calendar;
184        }
185        
186        private Calendar getDefaultCalendear(){
187                String timeZone = configProperties.getString("DefaultTimeZone", "GMT");
188        long msGap = Calendar.getInstance().getTimeInMillis() + gap;
189                Calendar defaultCalendar = Calendar.getInstance();
190                TimeZone calTimeZone = TimeZone.getTimeZone(timeZone);
191                defaultCalendar.setTimeZone(calTimeZone);
192                defaultCalendar.setTimeInMillis(msGap);
193                return defaultCalendar;
194        }
195
196        public java.util.Date getDate() {
197                return new java.util.Date(getCalendar().getTimeInMillis());
198        }
199
200        public long getTime() {
201                return getCalendar().getTimeInMillis();
202        }
203
204        public java.sql.Date getSqlDate() {
205                return new java.sql.Date(getTime());
206        }
207
208        public java.sql.Time getSqlTime() {
209                return new java.sql.Time(getTime());
210        }
211
212        public Timestamp getSqlTimestamp() {
213                return new Timestamp(getTime());
214        }
215
216        public LocalDate getLocalDate() {
217                ZoneId zoneId = getCalendar().getTimeZone().toZoneId();
218                return Instant.ofEpochMilli(getTime()).atZone(zoneId).toLocalDate();
219        }
220
221        public LocalDateTime getLocalDateTime() {
222                ZoneId zoneId = getCalendar().getTimeZone().toZoneId();
223                return LocalDateTime.ofInstant(Instant.ofEpochMilli(getTime()), zoneId);
224        }
225        
226        public static DriverDataSource getDataSource(){
227            return dataSource;
228        }
229        
230        public static ConfigProperties getConfigProperties(){
231            return configProperties;
232        }
233
234}