/* * Copyright 2013 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.araqne.logdb.query.engine; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.StringTokenizer; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; 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.araqne.logdb.FunctionRegistry; import org.araqne.logdb.QueryCommand; import org.araqne.logdb.QueryCommandParser; import org.araqne.logdb.QueryCommandPipe; import org.araqne.logdb.QueryContext; import org.araqne.logdb.QueryErrorMessage; import org.araqne.logdb.QueryParseException; import org.araqne.logdb.QueryParserService; import org.araqne.logdb.QueryStopReason; import org.araqne.logdb.query.parser.QueryTokenizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Component(name = "logdb-query-parser-service") @Provides public class QueryParserServiceImpl implements QueryParserService { private final Logger slog = LoggerFactory.getLogger(QueryParserServiceImpl.class); private ConcurrentMap<String, QueryCommandParser> commandParsers = new ConcurrentHashMap<String, QueryCommandParser>(); private ConcurrentMap<String, QueryErrorMessage> errorMappings = new ConcurrentHashMap<String, QueryErrorMessage>(); @Requires private FunctionRegistry functionRegistry; @Validate public void start() { commandParsers.clear(); errorMappings.clear(); registerBuiltinErrors(); } // support unit test public void setFunctionRegistry(FunctionRegistry functionRegistry) { this.functionRegistry = functionRegistry; } @Override public QueryCommandParser getCommandParser(String name) { return commandParsers.get(name); } @Override public List<QueryCommandParser> getCommandParsers() { return new ArrayList<QueryCommandParser>(commandParsers.values()); } @Override public List<QueryCommand> parseCommands(QueryContext context, String queryString) { List<QueryCommand> commands = new ArrayList<QueryCommand>(); int offsetCnt = 0; // try { for (String q : QueryTokenizer.parseCommands(queryString)) { q = q.trim().replaceAll("\n", " "); StringTokenizer tok = new StringTokenizer(q, " \n\t"); String commandType = tok.nextToken(); QueryCommandParser parser = commandParsers.get(commandType); if (parser == null) { // throw new QueryParseException("unsupported-command", -1, // "command is [" + commandType + "]"); Map<String, String> params = new HashMap<String, String>(); params.put("command", commandType); throw new QueryParseException("92000", -1, -1, params); } QueryCommand cmd = parser.parse(context, q); commands.add(cmd); offsetCnt++; // } } catch (QueryParseException e) { closePrematureCommands(commands); // XXX : 오프셋 위치가 정확하지 않을 수 있으니 테스트 해 볼 것! e.addOffset(offsetCnt); Locale locale = Locale.ENGLISH; if (context.getSession() != null && context.getSession().getProperty("locale") != null) locale = (Locale) context.getSession().getProperty("locale"); String errorMessage = formatErrorMessage(e.getType(), locale, e.getParams()); if (errorMessage == null) errorMessage = e.getNote(); throw new QueryParseException(e.getType(), e.getStartOffset(), errorMessage, e); } catch (Throwable t) { closePrematureCommands(commands); slog.debug("QueryParserServiceImpl", t); throw new QueryParseException("parse failure", -1, t.toString(), t); } if (commands.isEmpty()) throw new IllegalArgumentException("empty query"); for (int i = 0; i < commands.size(); i++) { QueryCommand command = commands.get(i); if (i < commands.size() - 1) command.setOutput(new QueryCommandPipe(commands.get(i + 1))); } return commands; } private void closePrematureCommands(List<QueryCommand> commands) { for (QueryCommand cmd : commands) { try { slog.debug("araqne logdb: parse failed, closing command [{}]", cmd.toString()); cmd.tryClose(QueryStopReason.CommandFailure); } catch (Throwable t2) { slog.error("araqne logdb: cannot close command", t2); } } } @Override public String formatErrorMessage(String errorCode, Locale locale, Map<String, String> params) { QueryErrorMessage m = errorMappings.get(errorCode); if (m == null) return null; return m.format(locale, params); } @Override public void addCommandParser(QueryCommandParser parser) { parser.setQueryParserService(this); commandParsers.putIfAbsent(parser.getCommandName(), parser); for (Entry<String, QueryErrorMessage> e : parser.getErrorMessages().entrySet()) { errorMappings.put(e.getKey(), e.getValue()); } } @Override public void removeCommandParser(QueryCommandParser parser) { for (Entry<String, QueryErrorMessage> e : parser.getErrorMessages().entrySet()) { errorMappings.remove(e.getKey(), parser); } commandParsers.remove(parser.getCommandName(), parser); } @Override public FunctionRegistry getFunctionRegistry() { return functionRegistry; } private void registerBuiltinErrors() { /* QueryTokenizer */ add("90000", "option-space-not-allowed", "옵션과 '=' 사이에 공백은 허용되지 않습니다."); add("90001", "invalid-option", "[option]은 지원하지 않는 옵션입니다."); add("90002", "string-quote-mismatch", "\"의 짝이 맞지 않습니다."); add("90003", "empty-command", "쿼리가 없습니다."); add("90004", "need-string-token", "입력된 쿼리가 없습니다."); add("90005", "string-quote-mismatch", "\"의 짝이 맞지 않습니다."); /* EvalOpEmitterFactory */ add("90100", "broken-expression", "잘못된 표현식입니다."); add("90101", "unsupported operator", "[op]은 지원하지 않는 연산자입니다."); /* ExpressionParser */ add("90200", "unexpected-term", "쿼리가 없습니다."); add("90201", "remain-terms", "잘못된 쿼리 입니다."); add("90202", "parens-mismatch", "잘못된 쿼리 입니다."); add("90203", "quote-mismatch", "\"가 짝이 맞지 않습니다."); add("90204", "sqbracket-mismatch", "'['가 짝이 맞지 않습니다."); add("90205", "invalid-escape-sequence", "잘못된 이스케이프 문자입니다.([escape])"); /* MetadataMatcher */ add("90300", "broken-expression", "쿼리가 없습니다."); /* Strings */ add("90400", "invalid-escape-sequence", "[char] ]지원하지 않는 이스케이프 문자입니다."); /* TimeSpan */ add("90500", "invalid-timespan", "mon 은 12의 소인수(1,2,3,4,6)만 사용가능합니다."); add("90501", "year should be 1", "y 앞에는 1만 사용가능합니다."); add("90502", "invalid time unit", "시간 단위는 y, mon, m, w, d, m, s 중 하나만 사용 가능합니다."); /* Abs */ add("90600", "invalid-abs-args", "abs의 매개변수는 하나여야만 합니다."); /* ContextReference */ add("90610", "null-context-reference", "값이 입력되지 않았습니다."); add("90611", "null-context-reference", "null 값이 입력되었습니다."); /* dateAdd */ add("90620", "invalid-dateadd-args", "올바르지 않은 dateadd 매개변수입니다."); add("90621", "invalid-dateadd-calendar-field", "[field]는 잘못된 유형입니다."); add("90622", "invalid-dateadd-delta-type", "[time]는 잘못된 시간입니다."); /* DateDiff */ add("90630", "invalid-datediff-args", "올바르지 않은 datediff 매개변수입니다."); add("90631", "invalid-datediff-unit", "[field]는 잘못된 유형입니다."); /* DateTrunc */ add("90640", "invalid-datetrunc-args", "올바르지 않은 datetrunc 매개변수입니다."); /* Decrypt */ add("90650", "insufficient-decrypt-args", "올바르지 않은 decrypt 매개변수입니다."); add("90651", "invalid-cipher-algorithm", "[algorithm]은 지원하지 않는 암호화 알고리즘 입니다."); /* Encrypt */ add("90660", "insufficient-encrypt-args", "올바르지 않은 encrypt 매개변수입니다."); add("90661", "invalid-cipher-algorithm", "[algorithm]은 지원하지 않는 암호화 알고리즘 입니다."); /* Field */ add("90670", "missing-field-name", "필드 이름을 입력하십시오."); /* FromBase64 */ add("90680", "frombase64-arg-missing", "변환할 문자열을 입력하십시오."); /* Hash */ add("90690", "missing-hash-algorithm", "해시 알고리즘을 입력하십시오."); add("90691", "missing-hash-data", "바이너리 표현식을 입력하십시오."); add("90692", "unsupported-hash", "[algorithm]은 지원하지 않는 해시 알고리즘 입니다."); /* In */ // add("90700", "insufficient-arguments", "올바르지 않은 입력 형식입니다."); - 99000 // 으로 대체 /* Ip2Long */ // add("90710", "invalid-ip2long-args", "올바르지 않은 ip2long 매개변수입니다."); - // 99000으로 대체 /* Left */ add("90720", "left-func-negative-length", "길이는 0보다 커야 합니다.(입력값 : [length])"); /* Right */ add("90721", "right-func-negative-length", "길이는 0보다 커야 합니다.(입력값 : [length])"); /* Long2Ip */ // add("90730", "invalid-long2ip-args", "올바르지 않은 long2ip 매개변수입니다."); - // 99000으로 대체 /* Network */ add("90740", "invalid-mask", "CIDR 값이 올바르지 않습니다.(입력값: [mask])"); /* Rand */ add("90750", "invalid-rand-argument", "경계값은 숫자여야 합니다.(입력값: [bound])"); add("90751", "rand-bound-should-be-positive", "경계값은 양수여야 합니다.(입력값: [bound])"); /* RandBytes */ add("90760", "invalid-rand-argument", "길이는 숫자여야 합니다.(입력값: [length])"); add("90761", "invalid-randbytes-len", "길이는 0보다 크거나 1000000보다 작아야 합니다.(입력값: [length])"); /* Split */ // add("90770", "missing-split-args", "올바르지 않은 split 매개변수입니다."); - 99000 // 으로 // 대체 add("90771", "invalid-delimiters", "올바르지 않은 구분자입니다.([exception])"); add("90772", "empty-delimiters", "구분자가 없습니다."); /* StrJoin */ add("90780", "nvalid-strjoin-args", "올바르지 않은 구분자입니다.([exception])"); add("90781", "strjoin-require-constant-separator", "strjoin 구분자는 상수이어야 합니다."); /* Substr */ // add("90790", "invalid-substr-range", // "시작 위치는 0보다 커야합니다(입력값 : [begin])"); // add("90791", "invalid-substr-range", // "끝위치([end])는 시작위치(begin])보다 커야합니다."); /* ToBase64 */ // add("90800", "tobase64-arg-missing", "올바르지 않는 입력 형식입니다."); - 99000 으로 // 대체 /* ToBinary */ // add("90810", "missing-data", "올바르지 않는 입력 형식입니다."); - 99000 으로 대체 add("90811", "unsupported-charset", "[charset]은 지원하지 않는 인코딩입니다."); /* ToDate */ add("90820", "invalid date format pattern", "날짜변환에 실패하였습니다.([exception])"); /* ToInt */ add("90830", "invalid-argument radix should be 10", "radix는 10이여야 합니다..(입력값: [radix])"); /* ToLong */ add("90840", "invalid-argument radix should be 10", "radix는 10이여야 합니다..(입력값: [radix])"); /* UrlDecode */ add("90850", "invalid-charset", "[charset]은 지원하지 않는 인코딩입니다."); /* ValueOf */ // add("90860", "insufficient-valueof-args", "올바르지 않는 입력 형식입니다."); - // 99000 으로 대체 /* Values */ add("90870", "missing-values-arg", "올바르지 않은 입력 형식입니다."); /* DatePart */ add("90880", "invalid-datepart-args", "datepart 매개변수 개수가 일치하지 않습니다."); add("90881", "invalid-datepart-type", "datepart에서 지원하지 않는 날짜 구성요소입니다."); /* Count */ add("91010", "invalid-count-args", "올바르지 않은 입력 형식입니다."); /* First */ add("91020", "invalid-parameter-count", "올바르지 않은 입력 형식입니다."); /* Format */ add("91030", "invalid-format-string", "[exp]은 문자열 형식이 아닙니다."); /* FunctionExpression */ add("99000", "[name]-arg-missing([args]-[min])", "[name]의 매개변수는 [min] 개 이상이여야 합니다.( [args] 개 입력 됨)"); /* MetadataServiceImpl */ add("95000", "invalid-system-object-type, type=[type]", "[type]는 잘못된 타입입니다."); /* LoggerMetadataProvider */ add("95010", "no-read-permission", "읽기 권한이 없습니다."); add("95011", "logger-load-fail", "로거를 읽어오는데 실패했습니다.(msg:[msg])"); /* LogMetadataProvider */ add("95020", "no-read-permission", "읽기 권한이 없습니다."); /* MetadataQueryStringParser */ add("95030", "invalid-from", "from 값이 유효하지 않습니다."); add("95031", "invalid-to", "to 값이 유효하지 않습니다."); add("95032", "invalid-date-rangen", "from 값이 유효하지 않습니다.(from:[from])"); /* ThreadMetadataProvider */ add("95040", "no-read-permission", "읽기 권한이 없습니다."); /* TopThreadMetadataProvider */ add("95050", "no-read-permission", "읽기 권한이 없습니다."); add("95051", "topthread-not-supported", "자바가상머신이 스레드 CPU 사용량 측정을 지원하지 않습니다."); /* FunctionRegistryImpl */ add("90900", "unsupported-function", "[function] 은 지원하지 않는 함수입니다."); add("90901", "cannot create function instance", "[function] 함수 오류: [msg]."); add("90902", "cannot create function instance", "[function] 함수 오류 : [msg]."); add("90903", "no-read-permission", "[funtion] 함수 읽기 권한이 없습니다."); /* Unsupported Command Type */ add("92000", "unsupported-command", "지원하지 않는 명령어: [command]"); add("92001", "unauthorized-table", "[nodeName]:[tableName] 테이블 읽기 권한이 없습니다."); add("92002", "node-not-exist", "[nodeName]이 존재하지 않습니다."); add("92003", "table-not-exist", "[nodeName]:[tableName]이 존재하지 않습니다."); } private void add(String code, String en, String ko) { errorMappings.put(code, new QueryErrorMessage(en, ko)); } @Override public Map<String, QueryErrorMessage> getErrorMessages() { return new HashMap<String, QueryErrorMessage>(errorMappings); } }