I got code like this:
paths.forEach(folderPath -> {
Path to = folderPath.getRoot().resolve(folderPath.getParent().subpath(0, folderPath.getNameCount() - 1)); // До имени (исключительно)
try {
Files.list(folderPath).forEach(filePath -> {
try { Files.move(filePath, to.resolve(filePath.getFileName()), StandardCopyOption.ATOMIC_MOVE); }
catch (IOException e) { processException(e); }
});
if (Files.list(folderPath).count() == 0)
Files.deleteIfExists(folderPath); // this call
} catch (IOException e) { processException(e); }
});
After I call delete methods, I get my empty directory locked (right after it was called, checked it), but not deleted until application is closed. I find it a bit strange, but want to know why is this happening.
(I use Windows 10)
From the documentation of Files.list(Path):
This method must be used within a try-with-resources statement or similar control structure to ensure that the stream's open directory is closed promptly after the stream's operations have completed.
You are not doing this, so the following part of Files.deleteIfExists(…) applies:
On some operating systems it may not be possible to remove a file when it is open and in use by this Java virtual machine or other programs.
You should use
paths.forEach(folderPath -> {
Path to = folderPath.getParent();
try {
try(Stream<Path> files = Files.list(folderPath)) {
files.forEach(filePath -> {
try{Files.move(filePath, to.resolve(filePath.getFileName()), ATOMIC_MOVE);}
catch (IOException e) { processException(e); }
});
}
try {
Files.deleteIfExists(folderPath);
} catch(DirectoryNotEmptyException ex) {
// may happen as you continue when Files.move fails,
// but you already reported the original exception then
}
} catch (IOException e) { processException(e); }
});
This closes the stream of files before trying to delete the directory. Note that the second stream operation has been removed, this kind of pre-check is wasteful and should be unneeded when all move operations succeeded. But if some other application inserts a new file concurrently, there is no guaranty that it doesn’t happen between your Files.list(folderPath).count() == 0 check and the subsequent deleteIfExists call.
The cleaner solution would be to remember when a move failed. When no move failed, a still not empty directory should be considered an erroneous situation that should be reported like any other error, e.g.
paths.forEach(folderPath -> {
Path to = folderPath.getParent();
try {
boolean allMovesSucceeded;
try(Stream<Path> files = Files.list(folderPath)) {
allMovesSucceeded = files
.map(filePath -> {
try {
Files.move(filePath, to.resolve(filePath.getFileName()), ATOMIC_MOVE);
return true;
}
catch(IOException e) { processException(e); return false; }
}).reduce(Boolean.TRUE, Boolean::logicalAnd);
}
if(allMovesSucceeded) Files.deleteIfExists(folderPath);
} catch (IOException e) { processException(e); }
});
Related
In the springboot project, after the files have been merged, they need to be deleted.
The main code for the merge method is:
// chunkFolder indicates the file storage folder path
Files.list(Paths.get(chunkFolder))
.filter(path -> path.getFileName().toString().contains(HYPHEN))
.sorted((p1, p2) -> {
String fileName1 = p1.getFileName().toString();
String fileName2 = p2.getFileName().toString();
int index1 = fileName1.indexOf(HYPHEN);
int index2 = fileName2.indexOf(HYPHEN);
return Integer.valueOf(fileName1.substring(0, index1)).compareTo(Integer.valueOf(fileName2.substring(0, index2)));
})
.forEach(path -> {
try {
Files.write(Paths.get(target), Files.readAllBytes(path), StandardOpenOption.APPEND);
} catch (IOException e) {
e.printStackTrace();
}
}
);
The delete method is:
public void deleteDirectory(Path targetPath) throws IOException {
Files.walk(targetPath).sorted(Comparator.reverseOrder()).forEach(path -> {
try {
Files.delete(path);
} catch (IOException e) {
e.printStackTrace();
}
});
}
In the windows environment test, delete the storage path after merging. However, the results show that the folder still exists, but cannot be accessed. If you stop the springboot project, the folder disappears.
This problem happens on Windows when you are not closing all the directory streams correctly. You must close all directory streams scanned in your code. The two examples you've shown can be fixed with try with resources:
try(Stream<Path> stream = Files.list( ... )) {
... your code
stream.xyz(...);
}
... plus same for Files.walk() in deleteDirectory. Check other similar calls in all code.
When this occurs the directory is in a strange state when viewed in Windows Explorer - visible but not accessible. Shutting down the VM clears up correctly and the folder disappears from Explorer.
Here is my code:
whatever exception it throws I don't want to catch it outside, I want to continue my loop again by handling it separately. I don't want to use another try catch inside this try catch. Can someone guide me on this?
I don't want to use another try catch inside this try catch.
Yes you do.
MarketplaceBO marketplaceBOObject = new MarketplaceBO(entity.getMarketplaceID());
try {
marketplaceBOObject.loadFromSable();
} catch (WhateverException e) {
// Do something here, or, if you prefer, add the exception to a list and process later
doSomething() ;
// Continue your loop above
continue ;
}
if (marketplaceBOObject.isActive()) {
If you REALLY don't want to do this, your loadFromSable() method could return some object that provides information about success/failure of the call. But I wouldn't recommend that.
do this way -- this way your rest of the code will run no matter there is an exception or not
for (MerchantMarketplaceBO entity : merchantMarketplaceBOList) {
MarketplaceBO marketplaceBOObject = new MarketplaceBO(entity.getMarketplaceID());
try{
marketplaceBOObject.loadFromSable();
if (marketplaceBOObject.isActive()) {
resultVector.add(marketplaceBOObject.getCodigoMarketplace());
}
}
catch{
if (marketplaceBOObject.isActive()) {
resultVector.add(marketplaceBOObject.getCodigoMarketplace());
}
}
}
Another "trick" to deal with that is to move the body to the loop into a separate method having the "additional" try/catch block:
private MarketplaceBO loadFromSable(MerchantMarketplaceBO entity){
MarketplaceBO marketplaceBOObject = new MarketplaceBO(entity.getMarketplaceID());
try {
marketplaceBOObject.loadFromSable();
} catch (WhateverException e) {
// do something to make marketplaceBOObject a valid object
// or at least log the exception
}
return marketplaceBOObject;
}
But since we want to stick to the Same Layer of Abstraction principle we also need to move other part of that method to new smaller methods:
public void serveFromSableV2() {
String merchantCustomerID = ObfuscatedId.construct(request.getMerchantCustomerID()).getPublicEntityId();
try {
List<MerchantMarketplaceBO> merchantMarketplaceBOList =
getAllMerchantMarketplacesBOsByMerchant();
Vector<Marketplace> resultVector = new Vector<>();
for (MerchantMarketplaceBO entity : merchantMarketplaceBOList) {
MarketplaceBO marketplaceBOObject = loadFromSable(entity);
addToActiveMarketplacesList(marketplaceBOObject,resultVector);
}
verifyHavingActiveMarketPlaces(resultVector);
setResponseWithWrapped(resultVector);
} catch (EntityNotFoundException | SignatureMismatchException | InvalidIDException e) {
throw new InvalidIDException("merch=" + merchantCustomerID + "[" + request.getMerchantCustomerID() + "]"); //C++ stack throws InvalidIDException if marketplace is not found in datastore
}
}
You could refactor the load into a separate method that catches and returns the exception instead of throwing it:
private Optional<Exception> tryLoadFromSable(MarketplaceBO marketplaceBOObject) {
try {
marketplaceBOObject.loadFromSable();
return Optional.empty();
}
catch(Exception e) {
return Optional.of(e);
}
}
Then inside your loop:
//inside for loop...
MarketplaceBO marketplaceBOObject = new MarketplaceBO(entity.getMarketplaceID());
Optional<Exception> loadException = tryLoadFromSable(marketplaceBOObject);
if(loadException.isPresent()) {
//Do something here, log it, save it in a list for later processing, etc.
}
I know with Java 7 we can include multiple exceptions in the same catch block and separate them with pipe symbol. My question is for listing them in another file and catching all the exceptions listed in that file
You can do something like this:
Set<String> exceptionClasses = ... // load class names from file
try {
// ...
} catch (Exception e) {
if (exceptionClasses.contains(e.getClass().getName())) {
// handle exception
} else {
throw e; // propagate exception
}
}
I am reusing ObjectOutputStream to send updates between the two clients, this is the server code,
public void run() {
try {
toPlayer1.writeBoolean(true);
toPlayer1.flush();
while (true) {
try {
found = (boolean[][]) fromPlayer1.readObject();
player1Int = fromPlayer1.readInt();
} catch (Exception ex) {
// Handle exception here...
}
if (isWon(player1Int)) {
toPlayer1.writeInt(P1_WON);
toPlayer1.flush();
toPlayer2.writeInt(P1_WON);
toPlayer2.flush();
sendMove(toPlayer2, found, player1Int);
break;
} else {
toPlayer2.writeInt(CONTINUE);
toPlayer2.flush();
sendMove(toPlayer2, found, player1Int);
}
try {
found = (boolean[][]) fromPlayer2.readObject();
player2Int = fromPlayer2.readInt();
} catch (Exception ex) {
// Handle exception here...
}
if (isWon(player2Int)) {
toPlayer1.writeInt(P2_WIN);
toPlayer1.flush();
toPlayer2.writeInt(P2_WIN);
toPlayer2.flush();
sendMove(toPlayer1, found, player2Int);
break;
} else {
toPlayer1.writeInt(CONTINUE);
toPlayer1.flush();
sendMove(toPlayer1, found, player2Int);
}
}
} catch (IOException ex) {
System.err.println(ex);
}
}
private void sendMove(ObjectOutputStream out, boolean[][] found, int score) throws IOException {
try {
out.reset();
out.writeObject(found);
out.writeInt(score);
out.flush();
} catch (Exception ex) {
// Handle exception here...
}
out.writeInt(score);
}
the problem seems to be that some messages are not being delivered correctly, any suggestions? Am I using the flush() correctly? I have added reset(); it is still not working
update, these are the streams:
public void run() {
try {
toPlayer1 = new ObjectOutputStream(player1.getOutputStream());
fromPlayer1 = new ObjectInputStream(player1.getInputStream());
toPlayer2 = new ObjectOutputStream(player2.getOutputStream());
fromPlayer2 = new ObjectInputStream(player2.getInputStream());
regards, c
If you want an object or objects to be sent again, you need to call reset() on the ObjectOutputStream object.
The problem that reset() solves is that when you send an object in a object stream, the protocol attempts to preserve object identity. The first time you send it, the stream sends the object state. Subsequent times, it just sends a marker that says (in effect) "use this object that I sent you previously".
The reset() method says (in effect) to the ObjectOutputStream ... "forget about all objects that I sent previously".
So if you want to send the same object twice, you need to do something like this:
out.writeObject(found);
// change the state of 'found'
out.reset();
out.writeObject(found);
Note that this doesn't affect primitive values sent using their corresponding write methods. Primitive values don't have "identity" and are sent literally each time.
I should also point out that the following is very bad practice.
} catch (Exception ex) {
}
You are silently ignoring all exceptions. This is lazy and dangerous, and you are likely to come to regret it. (Don't do it even in sample code in SO Questions ... 'cos someone might copy your bad code or some Java beginner might emulate your bad habits.)
There are some task that should't be done in parallel, (for example opening a file, reading, writing, and closing, there is an order on that...)
But... Some task are more like a shoping list, I mean they could have a desirable order but it's not a must..example in communication or loading independient drivers etc..
For that kind of tasks,
I would like to know a java best practice or pattern for manage exceptions..
The java simple way is:
getUFO {
try {
loadSoundDriver();
loadUsbDriver();
loadAlienDetectorDriver();
loadKeyboardDriver();
} catch (loadSoundDriverFailed) {
doSomethingA;
} catch (loadUsbDriverFailed) {
doSomethingB;
} catch (loadAlienDetectorDriverFailed) {
doSomethingC;
} catch (loadKeyboardDriverFailed) {
doSomethingD;
}
}
But what about having an exception in one of the actions but wanting to
try with the next ones??
I've thought this approach, but don't seem to be a good use for exceptions
I don't know if it works, doesn't matter, it's really awful!!
getUFO {
Exception ex=null;
try {
try{ loadSoundDriver();
}catch (Exception e) { ex=e; }
try{ loadUsbDriver();
}catch (Exception e) { ex=e; }
try{ loadAlienDetectorDriver();
}catch (Exception e) { ex=e; }
try{ loadKeyboardDriver()
}catch (Exception e) { ex=e; }
if(ex!=null)
{ throw ex;
}
} catch (loadSoundDriverFailed) {
doSomethingA;
} catch (loadUsbDriverFailed) {
doSomethingB;
} catch (loadAlienDetectorDriverFailed) {
doSomethingC;
} catch (loadKeyboardDriverFailed) {
doSomethingD;
}
}
seems not complicated to find a better practice for doing that.. I still didn't
thanks for any advice
Consider the execute around idiom.
Another option (which isn't really all that different, it just decouples them more) is to do each task in a separate thread.
Edit:
Here is the kind of thing I have in mind:
public interface LoadableDriver {
public String getName();
public void loadDriver() throws DriverException;
public void onError(Throwable e);
}
public class DriverLoader {
private Map<String, Exception> errors = new HashMap<String, Exception>();
public void load(LoadableDriver driver) {
try {
driver.loadDriver();
} catch (DriverException e) {
errors.put(driver.getName(), e);
driver.onError(e);
}
}
public Map<String, Exception> getErrors() { return errors; }
}
public class Main {
public void loadDrivers() {
DriverLoader loader = new DriverLoader();
loader.loadDriver(new LoadableDriver(){
public String getName() { return "SoundDriver"; }
public void loadDriver() { loadSoundDriver(); }
public void onError(Throwable e) { doSomethingA(); }
});
//etc. Or in the alternative make a real class that implements the interface for each driver.
Map<String, Exception> errors = loader.getErrors();
//react to any specific drivers that were not loaded and try again.
}
}
Edit: This is what a clean Java version would ultimately look like if you implemented the drivers as classes (which is what the Java OO paradigm would expect here IMHO). The Main.loadDrivers() method would change like this:
public void loadDrivers(LoadableDriver... drivers) {
DriverLoader loader = ...
for(LoadableDriver driver : drivers) {
loader.load(driver);
}
//retry code if you want.
Set<LoadableDriver> failures = loader.getErrors();
if(failures.size() > 0 && tries++ > MAX_TRIES) {
//log retrying and then:
loadDrivers(drivers.toArray(new LoadableDriver[0]));
}
}
Of course I no longer use a map because the objects would be self-sufficient (you could get rid of the getName() method as well, but probably should override toString()), so the errors are just returned in a set to retry. You could make the retry code even simpler if each driver was responsible for knowing how often it should it retry.
Java won't look as nice as a well done C++ template, but that is the Java language design choice - prefer simplicity over complex language features that can make code hard to maintain over time if not done properly.
Try this:
protected void loadDrivers() {
loadSoundDriver();
loadUsbDriver();
loadAlienDetectorDriver();
loadKeyboardDriver();
}
Then:
protected void loadSoundDriver() {
try {
// original code ...
}
catch( Exception e ) {
soundDriverFailed( e );
}
}
protected void soundDriverFailed( Exception e ) {
log( e );
}
This gives subclasses a chance to change the behaviour. For example, a subclass could implement loading each driver in a separate thread. The main class need not care about how the drivers are loaded, nor should any users of the main class.
IMO, for your case, if the exception is "ignorable" it's best if the "loadSoundDriver" method catches the exception and simply returns an error.
Then in the function that loads stuff, you can record all the errors and at the end of the sequence, decide what to do with them.
[edit]
Something like this:
// init
MyError soundErr = loadSoundDriver();
MyError otherErr = loadOtherDriver();
if(soundErr!=null || otherErr !=null){
// handle the error(s)
}
Just surround every single load operation with its own try / catch block.
try {
loadSoundDriver();
} catch (loadSoundDriverFailed) {
doSomethingA;
}
try {
loadUsbDriver();
} catch (loadUsbDriverFailed) {
doSomethingB;
}
// ...
So you can handle every exception by itself and continue processing the oder operations.