Search in sources :

Example 1 with BaseHandler

use of io.datarouter.web.handler.BaseHandler in project datarouter by hotpads.

the class ExceptionHandlingFilter method tryRecordExceptionAndRequestNotification.

private Optional<ExceptionRecordKey> tryRecordExceptionAndRequestNotification(HttpServletRequest request, Throwable exception) {
    try {
        String callOrigin;
        Optional<Class<? extends BaseHandler>> handlerClass = RequestAttributeTool.get(request, BaseHandler.HANDLER_CLASS);
        Optional<Method> handlerMethod = RequestAttributeTool.get(request, BaseHandler.HANDLER_METHOD);
        if (handlerClass.isPresent() && handlerMethod.isPresent()) {
            callOrigin = handlerClass.get().getName() + "." + handlerMethod.get().getName();
        } else {
            callOrigin = null;
        }
        String methodName = null;
        String name = null;
        String type = null;
        Pair<String, Integer> pair = searchJspName(exception);
        String location = pair.getLeft();
        Integer lineNumber = pair.getRight();
        if (location == null) {
            ExceptionRecorderDetails details = exceptionDetailsDetector.detect(exception, callOrigin, webSettings.stackTraceHighlights.get());
            location = details.className;
            methodName = details.methodName;
            name = details.parsedName;
            type = details.type;
            lineNumber = details.lineNumber;
        }
        ExceptionRecordDto exceptionRecord = exceptionRecorder.recordExceptionAndHttpRequest(exception, location, methodName, name, type, lineNumber, request, callOrigin);
        return Optional.of(new ExceptionRecordKey(exceptionRecord.id));
    } catch (Exception e) {
        logger.error("Exception while logging", e);
        return Optional.empty();
    }
}
Also used : ExceptionRecorderDetails(io.datarouter.exception.utils.ExceptionDetailsDetector.ExceptionRecorderDetails) BaseHandler(io.datarouter.web.handler.BaseHandler) Method(java.lang.reflect.Method) ExceptionRecordKey(io.datarouter.exception.storage.exceptionrecord.ExceptionRecordKey) ExceptionRecordDto(io.datarouter.instrumentation.exception.ExceptionRecordDto) IOException(java.io.IOException)

Example 2 with BaseHandler

use of io.datarouter.web.handler.BaseHandler in project datarouter by hotpads.

the class TraceFilter method doFilter.

@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain fc) throws IOException, ServletException {
    try {
        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;
        // get or create TraceContext
        Long traceCreated = Trace2Dto.getCurrentTimeInNs();
        String traceparentStr = request.getHeader(DatarouterHttpClientIoExceptionCircuitBreaker.TRACEPARENT);
        String tracestateStr = request.getHeader(DatarouterHttpClientIoExceptionCircuitBreaker.TRACESTATE);
        W3TraceContext traceContext = new W3TraceContext(traceparentStr, tracestateStr, traceCreated);
        String initialParentId = traceContext.getTraceparent().parentId;
        traceContext.updateParentIdAndAddTracestateMember();
        RequestAttributeTool.set(request, BaseHandler.TRACE_URL_REQUEST_ATTRIBUTE, urlBuilder.buildTraceForCurrentServer(traceContext.getTraceId(), traceContext.getParentId()));
        RequestAttributeTool.set(request, BaseHandler.TRACE_CONTEXT, traceContext.copy());
        // traceflag might be incorrect because it might have been modified during request processing
        if (traceSettings.addTraceparentHeader.get()) {
            response.setHeader(DatarouterHttpClientIoExceptionCircuitBreaker.TRACEPARENT, traceContext.getTraceparent().toString());
        }
        // bind these to all threads, even if tracing is disabled
        Tracer tracer = new DatarouterTracer(serverName.get(), null, traceContext);
        tracer.setSaveThreadCpuTime(traceSettings.saveThreadCpuTime.get());
        tracer.setSaveThreadMemoryAllocated(traceSettings.saveThreadMemoryAllocated.get());
        tracer.setSaveSpanCpuTime(traceSettings.saveSpanCpuTime.get());
        tracer.setSaveSpanMemoryAllocated(traceSettings.saveSpanMemoryAllocated.get());
        TracerThreadLocal.bindToThread(tracer);
        String requestThreadName = (request.getContextPath() + " request").trim();
        tracer.createAndStartThread(requestThreadName, Trace2Dto.getCurrentTimeInNs());
        Long threadId = Thread.currentThread().getId();
        boolean saveCpuTime = traceSettings.saveTraceCpuTime.get();
        Long cpuTimeBegin = saveCpuTime ? MxBeans.THREAD.getCurrentThreadCpuTime() : null;
        boolean saveAllocatedBytes = traceSettings.saveTraceAllocatedBytes.get();
        Long threadAllocatedBytesBegin = saveAllocatedBytes ? MxBeans.THREAD.getThreadAllocatedBytes(threadId) : null;
        boolean errored = false;
        try {
            fc.doFilter(req, res);
        } catch (Exception e) {
            errored = true;
            throw e;
        } finally {
            long ended = Trace2Dto.getCurrentTimeInNs();
            Long cpuTimeEnded = saveCpuTime ? MxBeans.THREAD.getCurrentThreadCpuTime() : null;
            Long threadAllocatedBytesEnded = saveAllocatedBytes ? MxBeans.THREAD.getThreadAllocatedBytes(threadId) : null;
            Traceparent traceparent = tracer.getTraceContext().get().getTraceparent();
            Trace2ThreadDto rootThread = null;
            if (tracer.getCurrentThreadId() != null) {
                rootThread = ((DatarouterTracer) tracer).getCurrentThread();
                rootThread.setCpuTimeEndedNs(cpuTimeEnded);
                rootThread.setMemoryAllocatedBytesEnded(threadAllocatedBytesEnded);
                rootThread.setEnded(ended);
                ((DatarouterTracer) tracer).setCurrentThread(null);
            }
            Trace2Dto trace2 = new Trace2Dto(traceparent, initialParentId, request.getContextPath(), request.getRequestURI().toString(), request.getQueryString(), traceCreated, ended, serviceName.get(), tracer.getDiscardedThreadCount(), tracer.getThreadQueue().size(), cpuTimeBegin, cpuTimeEnded, threadAllocatedBytesBegin, threadAllocatedBytesEnded);
            Long traceDurationMs = trace2.getDurationInMs();
            Long cpuTime = saveCpuTime ? TimeUnit.NANOSECONDS.toMillis(cpuTimeEnded - cpuTimeBegin) : null;
            Long threadAllocatedKB = saveAllocatedBytes ? (threadAllocatedBytesEnded - threadAllocatedBytesBegin) / 1024 : null;
            String saveReason = null;
            if (traceSettings.saveTraces.get()) {
                if (traceDurationMs > traceSettings.saveTracesOverMs.get()) {
                    saveReason = "duration";
                }
                if (RequestTool.getBoolean(request, "trace", false)) {
                    saveReason = "queryParam";
                }
                if (tracer.shouldSample()) {
                    saveReason = "traceContext";
                }
                if (errored) {
                    saveReason = "error";
                }
            }
            if (saveReason != null) {
                Counters.inc("traceSaved " + saveReason);
                List<Trace2ThreadDto> threads = new ArrayList<>(tracer.getThreadQueue());
                List<Trace2SpanDto> spans = new ArrayList<>(tracer.getSpanQueue());
                if (rootThread != null) {
                    rootThread.setTotalSpanCount(spans.size());
                    // force to save the rootThread even though the queue could be full
                    threads.add(rootThread);
                }
                String userAgent = RequestTool.getUserAgent(request);
                String userToken = currentSessionInfo.getSession(request).map(Session::getUserToken).orElse("unknown");
                HttpRequestRecordDto httpRequestRecord = buildHttpRequestRecord(errored, request, traceCreated, userToken, traceparent);
                String destination = offerTrace2(new Trace2BundleDto(trace2, threads, spans), httpRequestRecord);
                logger.warn("Trace saved to={} traceparent={} initialParentId={} durationMs={}" + " cpuTimeMs={} threadAllocatedKB={} path={} query={} userAgent=\"{}\" userToken={}", destination, traceparent, initialParentId, traceDurationMs, cpuTime, threadAllocatedKB, trace2.type, trace2.params, userAgent, userToken);
            } else if (traceDurationMs > traceSettings.logTracesOverMs.get() || TracerTool.shouldLog()) {
                // only log once
                logger.warn("Trace logged durationMs={} cpuTimeMs={} threadAllocatedKB={} path={}" + " query={}, traceparent={}", traceDurationMs, cpuTime, threadAllocatedKB, trace2.type, trace2.params, traceparent);
            }
            Optional<Class<? extends BaseHandler>> handlerClassOpt = RequestAttributeTool.get(request, BaseHandler.HANDLER_CLASS);
            Optional<Method> handlerMethodOpt = RequestAttributeTool.get(request, BaseHandler.HANDLER_METHOD);
            if (handlerClassOpt.isPresent() && handlerMethodOpt.isPresent()) {
                Class<? extends BaseHandler> handlerClass = handlerClassOpt.get();
                if (traceSettings.latencyRecordedHandlers.get().contains(handlerClass.getName())) {
                    handlerMetrics.saveMethodLatency(handlerClass, handlerMethodOpt.get(), traceDurationMs);
                }
            }
        }
    } finally {
        TracerThreadLocal.clearFromThread();
    }
}
Also used : DatarouterTracer(io.datarouter.util.tracer.DatarouterTracer) ArrayList(java.util.ArrayList) Trace2ThreadDto(io.datarouter.instrumentation.trace.Trace2ThreadDto) Traceparent(io.datarouter.instrumentation.trace.Traceparent) HttpServletRequest(javax.servlet.http.HttpServletRequest) Tracer(io.datarouter.instrumentation.trace.Tracer) DatarouterTracer(io.datarouter.util.tracer.DatarouterTracer) HttpServletResponse(javax.servlet.http.HttpServletResponse) W3TraceContext(io.datarouter.instrumentation.trace.W3TraceContext) Method(java.lang.reflect.Method) ServletException(javax.servlet.ServletException) IOException(java.io.IOException) Trace2SpanDto(io.datarouter.instrumentation.trace.Trace2SpanDto) BaseHandler(io.datarouter.web.handler.BaseHandler) HttpRequestRecordDto(io.datarouter.instrumentation.exception.HttpRequestRecordDto) Trace2BundleAndHttpRequestRecordDto(io.datarouter.instrumentation.trace.Trace2BundleAndHttpRequestRecordDto) Trace2BundleDto(io.datarouter.instrumentation.trace.Trace2BundleDto) Trace2Dto(io.datarouter.instrumentation.trace.Trace2Dto)

Example 3 with BaseHandler

use of io.datarouter.web.handler.BaseHandler in project datarouter by hotpads.

the class Dispatcher method handleRequestIfUrlMatch.

public RoutingResult handleRequestIfUrlMatch(HttpServletRequest request, HttpServletResponse response, BaseRouteSet routeSet) throws ServletException, IOException {
    String uri = request.getRequestURI();
    if (!uri.startsWith(servletContext.get().getContextPath() + routeSet.getUrlPrefix())) {
        return RoutingResult.NOT_FOUND;
    }
    BaseHandler handler = null;
    String afterContextPath = uri.substring(servletContext.get().getContextPath().length());
    if (afterContextPath.contains(JSESSIONID_PATH_PARAM)) {
        afterContextPath = afterContextPath.substring(0, afterContextPath.indexOf(JSESSIONID_PATH_PARAM));
    }
    for (DispatchRule rule : routeSet.getDispatchRules()) {
        if (rule.getPattern().matcher(afterContextPath).matches()) {
            SecurityValidationResult securityCheckResult = rule.applySecurityValidation(request);
            request = securityCheckResult.getWrappedRequest();
            if (!securityCheckResult.isSuccess()) {
                injector.getInstance(rule.getDefaultHandlerEncoder()).sendForbiddenResponse(request, response, securityCheckResult);
                return RoutingResult.FORBIDDEN;
            }
            if (authenticationConfig.useDatarouterAuthentication() && !rule.checkRoles(request)) {
                handleMissingRoles(request, response, rule.getAllowedRoles());
                return RoutingResult.ROUTED;
            }
            handler = injector.getInstance(rule.getHandlerClass());
            handler.setDefaultHandlerEncoder(rule.getDefaultHandlerEncoder());
            handler.setDefaultHandlerDecoder(rule.getDefaultHandlerDecoder());
            RequestAttributeTool.set(request, BaseHandler.HANDLER_ENCODER_ATTRIBUTE, injector.getInstance(rule.getDefaultHandlerEncoder()));
            RequestAttributeTool.set(request, TRANSMITS_PII, rule.doesTransmitPii());
            if (rule.hasApiKey()) {
                // TODO avoid re evaluating the rule
                Pair<Boolean, String> apiKeyPredicateExistsWithName = rule.getApiKeyPredicate().check(rule, request);
                if (apiKeyPredicateExistsWithName.getLeft()) {
                    handler.setAccountName(apiKeyPredicateExistsWithName.getRight());
                }
            }
            break;
        }
    }
    Class<? extends BaseHandler> defaultHandlerClass = routeSet.getDefaultHandlerClass();
    if (handler == null) {
        if (defaultHandlerClass == null) {
            return RoutingResult.NOT_FOUND;
        }
        handler = injector.getInstance(defaultHandlerClass);
    }
    handler.setRequest(request);
    handler.setResponse(response);
    handler.setServletContext(servletContext.get());
    Params params = parseParams(request, handler.getDefaultMultipartCharset());
    handler.setParams(params);
    handler.handleWrapper();
    return RoutingResult.ROUTED;
}
Also used : SecurityValidationResult(io.datarouter.web.security.SecurityValidationResult) Params(io.datarouter.web.handler.params.Params) MultipartParams(io.datarouter.web.handler.params.MultipartParams) BaseHandler(io.datarouter.web.handler.BaseHandler)

Example 4 with BaseHandler

use of io.datarouter.web.handler.BaseHandler in project datarouter by hotpads.

the class ApiDocService method buildEndpointDocumentation.

private List<DocumentedEndpointJspDto> buildEndpointDocumentation(DispatchRule rule) {
    List<DocumentedEndpointJspDto> endpoints = new ArrayList<>();
    Class<? extends BaseHandler> handler = rule.getHandlerClass();
    while (handler != null && !handler.getName().equals(BaseHandler.class.getName())) {
        for (Method method : handler.getDeclaredMethods()) {
            if (!method.isAnnotationPresent(Handler.class)) {
                continue;
            }
            Handler handlerAnnotation = method.getAnnotation(Handler.class);
            String url = rule.getPattern().pattern();
            if (!url.contains(BaseRouteSet.REGEX_ONE_DIRECTORY)) {
                // not a handleDir
                if (!url.endsWith(method.getName()) && !handlerAnnotation.defaultHandler()) {
                    continue;
                }
            }
            if (url.contains(BaseRouteSet.REGEX_ONE_DIRECTORY)) {
                String urlSuffix = handlerAnnotation.defaultHandler() ? "" : "/" + method.getName();
                url = url.replace(BaseRouteSet.REGEX_ONE_DIRECTORY, "") + urlSuffix;
            }
            String implementation = handler.getSimpleName();
            List<DocumentedParameterJspDto> parameters = new ArrayList<>();
            String description = handlerAnnotation.description();
            if (description.isEmpty()) {
                if (EndpointTool.paramIsEndpointObject(method)) {
                    @SuppressWarnings("unchecked") Class<BaseEndpoint<?, ?>> baseEndpoint = (Class<BaseEndpoint<?, ?>>) method.getParameters()[0].getType();
                    Endpoint annotation = baseEndpoint.getAnnotation(Endpoint.class);
                    if (annotation != null) {
                        description = annotation.description();
                    }
                }
            }
            DocumentedSecurityDetails securityDetails = createApplicableSecurityParameters(rule);
            parameters.addAll(securityDetails.parameters);
            parameters.addAll(createMethodParameters(method));
            Type responseType = method.getGenericReturnType();
            String responseExample;
            Set<DocumentedExampleEnumDto> responseExampleEnumDtos = new HashSet<>();
            if (responseType == Void.TYPE) {
                responseExample = null;
            } else {
                try {
                    DocumentedExampleDto responseObject = createBestExample(responseType, new HashSet<>());
                    responseExampleEnumDtos = responseObject.exampleEnumDtos;
                    responseExample = GSON.toJson(responseObject.exampleObject);
                } catch (Exception e) {
                    responseExample = "Impossible to render";
                    logger.warn("Could not create response example for {}", responseType, e);
                }
            }
            String responseTypeString = buildTypeString(responseType);
            var response = new DocumentedResponseJspDto(responseTypeString, responseExample, buildEnumValuesString(responseExampleEnumDtos));
            boolean isDeprecated = method.isAnnotationPresent(Deprecated.class) || handler.isAnnotationPresent(Deprecated.class);
            List<DocumentedErrorJspDto> errors = buildError(method);
            Set<DocumentedExampleEnumDto> requestParamExampleEnumDtos = Scanner.of(parameters).concatIter(parameter -> parameter.exampleEnumDtos).collect(Collectors.toSet());
            var endpoint = new DocumentedEndpointJspDto(url, implementation, parameters, securityDetails.apiKeyFieldName, description, response, isDeprecated, errors, buildEnumValuesString(requestParamExampleEnumDtos));
            endpoints.add(endpoint);
        }
        handler = handler.getSuperclass().asSubclass(BaseHandler.class);
    }
    return endpoints;
}
Also used : JsonObject(com.google.gson.JsonObject) Scanner(io.datarouter.scanner.Scanner) Array(java.lang.reflect.Array) Endpoint(io.datarouter.httpclient.endpoint.Endpoint) BaseRouteSet(io.datarouter.web.dispatcher.BaseRouteSet) Date(java.util.Date) DocumentedGenericHolder(io.datarouter.httpclient.DocumentedGenericHolder) LoggerFactory(org.slf4j.LoggerFactory) ReflectionTool(io.datarouter.util.lang.ReflectionTool) GsonBuilder(com.google.gson.GsonBuilder) DispatchRule(io.datarouter.web.dispatcher.DispatchRule) Gson(com.google.gson.Gson) OptionalParameter(io.datarouter.web.handler.types.optional.OptionalParameter) Map(java.util.Map) LocalTime(java.time.LocalTime) IgnoredField(io.datarouter.httpclient.endpoint.IgnoredField) Method(java.lang.reflect.Method) CompatibleDateTypeAdapter(io.datarouter.gson.serialization.CompatibleDateTypeAdapter) OptionalScanner(io.datarouter.scanner.OptionalScanner) Collection(java.util.Collection) Set(java.util.Set) EndpointParam(io.datarouter.httpclient.endpoint.EndpointParam) Collectors(java.util.stream.Collectors) List(java.util.List) JsonArray(com.google.gson.JsonArray) BaseEndpoint(io.datarouter.httpclient.endpoint.BaseEndpoint) Type(java.lang.reflect.Type) Modifier(java.lang.reflect.Modifier) Optional(java.util.Optional) SortedMap(java.util.SortedMap) WildcardType(java.lang.reflect.WildcardType) LocalDateTime(java.time.LocalDateTime) RequestBody(io.datarouter.web.handler.types.RequestBody) Singleton(javax.inject.Singleton) ArrayList(java.util.ArrayList) HashSet(java.util.HashSet) SecurityParameters(io.datarouter.httpclient.security.SecurityParameters) Parameter(java.lang.reflect.Parameter) Handler(io.datarouter.web.handler.BaseHandler.Handler) Param(io.datarouter.web.handler.types.Param) Logger(org.slf4j.Logger) TypeVariable(java.lang.reflect.TypeVariable) Field(java.lang.reflect.Field) EndpointRequestBody(io.datarouter.httpclient.endpoint.EndpointRequestBody) ParameterizedType(java.lang.reflect.ParameterizedType) TreeMap(java.util.TreeMap) ParamDefaultEnum(io.datarouter.web.handler.types.ParamDefaultEnum) DocumentedExampleEnumDto(io.datarouter.web.handler.documentation.DocumentedExampleDto.DocumentedExampleEnumDto) EndpointTool(io.datarouter.httpclient.endpoint.EndpointTool) BaseHandler(io.datarouter.web.handler.BaseHandler) Comparator(java.util.Comparator) Collections(java.util.Collections) ArrayList(java.util.ArrayList) Endpoint(io.datarouter.httpclient.endpoint.Endpoint) BaseEndpoint(io.datarouter.httpclient.endpoint.BaseEndpoint) HashSet(java.util.HashSet) Handler(io.datarouter.web.handler.BaseHandler.Handler) BaseHandler(io.datarouter.web.handler.BaseHandler) Method(java.lang.reflect.Method) Type(java.lang.reflect.Type) WildcardType(java.lang.reflect.WildcardType) ParameterizedType(java.lang.reflect.ParameterizedType) BaseEndpoint(io.datarouter.httpclient.endpoint.BaseEndpoint) DocumentedExampleEnumDto(io.datarouter.web.handler.documentation.DocumentedExampleDto.DocumentedExampleEnumDto) BaseHandler(io.datarouter.web.handler.BaseHandler)

Aggregations

BaseHandler (io.datarouter.web.handler.BaseHandler)4 Method (java.lang.reflect.Method)3 ArrayList (java.util.ArrayList)2 Gson (com.google.gson.Gson)1 GsonBuilder (com.google.gson.GsonBuilder)1 JsonArray (com.google.gson.JsonArray)1 JsonObject (com.google.gson.JsonObject)1 ExceptionRecordKey (io.datarouter.exception.storage.exceptionrecord.ExceptionRecordKey)1 ExceptionRecorderDetails (io.datarouter.exception.utils.ExceptionDetailsDetector.ExceptionRecorderDetails)1 CompatibleDateTypeAdapter (io.datarouter.gson.serialization.CompatibleDateTypeAdapter)1 DocumentedGenericHolder (io.datarouter.httpclient.DocumentedGenericHolder)1 BaseEndpoint (io.datarouter.httpclient.endpoint.BaseEndpoint)1 Endpoint (io.datarouter.httpclient.endpoint.Endpoint)1 EndpointParam (io.datarouter.httpclient.endpoint.EndpointParam)1 EndpointRequestBody (io.datarouter.httpclient.endpoint.EndpointRequestBody)1 EndpointTool (io.datarouter.httpclient.endpoint.EndpointTool)1 IgnoredField (io.datarouter.httpclient.endpoint.IgnoredField)1 SecurityParameters (io.datarouter.httpclient.security.SecurityParameters)1 ExceptionRecordDto (io.datarouter.instrumentation.exception.ExceptionRecordDto)1 HttpRequestRecordDto (io.datarouter.instrumentation.exception.HttpRequestRecordDto)1