All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.datarouter.exception.utils.ExceptionDetailsDetector Maven / Gradle / Ivy

The newest version!
/*
 * Copyright © 2009 HotPads ([email protected])
 *
 * 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 io.datarouter.exception.utils;

import java.util.List;
import java.util.Optional;
import java.util.Set;

import io.datarouter.exception.utils.nameparser.ExceptionNameParser;
import io.datarouter.exception.utils.nameparser.ExceptionNameParserRegistry;
import io.datarouter.exception.utils.nameparser.ExceptionSnapshot;
import io.datarouter.exception.utils.nameparser.ExceptionSnapshot.ExceptionCauseSnapshot;
import io.datarouter.exception.utils.nameparser.ExceptionSnapshot.StackTraceElementSnapshot;
import io.datarouter.inject.DatarouterInjector;
import io.datarouter.scanner.Scanner;
import jakarta.inject.Inject;
import jakarta.inject.Singleton;

@Singleton
public class ExceptionDetailsDetector{

	@Inject
	private ExceptionNameParserRegistry registry;
	@Inject
	private DatarouterInjector injector;

	public ExceptionRecorderDetails detect(Throwable wholeException, String callOrigin, Set highlights){
		return detect(new ExceptionSnapshot(wholeException), callOrigin, highlights);
	}

	public ExceptionRecorderDetails detect(ExceptionSnapshot snapshot, String callOrigin, Set highlights){
		Optional rootCause = snapshot.getRootCause();
		Optional parsedName = Optional.empty();
		Optional parserAndCauseOpt = Scanner.of(registry
				.getNameParserClasses())
				.map(injector::getInstance)
				.map(ExceptionNameParser.class::cast)
				.concatOpt(parser -> parser.getCausesFromType(snapshot)
						.map(cause -> new ExceptionParserAndOutput(parser, cause)))
				.findFirst();
		if(parserAndCauseOpt.isPresent()){
			var parserAndCause = parserAndCauseOpt.get();
			rootCause = Optional.of(parserAndCause.output().getFirst());
			ExceptionNameParser exceptionWithParser = parserAndCause.parser();
			parsedName = exceptionWithParser.parseExceptionName(parserAndCause.output());
		}
		ExceptionCauseSnapshot exception = rootCause
				.orElse(new ExceptionCauseSnapshot(null, null, null, List.of()));
		Optional stackTraceElement = searchClassName(exception, highlights);
		if(stackTraceElement.isEmpty()){
			stackTraceElement = exception.stackTraces.stream()
					.findFirst();
		}
		return new ExceptionRecorderDetails(exception, stackTraceElement, parsedName, callOrigin);
	}

	private static Optional searchClassName(ExceptionCauseSnapshot cause,
			Set highlights){
		return Scanner.of(cause.stackTraces)
			.include(element -> Scanner.of(highlights)
					.anyMatch(element.declaringClass::contains))
			.findFirst();
	}

	public record ExceptionParserAndOutput(
			ExceptionNameParser parser,
			List output){
	}

	public static class ExceptionRecorderDetails{

		public final String className;
		public final String methodName;
		public final String type;
		public final int lineNumber;
		public final String detailsMessage;
		public final String parsedName;

		private ExceptionRecorderDetails(ExceptionCauseSnapshot rootCause, Optional element,
				Optional parsedName, String callOrigin){
			this.className = element.map(e -> e.declaringClass).orElse("noClass");
			this.methodName = element.map(e -> e.methodName).orElse("noMethod");
			this.type = rootCause.typeName;
			this.lineNumber = element.map(e -> e.lineNumber).orElse(0);
			this.detailsMessage = rootCause.detailMessage;
			this.parsedName = parsedName.orElse(getDefaultName(type, className, callOrigin));
		}

		public static String getDefaultName(String type, String className, String callOrigin){
			callOrigin = Optional.ofNullable(callOrigin).orElse(className);
			return String.format("%s at %s in %s", getSimpleClassName(type), getSimpleClassName(className),
					getSimpleClassName(callOrigin));
		}

		private static String getSimpleClassName(String fullClassName){
			return Optional.ofNullable(fullClassName)
					.map(ExceptionRecorderDetails::parseName)
					.orElse("");
		}

		private static String parseName(String fullClassName){
			String[] tokens = fullClassName.split("\\.");
			if(tokens.length < 2){
				return fullClassName;
			}else if(Character.isUpperCase(tokens[tokens.length - 1].charAt(0))){
				return tokens[tokens.length - 1];
			}else{
				return tokens[tokens.length - 2] + "." + tokens[tokens.length - 1];
			}
		}
	}
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy