Java tutorial
/* * This program 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 2 of the License, or * (at your option) any later version. * * This program 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * @copyright 2008 Camptocamp SA */ package org.cartoweb.stats.report; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.cartoweb.stats.BaseStats; import org.cartoweb.stats.Utils; import org.cartoweb.stats.report.classifier.Classifier; import org.cartoweb.stats.report.classifier.GridClassifier; import org.cartoweb.stats.report.classifier.SimpleClassifier; import org.cartoweb.stats.report.dimension.DimensionMetaData; import org.cartoweb.stats.report.dimension.IntFieldMetaData; import org.cartoweb.stats.report.dimension.LayerMetaData; import org.cartoweb.stats.report.dimension.RangeDoubleFieldMetaData; import org.cartoweb.stats.report.filter.*; import org.cartoweb.stats.report.result.CounterResult; import org.cartoweb.stats.report.result.Result; import org.cartoweb.stats.report.result.SurfaceResult; import org.ini4j.Ini; import org.pvalsecc.jdbc.JdbcUtilities; import org.pvalsecc.misc.StringUtils; import org.pvalsecc.misc.UnitUtilities; import org.pvalsecc.opts.Option; import java.io.FileInputStream; import java.io.IOException; import java.sql.*; import java.util.*; import java.util.regex.Matcher; import java.util.regex.Pattern; public class Reports extends BaseStats { private static final Log LOGGER = LogFactory.getLog(Reports.class); private static final Pattern TRANSFORM = Pattern.compile("[^\\w]", Pattern.CASE_INSENSITIVE); @Option(desc = "Location of the INI file describing the reports to generate", mandatory = true, environment = "STATS_INI_FILENAME") private String iniFilename = ""; @Option(desc = "If true, the reports are purged in case of configuration change") private boolean purgeOnConfigurationChange = false; private final List<Report> reports = new ArrayList<Report>(); private Timestamp lastRecordDate; private static final String FILTERS_PREFIX = "filters."; private static final String PERIODS_PREFIX = "periods."; public Reports(String[] args) { super(); parseArgs(args); } public void parseIniFile(Connection con) throws IOException, SQLException { FileInputStream file = new FileInputStream(iniFilename); Ini ini = new Ini(file); for (Ini.Section section : ini.values()) { TimeScaleDefinition[] timeScales = parseTimeScales(section); final Classifier<?> classifier = parseType(section, timeScales); DimensionMetaData<?>[] dimensionMetaDatas = parseDimensions(section); Filter[] filters = parseFilters(con, section); final Result[] results = parseValues(con, section); final String label = getMandatoryField(section, "label"); Report report = new Report(dimensionMetaDatas, filters, classifier, results, section.getName(), label); reports.add(report); } } private Result[] parseValues(Connection con, Ini.Section section) throws SQLException { String[] values = getMandatoryField(section, "values").split(",\\s*"); final Result[] results = new Result[values.length]; for (int i = 0; i < values.length; ++i) { String value = values[i]; if (value.equalsIgnoreCase("count")) { results[i] = new CounterResult(null, "count"); } else if (value.equalsIgnoreCase("countPDF")) { results[i] = new CounterResult( Utils.getIndirectValue(con, tableName, "general_export_plugin", "exportPdf"), "count_pdf"); } else if (value.equalsIgnoreCase("countDXF")) { results[i] = new CounterResult( Utils.getIndirectValue(con, tableName, "general_export_plugin", "exportDxf"), "count_dxf"); } else if (value.equalsIgnoreCase("pixel")) { results[i] = new SurfaceResult(); } else { throw new RuntimeException("Unknown value type: " + value); } } return results; } private Filter[] parseFilters(Connection con, Ini.Section section) throws SQLException { List<Filter> filterList = new ArrayList<Filter>(); for (Map.Entry<String, String> cur : section.entrySet()) { if (cur.getKey().startsWith(FILTERS_PREFIX)) { String type = cur.getKey().substring(FILTERS_PREFIX.length()); if (type.equalsIgnoreCase("scale")) { filterList.add(new DoubleRangeFilter("location_scale", cur.getValue(), type)); } else if (type.equalsIgnoreCase("project")) { filterList.add( new IdSetFilter(con, tableName, "general_mapid", cur.getValue().toLowerCase(), type)); } else if (type.equalsIgnoreCase("layer")) { filterList.add(new LayerFilter(con, tableName, cur.getValue().toLowerCase())); } else if (type.equalsIgnoreCase("width")) { filterList.add(new IntegerRangeFilter("images_mainmap_width", cur.getValue(), type)); } else if (type.equalsIgnoreCase("height")) { filterList.add(new IntegerRangeFilter("images_mainmap_height", cur.getValue(), type)); } else if (type.equalsIgnoreCase("theme")) { filterList.add(new IdSetFilter(con, tableName, "layers_switch_id", cur.getValue().toLowerCase(), type)); } else if (type.equalsIgnoreCase("user")) { filterList.add(new IdSetFilter(con, tableName, "general_security_user", cur.getValue().toLowerCase(), type)); } else if (type.equalsIgnoreCase("pdfFormat")) { filterList.add(new IdSetFilter(con, tableName, "exportpdf_format", cur.getValue(), type)); } else if (type.equalsIgnoreCase("pdfRes")) { filterList.add(new IntegerRangeFilter("exportpdf_resolution", cur.getValue(), type)); } else if (type.equalsIgnoreCase("ip")) { filterList.add(new IPFilter(cur.getValue())); } else { throw new RuntimeException("Unknown filter type: " + type); } } } Filter[] filters = new Filter[filterList.size()]; filters = filterList.toArray(filters); return filters; } private TimeScaleDefinition[] parseTimeScales(Ini.Section section) { List<TimeScaleDefinition> timeScaleList = new ArrayList<TimeScaleDefinition>(); for (Map.Entry<String, String> cur : section.entrySet()) { if (cur.getKey().startsWith(PERIODS_PREFIX)) { String type = cur.getKey().substring(PERIODS_PREFIX.length()); timeScaleList.add(new TimeScaleDefinition(type, Integer.parseInt(cur.getValue()))); } } TimeScaleDefinition[] timeScales = new TimeScaleDefinition[timeScaleList.size()]; timeScales = timeScaleList.toArray(timeScales); initTimeScales(timeScales, lastRecordDate); return timeScales; } private Classifier<?> parseType(Ini.Section section, TimeScaleDefinition[] timeScales) { final String reportType = getMandatoryField(section, "type"); final String resultTable = tableName + "_" + toDbName(section.getName()); final Classifier<?> classifier; if (reportType.equalsIgnoreCase("simple")) { classifier = new SimpleClassifier(resultTable, timeScales); } else if (reportType.equalsIgnoreCase("gridbbox") || reportType.equalsIgnoreCase("gridcenter")) { classifier = new GridClassifier(getMandatoryDoubleField(section, "minx"), getMandatoryDoubleField(section, "size"), getMandatoryIntField(section, "nx"), getMandatoryDoubleField(section, "miny"), getMandatoryDoubleField(section, "size"), getMandatoryIntField(section, "ny"), reportType.equalsIgnoreCase("gridbbox"), resultTable, timeScales); } else { throw new RuntimeException("Unknown report type: " + reportType); } return classifier; } private DimensionMetaData<?>[] parseDimensions(Ini.Section section) { final String conf = section.get("dimensions"); if (conf != null) { String[] dimensions = conf.split(",\\s*"); DimensionMetaData<?>[] dimensionMetaDatas = new DimensionMetaData<?>[dimensions.length]; for (int i = 0; i < dimensions.length; ++i) { String dimension = dimensions[i]; if (dimension.equalsIgnoreCase("project")) { dimensionMetaDatas[i] = new IntFieldMetaData("general_mapid", dimension, true); } else if (dimension.equalsIgnoreCase("user")) { dimensionMetaDatas[i] = new IntFieldMetaData("general_security_user", dimension, true); } else if (dimension.equalsIgnoreCase("scale")) { final String scales = getMandatoryField(section, "scales"); dimensionMetaDatas[i] = new RangeDoubleFieldMetaData("location_scale", scales, dimension); } else if (dimension.equalsIgnoreCase("size")) { dimensionMetaDatas[i] = new IntFieldMetaData("images_mainmap_size", dimension, false); } else if (dimension.equalsIgnoreCase("theme")) { dimensionMetaDatas[i] = new IntFieldMetaData("layers_switch_id", dimension, true); } else if (dimension.equalsIgnoreCase("layer")) { dimensionMetaDatas[i] = new LayerMetaData(); } else if (dimension.equalsIgnoreCase("pdfFormat")) { dimensionMetaDatas[i] = new IntFieldMetaData("exportpdf_format", dimension, true); } else if (dimension.equalsIgnoreCase("pdfRes")) { dimensionMetaDatas[i] = new IntFieldMetaData("exportpdf_resolution", dimension, false); } else { throw new RuntimeException("Unknown dimension type: " + dimension); } } return dimensionMetaDatas; } else { return new DimensionMetaData<?>[0]; } } private String toDbName(String value) { Matcher matcher = TRANSFORM.matcher(value); return matcher.replaceAll("_"); } private int getMandatoryIntField(Ini.Section section, String name) { final String result = getMandatoryField(section, name); return Integer.parseInt(result); } private double getMandatoryDoubleField(Ini.Section section, String name) { final String result = getMandatoryField(section, name); return Double.parseDouble(result); } private String getMandatoryField(Ini.Section section, String name) { final String result = section.get(name); if (result == null) { throw new RuntimeException("Missing mandatory field [" + name + "]"); } return result; } private static void initTimeScales(TimeScaleDefinition[] timeScales, Timestamp lastRecordDate) { for (int i = 0; i < timeScales.length; ++i) { TimeScaleDefinition timeScale = timeScales[i]; timeScale.init(lastRecordDate); } } /** * Check for reports that have been removed from the configuration file. */ private void checkDeletedReports(Connection con) throws SQLException { final Set<String> missings = new HashSet<String>(); JdbcUtilities.runSelectQuery("reading list of existing reports", "SELECT name FROM " + tableName + "_reports", con, new JdbcUtilities.SelectTask() { public void setupStatement(PreparedStatement stmt) throws SQLException { } public void run(ResultSet rs) throws SQLException { while (rs.next()) { final String name = rs.getString(1); if (!hasReport(name)) { missings.add(name); } } } }); if (missings.size() > 0) { if (!purgeOnConfigurationChange) { throw new ConfigurationChangeException( "Some reports have been deleted: " + StringUtils.join(missings, ", ")); } else { for (String missing : missings) { LOGGER.info("Report [" + missing + "] has been removed, deleting its DB content."); Utils.dropReportTables(con, tableName, missing); } } } } private void generate(Connection con) throws SQLException { long startTime = System.currentTimeMillis(); for (int i = 0; i < reports.size(); i++) { Report report = reports.get(i); try { report.init(con, tableName, purgeOnConfigurationChange); report.compute(con, tableName); } catch (ConfigurationChangeException ex) { LOGGER.warn(ex.getMessage()); LOGGER.warn("Report [" + report.getName() + "] not generated, use --purgeOnConfigurationChange to force the generation."); con.rollback(); } } con.close(); LOGGER.info("Time to generate all the reports: " + UnitUtilities.toElapsedTime(System.currentTimeMillis() - startTime)); } protected void runImpl() throws SQLException, IOException, ClassNotFoundException { Connection con = getConnection(); lastRecordDate = getLastRecordDate(con); if (lastRecordDate != null) { checkDB(con); parseIniFile(con); checkDeletedReports(con); generate(con); } else { LOGGER.warn("No data found in " + tableName); } } private void checkDB(Connection con) throws SQLException { checkDB(con, tableName); } /** * check the _reports table exists, if not, create it. Same with _dimensions. */ public static void checkDB(Connection con, final String tableName) throws SQLException { if (!Utils.doesTableExist(con, tableName + "_reports")) { LOGGER.warn("Table " + tableName + "_reports missing. Creating it."); JdbcUtilities.runDeleteQuery("creating table " + tableName + "_reports", "CREATE TABLE " + tableName + "_reports (name text, config text, last_id bigint, last_time timestamp without time zone, tables text, label text)", con, null); } if (!Utils.doesTableExist(con, tableName + "_dimensions")) { LOGGER.warn("Table " + tableName + "_dimensions missing. Creating it."); JdbcUtilities.runDeleteQuery("creating table " + tableName + "_dimensions", "CREATE TABLE " + tableName + "_dimensions (report_name text, field_name text, id int)", con, null); } } private boolean hasReport(String name) { for (int i = 0; i < reports.size(); i++) { if (reports.get(i).getName().equals(name)) { return true; } } return false; } public static void main(String[] args) { Reports reports = new Reports(args); reports.run(); } }