/* * 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.query; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArraySet; import org.apache.felix.ipojo.annotations.Component; import org.apache.felix.ipojo.annotations.Provides; import org.apache.felix.ipojo.annotations.Requires; import org.apache.felix.ipojo.annotations.Validate; import org.krakenapps.log.api.LogParserFactoryRegistry; import org.krakenapps.logdb.DataSourceRegistry; import org.krakenapps.logdb.EmptyLogQueryCallback; import org.krakenapps.logdb.LogQuery; import org.krakenapps.logdb.LogQueryEventListener; import org.krakenapps.logdb.LogQueryParser; import org.krakenapps.logdb.LogQueryService; import org.krakenapps.logdb.LogQueryStatus; import org.krakenapps.logdb.LogScriptRegistry; import org.krakenapps.logdb.LookupHandlerRegistry; import org.krakenapps.logdb.SyntaxProvider; import org.krakenapps.logdb.query.parser.DatasourceParser; import org.krakenapps.logdb.query.parser.DropParser; import org.krakenapps.logdb.query.parser.EvalParser; import org.krakenapps.logdb.query.parser.FieldsParser; import org.krakenapps.logdb.query.parser.FulltextParser; import org.krakenapps.logdb.query.parser.FunctionParser; import org.krakenapps.logdb.query.parser.LookupParser; import org.krakenapps.logdb.query.parser.OptionCheckerParser; import org.krakenapps.logdb.query.parser.OptionParser; import org.krakenapps.logdb.query.parser.OutputCsvParser; import org.krakenapps.logdb.query.parser.RenameParser; import org.krakenapps.logdb.query.parser.ReplaceParser; import org.krakenapps.logdb.query.parser.ScriptParser; import org.krakenapps.logdb.query.parser.SearchParser; import org.krakenapps.logdb.query.parser.Sort2Parser; import org.krakenapps.logdb.query.parser.SortParser; import org.krakenapps.logdb.query.parser.Stats2Parser; import org.krakenapps.logdb.query.parser.StatsParser; import org.krakenapps.logdb.query.parser.TableParser; import org.krakenapps.logdb.query.parser.TermParser; import org.krakenapps.logdb.query.parser.TextFileParser; import org.krakenapps.logdb.query.parser.TimechartParser; import org.krakenapps.logdb.query.parser.TimechartParser2; import org.krakenapps.logdb.query.parser.ZipFileParser; import org.krakenapps.logstorage.LogIndexer; import org.krakenapps.logstorage.LogStorage; import org.krakenapps.logstorage.LogTableRegistry; import org.osgi.framework.BundleContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Component(name = "logdb-query") @Provides public class LogQueryServiceImpl implements LogQueryService { private final Logger logger = LoggerFactory.getLogger(LogQueryServiceImpl.class); @Requires private DataSourceRegistry dataSourceRegistry; @Requires private LogStorage logStorage; @Requires private LogIndexer logIndexer; @Requires private LogTableRegistry tableRegistry; @Requires private SyntaxProvider syntaxProvider; @Requires private LookupHandlerRegistry lookupRegistry; @Requires private LogScriptRegistry scriptRegistry; @Requires private LogParserFactoryRegistry parserFactoryRegistry; private BundleContext bc; private ConcurrentMap<Integer, LogQuery> queries; private CopyOnWriteArraySet<LogQueryEventListener> callbacks; public LogQueryServiceImpl(BundleContext bc) { this.bc = bc; this.queries = new ConcurrentHashMap<Integer, LogQuery>(); this.callbacks = new CopyOnWriteArraySet<LogQueryEventListener>(); } @Validate public void start() { File logdbDir = new File(System.getProperty("kraken.data.dir"), "kraken-logdb/"); if (!logdbDir.exists()) logger.info("kraken logdb: create logdb directory [{}]", logdbDir.mkdir()); File queryDir = new File(logdbDir, "query/"); if (!queryDir.exists()) logger.info("kraken logdb: create logdb query directory [{}]", queryDir.mkdir()); @SuppressWarnings("unchecked") List<Class<? extends LogQueryParser>> parserClazzes = Arrays.asList(DropParser.class, EvalParser.class, SearchParser.class, FieldsParser.class, FunctionParser.class, OptionCheckerParser.class, OptionParser.class, RenameParser.class, ReplaceParser.class, SortParser.class, StatsParser.class, TermParser.class, TimechartParser.class, Stats2Parser.class, Sort2Parser.class, TimechartParser2.class); List<LogQueryParser> parsers = new ArrayList<LogQueryParser>(); for (Class<? extends LogQueryParser> clazz : parserClazzes) { try { parsers.add(clazz.newInstance()); } catch (Exception e) { logger.error("kraken logdb: failed to add syntax: " + clazz.getSimpleName(), e); } } // add table and lookup (need some constructor injection) parsers.add(new FulltextParser(logStorage, logIndexer)); parsers.add(new DatasourceParser(dataSourceRegistry, logStorage, tableRegistry, parserFactoryRegistry)); parsers.add(new TableParser(logStorage, tableRegistry, parserFactoryRegistry)); parsers.add(new LookupParser(lookupRegistry)); parsers.add(new ScriptParser(bc, scriptRegistry)); parsers.add(new TextFileParser(parserFactoryRegistry)); parsers.add(new ZipFileParser(parserFactoryRegistry)); parsers.add(new OutputCsvParser()); syntaxProvider.addParsers(parsers); // receive log table event and register it to data source registry } @Override public LogQuery createQuery(String query) { LogQuery lq = new LogQueryImpl(syntaxProvider, query); queries.put(lq.getId(), lq); lq.registerQueryCallback(new EofReceiver(lq)); invokeCallbacks(lq, LogQueryStatus.Created); return lq; } @Override public void startQuery(int id) { LogQuery lq = getQuery(id); if (lq == null) throw new IllegalArgumentException("invalid log query id: " + id); new Thread(lq, "Log Query " + id).start(); invokeCallbacks(lq, LogQueryStatus.Started); } @Override public void removeQuery(int id) { LogQuery lq = queries.remove(id); if (lq == null) { logger.debug("kraken logdb: query [{}] not found, remove failed", id); return; } try { lq.clearTimelineCallbacks(); lq.clearQueryCallbacks(); if (!lq.isEnd()) lq.cancel(); } catch (Throwable t) { logger.error("kraken logdb: cannot cancel query " + lq, t); } try { lq.purge(); } catch (Throwable t) { logger.error("kraken logdb: cannot close file buffer list for query " + lq.getId(), t); } invokeCallbacks(lq, LogQueryStatus.Removed); } @Override public Collection<LogQuery> getQueries() { return queries.values(); } @Override public LogQuery getQuery(int id) { return queries.get(id); } @Override public void addListener(LogQueryEventListener listener) { callbacks.add(listener); } @Override public void removeListener(LogQueryEventListener listener) { callbacks.remove(listener); } private void invokeCallbacks(LogQuery lq, LogQueryStatus status) { logger.debug("kraken logdb: invoking callback to notify query [{}], status [{}]", lq.getId(), status); for (LogQueryEventListener callback : callbacks) { try { callback.onQueryStatusChange(lq, status); } catch (Exception e) { logger.warn("kraken logdb: query event listener should not throw any exception", e); } } } private class EofReceiver extends EmptyLogQueryCallback { private LogQuery query; public EofReceiver(LogQuery query) { this.query = query; } @Override public void onEof() { invokeCallbacks(query, LogQueryStatus.Eof); } } }