Using Loadtime weaver with third party jar class method interceptor - java

I am trying to intercept a class in third party jar from my spring boot application.
I am trying to do it as
<context:load-time-weaver aspectj-weaving="on" weaver-class="org.springframework.instrument.classloading.ReflectiveLoadTimeWeaver"/>
<bean id="instrumentationLoadTimeWeaver"
class="org.springframework.instrument.classloading.InstrumentationLoadTimeWeaver"/>
While my Aspect logger class looks like
#Aspect
public class LogInterceptor
{
private static final Logger PERF_LOGGER = LoggerFactory.getLogger("PERFLOG");
private static final Logger LOGGER = LoggerFactory.getLogger(LogInterceptor.class);
private static final int SYSTEM_ERROR_STATUS_CODE = -1;
private static final int SYSTEM_SUCCESS_STATUS_CODE = 0;
#Around(
"execution(* com.alpha.executor.dao..*(..)) || "
+ "execution(* com.alpha.executor.resource..*(..)) || "
+ "execution(* com.alpha.executor.client..*(..)) || "
+ "execution(* com.alpha.executor.service..*(..)) || "
+ "execution(* com.alpha.executor.proxy.*.*(..)) ")
public Object logRecordInPerflog(ProceedingJoinPoint joinPoint) throws Throwable
{
String activityName = joinPoint.getTarget().getClass().getSimpleName();
String operationName = activityName + "." + joinPoint.getSignature().getName();
RequestLog reqLog = new RequestLog(operationName, activityName, joinPoint.getArgs());
PERF_LOGGER.info(reqLog.getLoggerString());
try
{
Object result = joinPoint.proceed();
ResponseLog resLog = new ResponseLog(reqLog, result, SYSTEM_SUCCESS_STATUS_CODE);
PERF_LOGGER.info(resLog.getLoggerString());
return result;
}
catch (Exception ex)
{
logException(reqLog, ex);
throw ex;
}
}
private void logException(RequestLog reqLog, Exception ex)
{
ResponseLog resLog;
if (ex instanceof SvxException)
{
SvxException svxException = (SvxException) ex;
ErrorCode errorCode = svxException.getSvxExceptionDetail().getErrorCode();
ErrorResponse errorResponse = new ErrorResponse(errorCode,svxException.getSvxExceptionDetail().getErrorDescription());
resLog = new ResponseLog(reqLog,null,errorCode.getHttpStatus().value(),errorResponse,errorCode.getErrorType().toString());
}
else
{
LOGGER.error("Error: ", ex);
resLog = new ResponseLog(reqLog, null, SYSTEM_ERROR_STATUS_CODE, ErrorType.SYSTEM_ERROR.toString(), ex.getMessage());
}
PERF_LOGGER.info(resLog.getLoggerString());
}
}
However, the above code gives an error that No custom load time weaver found. I don't want to use spring-instrumentation.jar in VM args.
Also already tried the class-based method with annotation, however, read somewhere that Spring might be loading that class after some classes are loaded hence that might not work.
EDIT:
Added the logger and advice. Here I have manually created a proxy, see the last line, which isn't a good solution I believe.

Related

Mockito.doThrow() not throwing any exception

I am trying to stub a void method of a mocked object to return an exception. This mocked object passed as dependency to the service which I am writing tests for.
Service:
#Component
public class FileHandler {
private static final Logger log = LoggerFactory.getLogger(FileHandler.class);
private final SSHAccess sshAccess;
#Value("${datastore.location}")
private String dataStoreDir;
public FileHandler(SSHAccess sshAccess){
this.sshAccess = sshAccess;
}
public Either<Pair<Exception, FileRecord>, FileRecord> transferFile(FileRecord fileRecord){
try {
var sourceURI = new URI(fileRecord.getSourceURI());
var digest = sshAccess.execute("md5sum " + sourceURI.getPath())
.replaceFirst("([^\\s]+)[\\d\\D]*", "$1");
if (digest.equals(fileRecord.getSourceDigest())) {
log.info(Thread.currentThread().getName() + ": Copying file: " + fileRecord.getId() + " of submission: " + fileRecord.getOwnedBy());
sshAccess.download(sourceURI.getPath(),new File(mkdir(dataStoreDir, digest), digest));
log.info(Thread.currentThread().getName() + ": Copying of file: " + fileRecord.getId() + " of submission: " + fileRecord.getOwnedBy() + " finished.");
return Either.Right(fileRecord);
}else{
log.error("MD5 mismatch for source file {}", sourceURI.getPath());
return Either.Left(Pair.of(new FileHandlerException("MD5 mismatch"), fileRecord));
}
} catch (URISyntaxException
| IOException
e) {
return Either.Left(Pair.of(new FileHandlerException(e), fileRecord));
}
}
private File mkdir(String dataStoreDir, String digest) throws IOException {
File dir = new File(dataStoreDir, digest.substring(0, 3));
if (!dir.exists() && !dir.mkdirs()) {
log.error("Unable to create directory {}", dir);
throw new IOException("Unable to create directory " + dir);
}
return dir;
}
}
Test Class:
#SpringBootTest
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class FileHandlerTest {
private FileHandler fileHandler;
#Mock
private SSHAccess sshAccess;
#BeforeAll
public void init(){
fileHandler = Mockito.spy(new FileHandler(sshAccess));
}
#Test
public void transferFileShouldReturnFileHandlerExceptionOnEitherLeftWhenSSHclientThrowsIOException() throws IOException {
FileRecord fileRecord = getFileRecord();
var digest = "6e484ac23110fae10021e";
when(sshAccess.execute(anyString())).thenReturn(digest);
doThrow(IOException.class).when(sshAccess).download(anyString(), any(File.class));
var result = fileHandler.transferFile(fileRecord);
Assertions.assertTrue(result.getLeft().isPresent()
&& result.getLeft().get().getFirst() instanceof FileHandlerException);
}
private FileRecord getFileRecord() {
var fileRecord = new FileRecord();
fileRecord.setId(1L);
fileRecord.setOwnedBy(1000);
fileRecord.setSourceURI("scp:host/test/uri/filename");
fileRecord.setSourceDigest("6e484ac23110fae10021e");
return fileRecord;
}
}
But when I run this test case, doThrow() doesn't throw any exception. Method executed without any exception and test failed. I am not sure what I am doing wrong here. Please help.
Not sure why are you using the #SpringBootTest annotation which will try to raise a similar context with the one when you are running the app. So in this case you could stop instantiating your FileHandler and just spy on it and on your SSHAccess beans or use #MockBean instead of #Mock.
Basically you should have something like this
#SpyBean
private FileHandler fileHandler;
#MockBean
private SSHAccess sshAccess;
You are using Junit 5. For Junit 5 you don't need the method #SpringBootTest, you need to use #ExtendWith(MockitoExtension.class)
#SpringBootTest
#TestInstance(TestInstance.Lifecycle.PER_CLASS)
public class FileHandlerTest {
private FileHandler fileHandler;
#Mock
private SSHAccess sshAccess;
#BeforeAll
public void init(){
fileHandler = Mockito.spy(new FileHandler(sshAccess));
}
.....
.....
.....
}
Also instead of Mockito.spy(new FileHandler(sshAccess)) you can try Mockito.mock(new FileHandler(sshAccess))

Methods in interface not getting covered in mockito junit

I am using mockito-junit to test a piece of my code. As progressing I found out that there was an interface implemented in the main file which I was testing, when the test was running I found that the line where interface method is called get's covered but the real method doesn't get's covered.
This the code for the main file:
public class ExtractCurrencyDataTask {
private static final Logger LOGGER = LoggerFactory.getLogger(ExtractCurrencyDataTask.class);
#Autowired
private ExtractCurrencyService extractCurrencyService;
public void writeCurrencyListToFile(List<Currency> currencyList) {
if (currencyList != null && !currencyList.isEmpty()) {
String dir = "A path";
String fileName = "A filename";
String writeToFile = dir + "/" + fileName + ".writing";
String renameFile = dir + "/" + fileName + ".dat";
BufferedWriter writer = null;
FileWriter fileWriter = null;
try {
fileWriter = new FileWriter(writeToFile);
writer = new BufferedWriter(fileWriter);
extractCurrencyService.extractCurrencyList(currencyList, writer);
} catch (Exception e) {
throw new RuntimeException("Error writing Currency codes", e);
} finally {
if (writer != null) {
try {
writer.close();
fileWriter.close();
} catch (IOException e) {
LOGGER.info("Exception occured while closing the file writer", e);
}
moveFile(writeToFile, renameFile);
}
}
}
}
private void moveFile(String writeToFile, String renameFile) {
try {
FileUtils.moveFile(FileUtils.getFile(writeToFile), FileUtils.getFile(renameFile));
} catch (IOException e) {
LOGGER.info("Exception occured while moving file from writing to dat", e);
}
}
Here extractCurrencyService is the interface which I have mentioned.
The interface:
public interface ExtractCurrencyService {
public void extractCurrencyList(List<Currency> currency, Writer writer);
}
This the method definition which is done another file which implements same interface Filename:ExtractCurrencyServiceImpl.java
public class ExtractCurrencyServiceImpl implements ExtractCurrencyService {
private static final String SEP = "|";
private static final String NEWLINE = "\n";
#Override
public void extractCurrencyList(List<Currency> currencyList, Writer writer) {
if (currencyList != null) {
currencyList.forEach(currency -> {
String code = currency.getCode();
String name = currency.getName() == null ? "" : currency.getName();
Long noOfDecimals = currency.getNumberOfDecimals();
RoundingMethodValue roundingMethod = currency.getRoundingMethod();
boolean isDealCurrency = currency.isDealCurrency();
String description = currency.getDescription() == null ? "" : currency.getDescription();
try {
writer.write(createCurrencyDataLine(code,
name,
noOfDecimals,
roundingMethod,
isDealCurrency,
description));
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
}
private String createCurrencyDataLine(String code,
String name,
Long noOfDecimals,
RoundingMethodValue roundingMethod,
boolean isdealCurrency,
String description) {
return code + SEP + name + SEP + noOfDecimals.toString() + SEP + roundingMethod.toString() + SEP
+ isdealCurrency + SEP + description + NEWLINE;
}
public static <T> Predicate<T> distinctByKey(Function<? super T, Object> keyExtractor) {
Map<Object, Boolean> map = new ConcurrentHashMap<>();
return t -> map.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
}
This is the test file:
#RunWith(MockitoJUnitRunner.class)
public class ExtractCurrencyDataTaskTest {
#Mock
private Currency mockCurrency;
#Mock
private ExtractCurrencyService mockExtractCurrencyService;
#Mock
private BufferedWriter mockBufferWriter;
#Mock
private Bean mockBean;
#InjectMocks
private ExtractCurrencyDataTask extractCurrencyDataTask;
#Test
public void writeCurrencyListToFileTest() {
List<Currency> currencyList = new ArrayList();
when(mockCurrency.getCode()).thenReturn("USD");
when(mockCurrency.getNumberOfDecimals()).thenReturn((long) 2);
when(mockCurrency.getRoundingMethod()).thenReturn(enum value);
when(mockCurrency.isDealCurrency()).thenReturn(true);
when(mockCurrency.getName()).thenReturn("US Dollars");
when(mockCurrency.getDescription()).thenReturn("Currency Description");
currencyList.add(mockCurrency);
extractCurrencyDataTask.writeCurrencyListToFile(currencyList);
}
}
This the configuration for Autowired bean
#Bean
public ExtractCurrencyService extractCurrencyService() {
return new ExtractCurrencyServiceImpl();
}
As you can see the real output of this process is a file will be created in a path mentioned with some data. Here in this test I am mocking the data and passing it to main file. Main file is the created file in respective path but there is no data in the file.
The data writing part is done by the interface method. This is the part where I need help.
Thanks in advance....
You are injecting a mock of ExtractCurrencyService into your tested class. So the test is running with this mock instance instead of ExtractCurrencyServiceImpl. The current behaviour is that your ExtractCurrencyDataTasktested class is calling to extractCurrencyService#extractCurrencyList, but this extractCurrencyService is a mock, not your real implementation, so the call is done but it does nothing.
If you want to unit test ExtractCurrencyDataTask then thats ok, but maybe you should assert the call to extractCurrencyService#extractCurrencyList is done in the way you expect.
If you want to unit test ExtractCurrencyServiceImpl then create a unit test for this class.
If you want to test the interaction between these two classes then create an integration test where ExtractCurrencyDataTask has injected a real instance of ExtractCurrencyServiceImpl, not a mock.

How to test Rest API and mock url using spring boot and mockbeam

I have a Rest API
The class code is :
#SpringBootTest
#RunWith(SpringRunner.class)
public class FetchCoreVersionsListIT {
#MockBean
private RestTemplateBuilder restTemplateBuilder;
#MockBean
private RestTemplate restTemplate;
private VersionsService versionsService;
#Autowired
private FetchCoreVersionsList fetchCoreVersionsList;
private VersionList versionList;
private ArtifactoryFolderInfoChild version;
#Before
public void setUp() throws Exception {
this.version = new ArtifactoryFolderInfoChild();
this.version.setUri("2.1.0");
this.version.setFolder(true);
when(restTemplateBuilder.build()).thenReturn(restTemplate);
}
#Test
public void testCoreVersionsJsonHandle() throws Exception{
when(restTemplate.getForObject("https://openmrs.jfrog.io/openmrs/api/storage/public/org/openmrs/api/openmrs-api/",
String.class))
.thenReturn(getFileAsString("core-versions.json"));
("2.1.0"));*/
}
This is the core-versions.json . This is nothing else but the data received from this Rest API.
Basically I'm trying to run a test and I have a spring schedule that will parse the json received from that Rest url. Now, while testing the schedule, I want to return the same data but without connecting to the internet and hence want to return the contents of core-versions.json. I get the following error unfortunately :
java.lang.IllegalStateException: File downloaded from could not be parsed
My schedule class is this:
#Component
public class FetchCoreVersionsList {
private final Logger logger = LoggerFactory.getLogger(getClass());
private static final String[] STRINGS_TO_EXCLUDE = {"alpha", "beta", "RC", "SNAPSHOT"};
#Value("${core_version_list.url}")
private String url;
//#Value("${core_version_list.strategy}")
//private FetchCoreVersionsList.Strategy strategy = FetchCoreVersionsList.Strategy.FETCH;
private RestTemplateBuilder restTemplateBuilder;
private ObjectMapper mapper;
private VersionsService versionsService;
#Autowired
public FetchCoreVersionsList(RestTemplateBuilder restTemplateBuilder,
ObjectMapper mapper,
VersionsService versionsService) {
this.restTemplateBuilder = restTemplateBuilder;
this.mapper = mapper;
this.versionsService = versionsService;
}
#Scheduled(
initialDelayString = "${scheduler.fetch_core_versions_list.initial_delay}",
fixedDelayString = "${scheduler.fetch_core_versions_list.period}")
public void fetchCoreVersionsList() throws Exception {
logger.info("Fetching list of OpenMRS-Core versions");
// FetchCoreVersionsList.Strategy strategy = FetchCoreVersionsList.Strategy.FETCH;
String json;
/* if (strategy == Strategy.LOCAL) {
logger.debug("LOCAL strategy");
json = StreamUtils.copyToString(getClass().getClassLoader().getResourceAsStream("openmrs-core-versions.json"),
Charset.defaultCharset());
} else {*/
json = restTemplateBuilder.build().getForObject(url, String.class);
logger.info("FETCH strategy: " + json);
ArtifactoryFolderInfo versionlist;
try { logger.info("FETCH strategy: " + json);
logger.debug("papa strategy: " + url);
versionlist = mapper.readValue(json, ArtifactoryFolderInfo.class);
} catch (Exception ex) {
throw new IllegalStateException("File downloaded from " + url + " could not be parsed", ex);
}
if (logger.isInfoEnabled()) {
logger.info("There are " + versionlist.getChildren().size() + " openmrs-core versions");
}
if (versionlist.size() > 0) {
List<String> versions = new ArrayList<>();
List<ArtifactoryFolderInfoChild> allversions = versionlist.getChildren();
for (ArtifactoryFolderInfoChild candidateVersion : allversions) {
if (candidateVersion.getFolder() && !stringContainsItemFromList(candidateVersion.getUri(), STRINGS_TO_EXCLUDE)) {
versions.add(candidateVersion.getUri().replaceAll("/", ""));
}
}
versionsService.setVersions(versions);
} else {
logger.warn("File downloaded from " + url + " does not list any Core Versions to index. Keeping our current list");
}
}
private static boolean stringContainsItemFromList(String inputStr, String[] items) {
return Arrays.stream(items).parallel().anyMatch(inputStr::contains);
}
public enum Strategy {
FETCH, LOCAL
}
}
Kindly bear with me if this is a silly error as I am completely new to testing.

Tomcat doesn't create the "new Property()" instance

stackoverflow, please help me. I have a small web application (Servlet + jsp). Unit test passed correctly, but after deploy my Factory isn't able to create instances of my DAOs, because after action "Property property = new Property();" property = null. Why?(
public class DAOFactory <T>{
private String daoType;
private String propertyFilePath;
private Properties property;
private FileInputStream fis;
private static Logger LOGGER;
private String propertyKey;
public DAOFactory(String propertyFilePath,String propertyKey) {
this(propertyKey);
this.propertyFilePath = propertyFilePath;
}
public DAOFactory(String propertyKey) {
propertyFilePath = "src/main/resources/dao_factory.properties";
LOGGER = LoggerFactory.getLogger(DAOFactory.class);
this.propertyKey = propertyKey;
try {
property = new Properties();
fis = new FileInputStream(propertyFilePath);
property.load(fis);
} catch (FileNotFoundException ex) {
LOGGER.error("Property file " + propertyFilePath + " doesn't exist", ex);
} catch (IOException ex) {
LOGGER.error("Unable to download Property file: " + propertyFilePath, ex);
}
System.err.println("fis: " + fis);
System.err.println("propertyKey: " + propertyKey);
System.err.println("property: " + property);
daoType = property.getProperty(propertyKey);
System.err.println("daoType: " + daoType);
}
public T getInstance () throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
Class c = Class.forName(daoType);
Method method = c.getDeclaredMethod("getInstance");
return (T) method.invoke(null, null);
}
}
When I try to use my DAOFactory,
DAOFactory<BookDAO> daoFactory= new DAOFactory(""BookDAO"); // or even new DAOFactory("src/main/resources/dao_factory.properties","BookDAO");
I've got
IN TESTS
fis: java.io.FileInputStream#5025a98f
propertyKey: BookDAO
property: {BookDAO=com.softserve.siniaieva.bibliophile.dao.impl.BookDAOImitation, ReaderDAO=com.softserve.siniaieva.bibliophile.dao.impl.ReaderDAOImitation}
daoType: com.softserve.siniaieva.bibliophile.dao.impl.BookDAOImitation
WHEN TOMCAT CREATES DAOFactory
fis: null
propertyKey: BookDAO
property: null
daoType: null
Should I add smth in web.xml for Tomcat to make it see FileInputStream ?
The issue is likely to do with this
propertyFilePath = "src/main/resources/dao_factory.properties";
The src directory won't be available after packaging, and things instead will be respectively moved to the bin directory. Give something like the following a go
DAOFactory.class.getResourceAsStream("dao_factory.properties")
You can read more about this here.

How to get rid of PlaceHistoryMapper

Are there any way not to define all Places in the PlaceHistoryMapper?
At this moment I am using Generator in order to generate list of all places automatically, but I am not sure that this is a correct way.
public class AppPlaceHistoryMapper extends AbstractPlaceHistoryMapper<Object> {
#Override
protected PrefixAndToken getPrefixAndToken(Place place) {
if (place instanceof AbstractPlace) {
return new PrefixAndToken(((AbstractPlace) place).getName(), ((AbstractPlace) place).getTokenizer()
.getToken((AbstractPlace) place));
}
throw new RuntimeException("Invalid place: " + place);
}
/**
* This method will be overrided by the gwt-generated class, so any changes made in it will not be executed
*
* #see PlaceMapperGenerator
*/
#Override
protected PlaceTokenizer<?> getTokenizer(String prefix) {
AbstractPlace[] places = {/* List of places generated by PlaceMapperGenerator */};
for (AbstractPlace p : places) {
if (p.getName().equals(prefix)) {
return p.getTokenizer();
}
}
throw new RuntimeException("Unable to find place for provided prefix: " + prefix);
}
}
Generator:
public class PlaceMapperGenerator extends Generator {
// #formatter:off
private static final String GENERATED_METHOD_TEMPLATE =
"protected com.google.gwt.place.shared.PlaceTokenizer<?> getTokenizer(String prefix) {" +
"AbstractPlace[] places = { %s };" +
"for (AbstractPlace p : places) {" +
"if (p.getName().equals(prefix)) {" +
"return p.getTokenizer();" +
"}" +
"}" +
"throw new RuntimeException(\"Unable to find place for provided prefix: \" + prefix);" +
"}"
; // #formatter:on
#Override
public String generate(TreeLogger logger, GeneratorContext context, String typeName) {
JClassType type;
try {
type = context.getTypeOracle().getType(typeName);
} catch (NotFoundException e) {
throw new RuntimeException(e);
}
String implTypeName = type.getSimpleSourceName() + "Impl";
String implPackageName = type.getPackage().getName();
ClassSourceFileComposerFactory composerFactory = new ClassSourceFileComposerFactory(implPackageName,
implTypeName);
composerFactory.setSuperclass(AppPlaceHistoryMapper.class.getName());
#SuppressWarnings("resource")
PrintWriter printWriter = context.tryCreate(logger, implPackageName, implTypeName);
if (printWriter != null) {
SourceWriter sourceWriter = composerFactory.createSourceWriter(context, printWriter);
sourceWriter.print(GENERATED_METHOD_TEMPLATE, getPlaces(context));
sourceWriter.commit(logger);
printWriter.close();
}
return composerFactory.getCreatedClassName();
}
private static String getPlaces(GeneratorContext context) {
JPackage[] packages = context.getTypeOracle().getPackages();
List<String> places = new ArrayList<String>();
for (JPackage p : packages) {
if (p.getName().startsWith(AbstractPlace.class.getPackage().getName())) {
JClassType[] types = p.getTypes();
for (JClassType type : types) {
if (type.getSuperclass() != null
&& type.getSuperclass().getQualifiedSourceName().equals(AbstractPlace.class.getName())) {
places.add("new " + type.getQualifiedSourceName() + "()");
}
}
}
}
return places.toString().replaceAll("^\\[|\\]$", "");
}
}
I'm afraid that the only way to figure out what Places and Tokenizers are in your application, without maintaining a list with them, is with a generator like you are doing.
Anyway instead of maintaining a generator I would use the #WithTokenizers annotation and let GWT generate your PlaceHistoryMapper take a look to the GWT MVP dev-guide
#WithTokenizers({HelloPlace.Tokenizer.class, GoodbyePlace.Tokenizer.class})
public interface AppPlaceHistoryMapper extends PlaceHistoryMapper {}
What I do in my applications is to use a script to generate activities, views, places and update gin modules and mappers based on a template.

Categories

Resources