/* * Copyright 2011 Future Systems * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.krakenapps.logdb.msgbus; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.apache.felix.ipojo.annotations.Component; import org.apache.felix.ipojo.annotations.Requires; import org.krakenapps.logdb.DataSource; import org.krakenapps.logdb.DataSourceRegistry; import org.krakenapps.logdb.LogQuery; import org.krakenapps.logdb.LogQueryCallback; import org.krakenapps.logdb.LogQueryService; import org.krakenapps.logdb.LogTimelineCallback; import org.krakenapps.logdb.impl.LogQueryHelper; import org.krakenapps.logstorage.Log; import org.krakenapps.logstorage.LogRestoreService; import org.krakenapps.logstorage.LogStorage; import org.krakenapps.logstorage.LogTableRegistry; import org.krakenapps.msgbus.MsgbusException; import org.krakenapps.msgbus.PushApi; import org.krakenapps.msgbus.Request; import org.krakenapps.msgbus.Response; import org.krakenapps.msgbus.Session; import org.krakenapps.msgbus.handler.CallbackType; import org.krakenapps.msgbus.handler.MsgbusMethod; import org.krakenapps.msgbus.handler.MsgbusPlugin; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Component(name = "logdb-msgbus") @MsgbusPlugin public class LogQueryPlugin { private final Logger logger = LoggerFactory.getLogger(LogQueryPlugin.class.getName()); @Requires private LogQueryService service; @Requires private LogTableRegistry tableRegistry; @Requires private LogStorage storage; @Requires private LogRestoreService logRestore; @Requires private DataSourceRegistry dataSourceRegistry; @Requires private PushApi pushApi; private ConcurrentMap<Session, List<LogQuery>> queries = new ConcurrentHashMap<Session, List<LogQuery>>(); @MsgbusMethod public void logs(Request req, Response resp) { String tableName = req.getString("table"); int limit = req.getInteger("limit"); int offset = 0; if (req.has("offset")) offset = req.getInteger("offset"); if (!tableRegistry.exists(tableName)) throw new MsgbusException("logdb", "table-not-exists"); Collection<Log> logs = storage.getLogs(tableName, null, null, offset, limit); List<Object> serialized = new ArrayList<Object>(limit); for (Log log : logs) serialized.add(serialize(log)); resp.put("logs", serialized); } private Map<String, Object> serialize(Log log) { Map<String, Object> m = new HashMap<String, Object>(); m.put("table", log.getTableName()); m.put("id", log.getId()); m.put("date", log.getDate()); m.put("data", log.getData()); return m; } @MsgbusMethod public void getDataSources(Request req, Response resp) { List<Object> result = new ArrayList<Object>(); for (DataSource dataSource : dataSourceRegistry.getAll()) { Map<String, Object> m = new HashMap<String, Object>(); m.put("name", dataSource.getName()); m.put("type", dataSource.getType()); m.put("node_guid", dataSource.getNodeGuid()); m.put("metadata", dataSource.getMetadata()); result.add(m); } resp.put("sources", result); } @MsgbusMethod public void queries(Request req, Response resp) { List<Object> result = LogQueryHelper.getQueries(service); resp.put("queries", result); } @MsgbusMethod public void createQuery(Request req, Response resp) { try { LogQuery query = service.createQuery(req.getString("query")); resp.put("id", query.getId()); // for query cancellation at session close Session session = req.getSession(); queries.putIfAbsent(session, new ArrayList<LogQuery>()); List<LogQuery> l = queries.get(session); synchronized (l) { l.add(query); } } catch (Exception e) { logger.error("kraken logdb: cannot create query", e); logRestore.restoreByDelete(); throw new MsgbusException("logdb", e.getMessage()); } } @MsgbusMethod public void removeQuery(Request req, Response resp) { int id = req.getInteger("id"); LogQuery target = null; List<LogQuery> l = queries.get(req.getSession()); if (l == null) { logger.debug("kraken logdb: remove target query not found for session [{}]", req.getSession()); return; } synchronized (l) { for (LogQuery q : l) if (q.getId() == id) target = q; if (target != null) { l.remove(target); logger.debug("kraken logdb: removing query [{}] from session [{}]", target.getId(), req.getSession()); } } service.removeQuery(id); } @MsgbusMethod public void startQuery(Request req, Response resp) { String orgDomain = req.getOrgDomain(); int id = req.getInteger("id"); int offset = req.getInteger("offset"); int limit = req.getInteger("limit"); Integer timelineLimit = req.getInteger("timeline_limit"); LogQuery query = service.getQuery(id); // validation check if (query == null) throw new MsgbusException("logdb", "query not found"); if (!query.isEnd()) throw new MsgbusException("logdb", "already running"); // set query and timeline callback LogQueryCallback qc = new MsgbusLogQueryCallback(orgDomain, query, offset, limit); query.registerQueryCallback(qc); if (timelineLimit != null) { int size = timelineLimit.intValue(); LogTimelineCallback tc = new MsgbusTimelineCallback(orgDomain, query, size); query.registerTimelineCallback(tc); } // start query service.startQuery(query.getId()); } @MsgbusMethod public void stopQuery(Request req, Response resp) { int id = req.getInteger("id"); LogQuery query = service.getQuery(id); if (query != null) query.cancel(); else throw new MsgbusException("logdb", "query-not-found"); } @MsgbusMethod public void getResult(Request req, Response resp) throws IOException { int id = req.getInteger("id"); int offset = req.getInteger("offset"); int limit = req.getInteger("limit"); Map<String, Object> m = LogQueryHelper.getResultData(service, id, offset, limit); if (m != null) resp.putAll(m); } @MsgbusMethod(type = CallbackType.SessionClosed) public void sessionClosed(Session session) { List<LogQuery> q = queries.get(session); if (q != null) { for (LogQuery lq : q) { lq.cancel(); service.removeQuery(lq.getId()); } } queries.remove(session); } private class MsgbusLogQueryCallback implements LogQueryCallback { private String orgDomain; private LogQuery query; private int offset; private int limit; private MsgbusLogQueryCallback(String orgDomain, LogQuery query, int offset, int limit) { this.orgDomain = orgDomain; this.query = query; this.offset = offset; this.limit = limit; } @Override public int offset() { return offset; } @Override public int limit() { return limit; } @Override public void onQueryStatusChange() { try { Map<String, Object> m = new HashMap<String, Object>(); m.put("id", query.getId()); m.put("type", "status_change"); m.put("count", query.getResultCount()); pushApi.push(orgDomain, "logdb-query-" + query.getId(), m); pushApi.push(orgDomain, "logstorage-query-" + query.getId(), m); // deprecated } catch (IOException e) { logger.error("kraken logdb: msgbus push fail", e); } } @Override public void onPageLoaded() { try { Map<String, Object> m = LogQueryHelper.getResultData(service, query.getId(), offset, limit); m.put("id", query.getId()); m.put("type", "page_loaded"); pushApi.push(orgDomain, "logdb-query-" + query.getId(), m); pushApi.push(orgDomain, "logstorage-query-" + query.getId(), m); // deprecated } catch (IOException e) { logger.error("kraken logdb: msgbus push fail", e); } } @Override public void onEof() { try { Map<String, Object> m = new HashMap<String, Object>(); m.put("id", query.getId()); m.put("type", "eof"); m.put("total_count", query.getResultCount()); pushApi.push(orgDomain, "logdb-query-" + query.getId(), m); pushApi.push(orgDomain, "logstorage-query-" + query.getId(), m); // deprecated query.unregisterQueryCallback(this); } catch (IOException e) { logger.error("kraken logdb: msgbus push fail", e); } } } private class MsgbusTimelineCallback extends LogTimelineCallback { private Logger logger = LoggerFactory.getLogger(MsgbusTimelineCallback.class); private String orgDomain; private LogQuery query; private int size; private MsgbusTimelineCallback(String orgDomain, LogQuery query) { this(orgDomain, query, 10); } private MsgbusTimelineCallback(String orgDomain, LogQuery query, int size) { this.orgDomain = orgDomain; this.query = query; this.size = size; } @Override public int getSize() { return size; } @Override protected void callback(Date beginTime, SpanValue spanValue, int[] values, boolean isEnd) { try { Map<String, Object> m = new HashMap<String, Object>(); m.put("id", query.getId()); m.put("type", isEnd ? "eof" : "periodic"); m.put("span_field", spanValue.getFieldName()); m.put("span_amount", spanValue.getAmount()); m.put("begin", beginTime); m.put("values", values); pushApi.push(orgDomain, "logdb-query-timeline-" + query.getId(), m); m.put("count", query.getResultCount()); pushApi.push(orgDomain, "logstorage-query-timeline-" + query.getId(), m); // deprecated Object[] trace = new Object[] { query.getId(), spanValue.getFieldName(), spanValue.getAmount(), beginTime, Arrays.toString(values), query.getResultCount() }; logger.trace("kraken logdb: timeline callback => " + "{id={}, span_field={}, span_amount={}, begin={}, values={}, count={}}", trace); } catch (IOException e) { logger.error("kraken logdb: msgbus push fail", e); } } } }