/* * This software is distributed under the terms of the FSF * Gnu Lesser General Public License (see lgpl.txt). * * This program is distributed WITHOUT ANY WARRANTY. See the * GNU General Public License for more details. */ package com.scooterframework.orm.activerecord; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Observable; import java.util.Observer; import java.util.Properties; import java.util.Timer; import java.util.TimerTask; import com.scooterframework.orm.sqldataexpress.config.DatabaseConfig; import com.scooterframework.orm.sqldataexpress.object.RowData; import com.scooterframework.orm.sqldataexpress.service.SqlServiceClient; /** * ReferenceDataLoader is responsible for loading reference data. * * @author (Fei) John Chen */ public class ReferenceDataLoader implements Observer { private static Map<String, ReferenceDataTimerTask> referenceDataTaskMap = new HashMap<String, ReferenceDataTimerTask>(); private static long oneHundredDays = 8640000; private static boolean started = false; private Date oldDate = null; private Timer timer = null; public ReferenceDataLoader() { oldDate = new Date(); oldDate.setTime(oldDate.getTime()-oneHundredDays); DatabaseConfig.getInstance().addObserver(this); } public void start() { if (started) return; timer = new Timer(); List<String> refNames = DatabaseConfig.getInstance().getReferenceDataNames(); if (refNames != null && refNames.size() > 0) { Iterator<String> it = refNames.iterator(); while(it.hasNext()) { String name = it.next(); Properties prop = DatabaseConfig.getInstance().getReferenceDataProperties(name); ReferenceDataTimerTask task = new ReferenceDataTimerTask(name, prop); referenceDataTaskMap.put(name, task); schedule(task, task.period); } } started = true; } /** * Terminates this loader, discarding any currently scheduled tasks. * * @see java.util.Timer#cancel() */ public void cancel() { timer.cancel(); } /** * Terminates this loader, discarding any currently scheduled tasks. * * @see java.util.Timer#cancel() */ public void stop() { timer.cancel(); started = false; } /** * Updates this loader, restarts the timer task if its period is changed. */ public void update(Observable o, Object arg) { List<String> refNames = DatabaseConfig.getInstance().getReferenceDataNames(); if (refNames != null && refNames.size() > 0) { Iterator<String> it = refNames.iterator(); while(it.hasNext()) { String name = it.next(); Properties prop = DatabaseConfig.getInstance().getReferenceDataProperties(name); if (referenceDataTaskMap.containsKey(name)) { ReferenceDataTimerTask task = (ReferenceDataTimerTask)referenceDataTaskMap.get(name); task.resetProperties(prop); if (task.periodModified) { task.cancel(); task = new ReferenceDataTimerTask(name, prop); referenceDataTaskMap.put(name, task); schedule(task, task.period); } } else { ReferenceDataTimerTask task = new ReferenceDataTimerTask(name, prop); referenceDataTaskMap.put(name, task); schedule(task, task.period); } } } //now remove all reference data tasks that have been removed from property file. for (Map.Entry<String, ReferenceDataTimerTask> entry : referenceDataTaskMap.entrySet()) { String name = entry.getKey(); if (!refNames.contains(name)) { ReferenceDataTimerTask task = entry.getValue(); task.cancel(); referenceDataTaskMap.remove(name); } } } public static boolean isStarted() { return started; } public static Map<String, ReferenceDataTimerTask> getReferenceDataTasks() { return referenceDataTaskMap; } private void schedule(TimerTask task, long period) { if (period > 0) { timer.schedule(task, oldDate, period); } else { timer.schedule(task, oldDate); } } private static class ReferenceDataTimerTask extends TimerTask { String theName = ""; String clz = null; String sql = null; String key = ""; String value = ""; long period = 0;//in milliseconds Date loadedTime = null; boolean hasRun = false; boolean periodModified = false; boolean runOnlyOnce = false; boolean donotRun = false; public ReferenceDataTimerTask(String name, Properties prop) { super(); theName = name; init(prop); } private void init(Properties prop) { clz = prop.getProperty("class"); sql = prop.getProperty("sql"); if (clz == null && sql == null) throw new IllegalArgumentException("Either clz or sql must be specified for reference data " + theName + "."); key = prop.getProperty("key"); value = prop.getProperty("value"); long thePeriod = 0; try { thePeriod = Long.parseLong(prop.getProperty("period", "0")); } catch(NumberFormatException nfex) { thePeriod = 0; } periodModified = false; //if ((hasRun && (thePeriod != 0) || !hasRun && (thePeriod == 0)) && (period != thePeriod)) { if (period != thePeriod) { periodModified = true; } period = thePeriod; if (period == 0) { runOnlyOnce = true; } else if (period < 0) { donotRun = true; } } public void resetProperties(Properties prop) { init(prop); } public void run() { if (donotRun || (runOnlyOnce && hasRun)) return; if (clz != null) { List<ActiveRecord> records = null; if (sql != null) { records = ActiveRecordUtil.getGateway(clz).findAllBySQL(sql); } else { records = ActiveRecordUtil.getGateway(clz).findAll(); } ReferenceDataStore.setReferenceData(theName, convertRecordsToReferenceDataList(records)); } else { List<RowData> rows = SqlServiceClient.retrieveRowsBySQL(sql); ReferenceDataStore.setReferenceData(theName, convertRowsToReferenceDataList(rows)); } loadedTime = new Date(); hasRun = true; } public String toString() { StringBuilder sb = new StringBuilder(); sb.append("name=" + theName).append(", "); sb.append("clz=" + clz).append(", "); sb.append("sql=" + sql).append(", "); sb.append("key=" + key).append(", "); sb.append("value=" + value).append(", "); sb.append("period=" + period).append(", "); sb.append("loadedTime=" + loadedTime); return sb.toString(); } private List<ReferenceData> convertRecordsToReferenceDataList(List<ActiveRecord> records) { if (records == null) return null; List<ReferenceData> list = new ArrayList<ReferenceData>(records.size()); Iterator<ActiveRecord> it = records.iterator(); while(it.hasNext()) { ActiveRecord record = it.next(); ReferenceDataRecord rdr = new ReferenceDataRecord(theName, key, value, record); list.add(rdr); } return list; } private List<ReferenceData> convertRowsToReferenceDataList(List<RowData> records) { if (records == null) return null; List<ReferenceData> list = new ArrayList<ReferenceData>(records.size()); Iterator<RowData> it = records.iterator(); while(it.hasNext()) { RowData row = it.next(); ReferenceData rdr = new ReferenceDataRecord(theName, key, value, row); list.add(rdr); } return list; } } }