001 /* 002 @license.text@ 003 */ 004 package biz.hammurapi.metrics.persistent; 005 006 import java.sql.Connection; 007 import java.sql.SQLException; 008 import java.util.Collection; 009 import java.util.HashMap; 010 import java.util.Iterator; 011 import java.util.Map; 012 import java.util.StringTokenizer; 013 014 import biz.hammurapi.convert.CompositeConverter; 015 import biz.hammurapi.metrics.HousekeepingSliceConsumer; 016 import biz.hammurapi.metrics.Slice; 017 import biz.hammurapi.sql.IdentityGenerator; 018 import biz.hammurapi.sql.IdentityManager; 019 import biz.hammurapi.sql.IdentityRetriever; 020 import biz.hammurapi.sql.SQLProcessor; 021 022 023 /** 024 * @author Pavel Vlasov 025 * 026 * @version $Revision: 1.2 $ 027 */ 028 public class PersistingSliceConsumer implements HousekeepingSliceConsumer { 029 public static final long SECOND=1000; 030 public static final long MINUTE=60*SECOND; 031 public static final long HOUR=60*MINUTE; 032 public static final long DAY=24*HOUR; 033 public static final long WEEK=1000; 034 public static final long MONTH=1000; 035 public static final long YEAR=1000; 036 037 private SQLProcessor processor; 038 protected boolean isActive=false; 039 protected Number rootId; 040 private long history; 041 private long maintenanceInterval; 042 private IdentityManager identityManager; 043 044 045 /** 046 * @param tick 047 * @param keepMeasurements 048 * @throws SQLException 049 */ 050 public PersistingSliceConsumer( 051 String category, 052 long history, 053 long maintenanceInterval, 054 SQLProcessor processor, 055 IdentityManager identityManager) throws SQLException { 056 this.processor=processor; 057 if (identityManager==null) { 058 this.identityManager=new IdentityGenerator() { 059 060 public int generate(Connection con, String name) throws SQLException { 061 return PersistingSliceConsumer.this.processor.nextPK(con, "PRIMARY_KEY", name); 062 } 063 064 }; 065 } else { 066 this.identityManager=identityManager; 067 } 068 this.history=history; 069 this.maintenanceInterval=maintenanceInterval; 070 if (category!=null) { 071 Connection con=processor.getConnection(); 072 boolean cac=con.getAutoCommit(); 073 try { 074 con.setAutoCommit(false); 075 MetricsEngine tEngine=new MetricsEngine(new SQLProcessor(con, processor.getNameMap())); 076 StringTokenizer st=new StringTokenizer(category, "."); 077 while (st.hasMoreTokens()) { 078 String token=st.nextToken(); 079 Number newRoot; 080 if (rootId==null) { 081 newRoot=tEngine.getRootMetricId(token); 082 } else { 083 newRoot=tEngine.getMetricId(rootId.intValue(), token); 084 } 085 086 if (newRoot==null) { 087 MetricImpl metric=new MetricImpl(true); 088 metric.setName(token); 089 if (rootId!=null) { 090 metric.setParent((Integer) CompositeConverter.getDefaultConverter().convert(rootId, Integer.class, false)); 091 } 092 093 insertMetric(con, tEngine, metric); 094 insertKindred(tEngine, rootId, metric.getId()); 095 newRoot=new Integer(metric.getId()); 096 } 097 098 rootId=newRoot; 099 } 100 con.commit(); 101 } catch (SQLException e) { 102 con.rollback(); 103 throw e; 104 } finally { 105 con.setAutoCommit(cac); 106 processor.releaseConnection(con); 107 } 108 } 109 isActive=true; 110 } 111 112 /** 113 * @param con 114 * @param tEngine 115 * @param metric 116 * @throws SQLException 117 */ 118 private void insertMetric(Connection con, MetricsEngine tEngine, MetricImpl metric) throws SQLException { 119 if (this.identityManager instanceof IdentityGenerator) { 120 metric.setId(((IdentityGenerator) this.identityManager).generate(con, "METRIC")); 121 } 122 tEngine.insertMetric(metric); 123 if (this.identityManager instanceof IdentityRetriever) { 124 metric.setId(((IdentityRetriever) this.identityManager).retrieve(con)); 125 } 126 } 127 128 /** 129 * Breaks metric name into individual tokens. E.g. <code>biz.hammurapi.sql</code> could be 130 * broken into <code>com, pavelvlasov</code>, and <code>sql</code> 131 * Default implementation does nothing - it returns <code>new String[] {name}</code> 132 * @return metric name broken into individual elements 133 */ 134 protected String[] tokenize(String name) { 135 return new String[] {name}; 136 } 137 138 private Map categoryMap=new HashMap(); 139 140 private Number getCategoryId(Connection con, MetricsEngine engine, String category) throws SQLException { 141 if (category==null || category.length()==0) { 142 return rootId; 143 } 144 145 Number ret = (Number) categoryMap.get(category); 146 if (ret==null) { 147 int idx=category.lastIndexOf('.'); 148 if (idx==-1) { 149 ret = rootId==null ? engine.getRootMetricId(category) : engine.getMetricId(rootId.intValue(), category); 150 if (ret==null) { 151 MetricImpl metric=new MetricImpl(true); 152 metric.setName(category); 153 if (rootId!=null) { 154 metric.setParent((Integer) CompositeConverter.getDefaultConverter().convert(rootId, Integer.class, false)); 155 } 156 insertMetric(con, engine, metric); 157 insertKindred(engine, metric.getParent(), metric.getId()); 158 ret=new Integer(metric.getId()); 159 } 160 } else { 161 Number parentId = getCategoryId(con, engine, category.substring(0, idx)); 162 MetricImpl metric=new MetricImpl(true); 163 metric.setName(category.substring(idx+1)); 164 metric.setParent((Integer) CompositeConverter.getDefaultConverter().convert(parentId, Integer.class, false)); 165 insertMetric(con, engine, metric); 166 insertKindred(engine, parentId, metric.getId()); 167 ret=new Integer(metric.getId()); 168 } 169 categoryMap.put(category, ret); 170 } 171 172 return ret; 173 } 174 175 /** 176 * @param parentId 177 * @param metricId 178 * @param engine 179 * @throws SQLException 180 */ 181 private void insertKindred(MetricsEngine engine, Number parent, int metricId) throws SQLException { 182 engine.insertKindred(metricId, metricId); 183 if (parent!=null) { 184 engine.newRelative(metricId,parent.intValue()); 185 } 186 } 187 188 public boolean consumeSlice(String category, Slice slice) { 189 if (isActive) { 190 try { 191 Connection con=processor.getConnection(); 192 boolean cac=con.getAutoCommit(); 193 try { 194 con.setAutoCommit(false); 195 MetricsEngine tEngine=new MetricsEngine(new SQLProcessor(con, processor.getNameMap())); 196 Number metricId=getCategoryId(con, tEngine, category); 197 String[] tokens=tokenize(slice.getName()==null ? "(null)" : slice.getName()); 198 for (int i=0; i<tokens.length; i++) { 199 Number newMetricId = metricId==null ? tEngine.getRootMetricId(tokens[i]) : tEngine.getMetricId(metricId.intValue(), tokens[i]); 200 if (newMetricId==null) { 201 MetricImpl metric=new MetricImpl(true); 202 metric.setName(tokens[i]); 203 metric.setParent((Integer) CompositeConverter.getDefaultConverter().convert(metricId, Integer.class, false)); 204 insertMetric(con, tEngine, metric); 205 insertKindred(tEngine, metricId, metric.getId()); 206 newMetricId=new Integer(metric.getId()); 207 } 208 metricId=newMetricId; 209 } 210 211 SliceImpl simpl=new SliceImpl(true); 212 213 simpl.setMetricId(metricId.intValue()); 214 simpl.setTimeFrom(slice.getFrom()); 215 simpl.setTimeTo(slice.getTo()); 216 simpl.setMeasurements(slice.getNumber()); 217 simpl.setTotal(slice.getTotal()); 218 simpl.setMinValue(slice.getMin()); 219 simpl.setMaxValue(slice.getMax()); 220 simpl.setDeviation(slice.getDeviation()); 221 222 if (this.identityManager instanceof IdentityGenerator) { 223 simpl.setId(((IdentityGenerator) this.identityManager).generate(con, "SLICE")); 224 } 225 tEngine.insertSlice(simpl); 226 if (this.identityManager instanceof IdentityRetriever) { 227 simpl.setId(((IdentityRetriever) this.identityManager).retrieve(con)); 228 } 229 230 Collection measurements = slice.getMeasurements(); 231 if (measurements!=null && !measurements.isEmpty()) { 232 Iterator it=measurements.iterator(); 233 while (it.hasNext()) { 234 biz.hammurapi.metrics.Metric.Measurement m=(biz.hammurapi.metrics.Metric.Measurement) it.next(); 235 Measurement dm=new MeasurementImpl(); 236 dm.setMeasurementTime(m.getTime()); 237 dm.setMeasurementValue(m.getValue()); 238 dm.setSliceId(simpl.getId()); 239 if (this.identityManager instanceof IdentityGenerator) { 240 dm.setId(((IdentityGenerator) this.identityManager).generate(con, "MEASUREMENT")); 241 } 242 tEngine.insertMeasurement(dm); 243 } 244 } 245 246 con.commit(); 247 return true; 248 } catch (SQLException e) { 249 con.rollback(); 250 throw e; 251 } finally { 252 con.setAutoCommit(cac); 253 processor.releaseConnection(con); 254 } 255 } catch (SQLException e) { 256 System.err.println("WARN ["+getClass().getName()+"] Exception - deactivated."); 257 isActive=false; 258 e.printStackTrace(); 259 return false; 260 } 261 } 262 263 return true; 264 } 265 266 private long lastMaintenance; 267 268 public void onTick(long now) { 269 if (isActive && maintenanceInterval>0 && history>0 && lastMaintenance<now-maintenanceInterval) { 270 lastMaintenance=now; 271 try { 272 new MetricsEngine(processor).deleteSliceTimeToLE(now-history); 273 } catch (SQLException e) { 274 System.err.println("WARN ["+getClass().getName()+"] Exception - deactivated."); 275 isActive=false; 276 e.printStackTrace(); 277 } 278 } 279 } 280 281 public Number getRootId() { 282 return rootId; 283 } 284 285 }