We have an application that uses sevenzipjbinding to bundle and transport content. 99% of the time, everything goes smoothly, however there are some edge cases where these zip files contain upwards of a hundred thousand files. When this happens, the number of file handles that the process has a hold on gets too high and then things start to fail.
I haven't been able to find a recommended way to handle this issue. Currently, we create a RandomAccessFileInStream upon request using an instance of IOutCreateCallback, but these never get closed until the very end. There is no apparent method in the interface designed to signify when a stream should be closed.
Currently, my only idea is to keep track of the "last" file that was requested and then every time a new one is requested to close the last file. If I do this though, I'm worried that I risk that 7zip wants to read the file a second time.
Is this a bug in my code, or is it in sevenzipjbinding?
Current implementation:
public ISequentialInStream getStream(int i) throws SevenZipException {
ISequentialInStream is = null;
try {
AbstractItem item = getItem(i);
if (item instanceof FileItem) {
is = new RandomAccessFileInStream(((FileItem) item).getRandomAccessFile());
} else if (item instanceof StreamItem) {
is = new InputStreamSequentialInStream(((StreamItem) item).getInputStream());
}
} catch (IOException e) {
throw new SevenZipException("IO Error!", e);
} finally {
closeables.add(is);
}
return is;
}
Related
I am sending text to an input field then within the array of elements searching for specific by checking if element text equal street address
As you can see i have Thread sleep which is not the right approach, the reason i am using this is because i am not to sure how to proceed with the current state of the default values.
When i click on input field there are some values by default within the web elements i am locating which means if i remove Thread sleep it will fail due to the fact that the default values will be assigned to the web elements so i am wondering what can i do in such case
Since i know the default values i could potential just find one and wait until it disappears but thinking if there is a better approach to deal with it
default values on input click
the text i am then entering
public LocatedCarParksMap searchForCarPark(String carPark,String location) {
clickOnSearchInput();
searchCarPark.sendKeys(carPark);
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (WebElement i : locatedCarParkAddresses) {
if(i.getText().equals(location)){
click(i);
}
}
return new LocatedCarParksMap(driver);
}
You can look for some loaders that are appearing in between you enter the search text and the search result appearance. You can wait for that loader to vanish. After that, you can look for your search results.
Even if you have/don't have loader, this similar FluentWait approach should work:
public LocatedCarParksMap searchForCarPark(String carPark,String location) {
clickOnSearchInput();
searchCarPark.sendKeys(carPark);
//If loader available, write code here to wait for your loader to vanish. Just a simple invisibilityOf should work.
boolean clickStatus = waitAndClickSearchElement(location);
if(! clickStatus) {
throw new Exception(carPark + " is not available in Search Results");
}
return new LocatedCarParksMap(driver);
}
public boolean waitAndClickSearchElement() {
try {
//Change this timeout of 10 sec according to your need.
WebDriverWait webDriverWait = new WebDriverWait(driver,10);
//Increased the polling time to 2 secs to avoid checking the results frequently.
webDriverWait.pollingEvery(Duration.ofSeconds(2))
//Ignoring these 2 exceptions as they are more likely to occur.
webDriverWait.ignoring(NoSuchElementException.class, StaleElementReferenceException.class);
//This until method keeps checking the text for 10 secs. Once it finds the element, will click on it.
return webDriverWait.until(driver -> {
for (WebElement weAddress : locatedCarParkAddresses) {
if(weAddress.getText().equals(location)){
weAddress.click();
return true;
}
}
return false;
});
} catch (Exception ex) {
ex.printStackTrace();
}
return false;
}
There is one edge case here. Lets say your expected search result is already part of default search results, the above code may throw StaleElementReferenceException. But we have handled it so I hope it would work fine. Please let me know if you have any issues.
Working on an android app which gathers data from the Open Weather API as a JSON. However the JSON does not always contain the same keys (ie. sometimes cloud data or a weather description is included, sometimes it isn't).
Right now my code looks like (with some extra getters/setters I didn't include here):
public class WeatherDescrip {
private String weather;
private String weather_Desc;
private String icon;
public WeatherDescrip(JSONObject weatherObj) {
try {
weather = weatherObj.getString("main");
} catch (JSONException e) {
weather = null;
e.printStackTrace();
}
try {
weather_Desc = weatherObj.getString("description");
} catch (JSONException e) {
weather_Desc = null;
e.printStackTrace();
}
try {
icon = weatherObj.getString("icon");
} catch (JSONException e) {
icon = null;
e.printStackTrace();
}
}
}
Basically if the JSON I get from the API call doesn't have the necessary key I let the program throw an exception, which will usually happen with at least one piece of data each time the app is run (there is more done like this).
If anyone could please let me know whether this is an acceptable way to code, and possibly how to better implement this I would much appreciate it.
If you haven't noticed I'm also a total noob, sorry in advance if this is a terrible way of doing this.
Many Thanks
This is generally not the correct forum for asking opinions, as you're asking for subjective opinions, there's technically no way to gauge a 'correct' answer, although you're free to select whatever answer you choose, if any ;-)
But in the nature of good will, I'll give you a few of my opinions.
Firstly, Exceptions are for just that, exceptions. If you have a scenario where you are in control of the code, and are aware of a potential for something not to occur in an 'ideal' way (e.g. like this, you're receiving dodgy data), then code for it, i.e.
if (data.contains("somethingOfInterest")) {
consume(data);
} else {
getDataFromSomewhereElse();
}
Rather than throw an exception, and force your program to handle it somewhere else (or not). Here's some additional information on why it's not a good idea to use exceptions for control flow.
Also, and this is advice from personal experience; in most scenarios, it's a good idea to do as little as makes sense within an Object's constructor, as it's more ugly to recover if exceptions do occur inside a constructor's method body. Instead, it may be better to encapsulate the logic you have there in some other factory-esque class or method, passing only the gathered data to the constructor. Something like:
public class WeatherDescrip {
private String weather;
private String weather_Desc;
private String icon;
public WeatherDescrip(String weather, String weather_Desc, String icon) {
this.weather = weather;
this.weather_Desc = weather_Desc;
this.icon = icon;
}
}
...
public static WeatherDescrip createWeatherDescrip(JSONObject weatherObj) {
if (!weatherObj.containsKey("main")
|| !weatherObj.containsKey("description")
|| !weatherObj.containsKey("icon")) {
throw SomeNewMeaningfulException("That I understand and can explicitly handle");
or....
return getMyDataFromSomeWhereElse();
}
return new WeatherDescrip(
weatherObj.getString("main"),
weatherObj.getString("description"),
weatherObj.getString("icon")
);
}
I hope this helps.
It's acceptable to throw exceptions whenever you decide. You just need to play how you want to handle it.
Is it acceptable to crash the program and boot your user back to the home screen? Absolutely not. Ever
Just read your data and handle the exceptions gracefully - no icon? Display a default. No data? Tell the user there is a problem right now so they aren't misled by the old data being displayed.
An alternate to avoid the majority of exceptions is to use GSON and Retrofit (I've linked a useful set of tutorials, not the home of GSON or Retrofit). With GSON you can create a model object, automatically map the data and then on your getters always return a value even if the JSON was incomplete
Example:
class MyObj {
#SerializedName("main")
private String weather;
public String getWeather() {
String weatherResult = weather;
if (weatherResult == null || "".equals(weatherResult) {
weatherResult = getString(R.strings.weather_unavailable);
}
return weatherResult;
}
}
Throwing an exception is usually reserved for when an error occurs, rather than having it it being an expected result of running your code, since there is overhead in throwing an exception which can make your program execute (slightly) slower.
Realistically, it can be used whenever you like, however you like, but I might instead suggest using has() to check if the key exists before trying to access it. It's a more efficient way of achieving the same result, without having to throw or catch an exception.
if(weatherObj.has('description')) {
weather_Desc = weatherObj.getString("description");
} else {
weather_Desc = null;
}
please, could anyone tell me, how can i check if file exists on URL where is only FTP protocol? Im using this code:
public boolean exists(String URLName) throws IOException {
input = null;
boolean result = false;
try {
input = new URL(URLName).openStream();
System.out.println("SUCCESS");
result = true;
} catch (Exception e) {
System.out.println("FAIL");
} finally {
if (input != null) {
input.close();
input = null;
}
}
return result;
}
It doesnt work when i send there more then one or two, it just sais
sun.net.ftp.FtpProtocolException: Welcome message: 421 Too many connections (2) from this IP
at sun.net.ftp.FtpClient.openServer(FtpClient.java:490)
at sun.net.ftp.FtpClient.openServer(FtpClient.java:475)
at sun.net.www.protocol.ftp.FtpURLConnection.connect(FtpURLConnection.java:270)
at sun.net.www.protocol.ftp.FtpURLConnection.getInputStream(FtpURLConnection.java:352)
at java.net.URL.openStream(URL.java:1010)
at bibparsing.PDFImage.exists(PDFImage.java:168)
at bibparsing.PDFImage.main(PDFImage.java:189)
It works great when the protocol is HTTP. I mean adresses like:
ftp://cmp.felk.cvut.cz/pub/cmp/articles/chum/Chum-TR-2001-27.pdf
ftp://cmp.felk.cvut.cz/pub/cmp/articles/martinec/Kamberov-ISVC2006.pdf
and something like that
The problem here is that this method isn't thread safe; if two threads use this method simultaneously one can overwrite the instance variable named input, causing the other thread to not closing the connection it opened (and closing either nothing, or the connection opened by the other thread).
This is easily fixed, by making the input variable local:
InputStream input=null;
Code style: within a method, you can return the result as soon as you know it. Beginners often declare the variables first, then execute the logic and return the result at the end of the method. You can save a lot of code and complexity by
declaring variables as late as possible (when you first need them)
declaring as few variables as necessary (readability is always a good reason to add variables, but less variables means less complexity)
returning as soon as you know the result (reducing paths through your code, and thus reducing complexity)
The code can be simply written as:
public static boolean exists (String urlName) throws IOException {
try {
new URL(urlName).openStream().close();
return true;
} catch (IOException e) {
return false;
}
}
I need to be able to re-use a java.io.InputStream multiple times, and I figured the following code would work, but it only works the first time.
Code
public class Clazz
{
private java.io.InputStream dbInputStream, firstDBInputStream;
private ArrayTable db;
public Clazz(java.io.InputStream defDB)
{
this.firstDBInputStream = defDB;
this.dbInputStream = defDB;
if (db == null)
throw new java.io.FileNotFoundException("Could not find the database at " + db);
if (dbInputStream.markSupported())
dbInputStream.mark(Integer.MAX_VALUE);
loadDatabaseToArrayTable();
}
public final void loadDatabaseToArrayTable() throws java.io.IOException
{
this.dbInputStream = firstDBInputStream;
if (dbInputStream.markSupported())
dbInputStream.reset();
java.util.Scanner fileScanner = new java.util.Scanner(dbInputStream);
String CSV = "";
for (int i = 0; fileScanner.hasNextLine(); i++)
CSV += fileScanner.nextLine() + "\n";
db = ArrayTable.createArrayTableFromCSV(CSV);
}
public void reloadDatabase()//A method called by the UI
{
try
{
loadDatabaseToArrayTable();
}
catch (Throwable t)
{
//Alert the user that an error has occurred
}
}
}
Note that ArrayTable is a class of mine, which uses arrays to give an interface for working with tables.
Question
In this program, the database is shown directly to the user immediately after the reloadDatabase() method is called, and so any solution involving saving the initial read to an object in memory is useless, as that will NOT refresh the data (think of it like a browser; when you press "Refresh", you want it to fetch the information again, not just display the information it fetched the first time). How can I read a java.io.InputStream more than once?
You can't necessarily read an InputStream more than once. Some implementations support it, some don't. What you are doing is checking the markSupported method, which is indeed an indicator if you can read the same stream twice, but then you are ignoring the result. You have to call that method to see if you can read the stream twice, and if you can't, make other arrangements.
Edit (in response to comment): When I wrote my answer, my "other arrangements" was to get a fresh InputStream. However, when I read in your comments to your question about what you want to do, I'm not sure it is possible. For the basics of the operation, you probably want RandomAccessFile (at least that would be my first guess, and if it worked, that would be the easiest) - however you will have file access issues. You have an application actively writing to a file, and another reading that file, you will have problems - exactly which problems will depend on the OS, so whatever solution would require more testing. I suggest a separate question on SO that hits on that point, and someone who has tried that out can perhaps give you more insight.
you never mark the stream to be reset
public Clazz(java.io.InputStream defDB)
{
firstDBInputStream = defDB.markSupported()?defDB:new BufferedInputStream(defDB);
//BufferedInputStream supports marking
firstDBInputStream.mark(500000);//avoid IOException on first reset
}
public final void loadDatabaseToArrayTable() throws java.io.IOException
{
this.dbInputStream = firstDBInputStream;
dbInputStream.reset();
dbInputStream.mark(500000);//or however long the data is
java.util.Scanner fileScanner = new java.util.Scanner(dbInputStream);
StringBuilder CSV = "";//StringBuilder is more efficient in a loop
while(fileScanner.hasNextLine())
CSV.append(fileScanner.nextLine()).append("\n");
db = ArrayTable.createArrayTableFromCSV(CSV.toString());
}
however you could instead keep a copy of the original ArrayTable and copy that when you need to (or even the created string to rebuild it)
this code creates the string and caches it so you can safely discard the inputstreams and just use readCSV to build the ArrayTable
private String readCSV=null;
public final void loadDatabaseToArrayTable() throws java.io.IOException
{
if(readCSV==null){
this.dbInputStream = firstDBInputStream;
java.util.Scanner fileScanner = new java.util.Scanner(dbInputStream);
StringBuilder CSV = "";//StringBuilder is more efficient in a loop
while(fileScanner.hasNextLine())
CSV.append(fileScanner.nextLine()).append("\n");
readCSV=CSV.toString();
fileScanner.close();
}
db = ArrayTable.createArrayTableFromCSV(readCSV);
}
however if you want new information you'll need to create a new stream to read from again
I need to determine if a user-supplied string is a valid file path (i.e., if createNewFile() will succeed or throw an Exception) but I don't want to bloat the file system with useless files, created just for validation purposes.
Is there a way to determine if the string I have is a valid file path without attempting to create the file?
I know the definition of "valid file path" varies depending on the OS, but I was wondering if there was any quick way of accepting C:/foo or /foo and rejecting banana.
A possible approach may be attempting to create the file and eventually deleting it if the creation succeeded, but I hope there is a more elegant way of achieving the same result.
Path class introduced in Java 7 adds new alternatives, like the following:
/**
* <pre>
* Checks if a string is a valid path.
* Null safe.
*
* Calling examples:
* isValidPath("c:/test"); //returns true
* isValidPath("c:/te:t"); //returns false
* isValidPath("c:/te?t"); //returns false
* isValidPath("c/te*t"); //returns false
* isValidPath("good.txt"); //returns true
* isValidPath("not|good.txt"); //returns false
* isValidPath("not:good.txt"); //returns false
* </pre>
*/
public static boolean isValidPath(String path) {
try {
Paths.get(path);
} catch (InvalidPathException | NullPointerException ex) {
return false;
}
return true;
}
Edit:
Note Ferrybig's
comment : "The only disallowed character in a file name on Linux is the NUL character, this does work under Linux."
This would check for the existance of the directory as well.
File file = new File("c:\\cygwin\\cygwin.bat");
if (!file.isDirectory())
file = file.getParentFile();
if (file.exists()){
...
}
It seems like file.canWrite() does not give you a clear indication if you have permissions to write to the directory.
File.getCanonicalPath() is quite useful for this purpose. IO exceptions are thrown for certain types of invalid filenames (e.g. CON, PRN, *?* in Windows) when resolving against the OS or file system. However, this only serves as a preliminary check; you will still need to handle other failures when actually creating the file (e.g. insufficient permissions, lack of drive space, security restrictions).
A number of things can go wrong when you try and create a file:
Your lack the requisite permissions;
There is not enough space on the device;
The device experiences an error;
Some policy of custom security prohibits you from creating a file of a particular type;
etc.
More to the point, those can change between when you try and query to see if you can and when you actually can. In a multithreaded environment this is one of the primary causes of race conditions and can be a real vulnerability of some programs.
Basically you just have to try and create it and see if it works. And that's the correct way to do it. It's why things like ConcurrentHashMap has a putIfAbsent() so the check and insert is an atomic operation and doesn't suffer from race conditions. Exactly the same principle is in play here.
If this is just part of some diagnostic or install process, just do it and see if it works. Again there's no guarantee that it'll work later however.
Basically your program has to be robust enough to die gracefully if it can't write a relevant file.
boolean canWrite(File file) {
if (file.exists()) {
return file.canWrite();
}
else {
try {
file.createNewFile();
file.delete();
return true;
}
catch (Exception e) {
return false;
}
}
}
Here's something you can do that works across operating systems
Using regex match to check for existing known invalid characters.
if (newName.matches(".*[/\n\r\t\0\f`?*\\<>|\":].*")) {
System.out.println("Invalid!");
} else {
System.out.println("Valid!");
}
Pros
This works across operating systems
You can customize it whatever way
you want by editing that regex.
Cons
This might not be a complete list and need more research to fill in more invalid patterns or characters.
Just do it (and clean up after yourself)
A possible approach may be attempting to create the file and eventually deleting it if the creation succeeded, but I hope there is a more elegant way of achieving the same result.
Maybe that's the most robust way.
Below is canCreateOrIsWritable that determines whether your program is able to create a file and its parent directories at a given path, or, if there's already a file there, write to it.
It does so by actually creating the necessary parent directories as well as an empty file at the path. Afterwards, it deletes them (if there existed a file at the path, it's left alone).
Here's how you might use it:
var myFile = new File("/home/me/maybe/write/here.log")
if (canCreateOrIsWritable(myFile)) {
// We're good. Create the file or append to it
createParents(myFile);
appendOrCreate(myFile, "new content");
} else {
// Let's pick another destination. Maybe the OS's temporary directory:
var tempDir = System.getProperty("java.io.tmpdir");
var alternative = Paths.get(tempDir, "second_choice.log");
appendOrCreate(alternative, "new content in temporary directory");
}
The essential method with a few helper methods:
static boolean canCreateOrIsWritable(File file) {
boolean canCreateOrIsWritable;
// The non-existent ancestor directories of the file.
// The file's parent directory is first
List<File> parentDirsToCreate = getParentDirsToCreate(file);
// Create the parent directories that don't exist, starting with the one
// highest up in the file system hierarchy (closest to root, farthest
// away from the file)
reverse(parentDirsToCreate).forEach(File::mkdir);
try {
boolean wasCreated = file.createNewFile();
if (wasCreated) {
canCreateOrIsWritable = true;
// Remove the file and its parent dirs that didn't exist before
file.delete();
parentDirsToCreate.forEach(File::delete);
} else {
// There was already a file at the path → Let's see if we can
// write to it
canCreateOrIsWritable = java.nio.file.Files.isWritable(file.toPath());
}
} catch (IOException e) {
// File creation failed
canCreateOrIsWritable = false;
}
return canCreateOrIsWritable;
}
static List<File> getParentDirsToCreate(File file) {
var parentsToCreate = new ArrayList<File>();
File parent = file.getParentFile();
while (parent != null && !parent.exists()) {
parentsToCreate.add(parent);
parent = parent.getParentFile();
}
return parentsToCreate;
}
static <T> List<T> reverse(List<T> input) {
var reversed = new ArrayList<T>();
for (int i = input.size() - 1; i >= 0; i--) {
reversed.add(input.get(i));
}
return reversed;
}
static void createParents(File file) {
File parent = file.getParentFile();
if (parent != null) {
parent.mkdirs();
}
}
Keep in mind that between calling canCreateOrIsWritable and creating the actual file, the contents and permissions of your file system might have changed.