package com.alcatel_lucent.nz.wnmsextract;
/*
* This file is part of wnmsextract.
*
* wnmsextract is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* wnmsextract is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import java.text.DateFormat;
import java.text.ParsePosition;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.log4j.Logger;
import org.quartz.JobListener;
import org.quartz.SchedulerException;
import org.quartz.SimpleTrigger;
import org.quartz.Trigger;
import com.alcatel_lucent.nz.wnmsextract.DataLogger.LogAppType;
import com.alcatel_lucent.nz.wnmsextract.schedule.ConfigPoller;
import com.alcatel_lucent.nz.wnmsextract.schedule.ConfigProperties;
import com.alcatel_lucent.nz.wnmsextract.schedule.ConversionJob;
import com.alcatel_lucent.nz.wnmsextract.schedule.ConversionJobFactory;
import com.alcatel_lucent.nz.wnmsextract.schedule.ConversionJobScheduler;
import com.alcatel_lucent.nz.wnmsextract.schedule.ConversionListener;
/**
* Multi threaded application entry point for WNMS parsing. This Class (the othet MAIN class) has
* fallen into disuse. It could be revived if the app is ever run on a server with a decent amount
* of RAM but at present any more than a single thread will throw OutOfMemor errors. :(
* @author jnramsay
* TODO FTP client for as a connector type
* TODO Jar-in-Jar classloading (!) - Not practical
* TODO Debug messed up parser type allocation
*/
public class WNMSDataScheduler {
//private Lock lock;
private static Logger jlog = Logger.getLogger("com.alcatel_lucent.nz.wnmsextract.WNMSDataExtractor");
private static Logger slog = Logger.getLogger("com.alcatel_lucent.nz.wnmsextract");
/*convenience constants useful for testing but can delete most in production*/
@SuppressWarnings("unused")
private static final long ONE_SECOND = 1000L;
private static final long FIVE_SECONDS = 5000L;
@SuppressWarnings("unused")
private static final long ONE_MINUTE = 60000L;
@SuppressWarnings("unused")
private static final long FIVE_MINUTES = 300000L;
@SuppressWarnings("unused")
private static final long TEN_MINUTES = 600000L;
@SuppressWarnings("unused")
private static final long ONE_HOUR = 3600000L;
private static final long ONE_DAY = 86400000L;
@SuppressWarnings("unused")
private static final long ONE_WEEK = 604800000L;
@SuppressWarnings("unused")
private static final long ONE_MONTH = 2419200000L;
private static final int START_DELAY = 10;
//maximum for a long is 2^63-1 = 0x7FFFFFFFFFFFFFFF
private static final String CONF_DIR = "conf";
private static final String DATE_FORMAT = "yyyy-MM-dd";
private static final String TIME_FORMAT = "HH:mm:ss";
public DateFormat df,tf;
private Map<String, ConversionJob> jobs;
private Map<String, Trigger> trigs;
private Map<String, ConversionListener> ears;
private List<ConfigProperties> cprops;
private ConversionJobScheduler jobscheduler;
private ConfigPoller poller;
private DataLogger wdl;
/* result data */
//private double[][] data;
//private double power;
/**
* Instantiate jobs and triggers
*/
public WNMSDataScheduler(){
this.wdl = new DataLogger(EnumSet.noneOf(LogAppType.class),slog);
this.df = new SimpleDateFormat(DATE_FORMAT);
this.tf = new SimpleDateFormat(TIME_FORMAT);
this.jobs = new HashMap<String,ConversionJob>();
this.trigs = new HashMap<String,Trigger>();
this.ears = new HashMap<String,ConversionListener>();
this.cprops = new ArrayList<ConfigProperties>();
this.jobscheduler = new ConversionJobScheduler();
this.poller = new ConfigPoller(CONF_DIR,new ArrayList<ConfigProperties>());
}
/**
* Initialise the Scheduler by reading config files
*/
public void init(){
//this.lock = new ReentrantLock();
//jobscheduler.setLock(this.lock);
this.poller.inspect();
schedule(this.poller.refresh());
}
/**
* Scan existing props vs new props and add/delete as required
*/
public void schedule(ArrayList<ConfigProperties> newprops){
//look for additions
for(ConfigProperties np : newprops){
if (!cprops.contains(np)){
String pid = np.getProperty("PROJ.id");
jlog.info("Adding "+ np);
cprops.add(np);
jobs.put(pid, createJob(np));
trigs.put(pid, createTrigger(np));
ears.put(pid, createListener(np));
scheduleJob(jobs.get(pid),trigs.get(pid));
attachListener(ears.get(pid));
}
}
//look for deletions
Iterator<ConfigProperties> cpiter = cprops.iterator();
while(cpiter.hasNext()) {
ConfigProperties cp = cpiter.next();
if (!newprops.contains(cp)){
String pid = cp.getProperty("PROJ.id");
jlog.info("Deleting "+ cp);
cancelJob(jobs.get(pid));
deleteJob(cp);
deleteTrigger(cp);
deleteListener(cp);
cpiter.remove();
}
}
}
/**
* Given the name of a file decide whether its a proper
* properties file and if so load it
*/
/*
public void addTarget(String pfn){
//scoping trouble?
if (ConfigProperties.validatePropFileName(pfn)){
ConfigProperties cp = new ConfigProperties(pfn);
cprops.add(cp);
}
}
*/
public List<Integer> readProperties(){
return new ArrayList<Integer>();
}
/**
* Create a new ConversionJob according to the provided props
*/
public ConversionJob createJob(ConfigProperties cprop){
ConversionJobFactory cjf = new ConversionJobFactory(cprop);
//add new job with proj.id as name
jlog.info("Create Job "+cprop.getProperty("PROJ.id"));
return cjf.getConversionJobInstance();
//jobs.put(cprop.getProperty("PROJ.id"),tjf.getInstance());
}
/**
* Remove a ConversionJob described by the provided props
*/
public void deleteJob(ConfigProperties cprop){
jobs.remove(cprop.getProperty("PROJ.id"));
jlog.info("Delete Job "+cprop.getProperty("PROJ.id"));
}
/**
* Parse time props to scheduler trigger
*/
public Trigger createTrigger(ConfigProperties cprop){
int repeat;
long interval;
Calendar sd = calculateStartTime(cprop);
Calendar ed = calculateEndTime(cprop);
String name = cprop.getProperty("PROJ.name");
String rstr = cprop.getProperty("TIME.repeat");
String istr = cprop.getProperty("TIME.interval");
String fstr = cprop.getProperty("TIME.frequency");
//repeat this many times or forever
if(rstr!=null)
repeat = Integer.parseInt(rstr);
else
repeat = SimpleTrigger.REPEAT_INDEFINITELY;
//repeat every interval milliseconds or daily
//interval overrides frequency
if(istr!=null)
interval = Long.parseLong(istr);
else if (fstr!=null)
interval = ONE_DAY/Long.parseLong(fstr);
else
interval = ONE_DAY;
jlog.info("Create Trigger n="+name+" sd="+sd.getTime()+" ed="+ed.getTime()+" r="+repeat+" i="+interval);
Trigger trigger = new SimpleTrigger(name, null,sd.getTime(),ed.getTime(),repeat,interval);
trigger.setMisfireInstruction(SimpleTrigger.MISFIRE_INSTRUCTION_RESCHEDULE_NOW_WITH_EXISTING_REPEAT_COUNT);
return trigger;
}
/**
* Remove trigger property
* @param cprop
*/
public void deleteTrigger(ConfigProperties cprop){
trigs.remove(cprop.getProperty("PROJ.id"));
}
/**
* Create a new ConversionListener according to the provided props
*/
public ConversionListener createListener(ConfigProperties cprop){
jlog.info("Create Listener "+cprop.getProperty("PROJ.id"));
return new ConversionListener(cprop.getProperty("PROJ.id"));
}
/**
* Remove a ConversionListener described by the provided props
*/
public void deleteListener(ConfigProperties cprop){
ears.remove(cprop.getProperty("PROJ.id"));
jlog.info("Delete Listener "+cprop.getProperty("PROJ.id"));
}
/**
* Read scheduler start date/time from cprops and return as Calendar instance
* @param cprop
* @return
*/
public Calendar calculateStartTime(ConfigProperties cprop){
Calendar d = Calendar.getInstance();
String sd = cprop.getProperty("TIME.startdate");
String st = cprop.getProperty("TIME.starttime");
//SD defined
if(sd!=null){
d.setTime(df.parse(sd,new ParsePosition(0)));
}
//SD not defined, use current date
//ST defined
if(st!=null){
Calendar t = Calendar.getInstance();
t.setTime(tf.parse(st,new ParsePosition(0)));
//take the starttime and add it to the startdate
d.add(Calendar.HOUR_OF_DAY,t.get(Calendar.HOUR_OF_DAY));
d.add(Calendar.MINUTE,t.get(Calendar.MINUTE));
d.add(Calendar.SECOND,t.get(Calendar.SECOND));
}
//ST not defined use current plus a small delay to avoid misfires
else {
d.add(Calendar.SECOND,START_DELAY);
}
return d;
}
/**
* Read scheduler end date/time from cprops and return as Calendar instance
* @param cprop
* @return
*/
public Calendar calculateEndTime(ConfigProperties cprop){
Calendar d = Calendar.getInstance();
String ed = cprop.getProperty("TIME.enddate");
String et = cprop.getProperty("TIME.endtime");
//ED defined
if(ed!=null){
d.setTime(df.parse(ed,new ParsePosition(0)));
}
//ED not defined, set to 1 year ahead
else {
d.add(Calendar.YEAR,1);
}
//ET defined
if(et!=null){
Calendar t = Calendar.getInstance();
t.setTime(tf.parse(et,new ParsePosition(0)));
//take the endtime and add it to enddate
d.add(Calendar.HOUR_OF_DAY,t.get(Calendar.HOUR_OF_DAY));
d.add(Calendar.MINUTE,t.get(Calendar.MINUTE));
d.add(Calendar.SECOND,t.get(Calendar.SECOND));
}
//ET not defined, use current time
return d;
}
/**
* From the configprops create new schedules
*/
public void scheduleJob(ConversionJob tjob, Trigger trig){
jobscheduler.scheduleJob(tjob,trig);
jlog.info("Schedule Job "+tjob.getFullName()+"/"+trig.getFullName());
}
/**
* Attach a listener to the job scheduler
* @param lstnr
*/
public void attachListener(JobListener lstnr){
jobscheduler.attachListener(lstnr);
jlog.info("Listen Job "+lstnr.getName());
}
/**
* Remove a conversion job from the scheduler
*/
public void cancelJob(ConversionJob tjob){
jobscheduler.cancelJob(tjob);
//jlog.info("Cancel Job "+tjob.getFullName());
}
/*
private String formatCalendar(Calendar c){
return String.valueOf(c.get(Calendar.YEAR))
+"/"+String.valueOf(c.get(Calendar.MONTH))
+"/"+String.valueOf(c.get(Calendar.DAY_OF_MONTH))
+" "+String.valueOf(c.get(Calendar.HOUR_OF_DAY))
+":"+String.valueOf(c.get(Calendar.MINUTE))
+":"+String.valueOf(c.get(Calendar.SECOND));
}
*/
/*get/set the config props poller*/
public ConfigPoller getPoller() {
return poller;
}
public void setPoller(ConfigPoller poller) {
this.poller = poller;
}
//---------------------------------------------------------------------------
/**
* Main loop method. Starts timers triggering executable jobs
*/
public void activateTimers(){
while(true){
try {
Thread.sleep(FIVE_SECONDS);
//System.out.print(".");
System.out.println(jobscheduler.getSchedulerStatus());
//if the props list changes
if(poller.inspect()!=0){
schedule(this.poller.refresh());
}
/*
for (String k : trigs.keySet()){
System.out.println(k+"//"+formatCalendar(Calendar.getInstance())+"->"+trigs.get(k).getNextFireTime());
}
*/
}
catch(InterruptedException ie){
System.err.println("Interrupted Exception : " + ie);
throw new RuntimeException(ie);
}
catch(SchedulerException se){
System.err.println("Scheduler Exception : " + se);
}
}
}
public static void main(String[] args) throws InterruptedException {
//WNMSDataScheduler.log();
WNMSDataScheduler wds = new WNMSDataScheduler();
wds.wdl.addLoggingAppender(LogAppType.Console);
wds.init();
jlog.info("START - DATA TRANSFER MONITOR");
wds.activateTimers();
}
}