I have about 1000 files with a date in their name, i would like to sort them by this date in the filename and pick the latest one which has a date same or earlier than an argument.
I have wrote this:
Pattern PATTERN = Pattern.compile("^\\d{4}-\\d{2}-\\d{2}-file.csv");
try {
deviceFiles = Files.list(filesDir.toPath())
.filter(path -> PATTERN.matcher(path.getFileName().toString()).matches()
&& !getDate(path).isAfter(ARGUMENT_DATE))
.collect(Collectors.toList());
Arrays.sort(deviceFiles.toArray(), new FileNamePathDateComparator());
logger.info("All files found are " + deviceFiles.stream().map(stream -> stream.toAbsolutePath().toString()).collect(Collectors.joining("\n")));
if (deviceFiles.isEmpty())
throw new IllegalStateException("There were no device files found");
else {
String deviceFilePath = deviceFiles.get(deviceFiles.size() - 1).toAbsolutePath().toString();
logger.info("Found device file: " + deviceFilePath);
return deviceFilePath;
}
} catch (IOException e) {
throw new UncheckedIOException(e);
}
The getDate method:
private LocalDate getDate(Path path)
{
try {
String[] parts = path.getFileName().toString().split("-");
return LocalDate.of(Integer.parseInt(parts[0]), Integer.parseInt(parts[1]), Integer.parseInt(parts[2]));
} catch (NumberFormatException ex) {
throw new IllegalArgumentException("bye", ex);
}
}
The comparator:
class FileNamePathDateComparator implements Comparator{
#Override
public int compare(Object o1, Object o2)
{
return getDate((Path)o1).compareTo(getDate((Path)o2));
}
}
When i run this locally i see that the logger prints all the files correctly sorted, the comparator works just fine.
But on a kubernetes cluster the files are printed randomly, i dont understand this.
Fixed! I have put the comparator in the stream rather than in the final list, and it works fine.
If in the mean while someone can provide an explanation, i would appreciate.
deviceFiles = Files.list(filesDir.toPath())
.filter(path -> PATTERN.matcher(path.getFileName().toString()).matches()
&& !getDate(path).isAfter(executionDate))
.sorted(new FileNamePathDateComparator()::compare)
.collect(Collectors.toList());
Related
I have a stream like this,
List<String> test = Arrays.asList("1", "2,", "3");
test.stream().map(t -> {
try {
validate(t);
} catch (Exception e) {
throw new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, e.getMessage(), e);
}
return true;
})
In case of an exception, I would like to store it somewhere, process the next element and then show all exceptions at once. Is this possible?
Well, don't rethrow the exception:
var exceptions = test.stream().map(t -> {
try {
validate(t);
} catch (Exception e) {
return e;
}
return null;
})
.filter(Objects::nonNull) // if you don't need to preserve the index
.toList();
I modified your code slightly. Your main issue is that you're re-throwing that exception when you catch it -- that will stop processing at the first exception. Then you need some way to store the inputs that cause an error (that's what invalidInputs is for. I'm assuming that your next step is to actually use the results of that input to convert, otherwise you don't need to use map(), so my "validate" method also just does the conversion too. I use a JUnit test harness for stuff like this, and I left in the assert()s; you should take them (and the #Test annotation) out for your code.
//throws NumberFormatException if input doesn't parse into an integer
public static Integer validate(String input) throws Exception{
return Integer.parseInt(input);
}
#Test
public void testStreamMap() {
List<String> test = Arrays.asList("1", "2,", "3");
List<String> invalidInputs = new ArrayList<>();
List<Integer> result= test.stream().map(t -> {
Integer localResult;
try {
localResult=validate(t);
} catch (Exception e) {
invalidInputs.add(t);
localResult=null;
}
return localResult;
}).collect(Collectors.toList());
assertTrue(invalidInputs.contains("2,"));
assertFalse(result.contains(2));
}
You can try this approach to collect all the exceptions in a list as follows:
Here,
I have iterated over the list using forEach and in case of catch , added the exception e in the listOfExceptions list.
As I have entered three invalid inputs in the list , it is giving three exceptions in the list at the end corresponding to each element which is giving exception.
For the demo purpose, I have created one more list listOfElementsWithoutExceptions showing the elements which are processed without any exceptions.
Note: You can have your own custom exception list in place of List<Exception>.
public class Test2 {
public static void main(String[] args) {
List<String> test = Arrays.asList("test1", "test2", "3","4","test5","6");
List<Exception> listOfExceptions = new ArrayList<>();
List<String> listOfElementsWithoutExceptions = new ArrayList<>();
test.forEach(t -> {
try {
validate(t);
listOfElementsWithoutExceptions.add(t);
} catch (Exception e) {
listOfExceptions.add(e);
}
});
System.out.println("list of exceptions:: " + listOfExceptions);
System.out.println("list of elements without exceptions:: "+ listOfElementsWithoutExceptions);
}
private static void validate(String t) {
Integer.parseInt(t);
}
}
Output:
list of exceptions::
[java.lang.NumberFormatException: For input string: "test1",
java.lang.NumberFormatException: For input string: "test2",
java.lang.NumberFormatException: For input string: "test5"]
list of elements without exceptions:: [3, 4, 6]
I am trying to get a solution to the following problem.
How can I find values from "conditions" in "stream"?
At the moment I can only filter with the "line.contains method". But I want that the user can give a number of conditions which would be saved in the Array "conditions". I tried to build a for-loop in the stream.filter but I failed.^^ Maybe you know an efficient way. :)
Thanks.
private static void streamSelectedFile(String p, String[] conditions) {
try (
Stream<String> stream = Files.lines(Paths.get(p), StandardCharsets.ISO_8859_1)) {
Stream<String> filteredStream =
stream.filter(line -> line.contains("conditionValue"));
filteredStream.forEach(elem -> {
System.out.println(elem + " Path: " + p);
});
} catch (IOException e) {
...
}
}
Use allMatch
stream.filter(line -> Stream.of(conditions).allMatch(line::contains))
I have a string:
"1, 2, 3 , -4"
it is split by ", ".
I have a function to convert a number to a enum type which works fine. I want to use java 8 to convert this string to a list of enum objects.
Pattern pattern = Pattern.compile(", ");
List<f> fList = pattern.splitAsStream(str)
.map(s -> {
try {
return this.getEnumObject(Integer.valueOf(s), f.class);
}
catch (NoEleException e) {
e.printStackTrace();
}
})
.collect(Collectors.toList());
This gives me an error:
missing return type.
How could I fix it?
Currently, if an exception occurs no result will be returned hence the compilation error. You'll need to return a value after the catch block .
Basically to ways of managing this:
catching the exception and return some value or encapsulate values in Optionals and filter accordingly
Throwing a RuntimeException which chains the original one
In the first case we use Optional to put something into the stream on error, and then manage these empty values further in the stream:
pattern.splitAsStream(str)
.map(s -> {
try {
return Optional.of(this.getEnumObject(Integer.valueOf(s), f.class));
}
catch (NoEleException e) {
e.printStackTrace();
return Optional.empty();
}
})
.filter(Optional::isPresent) // remove empty optionals
.map(Optional::get) // unwrap them
.collect(Collectors.toList());
In the second case the stream is stopped and you can then try to catch the RuntimeException and unchain the original one:
pattern.splitAsStream(str)
.map(s -> {
try {
return Optional.of(this.getEnumObject(Integer.valueOf(s), f.class));
}
catch (NoEleException e) {
e.printStackTrace();
throw new RuntimeException(e); // stop the stream
}
})
.collect(Collectors.toList());
You can create Null Object like MissingElement, return it in catch and them filter it out after map.
If you are certain that this won't happen you could return null in the catch and filter for non null before collecting:
Pattern pattern = Pattern.compile(", ");
List<f> fList = pattern.splitAsStream(str)
.map(s -> {
try {
return this.getEnumObject(Integer.valueOf(s), f.class);
}
catch (Exception e) {
return null;
}
})
.filter(Objects::nonNull)
.collect(Collectors.toList());
I have few files in an FTP folder with names like:
mainimport_31052017.csv
mainimport_21052017.csv
mainimport_15052017.csv
And I have a pattern string:
String pattern = "mainimport_ddmmyyy";
Now I am supposed to download the file with the latest date in its title. I am supposed to do it with Java 8 goodies.
I have a solution, but this is not pretty enough, I am doing it in 2 statements:
1) I first get the newest date:
Date newestDate = Collections.max(ftpFiles.stream().filter(fileName -> StringUtils.startsWith(fileName.getName(), prefix)).map(fileName -> {
String fileNameSuffix = fileName.getName().split("_")[1];
Date date = null;
try {
date = dateFormat.parse(fileNameSuffix);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}).collect(Collectors.toList()));
2) I then get the filename with the latest date:
Optional<FTPFile> file = ftpFiles.stream().filter(fileName->{
String fileNameSuffix = fileName.getName().split("_")[1];
Date date = null;
try {
date = dateFormat.parse(fileNameSuffix);
} catch (ParseException e) {
e.printStackTrace();
}
return StringUtils.startsWith(fileName.getName(), prefix) && date.equals(newestDate);
}).findFirst();
I am trying to do this both in a single statement, if it is possible.
Assuming that the dates always have the specified six-character representation, you may use
Optional<FTPFile> max = ftpFiles.stream()
.filter(file -> file.getName().startsWith(prefix))
.max(Comparator.comparing(file -> file.getName()
.replaceFirst(".*_([0-9]{2})([0-9]{2})([0-9]{4}).*", "$3$2$1")));
The factory method Comparator.comparing allows you to create a Comparator based on a property, so that the maximum element will be the element with the maximum value for that property.
Note that this simply converts the ddmmyyyy formatted date to a yyyymmdd string which can be compared lexicographically, which works as long as the day and months always have a two-digit form, i.e. with a leading zero.
You may optimize this a bit by preparing and reusing the regex pattern:
Pattern pattern = Pattern.compile(".*_([0-9]{2})([0-9]{2})([0-9]{4}).*");
Optional<FTPFile> max = ftpFiles.stream()
.filter(file -> file.getName().startsWith(prefix))
.max(Comparator.comparing(file ->
pattern.matcher(file.getName()).replaceFirst("$3$2$1")));
If the DateFormat is an unavoidable prerequisite, you may use
Optional<FTPFile> max = ftpFiles.stream()
.filter(file -> file.getName().startsWith(prefix))
.max(Comparator.comparing(file -> {
String name = file.getName();
name = name.substring(name.indexOf('_')+1);
try {
return dateFormat.parse(name);
} catch (ParseException e) {
throw new IllegalArgumentException(e);
}
}));
This performs the operation in one go, but has the disadvantage of performing the parsing operation more than necessary during the comparisons. If you want to avoid that, you may resort to the original two pass design, but you still don’t need to collect into a List:
ftpFiles.stream()
.map(FTPFile::getName)
.filter(name -> name.startsWith(prefix))
.map(name -> {
name = name.substring(name.indexOf('_')+1);
try {
return dateFormat.parse(name);
} catch (ParseException e) {
throw new IllegalArgumentException(e);
}
})
.max(Comparator.naturalOrder())
.map(date -> prefix+'_'+dateFormat.format(date))
.flatMap(fileName -> ftpFiles.stream()
.filter(file -> file.getName().equals(fileName)).findAny())
.ifPresent(System.out::println);
Our goal is to fetch some of the content from Liferay Portal via SOAP services using Java. We are successfully loading articles right now with JournalArticleServiceSoap. The problem is that the method requires both group id and entry id, and what we want is to fetch all of the articles from a particular group. Hence, we are trying to get the ids first, using AssetEntryServiceSoap but it fails.
AssetEntryServiceSoapServiceLocator aesssLocator = new AssetEntryServiceSoapServiceLocator();
com.liferay.client.soap.portlet.asset.service.http.AssetEntryServiceSoap assetEntryServiceSoap = null;
URL url = null;
try {
url = new URL(
"http://127.0.0.1:8080/tunnel-web/secure/axis/Portlet_Asset_AssetEntryService");
} catch (MalformedURLException e) {
e.printStackTrace();
}
try {
assetEntryServiceSoap = aesssLocator
.getPortlet_Asset_AssetEntryService(url);
} catch (ServiceException e) {
e.printStackTrace();
}
if (assetEntryServiceSoap == null) {
return;
}
Portlet_Asset_AssetEntryServiceSoapBindingStub assetEntryServiceSoapBindingStub = (Portlet_Asset_AssetEntryServiceSoapBindingStub) assetEntryServiceSoap;
assetEntryServiceSoapBindingStub.setUsername("bruno#7cogs.com");
assetEntryServiceSoapBindingStub.setPassword("bruno");
AssetEntrySoap[] entries;
AssetEntryQuery query = new AssetEntryQuery();
try {
int count = assetEntryServiceSoap.getEntriesCount(query);
System.out.println("Entries count: " + Integer.toString(count));
entries = assetEntryServiceSoap.getEntries(query);
if (entries != null) {
System.out.println(Integer.toString(entries.length));
}
for (AssetEntrySoap aes : assetEntryServiceSoap.getEntries(query)) {
System.out.println(aes.getEntryId());
}
} catch (RemoteException e1) {
e1.printStackTrace();
}
Although getEntriesCount() returns a positive value like 83, getEnries() always returns an empty array. I'm very new to Liferay portal, but it looks really weird to me.
By the way, we are obviously not looking for performance here, the key is just to fetch some specific content from the portal remotely. If you know any working solution your help would be much appreciated.
Normally a AssetEntryQuery would have a little more information in it for example:
AssetEntryQuery assetEntryQuery = new AssetEntryQuery();
assetEntryQuery.setClassNameIds(new long[] { ClassNameLocalServiceUtil.getClassNameId("com.liferay.portlet.journal.model.JournalArticle") });
assetEntryQuery.setGroupIds(new long[] { groupId });
So this would return all AssetEntries for the groupId you specify, that are also JournalArticles.
Try this and see, although as you say, the Count method returns a positive number so it might not make a difference, but give it a go! :)