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    }