I have looked at How to access resources in JAR file? and How do I copy a text file from a jar into a file outside of the jar? and many other questiions but couldnt actually get an answer. What I'm trying to do is copy contents of a file in res/CDC.txt that is in jar, to somewhere out of a jar. Now, on my computer it works but when I try it on different computer I get FileNotFoundException. So, I figured out why it works on mine. I have a CLASSPATH set to .;D:\myname\Java\JavaFiles where all my java files are located in packages. In "JavaFiles" directory there is also "res/CDC.txt". So, when I start my application, it first checks the current directory myapp.jar is located in for "res/CDC.txt", and then it checks "JavaFiles" and finds it. Other computers do not have it. So, this was my initial code:
public final class CT
{
//Other fields
private static CT ct;
private NTSystem nts;
private File f1;
private File f6;
private PrintWriter pw1;
private BufferedReader br1;
//Other fields
public static void main(String[] args)
{
try
{
showMessage("Executing program...");
ct = new CT();
ct.init();
ct.create();
ct.insertData();
//Other code
showMessage("Program executed!");
}
catch(Exception e)
{
showMessage("An exception occured! Program closed.");
e.printStackTrace();
System.exit(0);
}
}
private void init()
throws IOException
{
//Other initialization
nts = new NTSystem();
f1 = new File("C:\\Users\\" + nts.getName() + "\\blahblah");
f6 = new File("res\\CDC.txt");
br1 = new BufferedReader(new FileReader(f6));
//Other initialization
showMessage("Initialized");
}
private void create()
throws IOException
{
//Makes sure file/dir exists, etc
pw1 = new PrintWriter(new BufferedWriter(new FileWriter(f1)), true);
//Other Stuff
showMessage("Created");
}
private void insertData()
throws IOException
{
String line = br1.readLine();
while(line != null)
{
pw1.println(line);
line = br1.readLine();
}
//Other stuff
showMessage("Data inserted");
}
private static void showMessage(String msg)
{
System.out.println(msg);
}
}
which I changed to
public final class CT
{
//Other fields
private static CT ct;
private NTSystem nts;
private byte[] buffer;
private File f1;
private URL url1;
private FileOutputStream fos1;
private InputStream is1;
//Other fields
public static void main(String[] args)
{
try
{
showMessage("Executing program...");
ct = new CT();
ct.init();
ct.create();
ct.insertData();
//Other code
showMessage("Program executed!");
}
catch(Exception e)
{
showMessage("An exception occured! Program closed.");
e.printStackTrace();
System.exit(0);
}
}
private void init()
throws IOException
{
//Other initialization
nts = new NTSystem();
buffer = new byte[4096];
f1 = new File("C:\\Users\\" + nts.getName() + "\\blahblah");
url1 = getClass().getClassLoader.getResource("res\\CDC.txt"); //Also tried url1 = ct.getClass().getClassLoader.getResource("res\\CDC.txt"); or url1 = this.getClass().getClassLoader.getResource("res\\CDC.txt"); or url1 = CT.getClass().getClassLoader.getResource("res\\CDC.txt");
is1 = url1.openStream();
//Other initialization
showMessage("Initialized");
}
private void create()
throws IOException
{
//Makes sure file/dir exists, etc
pw1 = new PrintWriter(new BufferedWriter(new FileWriter(f1)), true);
//Other Stuff
showMessage("Created");
}
private void insertData()
throws IOException
{
int read = is1.read(buffer);
while(line != null)
{
fos1.write(buffer, 0, read);
read = is1.read(buffer);
}
//Other stuff
showMessage("Data inserted");
}
private static void showMessage(String msg)
{
System.out.println(msg);
}
}
And this time I always get NullPointerException. So, how to read folders and files that are within jar?
Thanks
You will want to use getSystemResourceAsStream() to read the contents from files in a jar.
This of course is assuming the file is actually on the classpath of the other users computers.
Related
I have a folder that must contain always one file config8, and if a new file is created in this folder the old file is deleted and replaced by the new file with the same name config8.
I write this code
File file1 = new File("/home/olfa/Bureau/config/config8");
File file2 = new File("/home/olfa/Bureau/config/config9");
while (file2.exists())
{
file1.delete();
file2.renameTo(file1); }
}
serverConnection = new ServerConnection("/home/olfa/Bureau/config/config8");
I need to add a loop to check everytime if config9 is created.
Instead of a loop try a WatchService.
Basically you would be watching a particular directory for change and then you can react on this change.
https://docs.oracle.com/javase/tutorial/essential/io/notification.html
For example :
import static java.nio.file.StandardWatchEventKinds.*;
WatchService watcher = FileSystems.getDefault().newWatchService();
Path dir = ...;
try {
WatchKey key = dir.register(watcher,
ENTRY_CREATE,
ENTRY_DELETE,
ENTRY_MODIFY);
} catch (IOException x) {
System.err.println(x);
}
Then you can process your key events.
If you have to solve this task with Java 1.6, you can use https://commons.apache.org/proper/commons-vfs/, version 2.1.
Here is an example for moving all incoming config files to "config8":
import org.apache.commons.vfs2.*;
import org.apache.commons.vfs2.impl.DefaultFileMonitor;
import java.io.File;
public class ConfigWatcher {
private static final String configDirName = "target/config";
private static final String configName = "config8";
private static final String absoluteConfigName = new File(configDirName + File.separator + configName).getAbsolutePath();
private FileSystemManager manager = null;
FileObject configDir = null;
private FileObject configFile = null;
private FileChangeEvent lastEvent = null;
public void watchConfig() throws Exception {
manager = VFS.getManager();
DefaultFileMonitor fm = new DefaultFileMonitor(new ConfigFileListener());
configFile = manager.resolveFile(absoluteConfigName);
configDir = manager.resolveFile(new File(configDirName).getAbsolutePath());
fm.setDelay(1000);
fm.addFile(configDir);
fm.start();
}
class ConfigFileListener implements FileListener {
public void fileCreated(FileChangeEvent fileChangeEvent) throws Exception {
FileObject latestConfigFile = fileChangeEvent.getFile();
String fileBaseName = fileChangeEvent.getFile().getName().getBaseName();
if (!configName.endsWith(fileBaseName) && !fileChangeEvent.equals(lastEvent)) {
System.out.println("new config detected - move config");
latestConfigFile.moveTo(configFile);
}
lastEvent = fileChangeEvent;
}
public void fileChanged(FileChangeEvent fileChangeEvent) {
}
public void fileDeleted(FileChangeEvent fileChangeEvent) {
}
}
public static void main(String[] args) throws Exception {
final ConfigWatcher configWatcher = new ConfigWatcher();
configWatcher.watchConfig();
while (true) {
Thread.sleep(1000);
}
}
}
I'm not sure if that's the right way to ask this, but I'm gonna try to explain my case and what I need.
I have a big java project, that upload files in many different java classes, like too many, and I have around 7 different main folders where the files are uploaded. The files at the moment are saved inside the webapp context, and I need to save them outside of context.
If there were only a few classes that upload these files I could spend a few days changing every class and direct it to a path outisde of context, but there are way too many classes, so I have to figure out a way to do it without changing every class, or any class at all, which would be ideal.
Every upload is done in the following way:
I get real path of one of my main folders:
String realpath = httpServletRequest.getSession()
.getServletContext()
.getRealPath("/mainfolder1/mainsubfolder1/");
Then I get the file and set custom file name:
FormFile file = myForm.getFile();
String contentType = file.getContentType();
String fileName = file.getFileName();
int fileSize = file.getFileSize();
customFileName = "anyName" + fileName.substring(fileName.lastIndexOf("."));
Then I validate and save the file:
if (fileSize > 0 && contentType != null && fileName.length() > 0){
InputStream in = file.getInputStream();
OutputStream bos = new FileOutputStream(realpath + "/" + customFileName);
int byteRead = 0;
byte[] buffer = new byte[8192];
while ((byteRead = in.read(buffer, 0, 8192)) != -1){
bos.write(buffer, 0, byteRead);
}
bos.close();
in.close();
}
Very simple way to save my files, and as you can see, they are saved inside context.
So if I could somehow override java.io.FileOutputStream, to not only save it inside context, but to make a copy outside of context too, that would be great, like save it in the specified path and also on some other path outside of context.
But I don't know if this is possible, or how to reproduce this behaviour.
What I need is to keep the class code exactly as it is but write the file 2 times:
First here: "/insideContext/mainfolder1/mainsubfolder1/"
Then here: "/outsideContext/mainfolder1/mainsubfolder1/"
Is this possible? If not, what would be the best way to accomplish this?
I'd refactor and use Decorator or Wrapper pattern. More about it here
Below some simple idea you could use.
public class ContextAwareDuplicatorOutputStream extends OutputStream {
FileOutputStream insideContext;
FileOutputStream outsideContext;
public ContextAwareDuplicatorOutputStream(String insideContextPath,
String outsideContextPath, String fileName)
throws FileNotFoundException {
insideContext = new FileOutputStream(insideContextPath
+ File.pathSeparator + fileName);
outsideContext = new FileOutputStream(outsideContextPath
+ File.pathSeparator + fileName);
}
#Override
public void close() throws IOException {
insideContext.close();
outsideContext.close();
}
#Override
public void flush() throws IOException {
insideContext.flush();
outsideContext.flush();
}
#Override
public void write(byte[] b) throws IOException {
insideContext.write(b);
outsideContext.write(b);
}
#Override
public void write(byte[] b, int off, int len) throws IOException {
insideContext.write(b, off, len);
outsideContext.write(b, off, len);
}
#Override
public void write(int b) throws IOException {
insideContext.write(b);
outsideContext.write(b);
}
}
Since you don't want to edit anything on your code, create a ServletContextListener that monitor the folder where you upload, and on the new file event, you copy it to the proper directory. Here is awnsered how to monitor a directory. Directory listener in Java
Below here is a small code, not really perfect, but the idea is there
public class FileMonitorServletContextListener implements
ServletContextListener {
public interface FileMonitor {
void start(String fromFolder, String toFolder);
void stop();
}
public class SimpleThreadedWatcher implements FileMonitor {
private class SimpleThread extends Thread {
private boolean running = true;
private String fromFolder;
private String toFolder;
public SimpleThread(String fromFolder, String toFolder) {
this.fromFolder = fromFolder;
this.toFolder = toFolder;
}
private void copy(Path child, String toFolder) {
// Copy the file to the folder
}
#Override
public void run() {
try {
WatchService watcher = FileSystems.getDefault()
.newWatchService();
Path fromPath = Paths.get(fromFolder);
watcher = FileSystems.getDefault().newWatchService();
WatchKey key = fromPath.register(watcher,
StandardWatchEventKinds.ENTRY_CREATE);
while (running) {
for (WatchEvent<?> event : key.pollEvents()) {
// Context for directory entry event is the file
// name of
// entry
#SuppressWarnings("unchecked")
WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path name = ev.context();
Path child = fromPath.resolve(name);
// print out event
System.out.format("%s: %s\n", event.kind().name(),
child);
copy(child, toFolder);
boolean valid = key.reset();
if (!valid) {
break;
}
}
Thread.sleep(1000);
}
} catch (Exception e) {
throw new RuntimeException("Error: ", e);
}
}
public void stopWorking() {
running = false;
}
}
private SimpleThread worker;
#Override
public void start(String fromFolder, String toFolder) {
worker = new SimpleThread(fromFolder, toFolder);
worker.start();
}
#Override
public void stop() {
worker.stopWorking();
}
}
FileMonitor fileMonitor = new SimpleThreadedWatcher();
#Override
public void contextDestroyed(ServletContextEvent arg0) {
fileMonitor.stop();
}
#Override
public void contextInitialized(ServletContextEvent arg0) {
fileMonitor.start("FROM", "TO");
}
}
I have a program that requires that an internal file (DICTIONARY) be copied to the user's computer into the folder defined like so:
private static final String DIC_NAME = "WordHelp.dic";
private static final String DIC_FOLDER = System.getProperty("user.home");
private static final String PATH_SEP = System.getProperty("file.separator");
public static final String DICTIONARY = DIC_FOLDER + PATH_SEP + DIC_NAME;
Here's what works on MY computer, where all the Java stuff is:
public static void createDictionaryIfNecessary() throws IOException{
Path out_path = FileSystems.getDefault().getPath(DICTIONARY);
boolean dic_exists = Files.exists(out_path,
new LinkOption[]{LinkOption.NOFOLLOW_LINKS});
if(dic_exists)
return;
File file = new File("src/dictionary"); // here's problem for user ************
Path in_path = file.toPath();
try {
Files.copy(in_path, out_path,
REPLACE_EXISTING, COPY_ATTRIBUTES, NOFOLLOW_LINKS);
} catch (IOException e) { JOptionPane.showMessageDialog(null, e); }
}
But user gets this error:
java.nio.file.NoSuchFileException: src\dictionary
SOURCE file (internal to .jar file) can't be found.
If I look at in_path while debugging, the value is:
(sun.nio.fs.Windowspath) src/dictionary
And below is a bunch of info about in_path:
This all works on MY computer and I could have sworn that it ONCE worked on a user's computer...
How should I define file (see line with ********** to enable copying internal file (src/dictionary) onto a user's computer?
Here's Netbeans project view:
I worked around it by using a Scanner to read individual strings from the internal file instead of using Files.copy. Here's the code. (It's not quite as fast using Scanner, but it works.)
public static void write(FileOutputStream outfile, String s) {
try {
for(int i = 0; i < s.length(); i++)
outfile.write(s.charAt(i));
outfile.write(13); outfile.write(10);
} catch (IOException ex) {JOptionPane.showMessageDialog(null, ex);}
}
public static Scanner openDic(){
InputStream myStream = null;
try { myStream = Class.forName("masterwords.Masterwords").getClassLoader()
.getResourceAsStream("dictionary");
}catch (ClassNotFoundException ex) {/* ... */}
return new Scanner(myStream).useDelimiter("\r");
}
public static void createDictionaryIfNecessary(){
Path out_path = FileSystems.getDefault().getPath(DICTIONARY);
if(Files.exists(out_path, new LinkOption[]{LinkOption.NOFOLLOW_LINKS})) return;
FileOutputStream outStream = null;
try {outStream = new FileOutputStream(out_path.toFile());}
catch (FileNotFoundException ex) {JOptionPane.showMessageDialog(null,ex);}
Scanner scInternalDic = IO.openDic();
while(scInternalDic.hasNext()){
Utilities.write(outStream,scInternalDic.next());
}
try {outStream.close();}
catch (IOException ex) {JOptionPane.showMessageDialog(null,ex);}
scInternalDic.close();
}
I'm try to create one simple reservation system, we'll read a file, then we'll add Train, Bus, etc., then we'll writer everything to output.
import java.io.*;
import java.util.*;
public class Company
{
private static ArrayList<Bus> bus = new ArrayList<Bus>();
static int buscount = 0, traincount = 0;
public static void main (String[] args) throws IOException
{
FileParser();
}
public Company()
{
}
public static void FileParser()
{
try {
File file = new File(); //i fill this later
File file2 = new File(); // i fill this later
FileInputStream fis = new FileInputStream(file);
FileOutputStream fos = new FileOutputStream(file2);
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos));
String line;
while ((line = br.readLine()) != null)
{
String[] splitted = line.split(",");
if(splitted[0].equals("ADDBUS"))
{
bus.add(buscount) = Bus(splitted[0],splitted[1],splitted[2],splitted[3],splitted[4],splitted[5]);
}
}
}
catch (FileNotFoundException fnfe) {
}
catch (IOException ioe) {
}
}
}
I try to read the file line by line. For example one of the line is "ADDBUS,78KL311,10,140,54" I split the line for "," then i try to add every pieces of array to Bus' class' constructor but i couldn't figured it out.
My Bus Class is like `
public class Bus extends Vehicle{
private String command;
private String busName;
private String busPlate;
private String busAge;
private String busSpeed;
private String busSeat;
public Bus(String command, String busname, String busplate, String busage, String busspeed, String busseat)
{
this.command = command;
this.busName = busname;
this.busPlate = busplate;
this.busAge = busage;
this.busSpeed = busspeed;
this.busSeat = busseat;
}
public String getBusName() {
return busName;
}
public void setBusName(String busName) {
this.busName = busName;
}
public String getBusPlate() {
return busPlate;
}
public void setBusPlate(String busPlate) {
this.busPlate = busPlate;
}
public String getBusAge() {
return busAge;
}
public void setBusAge(String busAge) {
this.busAge = busAge;
}
public String getBusSpeed() {
return busSpeed;
}
public void setBusSpeed(String busSpeed) {
this.busSpeed = busSpeed;
}
public String getBusSeat() {
return busSeat;
}
public void setBusSeat(String busSeat) {
this.busSeat = busSeat;
}
public String getCommand() {
return command;
}
public void setCommand(String command) {
this.command = command;
}
}
can someone show me a way to solve this problem?
Thank you,
You are missing the keyword new to create a new instance of the class:
bus.add(new Bus(...));
You can add items to ArrayList like this
bus.add( new Bus(splitted[0],splitted[1],splitted[2],splitted[3],splitted[4],splitted[5]));
you were missing new keyword before Bus constructor call. Then you can increment the counter (or do whatever)
bus.add( new Bus(splitted[0],splitted[1],splitted[2],splitted[3],splitted[4],splitted[5]));
buscount++;
try to add new Bus(...)
bus.add( new
Bus(splitted[0],splitted[1],splitted[2],splitted[3],splitted[4],splitted[5]));
As I understand if you want to call constructor you need to call new Bus(parms).
when you say new it will call constructor of your class
when you say this() again it going to call enclosing class' constructor
if you say super() it will call super class' constructor.
if you want it into a map order by counter you can use this:
Map(Integer, Bus) busPosition = new HashMap<>();
busPosition.put(buscount, new
Bus(splitted[0],splitted[1],splitted[2],splitted[3],splitted[4],splitted[5]));
I have a little problem here.
I am declaring a new object "Fleet" in these methods:
public void run() throws FileNotFoundException
{
File file = new File(getFile());
Fleet fleet = new Fleet(file);
buildFleet(file, fleet);
}
private void buildFleet(File file, Fleet fleet) throws FileNotFoundException
{
fleet.addVehicle(Honda);
userMenu(fleet);
}
The last line calls the userMenu() method. In this method I need to be able to change the value of "File" inside Fleet without creating a new instance of the class.
private void userMenu(Fleet fleet) throws FileNotFoundException
{
PrintWriter pw = new PrintWriter("temp.txt");
File file = new File("temp.txt");
fleet = new Fleet(file);
this.createMenu();
choice = this.menu.getChoice();
while(choice != 8)
{
switch(choice)
{
case 1:
//Do stuff
fleet.addVehicle(Honda);
break;
}
}
Also, I am not allowed to create any new class level data.
Any suggestions?
What about a setter on your Fleet class for the file:
public class Fleet {
private File file;
...
public void setFile( File file ){
this.file = file;
}
}
You can then call this method to change the file inside your fleet object by calling
fleet.setFile( myNewFile );
Solved:
I changed:
private void userMenu() throws FileNotFoundException
{
PrintWriter pw = new PrintWriter("temp.txt");
File file = new File("temp.txt");
to:
private void userMenu(Fleet fleet) throws FileNotFoundException
{
PrintWriter pw = new PrintWriter("temp.txt");
File file = new File("temp.txt");
fleet.file = file;