I am not all that knowledgeable in Java, and I'm trying to use some Java in a Processing.org 3 project. I have managed to reconstruct the problem in a small Processing example project called testprocjavapath
- and I am posting a bash
script (called testProcJavaLoadpath.sh
) that reconstructs the example project files at the end of this post, and runs the project once. The testprocjavapath
project files look like this:
~/sketchbook/testprocjavapath
├── testprocjavapath.pde
├── myprops.properties
└── MyJavaClass.java
When running the script, I get this:
$ bash testProcJavaLoadpath.sh
...
There was an exception myprops.properties: java.lang.NullPointerException : null
The properties file content is 'null';
Finished.
Debugging in Processing 3 IDE GUI, this error occurs on exactly the line properties.load(in);
:
... because the line InputStream in = MyJavaClass.class.getResourceAsStream(inFileName);
failed, and as a result, in
is a null pointer.
That much I understand - what I don't understand is this: how do I load a, say, .properties
text file, in the same directory as the .pde
Processing sketch file and the .java
file (that is, this particular sketch folder)?
As far as I gather the Java getResourceAsStream()
actually is used to load from a Java application packed as a .jar
file - so can it work for reading files from hard disk, that are not yet packed as .jar
files?
If not - I have also tried to do:
InputStream in = new FileInputStream( new File(PROPERTIES_FILENAME));
... but this didn't work either ( in
is again null
).
So, what command can I use in the .java
file, to load the myprops.properties
file? And if I should end up packing the whole Processing app as a .jar
file (not sure if Processing can do this, haven't looked it up yet), would I have to change that command?
Here is the testProcJavaLoadpath.sh
file (make sure you change PROCBINPATH
to your Processing install path):
PROCSKETCHDIR="~/sketchbook"
PROCSKETCHDIR="${PROCSKETCHDIR/#\~/$HOME}" # expand home dir ~
echo "$PROCSKETCHDIR"
PROCBINPATH="/PATH/TO/processing-3.3.6" # path/location of Processing executable `processing-java`
MYSKETCH="testprocjavapath"
MYSKETCHDIR="$PROCSKETCHDIR/$MYSKETCH"
# reconstruct folder:
rm -rfv "$MYSKETCHDIR"
mkdir -v "$MYSKETCHDIR"
echo "generating $MYSKETCHDIR/$MYSKETCH.pde"
cat > "$MYSKETCHDIR/$MYSKETCH.pde" <<'EOF'
void setup() {
size(640, 360); // Size should be the first statement
MyJavaClass myjc = new MyJavaClass();
String thefilecontents = myjc.GetPropsFileContent();
System.out.format("The properties file content is '%s';%n", thefilecontents);
}
EOF
echo "generating $MYSKETCHDIR/myprops.properties"
cat > "$MYSKETCHDIR/myprops.properties" <<'EOF'
teststr=HelloWorld
EOF
echo "generating $MYSKETCHDIR/MyJavaClass.java"
cat > "$MYSKETCHDIR/MyJavaClass.java" <<'EOF'
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.io.FileInputStream;
import java.io.File;
import java.io.ByteArrayOutputStream;
public class MyJavaClass {
private static final String PROPERTIES_FILENAME = "myprops.properties";
/**
* add a constructor
*/
public static void MyJavaClass() {
}
public static String GetPropsFileContent() {
String myret = null;
myret = readgetFileContent(PROPERTIES_FILENAME);
return myret;
}
public static String readgetFileContent(String inFileName) {
String result = null;
Properties properties = new Properties();
try {
InputStream in = MyJavaClass.class.getResourceAsStream(inFileName);
properties.load(in);
ByteArrayOutputStream resultbaos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
while ((length = in.read(buffer)) != -1) {
resultbaos.write(buffer, 0, length);
}
result = resultbaos.toString();
} catch (IOException e) {
System.err.println("There was an error reading " + inFileName + ": " + e.getCause()
+ " : " + e.getMessage());
} catch (Exception e) {
System.err.println("There was an exception " + inFileName + ": " + e
+ " : " + e.getMessage());
}
return result;
}
}
EOF
# run once:
"$PROCBINPATH"/processing-java --sketch="$MYSKETCHDIR" --run
Ok, I managed to get somewhere - but I'd still like a more qualified answer.
First of all, it turns out, Processing as such expect files to be read (like myprops.properties
in the OP example), to be stored in a data
subfolder of the sketch folder:
https://processing.org/tutorials/data/
And just as with image files, these text files should be placed in the sketch's “data” directory in order for them to be recognized by the Processing sketch.
So far so good - and indeed, inside the Processing .pde
sketch, we can use (say) loadStrings("myprops.properties");
, and the file at data/myprops.properties
will be read. However, I don't need to read the file there - I need to read it in the supporting .java
class.
Now, when you run the Processing patch (either from IDE, or from the command line), what happens is that Processing copies the source files from the sketch folder, inside a temporary folder in the /tmp
folder (at least on Linux); here's how that file structure looks like:
/tmp/testprocjavapath9179591342074530534temp/
├── MyJavaClass.class
├── source
│ ├── MyJavaClass.java
│ └── testprocjavapath.java
└── testprocjavapath.class
Notice that we have .java
source files, and .class
"compiled" files, but there is no data
subfolder or myprops.properties
file anywhere!
Now, notice also that what used to be testprocjavapath.pde
in the source sketch folder, becomes testprocjavapath.java
(and corresponding .class
) in the temporary folder; notice that testprocjavapath.java
defines:
public class testprocjavapath extends PApplet {
Now, the loadStrings
is actually a method of the PApplet
class; so if we read through it a bit:
https://github.com/processing/processing/blob/master/core/src/processing/core/PApplet.java
dataPath(String where)
: .... The data path is handled differently on each platform, and should not be considered a location to write files. It should also not be assumed that this location can be read from or listed. ... Libraries should use createInput() to get an InputStream or createOutput() to get an OutputStream. sketchPath() can be used to get a location relative to the sketch. Again, use this to get relative locations of files. 使用它来获取文件的相对位置。 ...
... we can see a method dataPath
, however it's usage is not recommended. On the other hand, there is a method sketchPath
- however, this method will only return the correct path (ie a sketch in ~/sketchbook
in this example) if called from the top-level .pde
file! If you try to have the class in the .java
file defined as extends PApplet
, and then call sketchPath
from there - it will simply return the current working directory!
So the solution for now is:
.java
class accept an input argument in the constructor, which will be used to record the proper sketch path: public MyJavaClass(String inSketchPath) {
sketchPath()
passed to the .java
class instance during instantiation in the .pde
file: MyJavaClass myjc = new MyJavaClass( sketchPath() );
.java
class, to calculate the absolute path of the .properties
file, and then load it with new FileInputStream( new File(theFilePath) );
( not with getResourceAsStream
!) Below a changed testProcJavaLoadpath.sh
is pasted, that has these modifications, and in principle, works - this is the terminal output:
$ bash testProcJavaLoadpath.sh
...
Sketch first lines: 'teststr=HelloWorld';
Sketch dataFile: '~/sketchbook/testprocjavapath/data/myprops.properties';
Sketch sketchPath: '~/sketchbook/testprocjavapath';
:: mySketchPath: '~/sketchbook/testprocjavapath'
:: The URL is 'file:/tmp/testprocjavapath4709659129218148940temp/';
:: name: MyJavaClass.class
:: resourcePath: file:/tmp/testprocjavapath4709659129218148940temp/MyJavaClass.class
:: theFilePath: '~/sketchbook/testprocjavapath/data/myprops.properties'
:: properties: key 'teststr' => value 'HelloWorld'
The properties file content is 'teststr=HelloWorld
';
... however, I imagine if I should want to pack this code in a .jar
or executable application/file, this approach would likely fail - which is why I'd still like a more qualified answer.
The changed testProcJavaLoadpath.sh
is this:
PROCSKETCHDIR="~/sketchbook"
PROCSKETCHDIR="${PROCSKETCHDIR/#\~/$HOME}" # expand home dir ~
echo "$PROCSKETCHDIR"
PROCBINPATH="/PATH/TO/processing-3.3.6" # path/location of Processing executable `processing-java`
MYSKETCH="testprocjavapath"
MYSKETCHDIR="$PROCSKETCHDIR/$MYSKETCH"
# reconstruct folder:
rm -rfv "$MYSKETCHDIR"
mkdir -v "$MYSKETCHDIR"
# https://processing.org/tutorials/data/
# "And just as with image files, these text files should be placed in the sketch’s “data” directory in order for them to be recognized by the Processing sketch."
# processing.core.PApplet.loadStrings - https://processing.github.io/processing-javadocs/core/
# https://github.com/processing/processing/blob/master/core/src/processing/core/PApplet.java
# "dataPath(String where): The data path is handled differently on each platform, and should not be considered a location to write files. It should also not be assumed that this location can be read from or listed. ... Libraries should use createInput() to get an InputStream or createOutput() to get an OutputStream. sketchPath() can be used to get a location relative to the sketch. Again, <b>do not</b> use this to get relative locations of files."
echo "generating $MYSKETCHDIR/$MYSKETCH.pde"
cat > "$MYSKETCHDIR/$MYSKETCH.pde" <<'EOF'
void setup() {
size(640, 360); // Size should be the first statement
String[] lines = loadStrings("myprops.properties"); // reads from data/myprops.properties
System.out.format("Sketch first lines: '%s';%n", lines[0]);
System.out.format("Sketch dataFile: '%s';%n", dataFile("myprops.properties")); // ~/sketchbook/testprocjavapath/data/myprops.properties
System.out.format("Sketch sketchPath: '%s';%n", sketchPath()); // ~/sketchbook/testprocjavapath
MyJavaClass myjc = new MyJavaClass( sketchPath() );
String thefilecontents = myjc.GetPropsFileContent();
System.out.format("The properties file content is '%s';%n", thefilecontents);
}
EOF
mkdir -v "$MYSKETCHDIR/data"
echo "generating $MYSKETCHDIR/data/myprops.properties"
cat > "$MYSKETCHDIR/data/myprops.properties" <<'EOF'
teststr=HelloWorld
EOF
echo "generating $MYSKETCHDIR/MyJavaClass.java"
cat > "$MYSKETCHDIR/MyJavaClass.java" <<'EOF'
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream; // "InputStream is by definition not seekable."
import java.io.InputStreamReader;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.io.FileInputStream; // is seekable
import java.io.File;
import java.io.ByteArrayOutputStream;
import java.net.URL;
//import processing.core.*;
import java.nio.file.Path;
import java.nio.file.Paths;
public class MyJavaClass {
private static final String PROPERTIES_FILENAME = "myprops.properties";
public String mySketchPath;
/**
* add a constructor
*/
public MyJavaClass(String inSketchPath) {
mySketchPath = inSketchPath;
}
public String GetPropsFileContent() {
//System.out.format(":: sketchPath: '%s'%n", sketchPath()); // if `MyJavaClass extends PApplet`, then sketchPath() just prints current working directory!
System.out.format(":: mySketchPath: '%s'%n", mySketchPath);
getLocations();
String myret = null;
myret = readgetFileContent(PROPERTIES_FILENAME);
return myret;
}
public String readgetFileContent(String inFileName) {
String result = null;
Properties properties = new Properties();
try {
//String theFilePath = inFileName; // verbatim relative path fails
Path inFileNameSketchPath = Paths.get(mySketchPath, "data", inFileName); // OS path join
String theFilePath = inFileNameSketchPath.toString();
System.out.format(":: theFilePath: '%s'%n", theFilePath);
//InputStream in = MyJavaClass.class.getResourceAsStream(theFilePath); // no can do, is 'null', also w/ abs path
//InputStream in = new FileInputStream( new File(theFilePath) ); // OK, but not seekable
FileInputStream in = new FileInputStream( new File(theFilePath) );
properties.load(in);
// double-check loaded properties:
for(String key : properties.stringPropertyNames()) {
String value = properties.getProperty(key);
System.out.format(":: properties: key '%s' => value '%s'%n", key, value);
}
ByteArrayOutputStream resultbaos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int length;
in.getChannel().position(0); // do reset - seek 0 (start), to reset stream again for reading
while ((length = in.read(buffer)) != -1) {
resultbaos.write(buffer, 0, length);
}
result = resultbaos.toString();
} catch (IOException e) {
System.err.println("There was an error reading " + inFileName + ": " + e.getCause()
+ " : " + e.getMessage());
} catch (Exception e) {
System.err.println("There was an exception " + inFileName + ": " + e
+ " : " + e.getMessage());
}
return result;
}
public void getLocations() {
URL classURL = getClass().getProtectionDomain().getCodeSource().getLocation();
System.out.format(":: The URL is '%s';%n", classURL); // file:/tmp/testprocjavapath3056820301028631180temp/
String s = getClass().getName();
int i = s.lastIndexOf(".");
if(i > -1) s = s.substring(i + 1);
s = s + ".class";
System.out.println(":: name: " + s);
Object resourcePath = this.getClass().getResource(s);
System.out.println(":: resourcePath: " + resourcePath); // file:/tmp/testprocjavapath9185318125154993853temp/MyJavaClass.class
}
}
EOF
# run once:
"$PROCBINPATH"/processing-java --sketch="$MYSKETCHDIR" --run
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.