Distributing a Java program with content - java

For the past couple of months I've been working on a game in java for a university project. It's coming up to the end of the project and I would like to compile the project into a single file which is easy to distribute. The game currently runs from inside the IDE and relies on the working directory being set somewhere specific (i.e. the content directory with sounds/textures etc). What's the best way to put all this together for portability? I'm hoping there is some way to compile the content into the jar file...
NB. I'm using NetBeans, so any solutions which are easy in netbeans get extra credit ;)
Addendum::
For future reference, I found a way of accessing things by directory, ythis may not be the best way but it works:
File directory = new File(ClassLoader.getSystemResource("fullprototypeone/Content/Levels/").toURI());
And now I can just use that file object as normal

You can embed resources in jar file - this is just a zip after all. The standard way to do that is to put resource files in some directory in your sources hierachy. Then you refer to them by Object.getClass().getResourceAsStream(). So you will need to change the way you retrieve them in your code.
You can read more here: Object.getClass().getResourceAsStream(). Of course instead of object you use some class from your package.
when you put those resource files in your src hierachy I believe Netbeans should jar them for you with standard build of the project.

Here is the manual for JNLP and Java Web Start. These technologies exist just for the task you've described.

Well one way is to access the resources via Class.getResourceAsStream (or Class.getResource). Then make sure that the files are in the JAR file.
Off the top of my head (and without trying it) you should be able to put the resources in with the source files which will get NetBeans to put them into the JAR file. Then change the File stuff to the getResource calls.
I would suggest making a simple program that plays a sound and trying it out before you try to convert the whole project over.
If you try and it doesn't work let me know and I'll see if I can dig into it a bit more (posting the code of the simple project would be good if it comes to that).

Here is the code I promised in another comment... it isn't quite what I remember but it might get you started.
Essentially you call: String fileName = FileUtils.getFileName(Main.class, "foo.txt");
and it goes and finds that file on disk or in a JAR file. If it is in the JAR file it extracts it to a temp directory. You can then use "new File(fileName)" to open the file which, no matter where it was before, will be on the disk.
What I would do is take a look at the getFile method and look at what you can do with the JAR file to iterate over the contents of it and find the files in a given directory.
Like I said, not exactly what you want, but does do a lot of the initial work for you.
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.security.CodeSource;
import java.security.ProtectionDomain;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
public class FileUtils
{
public static String getFileName(final Class<?> owner,
final String name)
throws URISyntaxException,
ZipException,
IOException
{
String fileName;
final URI uri;
try
{
final String external;
final String decoded;
final int pos;
uri = getResourceAsURI(owner.getPackage().getName().replaceAll("\\.", "/") + "/" + name, owner);
external = uri.toURL().toExternalForm();
decoded = external; // URLDecoder.decode(external, "UTF-8");
pos = decoded.indexOf(":/");
fileName = decoded.substring(pos + 1);
}
catch(final FileNotFoundException ex)
{
fileName = null;
}
if(fileName == null || !(new File(fileName).exists()))
{
fileName = getFileNameX(owner, name);
}
return (fileName);
}
private static String getFileNameX(final Class<?> clazz, final String name)
throws UnsupportedEncodingException
{
final URL url;
final String fileName;
url = clazz.getResource(name);
if(url == null)
{
fileName = name;
}
else
{
final String decoded;
final int pos;
decoded = URLDecoder.decode(url.toExternalForm(), "UTF-8");
pos = decoded.indexOf(":/");
fileName = decoded.substring(pos + 1);
}
return (fileName);
}
private static URI getResourceAsURI(final String resourceName,
final Class<?> clazz)
throws URISyntaxException,
ZipException,
IOException
{
final URI uri;
final URI resourceURI;
uri = getJarURI(clazz);
resourceURI = getFile(uri, resourceName);
return (resourceURI);
}
private static URI getJarURI(final Class<?> clazz)
throws URISyntaxException
{
final ProtectionDomain domain;
final CodeSource source;
final URL url;
final URI uri;
domain = clazz.getProtectionDomain();
source = domain.getCodeSource();
url = source.getLocation();
uri = url.toURI();
return (uri);
}
private static URI getFile(final URI where,
final String fileName)
throws ZipException,
IOException
{
final File location;
final URI fileURI;
location = new File(where);
// not in a JAR, just return the path on disk
if(location.isDirectory())
{
fileURI = URI.create(where.toString() + fileName);
}
else
{
final ZipFile zipFile;
zipFile = new ZipFile(location);
try
{
fileURI = extract(zipFile, fileName);
}
finally
{
zipFile.close();
}
}
return (fileURI);
}
private static URI extract(final ZipFile zipFile,
final String fileName)
throws IOException
{
final File tempFile;
final ZipEntry entry;
final InputStream zipStream;
OutputStream fileStream;
tempFile = File.createTempFile(fileName.replace("/", ""), Long.toString(System.currentTimeMillis()));
tempFile.deleteOnExit();
entry = zipFile.getEntry(fileName);
if(entry == null)
{
throw new FileNotFoundException("cannot find file: " + fileName + " in archive: " + zipFile.getName());
}
zipStream = zipFile.getInputStream(entry);
fileStream = null;
try
{
final byte[] buf;
int i;
fileStream = new FileOutputStream(tempFile);
buf = new byte[1024];
i = 0;
while((i = zipStream.read(buf)) != -1)
{
fileStream.write(buf, 0, i);
}
}
finally
{
close(zipStream);
close(fileStream);
}
return (tempFile.toURI());
}
private static void close(final Closeable stream)
{
if(stream != null)
{
try
{
stream.close();
}
catch(final IOException ex)
{
ex.printStackTrace();
}
}
}
}
Edit:
Sorry, I don't have time to work on this right now (if I get some I'll do it and post the code here). This is what I would do though:
Look at the ZipFile class and use the entries() method to find all of the files/directories in the zip file.
the ZipEntry has an isDirectory() method that you can use to figure out what it is.
I think the code I posted in this answer will give you a way to pick a temporary directory to extract the contents to.
I think the code I posted in this answer could help with copying the ZipEntry contents to the file system.
once the items are on the file system the code you already have for iterating over the directory would still work. You would add a new method to the FileUtils class in the code above and be able to find all of the files as you are doing now.
There is probably a better way to do it, but off the top of my head I think that will work.

yes ! put your compiled .class files and your resources with folders in a jar file .. you ll have to build a manifest file as well .. you can find the tutorial about making a .jar file on
google. most probably you ll be referred to java.sun.com .

Related

getClassLoader().getResourceAsStream method returns null for a file with ".LIB" extension

I'm trying to load a file from resources/ path using
getClassLoader().getResourceAsStream("file.LIB")
but the method always returns null, unless I rename the file into another extension, say ".dll".
I've looked into the official Java documentation, but to no avail.
Why does the method acts strange on that file type?
Note: I'm using JDK 1.8.0_111 x86 (due to constraints on that lib file, which only works well with a 32-bit JVM)
It does works for me, you need to be sure what exactly you are doing with lib file.
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
public class FileHelper {
public String getFilePathToSave() {
Properties prop = new Properties();
String filePath = "";
try {
InputStream inputStream =
getClass().getClassLoader().getResourceAsStream("abc.lib");
prop.load(inputStream);
filePath = prop.getProperty("json.filepath");
} catch (IOException e) {
e.printStackTrace();
}
return filePath;
}
public static void main(String args[]) {
FileHelper fh = new FileHelper();
System.out.println(fh.getFilePathToSave());
}
}

How can I load a file relative to a class's package?

I have the file allDepartments.json in a subdirectory called fixtures, to which I want to access from the Fixture.java class.
This is my Fixture.java code:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
public final class Fixture {
private static final String FIXTURES_PATH = "";
private final String fixture;
public Fixture(String fixtureName) throws IOException {
fixture = new String(Files.readAllBytes(Paths.get(FIXTURES_PATH + fixtureName)));
}
public final String getFixture() {
return fixture;
}
}
However every time he tries to access the file I get a java.nio.file.NoSuchFileException: allDepartments.json...
I have heard of the getResource() method and tried every combination possible of it, without success.
I need this to store multi-line strings for my JUnit tests.
What can I do?
The NIO.2 API can't be used to read files that are effectively project resources, i.e. files present on the classpath.
In your situation, you have a Maven project and a resource that you want to read during the unit test of the application. First, this implies that this resources should be placed under src/test/resources so that Maven adds it automatically to the classpath during the tests. Second, this implies that you can't use the Files utility to read it.
You will need to resort to using a traditional BufferedReader:
public Fixture(String fixtureName) throws IOException {
try (BufferedReader br = new BufferedReader(new InputStreamReader(Fixture.class.getResourceAsStream(FIXTURES_PATH + fixtureName)))) {
// do your thing with br.readLine();
}
}
Note the path given to getResourceAsStream is either relative to the current class or absolute. If the resources is located in src/test/resources/folder/allDepartments.json then a valid path would be /folder/allDepartments.json.
Add allDepartments.json to the.classpath file of the project and java should be able to pick it up.
Refer this topic if you want to know how to add a file to class path from eclipse
when you run Fixtures.java the relative path would be
../fixtures/allDepartments.json
try using this path.
Thank you all for helping and suggestions.
Thanks to you I was able to put things working, so here is the trick (which I guess only works for Maven projects):
I moved the allDepartments.json file to the default src/test/resources folder as suggested by you guys. I didn't even had to modify the pom.xml. And now everything works!
So this is my project structure now:
And the final Fixture.java code is:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.stream.Collectors;
public final class Fixture {
private final String fixture;
public Fixture(String fixtureName) throws IOException {
fixture = this.readFile(fixtureName);
}
private String readFile(String fileName) throws IOException {
final InputStream in = this.getClass().getClassLoader().getResource("fixtures/" + fileName).openStream();
final BufferedReader buffer = new BufferedReader(new InputStreamReader(in));
try {
return buffer.lines().collect(Collectors.joining("\n"));
} finally {
buffer.close();
}
}
public final String getFixture() {
return fixture;
}
}

Servlet does not open txt

Servlet is very good looking and reading files that have English names like hello.txt. It does not want to read files that have a Russian name, such pushkin.txt. Is anyone able to help to solve this problem?
Here is the code:
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class servlet extends HttpServlet {
/**
*
*/
private static final long serialVersionUID = 1L;
public static List<String> getFileNames(File directory, String extension) {
List<String> list = new ArrayList<String>();
File[] total = directory.listFiles();
for (File file : total) {
if (file.getName().endsWith(extension)) {
list.add(file.getName());
}
if (file.isDirectory()) {
List<String> tempList = getFileNames(file, extension);
list.addAll(tempList);
}
}
return list;
}
#SuppressWarnings("resource")
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException{
response.setContentType("text/html; charset=UTF-8");
String myName = request.getParameter("text");
List<String> files = getFileNames(new File("C:\\Users\\vany\\Desktop\\test"), "txt");
for (String string : files) {
if (myName.equals(string)) {
try {
File file = new File("C:\\Users\\vany\\Desktop\\test\\" + string);
FileReader reader = new FileReader(file);
int b;
PrintWriter writer = response.getWriter();
writer.print("<html>");
writer.print("<head>");
writer.print("<title>HelloWorld</title>");
writer.print("<body>");
writer.write("<div>");
while((b = reader.read()) != -1) {
writer.write((char) b);
}
writer.write("</div>");
writer.print("</body>");
writer.print("</html>");
}
finally {
if(reader != null) {
try{
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
}
}
The question is relevant, the problem is not solved
I thought that you have a problem whith the statements
for (String string : files) {
if (myName.equals(string)) {
I would compare in this way
for (File file: files) {
if (myName.equals(file.getName())) {
I hope that it help you.
Note: Thanks for the comments, you can try it.
Greetings
First of all I would use a debugger to check what's wrong with that code. It's quite difficult to find a bug without running the code. If you don't want to use a debugger print out all filenames found in the directory to ensure that some file names were found:
for (String string : files) {
System.out.println(string)
....
If files were found I would check whether I have rights to write to them. It might be that the application has not proper permissions to write in selected directory.
Are files "hello.txt" and pushkin.txt directly inside "C:\Users\vany\Desktop\test\" folder? Or is pushkin.txt file in another folder from "C:\Users\vany\Desktop\test\"?
Can you show us how you invoke the servlet?
If you have pushkin.txt in another folder and you invoke the servlet with something like "folder\pushkin.txt" it will not work because getFileNames() returns file names (without folder) and "myName.equals(string)" fails as "folder\pushkin.txt" is not equal to "pushkin.txt"

How to resize images in a directory?

This code attempts to resize images in a directory called "imgs". Unfortunately for some reason when I uncomment the listFiles(..) loop ImageIO.read(sourceImageFile) will return null. Yet processing the same file straightaway outside the loop (res("imgs/foto_3.jpg")) works. So apparently, this loop is preventing the files from being read. Solutions?
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import static org.imgscalr.Scalr.*;
public class App {
public static void main(String[] args) throws IOException {
// for (File sourceImageFile : new File("imgs").listFiles()) {
// res("imgs/"+sourceImageFile.getName());
// }
res("imgs/foto_3.jpg");
}
public static void res(String arg) throws IOException {
File sourceImageFile = new File(arg);
BufferedImage img = ImageIO.read(sourceImageFile);
BufferedImage thumbnail = resize(img, 500);
thumbnail.createGraphics().drawImage(thumbnail, 0, 0, null);
ImageIO.write(thumbnail, "jpg", new File("resized/" + sourceImageFile.getName()));
}
}
To reproduce the problem you can download the Maven project.
Can you change res to accept a File object rather than a String? Then you could write the following, which is a lot nicer:
for (File sourceImageFile : new File("imgs").listFiles()) {
res(sourceImageFile);
}
As to your original question, try adding some tracing statements or using a debugger to find what exactly gets passed to res.
I am not sure why the file listing iteration would mess with the thumbnail generation, but you mentioned privately to me that you were using imgscalr and were curious what the correct code to batch-process a directory would look like, so I wrote up this example code for you.
The code below will process any directory (imgs is hard-coded to stay consistent with your example code) and write it out to any other directory (resized is used to stay consistent -- feel free to change either directory)
import static org.imgscalr.Scalr.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class ScaleDirExample {
public static void main(String[] args) throws IOException {
File[] images = new File("imgs").listFiles();
for (File f : images) {
System.out.println("Processing: " + f.getName() + "...");
if (f.isDirectory()) {
System.out.println("\tSkipping, file is a directory...");
continue;
} else
process(f);
}
}
private static void process(File file) throws IOException {
// Load image.
BufferedImage image = ImageIO.read(file);
// Resize image.
image = resize(image, 500);
// Save the resized image as the thumbnail.
saveThumbnail(file, image);
}
private static void saveThumbnail(File originalFile, BufferedImage thumbnail)
throws IOException {
String filename = originalFile.getName();
// Determine file extension.
String fileExt = filename.substring(filename.lastIndexOf('.') + 1);
// Save the thumbnail to the resized dir.
ImageIO.write(thumbnail, fileExt, new File("resized/" + filename));
}
}
The call to resize(image, 500) can be modified to match any of the Scalr.resize methods - you can pass filters or improve quality if you want.
Example output from my test setup looks like:
Processing: flower-dog-gimp.jpg...
Processing: flower-dog.jpg...
Processing: logoOXdaComida.png...
Processing: mr-t-thumbnail.jpg...
Processing: mr-t.jpg...
Processing: problem-trans-peter-griffin.png...
Hope that helps!
OK, I figured it out. now it's working.
You (or whoever created the downloadable project u pasted here) are using Mac OS, and it automatically creates a .DS_Store file.
When you try to pass it to the res method, it doesn't know how to handle a non-image file and acts as null.
public static void main(String[] args) throws IOException {
for (File sourceImageFile : new File("imgs").listFiles()) {
if (sourceImageFile.getName().endsWith(".jpg"))
res(sourceImageFile.getAbsolutePath());
}
}
This is the modified void main method. it works, u can refine the if statement for more in depth filtering of wrong files.
Also I changed the argument given to the res method. it looks better now, as getAbsoulutePath returns the name and the path.
Let me know how it worked
One obvious way to debug this to output the specific sourceImageFile that ImageIO.read(..) is returning null on. I suspect its because listFiles will give you a list of all files and directories that are in the image directory. listFiles javadoc. You can prevent that by using a FileFilter that makes sure that listFiles only returns files and additionally files of the right type.
An example of a file filter that only returns file is below:
import java.io.FileFilter
class RegularFilesOnlyFileFilter implements FileFilter {
public boolean accept(File pathName) {
return pathName.isFile();
}
}
The way to use this filter is this - new File("imgs").listFiles(new RegularFilesOnlyFilesFilter())
I've changed res method:
public static void res(File arg) throws IOException {
if (arg.contains(".DS_Store")) {
return;
}
A mac-issue (or should filter non-image files, as was suggested)!

Why am I getting a NullPointerException when trying to read a file?

I use this test to convert txt to pdf :
package convert.pdf;
//getResourceAsStream(String name) : Returns an input stream for reading the specified resource.
//toByteArray : Get the contents of an InputStream as a byte[].
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.commons.io.IOUtils;
import convert.pdf.txt.TextConversion;
public class TestConversion {
private static byte[] readFilesInBytes(String file) throws IOException {
return IOUtils.toByteArray(TestConversion.class.getResourceAsStream(file));
}
private static void writeFilesInBytes(byte[] file, String name) throws IOException {
IOUtils.write(file, new FileOutputStream(name));
}
//just change the extensions and test conversions
public static void main(String args[]) throws IOException {
ConversionToPDF algorithm = new TextConversion();
byte[] file = readFilesInBytes("/convert/pdf/text.txt");
byte[] pdf = algorithm.convertDocument(file);
writeFilesInBytes(pdf, "text.pdf");
}
}
Problem:
Exception in thread "main" java.lang.NullPointerException
at org.apache.commons.io.IOUtils.copyLarge(IOUtils.java:1025)
at org.apache.commons.io.IOUtils.copy(IOUtils.java:999)
at org.apache.commons.io.IOUtils.toByteArray(IOUtils.java:218)
at convert.pdf.TestConversion.readFilesInBytes(TestConversion.java:17)
at convert.pdf.TestConversion.main(TestConversion.java:28)
I use the debugger, and the problem seems to be located here :
private static byte[] readFilesInBytes(String file) throws IOException {
return IOUtils.toByteArray(TestConversion.class.getResourceAsStream(file));
}
What is my problem?
Sounds like the resource probably doesn't exist with that name.
Are you aware that Class.getResourceAsStream() finds a resource relative to that class's package, whereas ClassLoader.getResourceAsStream() doesn't? You can use a leading forward slash in Class.getResourceAsStream() to mimic this, so
Foo.class.getResourceAsStream("/bar.png")
is roughly equivalent to
Foo.class.getClassLoader().getResourceAsStream("bar.png")
Is this actually a file (i.e. a specific file on the normal file system) that you're trying to load? If so, using FileInputStream would be a better bet. Use Class.getResourceAsStream() if it's a resource bundled in a jar file or in the classpath in some other way; use FileInputStream if it's an arbitrary file which could be anywhere in the file system.
EDIT: Another thing to be careful of, which has caused me problems before now - if this has worked on your dev box which happens to be Windows, and is now failing on a production server which happens to be Unix, check the case of the filename. The fact that different file systems handle case-sensitivity differently can be a pain...
Are you checking to see if the file exists before you pass it to readFilesInBytes()? Note that Class.getResourceAsStream() returns null if the file cannot be found. You probably want to do:
private static byte[] readFilesInBytes(String file) throws IOException {
File testFile = new File(file);
if (!testFile.exists()) {
throw new FileNotFoundException("File " + file + " does not exist");
}
return IOUtils.toByteArray(TestConversion.class.getResourceAsStream(file));
}
or better yet:
private static byte[] readFilesInBytes(String file) throws IOException {
InputStream stream = TestConversion.class.getResourceAsStream(file);
if (stream == null) {
throw new FileNotFoundException("readFilesInBytes: File " + file
+ " does not exist");
}
return IOUtils.toByteArray(stream);
}
This class reads a TXT file in the classpath and uses TextConversion to convert to PDF, then save the pdf in the file system.
Here TextConversion code :
package convert.pdf.txt;
//Conversion to PDF from text using iText.
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import convert.pdf.ConversionToPDF;
import convert.pdf.ConvertDocumentException;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.Font;
import com.lowagie.text.Paragraph;
import com.lowagie.text.pdf.PdfWriter;
public class TextConversion implements ConversionToPDF {
public byte[] convertDocument(byte[] documents) throws ConvertDocumentException {
try {
return this.convertInternal(documents);
} catch (DocumentException e) {
throw new ConvertDocumentException(e);
} catch (IOException e) {
throw new ConvertDocumentException(e);
}
}
private byte[] convertInternal(byte[] documents) throws DocumentException, IOException {
Document document = new Document();
ByteArrayOutputStream pdfResultBytes = new ByteArrayOutputStream();
PdfWriter.getInstance(document, pdfResultBytes);
document.open();
BufferedReader reader = new BufferedReader( new InputStreamReader( new ByteArrayInputStream(documents) ) );
String line = "";
while ((line = reader.readLine()) != null) {
if ("".equals(line.trim())) {
line = "\n"; //white line
}
Font fonteDefault = new Font(Font.COURIER, 10);
Paragraph paragraph = new Paragraph(line, fonteDefault);
document.add(paragraph);
}
reader.close();
document.close();
return pdfResultBytes.toByteArray();
}
}
And here the code to ConversionToPDF :
package convert.pdf;
// Interface implemented by the conversion algorithms.
public interface ConversionToPDF {
public byte[] convertDocument(byte[] documentToConvert) throws ConvertDocumentException;
}
I think the problem come from my file system (devbox on windows and server is Unix).
I will try to modify my classpath.
This problem may be caused by calling methods on test.txt, which can be a folder shortcut. In other words, you're calling a method on a file that doesn't exist, resulting in a NullPointerException.

Categories

Resources