/******************************************************************************* * Copyright (c) 2009 Daniel Grout. * * GNU GENERAL PUBLIC LICENSE - Version 3 * * This file is part of Report Runner (http://code.google.com/p/reportrunner). * * Report Runner 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. * * Report Runner 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 Report Runner. If not, see <http://www.gnu.org/licenses/>. * * Module: DashboardServiceImpl.java ******************************************************************************/ package binky.reportrunner.service.impl; import java.math.BigDecimal; import java.sql.Connection; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.sql.Types; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.Iterator; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Set; import java.util.TreeSet; import javax.sql.DataSource; import org.apache.log4j.Logger; import binky.reportrunner.dao.ReportRunnerDao; import binky.reportrunner.data.DashboardData; import binky.reportrunner.data.RunnerDashboardGrid; import binky.reportrunner.data.RunnerDashboardItem; import binky.reportrunner.data.RunnerDashboardItem.ItemType; import binky.reportrunner.data.RunnerDashboardSampler; import binky.reportrunner.data.RunnerGroup; import binky.reportrunner.data.sampling.SamplingData; import binky.reportrunner.data.sampling.TrendData; import binky.reportrunner.scheduler.Scheduler; import binky.reportrunner.scheduler.SchedulerException; import binky.reportrunner.service.DashboardService; import binky.reportrunner.service.DatasourceService; import com.googlecode.ehcache.annotations.Cacheable; import com.googlecode.ehcache.annotations.TriggersRemove; public class DashboardServiceImpl implements DashboardService { /** * */ private static final long serialVersionUID = 8560022031641311268L; private static final Logger logger = Logger.getLogger(DashboardServiceImpl.class); private DatasourceService datasourceService; private ReportRunnerDao<RunnerDashboardItem,Integer> dashboardDao; private ReportRunnerDao<RunnerGroup,String> groupDao; private Scheduler scheduler; public RunnerDashboardItem getItem(Integer id) { return dashboardDao.get(id); } @TriggersRemove(cacheName="dashboardCache") public void deleteItem(Integer id) throws SchedulerException { dashboardDao.delete(id); scheduler.removedDashboardAlert(id); } @Cacheable(cacheName="dashboardCache") public List<RunnerDashboardItem> getItemsForGroup(String groupName) { List<RunnerDashboardItem> as = dashboardDao.findByNamedQuery("getItemsByGroup", new String[]{groupName}); List<RunnerDashboardItem> alerts=new LinkedList<RunnerDashboardItem>(); //temp hack for (RunnerDashboardItem a: as) { long visualRefreshTime=60000; try { Date last = scheduler.getPreviousRunTime(a.getItemId()); Date next = scheduler.getNextRunTime(a.getItemId()); if ((last!=null) && (next!=null) && (last.getTime()<next.getTime())) { visualRefreshTime=(next.getTime()-last.getTime()); } } catch (SchedulerException e) { logger.error(e.getMessage(),e); } a.setVisualRefreshTime(visualRefreshTime); alerts.add(a); } return alerts; } @Cacheable(cacheName="dashboardCache") public List<RunnerDashboardItem> getAllItems() { return dashboardDao.getAll(); } public Integer saveUpdateItem(RunnerDashboardItem alert) throws SchedulerException { if (alert.getItemId()!=null){ scheduler.removedDashboardAlert(alert.getItemId()); } Integer ret; //hack to try to fix a batch update error RunnerGroup group = groupDao.get(alert.getGroup().getGroupName()); alert.setGroup(group); //hack to deal with interval change if ((alert.getItemType()==ItemType.Sampler) && (alert.getItemId()!=null)) { RunnerDashboardSampler s=(RunnerDashboardSampler)alert; RunnerDashboardSampler comp = (RunnerDashboardSampler)dashboardDao.get(alert.getItemId()); s.setData(comp.getData()); s.setSamplingData(comp.getSamplingData()); s.setTrendData(comp.getTrendData()); //need to do a compare if (s.isRecordTrendData() && !s.getInterval().equals(comp.getInterval())&& s.getTrendData()!=null) { s.getTrendData().clear(); } logger.debug("saving sampler"); ret=dashboardDao.saveOrUpdate(s); } else { logger.debug("saving alert"); ret=dashboardDao.saveOrUpdate(alert); } scheduler.addDashboardAlert(alert.getItemId(),alert.getCronTab()); return ret; } public void setDashboardDao(ReportRunnerDao<RunnerDashboardItem,Integer> dashboardDao) { this.dashboardDao = dashboardDao; } public Scheduler getScheduler() { return scheduler; } public void setScheduler(Scheduler scheduler) { this.scheduler = scheduler; } @Cacheable(cacheName="dashboardCache") public List<RunnerDashboardItem> getRunningItems() { List<String> runningJobs = scheduler.getCurrentRunningJobs(); List<RunnerDashboardItem> alerts = new LinkedList<RunnerDashboardItem>(); for (String string : runningJobs) { String groupName = string.split(":|:")[0]; if (groupName.equals(Scheduler.dashboardSchedulerGroup)) { logger.debug("job name: " + string); Integer id = Integer.parseInt(string.split(":|:")[2]); RunnerDashboardItem alert = dashboardDao.get(id); alerts.add(alert); } } return alerts; } public void interruptRunningDashboardItem(Integer alertId) throws SchedulerException { logger.debug("interrupt alert: " + alertId); scheduler.interruptRunningDashboardAlert(alertId); } public void invokeDashboardItem(Integer itemId) throws SchedulerException { scheduler.invokeDashboardItem(itemId); } public void setGroupDao(ReportRunnerDao<RunnerGroup,String> groupDao) { this.groupDao = groupDao; } @Override public void processDashboardItem(int itemId)throws SQLException { RunnerDashboardItem item = dashboardDao.get(itemId); if (item!=null) { switch (item.getItemType()) { case Sampler: processSampler(item); break; default: processQueryItem(item); } } else { logger.warn("null item found for item id: " + itemId); } } private void processSampler(RunnerDashboardItem item) throws SQLException { DataSource ds = datasourceService.getJDBCDataSource(item.getDatasource()); String sql = item.getAlertQuery(); Connection conn = ds.getConnection(); try { long start = item.getLastUpdated()!=null?item.getLastUpdated().getTime():0; logger.trace("running SQL for sampler"); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql); RunnerDashboardSampler sampler = (RunnerDashboardSampler)item; int period = Calendar.MINUTE; int amount=-1; //calculate the start of the window switch (sampler.getWindow()) { case YEAR: period = Calendar.YEAR; break; case MONTH: period = Calendar.MONTH; break; case WEEK: period = Calendar.DAY_OF_YEAR; amount=-7; break; case DAY: period = Calendar.DAY_OF_YEAR; break; case HOUR: period = Calendar.HOUR; break; } Calendar cal=Calendar.getInstance(); Date now = cal.getTime(); cal.add(period, amount); Date cutoff= cal.getTime(); //get the value - as this is a sampler we only grab the first row BigDecimal val= BigDecimal.ZERO; if (rs.next()) { val = rs.getBigDecimal(sampler.getValueColumn()); } rs.close(); sampler.getSamplingData().add(new SamplingData(sampler,now.getTime(),val)); if (sampler.isRecordTrendData()) { //record trending data SimpleDateFormat sdf; switch (sampler.getInterval()) { case DAY: sdf = new SimpleDateFormat("EEEEE"); break; case HOUR: sdf = new SimpleDateFormat("HH"); break; case MINUTE: sdf = new SimpleDateFormat("mm"); break; case MONTH: sdf = new SimpleDateFormat("MMMMM"); break; case SECOND: default: sdf = new SimpleDateFormat("ss"); } String timeString = sdf.format(now); boolean found=false; TrendData t = new TrendData(sampler, timeString); for (TrendData d: sampler.getTrendData()) { if (d.getTimeString().equals(timeString)) { t=d; found = true; break; } } if (found) { sampler.getTrendData().remove(t); BigDecimal newVal = new BigDecimal(((t.getMeanValue().doubleValue()* t.getSampleSize())+val.doubleValue()) /(t.getSampleSize()+1)); t.setMeanValue(newVal); t.setSampleSize(t.getSampleSize()+1); if (val.doubleValue()>t.getMaxValue().doubleValue()) t.setMaxValue(val); if (val.doubleValue()<t.getMinValue().doubleValue()) t.setMinValue(val); }else { //create new entry t.setMaxValue(val); t.setMeanValue(val); t.setMinValue(val); t.setSampleSize(1); if (sampler.getTrendData()==null) sampler.setTrendData(new TreeSet<TrendData>()); } sampler.getTrendData().add(t); } if (start >0) { sampler.setVisualRefreshTime(now.getTime()-start); } sampler.setLastUpdated(now); logger.trace("deleting entries older than " + cutoff); //first of all we need to clean out any that now fall outside of our window logger.trace("current sample size is :" + sampler.getData().size()); for (Iterator<SamplingData> it=sampler.getSamplingData().iterator();it.hasNext();) { SamplingData d = it.next(); if (d.getSampleTime()<cutoff.getTime()) it.remove(); } dashboardDao.saveOrUpdate(sampler); } finally { if (!conn.isClosed()) conn.close(); } } private void processQueryItem(RunnerDashboardItem item) throws SQLException { DataSource ds = datasourceService.getJDBCDataSource(item.getDatasource()); String sql = item.getAlertQuery(); Connection conn = ds.getConnection(); try { logger.trace("running SQL for item"); Statement stmt = conn.createStatement(); if (item.getItemType() == ItemType.Grid) { int rows = ((RunnerDashboardGrid) item).getRowsToDisplay(); if (rows > 0) { logger.trace("limiting grid result to " + rows + " rows"); stmt.setFetchSize(rows); stmt.setMaxRows(rows); } } ResultSet rs = stmt.executeQuery(sql); Set<DashboardData> data = new LinkedHashSet<DashboardData>(); int colCount=rs.getMetaData().getColumnCount(); ResultSetMetaData meta=rs.getMetaData(); Date now = Calendar.getInstance().getTime(); int rowNumber=0; while (rs.next()) { rowNumber++; for (int i=1;i<=colCount;i++) { Object value; switch (meta.getColumnType(i)) { case Types.TIME: case Types.TIMESTAMP: case Types.DATE: value=Long.valueOf(rs.getDate(i).getTime()); break; default: value=rs.getObject(i); } data.add(new DashboardData(item, now, meta.getColumnName(i), value.toString(), meta.getColumnType(i),rowNumber)); } } rs.close(); item.setData(data); item.setLastUpdated(now); dashboardDao.saveOrUpdate(item); } finally { if (!conn.isClosed()) conn.close(); } } public void setDatasourceService(DatasourceService datasourceService) { this.datasourceService = datasourceService; } @Override public void clearTrendData(int itemId) { RunnerDashboardItem item = dashboardDao.get(itemId); if (item!=null&&item.getItemType()==ItemType.Sampler) { RunnerDashboardSampler s = (RunnerDashboardSampler)item; if (s.isRecordTrendData()) { s.getTrendData().clear(); dashboardDao.saveOrUpdate(s); } } } }