Arquillian: path relative to project instead of server - java

How can I use a project-relative path when using Arquillian/Shrinkwrap to test my testcases?
IDataSet dataSet = new FlatXmlDataSetBuilder().build(new File("src\\ZZZZZ.xml"));
insertFromXML(dataSet);
Will give me this exception when testing
java.io.FileNotFoundException: C:\Uprogs\jboss-eap-6.2.4\bin\src\ZZZZZ.xml
It tries to locate the file within the folders of the server on which I deploy the test onto.
Rather than that, I want him to look in the folders relative to my project (e.g. C:\Users\xy\workspaces\MyProject\src\ZZZZZ.xml). Searched the internet but found nothing
Shrinkwrap gets deployed like this:
#Deployment
public static Archive<?> createDeployment() {
File[] libs = Maven.resolver()
.loadPomFromFile("pom.xml").resolve(
"jcifs:jcifs"
, "org.dbunit:dbunit"
, "com.ibm:db2jcc_license_cisuz"
, "com.ibm:db2jcc"
)
.withTransitivity()
.asFile();
return ShrinkWrap.create(WebArchive.class, "test.war")
.addAsLibraries(libs)
.addPackage("de.abc.RuleEditor")
.addAsResource("de/abc/RuleEditor/messages.properties", "messages.properties")
.addAsManifestResource("test-jboss-deployment-structure.xml","jboss-deployment-structure.xml")
.addAsWebInfResource("test-beans.xml", "beans.xml")
.addAsWebInfResource(
new StringAsset("<faces-config version=\"2.0\"/>"), "faces-config.xml")
.merge(ShrinkWrap.create(GenericArchive.class).as(ExplodedImporter.class)
.importDirectory("src/main/webapp").as(GenericArchive.class), "/", Filters.include(".*\\.xhtml$"));
}

How can I use a project-relative path when using Arquillian/Shrinkwrap (...)?
It's a ridiculous approach, don't go that way :]
The idea behind Arquillian is to create micro-deployments (it means: jar/war/ear archive using ShrinkWrap tool) and include everything inside that archive.
So please modify your deployment:
#Deployment
public static Archive<?> createDeployment() {
return ShrinkWrap.create(WebArchive.class, "test.war")
(...)
.addAsLibraries(libs)
// Add in below way any additional file to the archive
.addAsResource("path-to-testset.xml", "dbunit/testset.xml")
.addPackage("de.abc.RuleEditor")
.addAsResource("de/abc/RuleEditor/messages.properties", "messages.properties")
.addAsManifestResource("test-jboss-deployment-structure.xml","jboss-deployment-structure.xml")
(...)
}
And then use getResourceAsStream() to load file from the class path:
#Test
public void test_something() {
FlatXmlDataSetBuilder builder = new FlatXmlDataSetBuilder();
builder.setColumnSensing(true);
FlatXmlDataSet xmlDataSet = builder.build(
this.getClass().getResourceAsStream("/dbunit/testset.xml"));
// ...
It is much better, as everything what is needed to test, is already included inside archive. No relative, nor absolute path names.
My personal advice is: be careful about DbUnit, as perhaps you notice in the future, crafting and managing many xml data sets may become bigger and bigger problem as your project will grow. That is why I prefer DbSetup.

Related

Can I store an XML file inside a jar file?

I built an Jar using Maven.
The behavior I am trying to implement is: when the user uses the command create , I want to store an XML file inside a a folder of the jar. Is it possible to do it?
Let's say this is a project and I added the jar of the migration tool to it.
I want to be able to see and edit the properties file
3-It has some things that need to be altered like for example
migration files and the file with the database information
Not 100% sure if I understood your requirements correctly, but I've implemented some utility classes which support:
default config in JAR
config in installation directory
config in user directory
simple command line arguments/ switches support
and more...
Maybe this helps.
public static void main(String[] args) throws URISyntaxException, IOException, MissingPropertyException, Exception {
CommandLineArgs commandLineArgs = CommandLineArgs.parseCommandLineArgs(args);
DromblerClientStarter<DromblerClientConfiguration> main = new DromblerClientStarter<DromblerClientConfiguration>(new DromblerClientConfiguration(commandLineArgs)) {
#Override
protected ApplicationInstanceListener getApplicationInstanceListener() {
return additionalArgs -> {
// additionalArgs not handled
};
}
};
if (main.init()) {
main.start();
}
}
Source code: https://github.com/Drombler/drombler-commons/tree/master/drombler-commons-client/drombler-commons-client-startup-main
Maven:
<dependency>
<groupId>org.drombler.commons</groupId>
<artifactId>drombler-commons-client-startup-main</artifactId>
<version>1.0</version>
</dependency>
Javadoc: https://www.drombler.org/drombler-commons/1.0/docs/site/apidocs/org/drombler/commons/client/startup/main/package-summary.html

FileNotFoundException when running wiremock from jar file

I am running a spring boot application with wiremock. My files structure is like this:
project/main/
- java/package/Wiremock.java
- resources/wiremock/__files/file.json
Inside Wiremock.java I am calling WireMockServer like this:
WireMockServer wiremockServer = new WireMockServer(WireMockConfiguration.wireMockConfig()
.withRootDirectory(getClass().getResource("/wiremock").getPath())
.port(port));
wiremockServer.start();
wiremockServer.stubFor(get(urlEqualTo("/myurl"))
.willReturn(aResponse()
.withBodyFile("file.json")
.withHeader(CONTENT_TYPE, APPLICATION_JSON_VALUE)
.withStatus(HTTP_OK)));
When I am running it locally it works as expected.
When I compile the app to a jar file , a jar file /Users/user/project-0.0.1-SNAPSHOT.jar is generated with the structure:
BOOT-INF/classes/
- wiremock/__files/file.json
- package/Wiremock.class
But when I run the jar file , I'm getting the following error:
java.lang.RuntimeException: java.io.FileNotFoundException: /Users/user/jar:file:/Users/user/project-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/wiremock/__files/file.json (No such file or directory)
Please help, thanks
if this path is correct?
/Users/user/jar:file:/Users/user/project-0.0.1-SNAPSHOT.jar!/BOOT-
INF/classes!/wiremock/__files/file.json (No such file or directory)
I find there is one more "!" after XXXX.jar and classess.
I just meet the same issue today, when i run wire mock in IDEA, it works. but when i run the application by java -jar mode, wired mock server cannot find the json mock file. the root cause of this issue is that when initialization of wire mock server, it will found the json file by com.github.tomakehurst.wiremock.common.ClasspathFileSource
class, it will recursively add files to list of the config path which you specified. the logic of add file is showed below like this.
public List<TextFile> listFilesRecursively() {
if (this.isFileSystem()) {
this.assertExistsAndIsDirectory();
List<File> fileList = Lists.newArrayList();
this.recursivelyAddFilesToList(this.rootDirectory, fileList);
return this.toTextFileList(fileList);
} else {
return FluentIterable.from(toIterable(this.zipFile.entries())).filter(new Predicate<ZipEntry>() {
public boolean apply(ZipEntry jarEntry) {
return !jarEntry.isDirectory() && jarEntry.getName().startsWith(ClasspathFileSource.this.path);
}
}).transform(new Function<ZipEntry, TextFile>() {
public TextFile apply(ZipEntry jarEntry) {
return new TextFile(ClasspathFileSource.this.getUriFor(jarEntry));
}
}).toList();
}
}
it will recursively add file which is absolutely started with the path. but when you run with java -jar, the jarEntry.getName is started with 'BOOT-INF'. one of the solution is that override the method with a subclass extend ClasspathFileSource, and modify the match rule. it will fix

How to create a custom ClassLoader for nested JAR files

I am working with a Java library that has some nested JAR files in lib package.
I have 2 issues:
I cannot see referenced types in my IDE (I am using JetBrains IntelliJ)
Of course I get class not defined at runtime
I understand that I have to create and use a custom ClassLoader, will it solve both problems?
Is this the recommended way of achieving this result?
The JAR file is an Italian government provided library and I cannot modify it as it will be periodically updated as the regulation changes.
Yes, as far as I know, the standard ClassLoaders do not support nested JARs. Which is sad, since it would be a really nice idea, but Oracle just doesn't give a damn about it. Here is a 18-year old ticket:
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4735639
If you are getting those JARs from somebody else, the best thing would be to contact the vendor and ask them for a delivery in standards-compatible format. From your answer I realize that this might be difficult to achieve, but I would still try to talk to them, because it's the right thing to do. I'm pretty sure that everybody else in your position has the same issue. According to industry standards, such situation would usually hint your vendor into using Maven repository for their deliverables.
If talking to your vendor fails, you can re-pack the JARs as you get them. I would recommend writing an automated script for that and making sure it gets run on each delivery. You can either put all .class files into one uber-JAR, or just move the nested JARs outside the enclosing JAR. Caveat 1: there can be more than one class with the same name, so you need to make sure to take the correct one. Caveat 2: if the JARs were signed, you will lose the signature (unless you sign them with your own).
Option 3: you can always implement your own ClassLoader to load the classes from anywhere, even from the kitchen sink.
This guy did exactly this: https://www.ibm.com/developerworks/library/j-onejar/index.html
The short summary is that such a ClassLoader has to perform recursive unzipping, which is a bit of a pain-in-the-ass because archives are essentially made for stream access and not for random access, but apart from that it's perfectly doable.
You can use his solution as a "wrapper loader" which will replace your main class.
As far as IntelliJ IDEA goes, I don't believe it supports this functionality out-of-the box. The best thing would be either to re-package JARs as described above and add them as separate classpath entries, or to search if anybody has written a plugin for nested JAR support.
I don't know what you want to do after load jars.
In my case, use jar dynamic loading for Servlet samples.
try{
final URLClassLoader loader = (URLClassLoader)ClassLoader.getSystemClassLoader();
final Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class);
method.setAccessible(true);
new File(dir).listFiles(new FileFilter() {
#Override
public boolean accept(File jar) {
// load file if it is 'jar' type
if( jar.toString().toLowerCase().contains(".jar") ){
try {
method.invoke(loader, new Object[]{jar.toURI().toURL()});
XMLog.info_arr(logger, jar, " is loaded.");
JarInputStream jarFile = new JarInputStream(new FileInputStream(jar));
JarEntry jarEntry;
while (true) {
// load jar file
jarEntry = jarFile.getNextJarEntry();
if (jarEntry == null) {
break;
}
// load .class file in loaded jar file
if (jarEntry.getName().endsWith(".class")) {
Class loadedClass = Class.forName(jarEntry.getName().replaceAll("/", "\\.").replace(".class",""));
/*
* In my case, I load jar file for Servlet.
* If you want to use it for other case, then change below codes
*/
WebServlet annotaions = (WebServlet) loadedClass.getAnnotation(WebServlet.class);
// load annotation and mapping if it is Servlet
if (annotaions.urlPatterns().length > 0) {
ServletRegistration.Dynamic registration = servletContextEvent.getServletContext().addServlet(annotaions.urlPatterns().toString(), loadedClass);
registration.addMapping(annotaions.urlPatterns());
}
}
}
} catch (Exception e) {
System.err.println("Can't load classes in jar");
}
}
return false;
}
});
} catch(Exception e) {
throw new RuntimeException(e);
}
Interestingly I just solved a version of this problem for JesterJ, though I had the additional requirement of loading dependencies for the code in the jar file as well. JesterJ (as of this evening's commits!) runs from a fat jar and accepts an argument denoting a second fat jar containing the classes, dependencies and configuration for a document ingestion plan (the user's code that I need to run).
The way my solution works is I borrow the knowledge of how to load jars inside of jars from Uno-Jar (the library that produces the fat jar), and stuff my own classloader in above it to control the evaluation order of the class loaders.
The key bit from https://github.com/nsoft/jesterj/blob/jdk11/code/ingest/src/main/java/org/jesterj/ingest/Main.java looks like this:
JesterJLoader jesterJLoader;
File jarfile = new File(javaConfig);
URL planConfigJarURL;
try {
planConfigJarURL = jarfile.toURI().toURL();
} catch (MalformedURLException e) {
throw new RuntimeException(e); // boom
}
jesterJLoader = (JesterJLoader) ClassLoader.getSystemClassLoader();
ClassLoader loader;
if (isUnoJar) {
JarClassLoader jarClassLoader = new JarClassLoader(jesterJLoader, planConfigJarURL.toString());
jarClassLoader.load(null);
loader = jarClassLoader;
} else {
loader = new URLClassLoader(new URL[]{planConfigJarURL}, jesterJLoader);
}
jesterJLoader.addExtLoader(loader);
My JesterJLoader is here:
https://github.com/nsoft/jesterj/blob/jdk11/code/ingest/src/main/java/org/jesterj/ingest/utils/JesterJLoader.java
Though if you are happy to simply delegate up and rely on existing classes on the main class path (rather than loading additional dependencies from the sub-fat-jar like I'm doing) yours could be much simpler. I go to a lot of effort to allow it to check the sub-jar first rather than delegating up to the parent immediately, and then have to keep track of what's been sent to the sub-jar to avoid loops and subsequent StackOverflowError...
Also note that the line where I get the system class loader is going to NOT be what you want, I'm also monkeying with the system loader to work around impolite things that some of my dependencies are doing with class loading.
If you decide to try to check out Uno-Jar pls note that resource loading for this nested scenario may yet be wonky and things definitely won't work before https://github.com/nsoft/uno-jar/commit/cf5af42c447c22edb9bbc6bd08293f0c23db86c2
Also: recently committed thinly tested code warning :)
Disclosure: I maintain both JesterJ and Uno-Jar (a fork of One-JAR the library featured in the link supplied by jurez) and welcome any bug reports or comments or even contributions!

JavaFx MediaPlayer behaves differently in unit test vs application, why?

I want to load meta data from an MP3 file, to be played by a JavaFx MediaPlayer. This works fine in the unit test, but not in the application. In the unit test, 6 items of metaData reported, but zero in the application. The method that "does the work" is the same.
The main class of the application extends Application. The test class extends ApplicationTest from TestFx. Could that affect the behavior?
The application:
public class MediaMain extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
Map<String, Object> meta = metaData();
System.out.printf("Number of meta data: %d.%n", meta.size());
System.out.println(meta);
}
Map<String, Object> metaData() {
File audioFile = new File("src/main/resources", "beingBoiled.mp3");
final URI uri = audioFile.toURI();
final String source = uri.toString();
Media media = new Media(source);
new MediaPlayer(media);
return media.getMetadata();
}
}
The unit test:
class MediaMainTest extends ApplicationTest {
#Test
void testMeta() {
MediaMain main = new MediaMain();
Map<String, Object> metaData = main.metaData();
assertNotEquals(0, metaData.size());
System.out.printf("Number of meta data: %d.%n", metaData.size());
System.out.println(metaData);
}
}
Printout from the application:
Number of meta data: 0.
{}
Printout from the unit test:
Number of meta data: 6.
{year=1980, artist=The Human League, raw metadata={ID3=java.nio.HeapByteBufferR[pos=254 lim=3214 cap=3214]}, album=Travelogue, genre=(52), title=Being Boiled}
What could be the reason? It's a mystery to me. Written with Java 11, JavaFx 11.0.2 and TestFx 4.0.15-alpha.
You are referencing a file with a location of src/main/resources, this is probably not a good idea as your deployed application likely won't have a src/main/resources directory, plus the resource might be bundled within the application jar rather than as a file on disk, so using a file protocol to access it won't work.
It is probably best to use something like below:
String mediaLoc = getClass().getResource("/beingBoiled.mp3").toExternalForm()
Media media = new Media(mediaLoc)
Like in How load css file in javafx8. The exact location of the resource to be loaded may differ based on build and project structure. If you don't want to load from the class path, but instead via a File or over network http call, then you would need to use something else.
The above code assumes that your build system is setup to copy the media from the src/main/resources to your target packaging location and package the resource into the application distributable (e.g. an application jar file) in the root of the jar file.
Make sure that your build system is actually copying the file to the target location. You can check if it is there by running your build, looking at the resultant jar and running jar tvf <myjarfilename>.jar to see if the mp3 resource is in the correct location at the root of the jar file.

Unit test with files system dependency - hidden files

I am writing a "Total Commander" like application in Java. There is quite obvious file system dependency here.
I want to unit test it. I created directory structure for test purposes, I keep it in known location in SVN repository. It works great so far.
Now, I have a method that should ignore hidden files. How can I go about this? Can I mark file hidden in the SVN somehow?
Other solution would be to make one of the files hidden in the build script before running tests, but I'm afraid this would mark file as modified and always show in a commit dialog.
Any suggestions?
I would put all the initialization of a test directory into the tests themselves. And such a case would be simple:
create a directory
put some hidden and visible files into it
test
tear down by removing the directory
Essentially, accessing the file system is a big no-no when unit testing. For starters, these tests are slow(er) than your in-system tests, thus reducing the likelihood of you running your tests at a high frequency (such as with every compilation).
Much better if you use an adapter-pattern to abstract away the access to the file system. I'm a .NET developer so my example will be in C#, but I expect you to be able to translate it simply enough:
public class MyManager
{
private readonly IFileAdapter _fileAdapter;
public MyManager(IFileAdapter fileAdapter)
{
_fileAdapter = fileAdapter;
}
}
public interface IFileAdapter
{
public FileStream Open(string fileName);
public string ReadLine(FileStream fileStream);
// More methods...
}
public class FileAdapter : IFileAdapter
{
public FileStream Open(string fileName)
{
return System.Io.File.Open(fileName);
}
public string ReadLine(FileStream fileStream)
{
return File.Open(fileStream);
}
}
Now, as usual, you can mock the file system, as you would any other class, supplying the returned results. Remember - you are not testing Java's IO classes it is assumed that they work. You are only testing your classes (i.e. MyManager, in the example above).
Leave the tests that actually use the file system to your integration / acceptance tests.
Hope this helps,
Assaf.
I would prefer to abstract file system, so that my unit-test wouldn't require access to real file system. Of course, this abstraction layer must be tested with real file system, but this allow you to reduce dependency on it.
As for storing hidden files in SVN, I second artemb. You should create all files necessary to test in JUnit set up. Presumably, you should prefer setup per test method (#Before and #After). But if you encounter test slowness problems, have a look at #BeforeClass and #AfterClass. I consider they can be used with test suites too.
artemb's answer is correct, you can use #Before and #After to create and remove your structure for each test.
Here is some code I use to create a new directory with some files in it, it will create the directory in the systems temp dir, this is important because depending on the machine your tests will run on, you may will not be allowed to create files or dirs somewhere else. (I had to write this code to allow my tests to be executed on our linux integration machine...)
final String tempdir = System.getProperty("java.io.tmpdir");
final String dirname = Long.toHexString(System.currentTimeMillis());
final File dir = new File(tempdir, dirname);
dir.deleteOnExit();
dir.mkdir();
final String path = dir.getAbsolutePath();
assertTrue(dir.exists());
// pre condition, the directory is empty
assertTrue(dir.list().length == 0);
// create temp files in the directory
final int nbFiles = 3;
for (int i = 0; i < nbFiles; i++) {
(File.createTempFile("test", ".txt", dir)).deleteOnExit();
}
BTW you will have to know on what platform you run to be able to create hiden files...

Categories

Resources