Reading file inside WAR - maven

I am working on a small RESTFul-project with Maven and Tomcat. Inside my implementation I have to read a txt file.
Currently I am testing the implementation in Eclipse. When using the absolute path I can execute my code without any problem. Since I have to generate a war file, I need to encapsulate the txt file in it. Using Maven and
<build>
<sourceDirectory>src</sourceDirectory>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.6</version>
<configuration>
<warSourceDirectory>WebContent</warSourceDirectory>
<failOnMissingWebXml>false</failOnMissingWebXml>
</configuration>
</plugin>
</plugins>
</build>
my war file contains the txt file stored in the highest level. What is your recommendation to read from that txt file inside the war file? I have found several threads here, but all tries failed. I need a specification where I can read the file when testing in Eclipse and when executing it directly in Tomcat.
This code doesn't work when executing the implementation in Eclipse as the text file is not found. The file is in the WebContent folder and after genereting the snapshot/war, you find the file in the war-file.
InputStreamFactory isf = new InputStreamFactory() {
public InputStream createInputStream() throws IOException {
return new FileInputStream("/text_de.txt");
}
};
I also tried the following when executing the implementation in Eclipse without access (I know, that would lead to an error when executing the war file):
return new FileInputStream("/WebContent/text_de.txt");
UPDATE:
Now I can execute the implementation within Eclipse with success. I first tried it with the annotation #Resource, but changed to #Context
#Context
ServletContext servletContext;
Then, I added this row:
String fileLocation = System.getProperty("com.example.resourceLocation",servletContext.getRealPath("/text_de.txt"));
Thanks

I am not sure if this helps in your case, but could you, using the HttpServletRequest request from your doGet() (doPost(), or one of the others), try:
return new FileInputStream(request.getServletContext().getRealPath("/text_de.txt"));
If you extract your .war file using an unzip program, you can determine the path needed, if "/text_de.txt" might not be correct, but a lot of times the contents of /WebContent/ ends up in the root of the .war file...
Hopes this helps,
note: The HttpServletRequest it self has a method with the same name, but that one is deprecated, so you'll know it the wrong one.
after discussion in the comments:
You need a different location during development,
find the file: tomcat/conf/catalina.properties of the instance of tomcat that is actually running. I am not sure if Eclipse is running this, or you point Eclipse to a tomcat-home folder
add a line like com.example.resourceLocation=/full/path/to/eclipse/project/text_de.txt
than use something like:
String fileLocation = System.getProperty(
"com.example.resourceLocation",
request.getServletContext().getRealPath("/text_de.txt"));
//this second parameter is a default value you can provide yourself
//if the property does not exists it chooses the second as return value
in response to the non-servlet question:
I am using Jersey link, here I use something like this
#Context
private HttpServletRequest httpRequest;
#GET
#Path("file")
#Produces(MediaType.APPLICATION_JSON)
public Response getFile () {
String fileLocation = System.getProperty(
"com.example.resourceLocation",
this.httpRequest.getServletContext().getRealPath("/text_de.txt"));
//this second parameter is a default value you can provide yourself
//if the property does not exists it chooses the second as return value
// do something with fileLocation
FileInputStream fi = new FileInputStream(fileLocation);
return Response.status(Response.Status.OK)
.entity("body")
.build();
}
within the class where the responding #POST or #GET etc. functions are located
a few years back in a SOAP service I used a servlet filter and retrieved the request object from the doFilter() function,
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
this.context = request.getServletContext( );
Eclipse can create Filters using the context-menu of your project...
in both cases you have the servletRequest object or servletContext object, so you can fit that into the file reader location solution
kind regards

Related

Maven: how to invoke a plugin with execution specific config

I have written a plugin and want to treat the goal vrs (check versions) differently,
depending on execution/command line.
In my code I added
#Parameter(name = "versionsWarnOnly", defaultValue = "true")
private boolean versionsWarnOnly;
public boolean getVersionsWarnOnly() {
System.out.println("invoked get");
return this.versionsWarnOnly;
}
public void setVersionsWarnOnly(boolean versionsWarnOnly) {
System.out.println("invoked set");
this.versionsWarnOnly = versionsWarnOnly;
}
I would expect, that without specifying versionsWarnOnly in the configuration in the pom, i just get the defaultValue specified.
The problem is, that does not happen, it is always false in that case.
If i configure in the configuration
of the plugin in the pom
<versionsWarnOnly>true</versionsWarnOnly>
Then this is done (well some success) if I build the phase mvn validate.
It is even true if I invoke the goal from the command line by goal mvn latex:vrs.
But if i specify that in an execution that like,
<execution>
<id>validate_converters</id>
<phase>validate</phase>
<configuration>
<versionsWarnOnly>true</versionsWarnOnly>
</configuration>
<goals>
<goal>vrs</goal>
</goals>
</execution>
again it has no effect.
I have no idea what i do wrong
or what kind of information you need to help me.
The problem is resolved and the question could be deleted altogether.
The problem was that I put almost all of the config into a class Settings.
On the other hand, I added the new parameter outside and so it had no effect.
Thats all.
Now all works fine.

Spring MVC Content Encoding Error after adding gzip filter

I have configured a GzipFilter in my custom WebApplicationInitializer and when I try to access the website. It shows me,
Content Encoding Error
The page you are trying to view cannot be shown because it uses an invalid or unsupported form of compression.
Please contact the website owners to inform them of this problem.
The GzipFilter I'm using is from this blog,
http://tutorials.jenkov.com/java-servlets/gzip-servlet-filter.html
I can see the browser request gzip content. And for the files that are downloaded I can see the Response header Content-Encoding:gzip. But, I don't see why I get this error. Below is my app config,
public class WebInitializer implements WebApplicationInitializer{
#Override
public void onStartup(ServletContext container) throws ServletException {
//configuration code
container.addFilter("GzipCompressionFilter",GzipFilter.class).addMappingForUrlPatterns(null, false, "/*");
}
}
If I use the filter from this post,
http://www.oodlestechnologies.com/blogs/Gzip-Servlet-Filter-in-Spring-MVC
The request never ends and the files download partially and loads infinitely. What could be the problem?
I'm using maven-jetty plugin to start jetty with mvn jetty:run goal. Currently I'm using the above filters to serve GZip content from the server. Is there any other way in maven-jetty plugin to enable GZip compression? GZipFilter is deprecated and I can't see any GZipHandler implementation for embedded jetty server. My maven-jetty config,
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>${jetty.version}</version>
<configuration>
<stopKey>jettyStop</stopKey>
<stopPort>9191</stopPort>
<httpConnector>
<host>0.0.0.0</host>
<port>8080</port>
</httpConnector>
</configuration>
</plugin>

Can I start a Spring Boot WAR with PropertiesLauncher?

I have a Spring Boot 1.2 app packaged as a WAR because I need to be able to deploy the app in an app server.
I also want to configure an external path which will contain jars to be added to the classpath. After reading the Launcher documentation, I configured the build to use PropertiesLauncher to this end :
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
...
<layout>ZIP</layout>
</configuration>
</plugin>
I tried to start the app with various combinations of this additional system property : -Dloader.path=lib/,lib-provided/,WEB-INF/classes,<my additional path>
But I always end up with this error :
java.lang.IllegalArgumentException: Invalid source folder C:\<path to my war>\<my war>.war
at org.springframework.boot.loader.archive.ExplodedArchive.<init> ExplodedArchive.java:78)
at org.springframework.boot.loader.archive.ExplodedArchive.<init>(ExplodedArchive.java:66)
at org.springframework.boot.loader.PropertiesLauncher.addParentClassLoaderEntries(PropertiesLauncher.java:530)
at org.springframework.boot.loader.PropertiesLauncher.getClassPathArchives(PropertiesLauncher.java:451)
at org.springframework.boot.loader.Launcher.launch(Launcher.java:60)
at org.springframework.boot.loader.PropertiesLauncher.main(PropertiesLauncher.java:609)
I looked at the source code and it seems that PropertiesLauncher can only handle jar archives (ending with ".jar" or ".zip") and "exploded archives" (not ending with the former)
Is it possible to do achieve what I want ? Am I doing it wrong ?
If it's not possible, which alternative is there ?
If somebody end up here this might be useful:
java -cp yourSpringBootWebApp.war -Dloader.path=yourSpringBootWebApp.war!/WEB-INF/classes/,yourSpringBootWebApp.war!/WEB-INF/,externalLib.jar org.springframework.boot.loader.PropertiesLauncher
(Spring-Boot 1.5.9)
https://docs.spring.io/spring-boot/docs/1.5.x/reference/html/executable-jar.html#executable-jar-launching
In Spring Boot 1.2, PropertiesLauncher handles .jar and .zip files as "jar archives" and everything else as "exploded archives" (unzipped jars). It does not properly handles .war
Here's the alternative I found :
I eventually switched back to the regular war launcher and I managed to configure a folder which jar contents are added to the classpath using a SpringApplicationRunListener such as this (pseudo-code for concision) :
public class ClasspathExtender implements SpringApplicationRunListener {
public void contextPrepared(ConfigurableApplicationContext context) {
// read jars folder path from environment
String path = context.getEnvironment().getProperty("my.jars-folder");
// enumerate jars in the folder
File[] files = new File(path).listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) { return name.endsWith(".jar"); }
});
URL[] urls = // convert files array to urls array
// create a new classloader which contains the jars...
ClassLoader extendedClassloader = new URLClassLoader(urls, context.getClassLoader());
// and replace the context's classloader
((DefaultResourceLoader) context).setClassLoader(extendedClassloader);
}
// other methods are empty
}
This listener is instanciated by declaring it in a META-INF/spring.factories file :
org.springframework.boot.SpringApplicationRunListener=my.ClasspathExtender
This worked for me (Spring Boot 1.3.2)
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
...
<layout>WAR</layout>
</configuration>
</plugin>

Spring Boot: Thymeleaf not resolving fragments after packaging

im using fragments like this:
#RequestMapping(value="/fragment/nodeListWithStatus", method= RequestMethod.GET)
public String nodeListWithStatus(Model model) {
// status der nodes
model.addAttribute("nodeList", nodeService.getNodeListWithOnlineStatus());
return "/fragments :: nodeList";
}
The templates are in /src/main/resources/templates. This works fine when starting the application from IntelliJ.
As soon as i create an .jar and start it, above code no longer works. Error:
[2014-10-21 20:37:09.191] log4j - 7941 ERROR [http-nio-666-exec-2] --- TemplateEngine: [THYMELEAF][http-nio-666-exec-2] Exception processing template "/fragments": Error resolving template "/fragments", template might not exist or might not be accessible by any of the configured Template Resolvers
When i open the .jar with winrar, i see /templates/fragments.html - so it seems to be there.
My pom.xml has this part for building the jar (Maven clean, install) :
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>de.filth.Application</mainClass>
<layout>JAR</layout>
</configuration>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Can anyone tell me what im doing wrong here?
Thanks!
You don't need the leading / on the view name, i.e. you should return fragments :: nodeList rather than /fragments :: nodeList. Having made this change Thymeleaf should be able to find the template when run from your IDE or from a jar file.
If you're interested, here's what's happening under the hood:
The view name is used to search for a resource on the classpath. fragments :: nodeList means that the resource name is /templates/fragments.html and /fragments :: nodeList means that the resource name is /templates//fragments.html (note the double slash). When you're running in your IDE the resource is available straight off the filesystem and the double slash doesn't cause a problem. When you're running from a jar file the resource is nested within that jar and the double slash prevents it from being found. I don't fully understand why there's this difference in behaviour and it is rather unfortunate. I've opened an issue so that we (the Spring Boot team) can see if there's anything we can do to make the behaviour consistent.
It's an old topic, but I stumbled upon it while having problem with similar symptoms and different root cause. Wanted to share solution which helped me in case it could help somebody else...
Apparently name of the messages.properties file is case sensitive, but not everywhere. I had mine called "Messages.properties" (with capital M) and it worked just fine from inside IDE (IntelliJ), but once I tried to run app from jar, all messages were replaced with ??parameter.name??. Replacing M with lowercase m resolved the problem.

Maven 3: Assembling a Jar file containing binary resources

I have setup a Maven project consisting of two child modules, one Java Jar module and one creating a Windows Executable using NPanday. My build is working great.
The problem I am having, is that I would like to create a Jar file containing my Java lib and have the Exe file embedded so I can load it as a resource from the code inside the lib.
It seems the assembly plugin would be the path to go, but I am having some trouble configuring this. I don't even know if this is the correct path to go in this case.
Could someone here please guide me to the right path or give me a hint as to how such an assembly descriptor should look like?
Chris
Well I have a Java Project, that only contains a test Class for now, as I am still in the stage of setting up my build:
Module de.cware.utils:lib-psexec-client:
/de/cware/utils/psexec/client/Test.java
Module de.cware.utils:lib-psexec-service:
outputs a file called "service.exe"
I want the output to look like the client jar, but to also contain the "service.exe" so I can load it from the code in the Client jar.
Module de.cware.utis:lib-psexec-assembly:
/de/cware/utils/psexec/client/Test.java
/service.exe
Ok ... so it seems I sorted out a solution on my own. I know this question was relatively special again ... as all of my questions seem to be :-)
The solution was to create a maven module containing a custom implementation of a PlexusIoResourceCollection and to reference this from a components.xml file in the "META-INF/plexus" directory.
After adding this as a dependency to my assembly plugin, I was able to embed the exe files into my jar :-)
Here comes the code of the component:
package npanday.plugin.archiver;
import org.codehaus.plexus.components.io.resources.PlexusIoCompressedFileResourceCollection;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
* Created with IntelliJ IDEA.
* User: cdutz
* Date: 02.03.12
* Time: 12:04
*/
public class PlexusIoExeResourceCollection extends PlexusIoCompressedFileResourceCollection {
#Override
protected String getDefaultExtension() {
return ".exe";
}
#Override
protected InputStream getInputStream(File file) throws IOException {
// Simply return an InputStream to the resource file.
// This will make it embed the source as a whole.
return new FileInputStream(file);
}
#Override
public String getPath() {
// Without overriding this, the exe would be included with its full path.
// This way it is included directly in the root of the result archive.
return super.getFile().getName();
}
}
Here the config xml in META-INF/plexus/components.xml
<component-set>
<components>
<component>
<role>org.codehaus.plexus.components.io.resources.PlexusIoResourceCollection</role>
<role-hint>exe</role-hint>
<implementation>npanday.plugin.archiver.PlexusIoExeResourceCollection</implementation>
<instantiation-strategy>per-lookup</instantiation-strategy>
</component>
</components>
</component-set>
And finally the usage in my assembly plugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.2.1</version>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
<dependencies>
<dependency>
<groupId>org.apache.npanday.plugins</groupId>
<artifactId>maven-exe-archiver-plugin</artifactId>
<version>${npanday.version}</version>
</dependency>
</dependencies>
</plugin>
Hopefully it will do the trick for me.

Resources