Why is everything inside a BOOT-INF folder and how to get rid of it in case it's breaking my classpath? - spring-boot

I am getting a classic well-known error in my Springboot app when it tries to load a static resource using getResourceAsStream():
java.io.FileNotFoundException: class path resource [blah blah blah] cannot be opened because it does not exist
While doing some basic troubleshooting, I opened the contents of the generated jar file and found that everything in the jar is contained inside a folder named BOOT-INF.
Why is everything inside BOOT-INF?
How do I get rid of this?
UPDATE
Here's the actual relevant part of the code
ClassPathResource cpr = new ClassPathResource(RESOURCE_CLASSPATH_PREFIX + url);
try (InputStream is = cpr.getInputStream()) {
return DigestUtils.sha1Hex(is);
} catch (IOException e) {
throw new RuntimeException(e);
}
Additional Clue
The failing code is inside a ServletFilter. I think this has something to do with class loaders.

Related

FileAlreadyExistsException for a deleted directory

For the purpose of making a backup, I copy a directory with tests scripts to another location. For this I'm using Files.walkFileTree(fromDir, SimpleFileVisitor).
This works as expected and I can copy it multiple times without any problems.
But when I delete the target directory via Windows File explorer I get a java.nio.file.FileAlreadyExistsException on the target directory when I try to make a back up.
I'm pretty sure that the target directory is not there.
When I close the Java program and start it again, it works again. It is as if manually deleting the directory locks the 'non-existing' target directory and Java throws a FileAlreadyExistsException.
Here are the relevant parts of the SimpleFileVisitor
I'm using these options when copying the files
StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES, LinkOption.NOFOLLOW_LINKS
#Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException
{
Path copyDir = toDir.resolve(fromDir.relativize(dir));
try {
Files.createDirectories(copyDir);
}
catch(IOException ioe) {
LOG.error("Failed to create directory " + copyDir.toString());
throw ioe;
}
return FileVisitResult.CONTINUE;
}
#Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException
{
Path toFile = toDir.resolve(fromDir.relativize(file));
try {
Files.copy(file, toFile, opts);
}
catch(IOException ioe) {
LOG.error("Failed to create file " + toFile.toString());
throw ioe;
}
return FileVisitResult.CONTINUE;
}
I was expecting that deleting the target directory via Windows File Explorer would not cause this problem, but apparently it does.
Is there anything I could do to make the copy action possible in case someone deletes the directory via Windows File Explorer.

PropertiesLoaderUtils.loadAllProperties is not able to load the property file from src/main/resources

I am using PropertiesLoaderUtils.loadAllProperties(abc.properties);
and have placed abc.properties in src/main/resources folder but still it's not able to get this property file.
Code that we tried ::
try {
props = PropertiesLoaderUtils.loadAllProperties("abc.properties");
} catch (IOException e) {
logger.error("Unable to load file", e);
}
Can someone help me out with the possible cause of not picking up abc.properties whereas if I change it to application.properties, its able to load.

How to create a permanent directory for my files with spring boot

I am working with spring boot and spring content. I want to store all my pictures and videos in one directory but my code continues to create different dir every time I rerun the application
I have such bean and when I run the app again it shows null pointer because the dir already exists but I want it to create it just once and every file is stored there
every time i run this tries to create the dir again
#Bean
File filesystemRoot() {
try {
return Files.createDirectory(Paths.get("/tmp/photo_video_myram")).toFile();
} catch (IOException io) {}
return null;
}
#Bean
FileSystemResourceLoader fileSystemResourceLoader() {
return new FileSystemResourceLoader(filesystemRoot().getAbsolutePath());
}
One solution, would be to check if the directory exists:
#Bean
File filesystemRoot() {
File tmpDir = new File("tmp/photo_video_myram");
if (!tmpDir.isDirectory()) {
try {
return Files.createDirectory(tmpDir.toPath()).toFile();
} catch (IOException e) {
e.printStackTrace();
}
}
return tmpDir;
}
You can use isDirectory() method first to check if the directory already exists. In case it does not exist, then create a new one.
Meanwhile there is another way to achieve this, when you use Spring Boot and accordingly spring-content-fs-boot-starter.
According to the documentation at https://paulcwarren.github.io/spring-content/refs/release/fs-index.html#_spring_boot_configuration it should be sufficient to add
spring.content.fs.filesystemRoot=/tmp/photo_video_myram
to your application.properties file.

Profiling Hadoop

UPDATE:
I had mailed Shevek, founder of Karmasphere, for help. He had given a presentation on hadoop profiling at ApacheCon 2011. He advised to look for Throwable. Catch block for Throwable shows :
localhost: java.lang.IncompatibleClassChangeError: class com.kannan.mentor.sourcewalker.ClassInfoGatherer has interface org.objectweb.asm.ClassVisitor as super class
localhost: at java.lang.ClassLoader.defineClass1(Native Method)
localhost: at java.lang.ClassLoader.defineClass(ClassLoader.java:792)
Hadoop has ASM3.2 jar and I am using 5.0. In 5.0, ClassVisitor is a Super Class and in 3.2 it is an Interface. I am planning to change my profiler to 3.2. Is there any other better way to fix this issue?
BTW, Shevek is super cool. A Founder and CEO, responding to some
anonymous guys emails. Imagine that.
END UPDATE
I am trying to profile Hadoop (JobTracker, Name Node, Data Node etc). Created a profiler using ASM5. Tested it on Spring and everything works fine.
Then tested the profiler on Hadoop in pseudo-distributed mode.
#Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
try {
/*1*/ System.out.println(" inside transformer " + className);
ClassReader cr = new ClassReader(classfileBuffer);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
/* c-start */ // CheckClassAdapter cxa = new CheckClassAdapter(cw);
ClassVisitor cv = new ClassInfoGatherer(cw);
/* c-end */ cr.accept(cv, ClassReader.EXPAND_FRAMES);
byte[] b = cw.toByteArray();
/*2*/System.out.println(" inside transformer - returning" + b.length);
return b;
} catch (Exception e) {
System.out.println( " class might not be found " + e.getMessage()) ;
try {
throw new ClassNotFoundException(className, e);
} catch (ClassNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
}
return null;
}
I can see the first sysout statement printed but not the second one. There is no error either. If I comment out from /* c-start / to / c-stop*/ and replace cw with classFileBuffer, I can see the second sysout statement. The moment I uncomment line
ClassVisitor cv = new ClassInfoGatherer(cw);
ClassInfoGatherer constructor:
public ClassInfoGatherer(ClassVisitor cv) {
super(ASM5, cv);
}
I am not seeing the second sysout statement.
What am i doing wrong here. Is Hadoop swallowing my sysouts. Tried sys err too. Even if that is the case why can i see the first sysout statement?
Any suggestion would be helpful. I think I am missing something simple and obvious here...but can't figure it out.
following lines were added to hadoop-env.sh
export HADOOP_NAMENODE_OPTS="-javaagent:path to jar $HADOOP_NAMENODE_OPTS"
export HADOOP_SECONDARYNAMENODE_OPTS="-path to jar $HADOOP_SECONDARYNAMENODE_OPTS"
export HADOOP_DATANODE_OPTS="-javaagent:path to jar $HADOOP_DATANODE_OPTS"
export HADOOP_BALANCER_OPTS="-javaagent:path to jar $HADOOP_BALANCER_OPTS"
export HADOOP_JOBTRACKER_OPTS="-javaagent:path to jar $HADOOP_JOBTRACKER_OPTS"
Hadoop had asm 3.2 and I was using ASM 5. In ASM5, ClassVisitor is a Superclass and in 3.2 it is an interface. For some reason, the error was a Throwable (credits to Shevek) and catch block was only catching exceptions. The throwable error wasn't captured in any of hadoop logs. So, it was very tough to debug.
Used jar jar links to fix asm version issues and everything works fine now.
If you are using Hadoop and something is not working and no logs to show any error's, then please try to catch Throwable.
Arun

Is spring Resource a file or directory?

I am using the spring Resource API and using a ResourcePatternResolver to scan my classpath for files.
In one situation the scan is picking up some directories and files that are in a pre-built jar and some that are on the file system.
In either case a 'resource' will either be a file or a directory. How can I reliably detect whether a resource points to a directory or file, whether in a jar file or not? Calling getFile() on a Resource inside a jar throws an Exception so I can't use that plus isFile() as I initially tried.
Spring’s Resource interface is meant to be a more capable interface for abstracting access to low-level resources.
It wraps File sometimes while sometimes not.
It has six built-in implements: UrlResource, ClassPathResource, FileSystemResource, ServletContextResource, InputStreamResource, ByteArrayResource.
You can implement yourself resource form.
The UrlResource wraps a java.net.URL, and may be used to access any Object that is normally accessible via a URL. If you use http: prefix ,the resource is a URL.
The ClassPathResource represents a resource which should be obtained from the classpath. This Resource implementation supports resolution as java.io.File if the class path resource resides in the file system, but not for classpath resources which reside in a jar and have not been expanded (by the servlet engine, or whatever the environment is) to the filesystem. To address this the various Resource implementations always support resolution as a java.net.URL.
FileSystemResource is an implement for java.io.File handles.It obviously supports resolution as a File and as a URL.
InputStreamResource is a resource implements for a given InputStream. Do not use it if you need to keep the resource descriptor somewhere, or if you need read a stream multiple times.
ByteArrayResource is a Resource implement for a given byte array. It creates a ByteArrayInputStream for the given byte array.
So you should not always use getFile() as Spring's Resource doesn't always represent a file system resource.For this reason, we recommend that you use getInputStream() to access resource contents because it is likely to function for all possible resource types.
Refer to: Resources
I think you can just surround the code checking for file by a try catch block:
boolean isFile = true;
try {
resource.getFile()
...
} catch (...Exception e) {
ifFile = false
}
I had a similar requirement, and solved it by excluding directories from my search pattern. Then for each resource found I lookup the parent item in the path, and ensure the directory has been created before writing the file.
In my case the file could be in the filesystem, or in the classpath, so I check the scheme of the URI first..
Although my search pattern may still pickup dirs if they have a dot in the name, so it would be better to catch the exception in that case -
search pattern - classpath*:/**/sprout/plugins/**/*.*
Example code -
private void extractClientPlugins() throws IOException {
Resource[] resourcePaths = resolver.getResourcePaths(sproutPluginSearchPattern);
Path pluginFolderPath = Paths.get(sproutHome, "./plugins/");
pluginFolderPath.toFile().mkdirs();
if (resourcePaths.length == 0) {
log.info("No Sprout client side plugins found");
}
for (Resource resource : resourcePaths) {
try {
Path destinationPath = generateDestinationPath(pluginFolderPath, resource);
File parentFolder = destinationPath.getParent().toFile();
if (!parentFolder.exists()) {
parentFolder.mkdirs();
}
destinationPath.toFile().mkdirs();
copy(resource, destinationPath.toFile());
} catch (IOException e) {
log.error("could not access resource", e);
throw e;
}
}
}
private Path generateDestinationPath(Path rootDir, Resource resource) throws IOException {
String relativePath = null;
String scheme = resource.getURI().getScheme();
if ("JAR".contains(scheme.toUpperCase())) {
String[] uriParts = resource.getURL().toString().split("!");
relativePath = trimPluginPathPrefix(uriParts[1]);
} else {
String filePath = resource.getFile().getAbsolutePath();
relativePath = trimPluginPathPrefix(filePath);
}
return Paths.get(rootDir.toString(), relativePath);
}
private String trimPluginPathPrefix(String filePath) {
String[] pathParts = filePath.split("sprout/plugins/");
if (pathParts.length != 2) {
throw new RuntimeException("The plugins must be located in a path containing '**/sprout/plugins/*'");
}
return pathParts[1];
}
Using it in this project -
https://github.com/savantly-net/sprout-platform/blob/master/sprout-core/src/main/java/net/savantly/sprout/core/ui/UiLoader.java

Resources