ExceptionHandling Library Using SpringBoot - spring-boot

I am using SpringBoot for creating Services. Now for Exception Handling (Using #COntrollerAdvice), I am trying to create a library instead of creating same files in each Service. This is my Exception Handler
#ControllerAdvice
public class CustomExceptionHandler {
private static final Logger logger = LoggerFactory.getLogger(CustomExceptionHandler.class);
/*
* This handles constraint voilation exception.
* #param exception
*/
#ExceptionHandler(ConstraintViolationException.class)
#ResponseBody
public ResponseEntity<?> handleConstraintVoilationException(ConstraintViolationException exception){
logger.info("Inside SumsExceptionHandler.handleConstraintVoilationException()");
ValidationErrorResponse validationResponse = null;
for(ConstraintViolation<?> constraintViolation : exception.getConstraintViolations()) {
validationResponse = this.makeValidationResponse(constraintViolation);
}
return new ResponseEntity<>(validationResponse, HttpStatus.BAD_REQUEST);
}
/*
* This handles MethodArgument Type Mismatch Exception.
* #param ex
*/
#ExceptionHandler(MethodArgumentTypeMismatchException.class)
#ResponseBody
public ResponseEntity<?> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException exception){
logger.info("Inside SumsExceptionHandler.handleMethodArgumentTypeMismatchException()");
ValidationErrorResponse validationResponse = new ValidationErrorResponse();
validationResponse.setField(exception.getName());
validationResponse.setCode(exception.getRequiredType().getSimpleName());
validationResponse.setMessage(exception.getValue()+" must have valid input of type "+exception.getRequiredType().getSimpleName());
return new ResponseEntity<>(validationResponse, HttpStatus.BAD_REQUEST);
}
/*
* This handles Missing Servlet Request Parameter Exception.
* #param ex
*/
#ExceptionHandler(MissingServletRequestParameterException.class)
#ResponseBody
public ResponseEntity<?> handleMissingServletRequestParameterException(MissingServletRequestParameterException exception){
logger.info("Inside SumsExceptionHandler.handleMissingServletRequestParameterException()");
ValidationErrorResponse validationResponse = new ValidationErrorResponse();
validationResponse.setField(exception.getParameterName());
validationResponse.setCode(exception.getParameterType());
validationResponse.setMessage(exception.getMessage());
return new ResponseEntity<>(validationResponse, HttpStatus.BAD_REQUEST);
}
private ValidationErrorResponse makeValidationResponse(ConstraintViolation<?> constraintViolation) {
String fieldStr = constraintViolation.getPropertyPath().toString();
String field = null;
if(fieldStr != null) {
String[] fieldArr = fieldStr.split("\\.");
field = fieldArr[fieldArr.length-1];
}
return new ValidationErrorResponse(field,
constraintViolation.getMessageTemplate(),
constraintViolation.getMessage());
}
}
a) How do I create it a stand-alone library?(This is also a springboot Project)
b) How to then use it in other Projects?

a) How do I create it a stand-alone library?(This is also a springboot Project)
Create separate project with minimum dependencies.
b) How to then use it in other Projects?
Build that project, Use that project as a library by adding jar in classpath
According to the documentation:
Spring Boot makes it easy to create stand-alone, production-grade
Spring based Applications that you can "just run".

You can also use maven to generate a new package with the required dependencies, then you can add the dependency in pom.xml. Gets a lookup on creating dependencies with Maven.

create a new Maven Java Project with version and artifact Id ...
will package it as a jar
add your needed dependencies related to all common facilities will
provide
add your facilities implementation >>>Ex #ControllerAdvice ...
in your main project will include it as a dependency

Related

My application can't find the extension with Pf4j

I'm using a Spring Boot application. For now, the use of the plugins is very simple. I'm just following the tutorial. My plugin is started, I'm trying to find the extensions like this:
final List<MyExtensionPoint> sections = pluginManager.getExtensions(MyExtensionPoint.class);
but Pf4j doesn't return the extensions.
When I'm following the code execution, I can see this code in the AbstractExtensionFinder:
if (type.isAssignableFrom(extensionClass)) {
ExtensionWrapper extensionWrapper = createExtensionWrapper(extensionClass);
result.add(extensionWrapper);
log.debug("Added extension '{}' with ordinal {}", className, extensionWrapper.getOrdinal());
} else {
log.trace("'{}' is not an extension for extension point '{}'", className, type.getName());
if (RuntimeMode.DEVELOPMENT.equals(pluginManager.getRuntimeMode())) {
checkDifferentClassLoaders(type, extensionClass);
}
}
I can understand the program is not entering inside the condition because I have 2 different classloaders: PluginClassLoader (for the extension) and RestartClassLoader (from Spring for the interface of the extension point).
I don't understand why it will be a problem because I think to instanciate the extension class, the PluginClassloader will use the parent class loader (RestartClassLoader) to find the interface.
Where is my mistake ? How to fix it ?
Thank you.
I extended the DevelopmentPluginLoader to pass the Spring classloader.
public class MyDevelopmentPluginLoader extends DevelopmentPluginLoader {
private ClassLoader parentClassLoader;
/**
* #param pluginManager
*/
public MyDevelopmentPluginLoader(final PluginManager pluginManager, final ClassLoader parentClassLoader) {
super(pluginManager);
this.parentClassLoader = parentClassLoader;
}
#Override
protected PluginClassLoader createPluginClassLoader(final Path pluginPath, final PluginDescriptor pluginDescriptor) {
return new PluginClassLoader(pluginManager, pluginDescriptor, parentClassLoader);
}
}
and in my custom PluginManager, I created and instance of my PluginLoader:
protected PluginLoader createPluginLoader() {
final CompoundPluginLoader compoundPluginLoader = new CompoundPluginLoader();
final PluginLoader developmentPluginLoader = new MyDevelopmentPluginLoader(this, getClass().getClassLoader());
final PluginLoader jarPluginLoader = new JarPluginLoader(this);
final PluginLoader defaultPluginLoader = new DefaultPluginLoader(this);
// #formatter:off
return compoundPluginLoader.
add(developmentPluginLoader, this::isDevelopment).
add(jarPluginLoader, this::isNotDevelopment).
add(defaultPluginLoader, this::isNotDevelopment);
// #formatter:on
}

How to fix issue RestControllerAdivse not working?

I have an issue related to RestControllerAdvice.
I have built an internal jar file as my own library and I implement some exception handler.
Anyway, that RestControllerAdvice is not working when have throw exception error.
RestControllerAdvice
#RestControllerAdvice
public class ApiControllerHandler {
#ExceptionHandler(ApiException.class)
public #ResponseBody
ApiResponse handleApiRequestException(ApiException e) {
ApiResponse response = new ApiResponse();
response.setCode(e.response.getCode());
response.setMessage(e.response.getMessage());
return response;
}
}
validator method
public static void request(JSONObject jsonReq, String requestKey) throws ApiException{
if (requestKey.isEmpty()) {
throw new ApiException("01", "Please input request validate key");
}
String key = jsonReq.getString(requestKey);
if (StringUtils.isEmpty(key)) {
throw new ApiException("01", requestKey + " Can not be null or empty.");
}
}
RestController
#PostMapping("/")
public String index(#RequestBody Map<String, Object> map){
JSONObject jsonObject = new JSONObject(map);
SPNValidator.request(jsonObject, "username");
return "Hello";
}
Request
{
"username" : ""
When post this request, exception will be throw because I already handled request not empty nor null
but my restControlleradvise is not working, it throws internal exception error.
Note: it works as normal if i use the same project,
but when build as jar file for other use, this function not work.
thanks.
One of the possible reason
When you build as internal JAR at that time spring dosen't know about any class in JAR so it will not search any package/Class from jar file so that's why your #RestControllerAdvice from internal JAR is not working
To solve this please use your internal JAR pacakge name in #ComponentScan
like below
#ComponentScan(basePackages = {"com.exception.base"})
so spring will inlcude ApiControllerHandler while scanning you project file.

How to mock Files.copy method in JUnit

I am using some of the methods of Files class like (delete, copy methods) to do upload and delete of file. Below is the code to perform these operations.
public String uploadFile(MultipartFile file) {
try {
String fileName = file.getOriginalFilename()
// Copy file to the target location (Replacing existing file with the same name)
Path targetLocation = Paths.get("uploadPath" + File.separator + StringUtils.cleanPath(fileName));
Files.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);
return fileName;
} catch (IOException ex) {
throw new FileStorageException("Not able to upload", ex);
}
}
But for this source code I am not able to write JUnit tests because not able to mock Files class. For mocking final classes we can use PowerMock which supports to mock static and final methods. But here if I do using PowerMock still it is not mocking. I am using Spring Framework 5.2.1.RELEASE , Is there any change in JUnit with this version to mock final classes or methods? Or can any one help me on writing the unit tests for this code (versions I am using Spring Framework 5.2.1 and JUnit4.12).
Mocking static and final classes is indeed possible only with tools like PowerMock or PowerMockito, and its not related to JUnit or Spring frameworks.
I think you should not Mock Files.copy operation.
Instead consider the following strategy:
Define an interface for working with files, a kind of DAO but for file system:
public interface FileSystemDAO {
void copy(InputStream is, Path target, StandardCopyOption ... options);
}
public class FileSystemDAOImpl implements FileSystemDAO {
void copy(InputStream is, Path target, StandatadCopyOption ... options) {
Files.copy(...)
}
}
Now use dependency injection in all the places that work with files (if you're using spring as you've said - define FileSystemDAOImpl as a bean).
class MySampleUploadService {
private final FileSystemDAO fileSystemDao;
public MySampleUploadService(FileSystemDAO dao) {
this.fileSystemDao = dao;
}
public String uploadFile(MultipartFile file) {
try {
String fileName = file.getOriginalFilename()
// Copy file to the target location (Replacing existing file with the same name)
Path targetLocation = Paths.get("uploadPath" + File.separator +
StringUtils.cleanPath(fileName));
fileSystemDao.copy(file.getInputStream(), targetLocation, StandardCopyOption.REPLACE_EXISTING);
return fileName;
} catch (IOException ex) {
throw new FileStorageException("Not able to upload", ex);
}
}
}
Now with this approach you can easily test the Upload service by mocking the FileSystemDao interface.

JBoss 5.1: Entity classes not found (vfszip)

I am using JBoss 5.1 with Hibernate 3.6, JPA 2.0 and Spring 3.0.5.
I use maven to build the EAR file which looks like :
AutoTrader.ear
-------> META-INF
--------------> application.xml
--------------> jboss-app.xml
--------------> MANIFEST.MF
-------> AutoTrader.war
if I deploy this ear file in JBoss 5.1, i get the error
org.springframework.dao.InvalidDataAccessApiUsageException: Not an entity: class uk.co.aol.shipmanager.domain.Manager; nested exception is ja
va.lang.IllegalArgumentException: Not an entity: class uk.co.aol.shipmanager.domain.Subscription
at org.springframework.orm.jpa.EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(EntityManagerFactoryUtils.java:286) ~[at_war-1.0.war:3
.0.5.RELEASE]
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:104) ~[at_war-1.0.war:3.0.5.RELEASE
]
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:368) ~[at_war-1.
0.war:3.0.5.RELEASE]
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:58
) ~[at_war-1.0.war:3.0.5.RELEASE]
However, if I deploy the war file exploded, it works fine.
Any suggestions are welcome.
Thanks,
Adi
UPDATE:
I have added a ResourceScanner which extends NativeScanner:
public class ResourceScanner extends NativeScanner {
#Override
public Set<Class<?>> getClassesInJar(final URL jarToScan,
final Set<Class<? extends Annotation>> annotationsToLookFor) {
return super.getClassesInJar(patchUrl(jarToScan), annotationsToLookFor);
}
#Override
public Set<NamedInputStream> getFilesInJar(final URL jarToScan, final Set<String> filePatterns) {
return super.getFilesInJar(patchUrl(jarToScan), filePatterns);
}
#Override
public Set<Package> getPackagesInJar(final URL jarToScan,
final Set<Class<? extends Annotation>> annotationsToLookFor) {
return super.getPackagesInJar(patchUrl(jarToScan), annotationsToLookFor);
}
#Override
public String getUnqualifiedJarName(final URL jarToScan) {
return super.getUnqualifiedJarName(patchUrl(jarToScan));
}
/**
* Patch the VFS URL to a FILE protocol URL.
*
* #param url
* original URL.
* #return either the original, either the corresponding FILE protocol of given VFS URL.
*/
protected URL patchUrl(final URL url) {
String protocol = url.getProtocol();
if (protocol.equals("vfs")) {
try {
File file = new File(url.getFile());
return file.toURI().toURL();
} catch (final MalformedURLException e) {
return url;
} catch (IOException e) {
e.printStackTrace();
return url;
}
}
return url;
}
}
and, in spring-persistence.xml,
<property name="hibernate.ejb.resource_scanner" value="uk.co.aol.shipmanager.ResourceScanner"/>
This again works in the exploded war file.
But in case of a EAR file, the protocol is vfszip not vfs.
Please tell what to do???
did you tried to to use the following system parameter and see if it helped resolved the issue?
-Dorg.jboss.net.protocol.file.useURI=false

How to copy Spring's component-scan

I want to search for some annotations in a Spring based web application, like #Entity. Therefore I need the same functionality like Spring involves when the server starts up and it looks for all classes that are annotated with #Component. In my case I don't create singleton's, it's just important for me to collect all those classes annotated with #Entity.
Is there any possibility to use existing Spring tools for that? I want to search exactly in the same namespace as Spring does for the #Component annotations.
Sure, look at parse() method in org.springframework.context.annotation.ComponentScanBeanDefinitionParser. This method is called when Spring encounters <context:component-scan/> in the XML configuration. Probably You can strip it a bit to better suit your needs, but it should serve as a comprehensive example.
The class You should be particularly interested in is org.springframework.context.annotation.ClassPathBeanDefinitionScanner. From JavaDoc:
Candidate classes are detected through configurable type filters. The default filters include classes that are annotated with Spring's #Component, #Repository, #Service, or #Controller stereotype.
BTW if you need less general solution, maybe your persistence provider has some API to fetch all entity classes?
Spring's built-in classpath scanning infrastructure (ClassPathBeanDefinitionScanner/ ComponentScanBeanDefinitionParser) is geared up for registering classes as BeanDefinitions within an Spring appcontext.
If you're just looking to obtain a list of classes annotated with a given annotation (rather than actually register them in Spring as bean definitions) take a look at the Google Reflections library.
Reflections allows you to scan your classpath using various filters, including an annotation filter.
Reflections reflections = new Reflections("my.project.prefix");
Set<Class<? extends SomeClassOrInterface>> subTypes = reflections.getSubTypesOf(SomeClassOrInterface.class);
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(SomeAnnotation.class);
Spring based solution
Use spring AnnotationTypeFilter and pass Entity.class as annotationType
using ResourcePatternResolver load all resouces(.class) under given pacakage
Use SimpleMetadataReaderFactory to get MetadataReader
for each resource you can call match on AnnotationTypeFilter using MetadataReader
metadataReader.getAnnotationMetadata().getClassName() will provide FQN of class
usage
AnnotatedClassFinder entityScanner = new AnnotatedClassFinder(Entity.class);
entityScanner.setPackages(Arrays.asList("org.myapp.domain"));
Collection<Class<?>> entities = entityScanner.findMarkedClassOfType();
public class AnnotatedClassFinder {
private static final String CLASS_RESOURCE_PATTERN = "**/*.class";
private List<String> packages;
private final ResourceLoader resourceLoader = new DefaultResourceLoader();
private final ResourcePatternResolver resourcePatternResolver = ResourcePatternUtils
.getResourcePatternResolver(resourceLoader);
private final MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory();
private final TypeFilter annotationFilter;
public AnnotatedClassFinder(final Class<? extends Annotation> annotationToScanFor) {
annotationFilter = new AnnotationTypeFilter(annotationToScanFor);
}
public Set<Class<?>> findMarkedClassOfType() {
if (packages == null) {
return new HashSet<Class<?>>();
}
final Set<Class<?>> annotatedClasses = new HashSet<Class<?>>();
try {
for (final String p : packages) {
final String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX
+ ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(p)) + "/"
+ CLASS_RESOURCE_PATTERN;
final Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
for (final Resource resource : resources) {
if (resource.isReadable()) {
final MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
if (annotationFilter.match(metadataReader, metadataReaderFactory)) {
annotatedClasses.add(Class.forName(metadataReader.getAnnotationMetadata().getClassName()));
}
}
}
}
return annotatedClasses;
} catch (final IOException ex) {
throw new RuntimeException("I/O failure during classpath scanning", ex);
} catch (final ClassNotFoundException ex) {
throw new RuntimeException("Class loading failure during classpath scanning", ex);
}
}
public void setPackages(final List<String> packages) {
this.packages = packages;
}
}

Resources