1 package org.lsst.ccs.localdb.statusdb;
2
3 import java.lang.reflect.Field;
4 import java.util.ArrayList;
5 import java.util.List;
6 import java.util.Map;
7 import java.util.Properties;
8 import java.util.Queue;
9 import java.util.concurrent.ConcurrentHashMap;
10 import java.util.concurrent.ConcurrentLinkedQueue;
11 import org.hibernate.LockMode;
12 import org.hibernate.Query;
13 import org.hibernate.Session;
14 import org.hibernate.SessionFactory;
15 import org.hibernate.Transaction;
16 import org.lsst.ccs.bus.BusMessage;
17 import org.lsst.ccs.bus.DataValueNotification;
18 import org.lsst.ccs.bus.EncodedDataStatus;
19 import org.lsst.ccs.bus.KVList;
20 import org.lsst.ccs.bus.KeyData;
21 import org.lsst.ccs.bus.KeyValueStatusListener;
22 import org.lsst.ccs.bus.MetadataStatus;
23 import org.lsst.ccs.bus.ObjectNType;
24 import org.lsst.ccs.bus.StatusListener;
25 import org.lsst.ccs.bus.TrendingStatus;
26 import org.lsst.ccs.bus.ValueNotification;
27 import org.lsst.ccs.bus.trending.TrendingData;
28 import org.lsst.ccs.localdb.Trending;
29 import org.lsst.ccs.localdb.statusdb.model.DataDesc;
30 import org.lsst.ccs.localdb.statusdb.model.DataMetaData;
31 import org.lsst.ccs.localdb.statusdb.model.RawData;
32 import org.lsst.ccs.localdb.statusdb.model.StatData;
33 import org.lsst.ccs.localdb.statusdb.model.StatDesc;
34 import org.lsst.ccs.localdb.statusdb.utils.StatusdbUtils;
35 import org.lsst.ccs.utilities.beanutils.Optional;
36 import org.lsst.ccs.utilities.logging.Logger;
37
38
39
40
41
42
43
44
45
46
47 public class StatusPersister implements StatusListener, KeyValueStatusListener {
48
49
50
51
52
53
54
55 static {
56
57 }
58 static Logger log = Logger.getLogger("org.lsst.ccs.localdb");
59 private static SessionFactory fac;
60
61 public static synchronized void init(Properties p) {
62 fac = StatusdbUtils.getSessionFactory(p);
63 }
64
65 Map<String, DataDesc> map = new ConcurrentHashMap<>();
66 protected DataWriter writer = new DataWriter();
67
68
69
70
71
72
73 @SuppressWarnings("unchecked")
74 public StatusPersister(Properties p) {
75 if (p == null) {
76 p = new Properties();
77 }
78 init(p);
79 log.info("Starting StatusPersister");
80 Session sess = fac.openSession();
81 List<DataDesc> l = sess.createQuery("from DataDesc").list();
82 for (DataDesc dd : l) {
83 String key = dd.getSrcSubsystem() + "/" + dd.getSrcName();
84 map.put(key, dd);
85 log.info("storing " + key);
86 }
87 sess.close();
88
89
90
91 new Thread(writer).start();
92
93
94 }
95
96
97
98 protected Queue<Object[]> rq = new ConcurrentLinkedQueue<>();
99
100 public class DataWriter implements Runnable {
101
102 @Override
103 public void run() {
104 ArrayList<Object[]> workingList = new ArrayList<>(1000);
105 long sleepTime = 1000L;
106 long lastSleep = -1L;
107
108 while (true) {
109
110
111 for (int i = 0; i < 1000; i++) {
112 Object[] d = rq.poll();
113 if (d == null) break;
114 workingList.add(d);
115 }
116
117
118
119
120 if (workingList.isEmpty()) {
121 log.info("empty list, sleeping");
122
123 try {
124 Thread.sleep(sleepTime);
125 } catch (InterruptedException e) {
126 e.printStackTrace();
127 }
128 } else {
129 log.info("batch persisting " + workingList.size()
130 + " objects");
131 Session sess = fac.openSession();
132 Transaction tx = sess.beginTransaction();
133 try {
134 for (Object[] o : workingList) {
135 if (o[0] instanceof RawData) {
136 persistData((RawData)o[0], (String)o[1], sess);
137 } else {
138 persistMetadataStatus((MetadataStatus)o[0],
139 sess);
140 }
141 }
142 } catch (Exception e) {
143 log.error(e.toString());
144 }
145 tx.commit();
146 sess.close();
147 workingList.clear();
148 }
149 }
150
151 }
152 }
153
154
155 @Override
156 public void onStatus(BusMessage s) {
157 if (s instanceof EncodedDataStatus) {
158 queueEncodedData((EncodedDataStatus)s);
159 } else if (s instanceof TrendingStatus) {
160 queueTrendingStatus((TrendingStatus)s);
161 } else if (s instanceof MetadataStatus) {
162 queueMetadataStatus((MetadataStatus)s);
163 } else {
164 log.info("can't persist message " + s + " of class " + s.getClass());
165 }
166 }
167
168
169 @Override
170 public void onKeyValueStatusDecomposition(String source, long timeStamp,
171 String key, Object value,
172 int commonID) {
173
174
175 queueImmediateScalar(timeStamp, source+'/'+key, value);
176 }
177
178
179 public void queueTrendingStatus(TrendingStatus pv) {
180 queueData(pv.getOrigin(), pv.getTimeStamp(), pv.getValue());
181 }
182
183
184
185
186
187
188 @Deprecated
189 public void queueEncodedData(EncodedDataStatus encodedDataStatus) {
190
191 for (EncodedDataStatus dataStatus : encodedDataStatus) {
192 KVList list = dataStatus.getContent();
193 for (KeyData keyData : list) {
194 long timeStamp = dataStatus.getDataTimestamp() ;
195 List<KeyData> detailsList = keyData.getContentAsList();
196 for(KeyData detaileddata : detailsList) {
197 String key = detaileddata.getKey() ;
198 Optional<Object> optional = detaileddata.getValue() ;
199 if(optional.isPresent()) {
200 queueImmediateScalar(timeStamp, key, optional.get());
201 }
202 }
203 }
204 }
205 }
206
207
208
209
210
211
212
213
214
215
216 public void queueData(String origin, long ts, Object p) {
217 if (p instanceof DataValueNotification[]) {
218 DataValueNotification[] vv = (DataValueNotification[]) p;
219 for (DataValueNotification v : vv) {
220 queueData(origin, v.gettStamp(), v);
221 }
222 } else if (p instanceof List<?> && !((List<?>) p).isEmpty()
223 && ((List<?>) p).get(0) instanceof DataValueNotification) {
224 @SuppressWarnings("unchecked")
225 List<DataValueNotification> vv = (List<DataValueNotification>) p;
226 for (DataValueNotification v : vv) {
227 queueData(origin, v.gettStamp(), v);
228 }
229
230 } else if (p instanceof DataValueNotification) {
231 DataValueNotification dvNotification = (DataValueNotification) p;
232 queueDataValueNotification(origin, dvNotification);
233 } else if (p instanceof ValueNotification[]) {
234 ValueNotification[] vv = (ValueNotification[]) p;
235 for (ValueNotification v : vv) {
236 queueData(origin, v.gettStamp(), v);
237 }
238 } else if (p instanceof List<?> && !((List<?>) p).isEmpty()
239 && ((List<?>) p).get(0) instanceof ValueNotification) {
240 @SuppressWarnings("unchecked")
241 List<ValueNotification> vv = (List<ValueNotification>) p;
242 for (ValueNotification v : vv) {
243 queueData(origin, v.gettStamp(), v);
244 }
245 } else if (p instanceof ValueNotification) {
246 ValueNotification dt = (ValueNotification) p;
247 Object data = dt.getData();
248 ts = dt.gettStamp();
249 if (data instanceof Number || data instanceof String) {
250 String key = origin + "/" + dt.getName();
251 queueImmediateScalar(ts, key, data);
252 } else {
253 Class<? extends Object> dataClass = data.getClass();
254 log.debug("will try to persist class " + dataClass);
255 boolean saveAll = dataClass.isAnnotationPresent(Trending.class);
256 Field[] fields = dataClass.getDeclaredFields();
257 for (Field f : fields) {
258 if (saveAll || f.isAnnotationPresent(Trending.class)) {
259 try {
260 log.debug("persisting field " + f.getName());
261 Object d = f.get(data);
262 String key = origin + "/" + dt.getName() + "/"
263 + f.getName();
264 queueImmediateScalar(ts, key, d);
265 } catch (IllegalArgumentException
266 | IllegalAccessException ex) {
267 log.error("pb reading field", ex);
268 }
269 }
270
271 }
272 }
273 } else {
274 if (p instanceof List) {
275 Object x = ((List) p).get(0);
276 log.info("can't persist a list of " + x + " class " + x != null ? p
277 .getClass() : "null");
278 } else {
279 log.info("can't persist " + p + " class " + p.getClass());
280 }
281
282
283
284 }
285 }
286
287
288 public void queueDataValueNotification(String origin,
289 DataValueNotification dv) {
290 if (dv.isOfWellKnownType()) {
291 String name = origin + "/" + dv.getName();
292 queueDescribedScalar(dv.gettStamp(), name, dv.getObjectNType());
293 } else {
294 Map<String, ObjectNType> mapDesc = dv.getTrendingMap();
295 if (mapDesc != null) {
296 for (Map.Entry<String, ObjectNType> entry : mapDesc.entrySet()) {
297 String name = origin + "/" + entry.getKey();
298 queueDescribedScalar(dv.gettStamp(), name, entry.getValue());
299 }
300 } else {
301 Object o = dv.getData();
302 queueObject(origin + "/" + dv.getName(), dv.gettStamp(), o);
303 }
304 }
305 }
306
307
308
309
310
311
312
313 private void queueObject(String name, long ts, Object data) {
314 Class<? extends Object> dataClass = data.getClass();
315 log.debug("will try to persist class " + dataClass);
316 boolean saveAll = dataClass.isAnnotationPresent(Trending.class);
317 Field[] fields = dataClass.getDeclaredFields();
318 for (Field f : fields) {
319 if (saveAll || f.isAnnotationPresent(Trending.class)
320 || f.isAnnotationPresent(TrendingData.class)) {
321 try {
322 log.debug("persisting field " + f.getName());
323 Object d = f.get(data);
324 String key = name + "/" + f.getName();
325 queueImmediateScalar(ts, key, d);
326 } catch (IllegalArgumentException | IllegalAccessException ex) {
327 log.error("pb reading field", ex);
328 }
329 }
330 }
331 }
332
333
334
335 public void queueDescribedScalar(long tStamp, String name,
336 ObjectNType descriptionAndValue) {
337 log.debug("got update " + name);
338
339
340 RawData data = new RawData();
341 data.setTstamp(tStamp);
342
343 String className = descriptionAndValue.getClassName();
344 boolean useString = false;
345 Object value = null;
346 try {
347 value = descriptionAndValue.getData();
348 } catch (ClassNotFoundException e) {
349
350 log.error("impossible deserialization", e);
351 return;
352 }
353 if (descriptionAndValue.isOfPrimitiveType()) {
354 if ("char".equals(className)) {
355 useString = true;
356 }
357 } else {
358 Class dataClass = null;
359 try {
360 dataClass = Class.forName(className);
361 } catch (ClassNotFoundException e) {
362 log.error("impossible deserialization", e);
363 return;
364 }
365 if (Number.class.isAssignableFrom(dataClass)) {
366 useString = false;
367 } else {
368 useString = true;
369 }
370 }
371 if (useString) {
372 data.setStringData(String.valueOf(value));
373 } else {
374 data.setDoubleData(((Number) value).doubleValue());
375 }
376 addToQueue(data, name);
377 }
378
379
380 public void queueImmediateScalar(long tStamp, String name, Object d) {
381
382 log.debug("got update " + name);
383
384 RawData data = new RawData();
385 data.setTstamp(tStamp);
386 if (d instanceof Double) {
387 data.setDoubleData((Double) d);
388 } else if (d instanceof Float) {
389 data.setDoubleData((Double) (double) (Float) d);
390 } else if (d instanceof Integer) {
391 data.setDoubleData((Double) (double) (Integer) d);
392 } else if (d instanceof Short) {
393 data.setDoubleData((Double) (double) (Short) d);
394 } else {
395
396 data.setStringData(String.valueOf(d));
397 }
398 addToQueue(data, name);
399 }
400
401
402 public void queueMetadataStatus(MetadataStatus mst) {
403 addToQueue(mst, null);
404 }
405
406
407 public void addToQueue(Object data, String name) {
408 rq.add(new Object[]{data, name});
409 }
410
411
412 private void persistData(RawData data, String name, Session sess) {
413
414 DataDesc dd = getDataDescription(name, sess);
415 if (dd == null) return;
416
417 data.setDescr(dd);
418 sess.lock(dd, LockMode.NONE);
419 sess.persist(data);
420
421 List<StatDesc> stats = dd.getDerived();
422
423 Query q = sess.createQuery("from StatData s where s.descr = :d "
424 + "order by s.tstampFirst desc");
425 q.setLockMode("s", LockMode.UPGRADE);
426 for (StatDesc stat : stats) {
427 q.setEntity("d", stat);
428 q.setMaxResults(1);
429 StatData sd = (StatData) q.uniqueResult();
430 if (sd == null) {
431 sd = new StatData(stat, data);
432 sess.persist(sd);
433 } else if (data.getTstamp() > sd.getTstampFirst()
434 + stat.getTimeBinWidth()) {
435 sd = new StatData(stat, data);
436 sess.persist(sd);
437 } else {
438 sd.accumulate(data);
439 }
440 }
441 }
442
443
444 private void persistMetadataStatus(MetadataStatus mst, Session sess) {
445
446 String dataName = mst.getOrigin() + "/" + mst.getDataName();
447 DataDesc dd = getDataDescription(dataName, sess);
448 if (dd == null) return;
449
450 sess.lock(dd, LockMode.NONE);
451
452 DataMetaData metadata = new DataMetaData();
453 metadata.setName(mst.getMetadataName());
454 metadata.setRawDescr(dd);
455 metadata.setValue(mst.getMetadataValue());
456 metadata.setTstart(mst.getTimeStamp());
457
458
459
460 Query q = sess.createQuery("from DataMetaData md "
461 + "where rawDescr_id = :id "
462 + "and name = :n and tstopmillis <= 0");
463 q.setParameter("id", dd.getId());
464 q.setParameter("n", metadata.getName());
465 DataMetaData oldMetaData = (DataMetaData) q.uniqueResult();
466 if (oldMetaData != null) {
467 oldMetaData.setTstop(metadata.getTstart());
468 sess.update(oldMetaData);
469 }
470
471 sess.persist(metadata);
472 }
473
474
475 private DataDesc getDataDescription(String key, Session sess) {
476
477 log.debug("Looking for data description in map " + map + " " + key);
478 DataDesc dd = map.get(key);
479
480
481
482
483
484
485
486 if (dd == null) {
487 log.debug("Adding default Data Description for " + key);
488 dd = new DataDesc();
489 dd.setDataType("a");
490 dd.setName(key);
491 int ind = key.indexOf("/");
492 dd.setSrcName(key.substring(ind + 1));
493 dd.setSrcSubsystem(key.substring(0, ind));
494 sess.persist(dd);
495 map.put(key, dd);
496 }
497
498 return dd;
499 }
500
501 }