I need to validate mandatory fields in my class
For example, 9 fields must not be null.
I need to check if they are all null but I am using multiple if statements for this now as below:
StringBuilder mandatoryExcessFields = new StringBuilder(MANDATORY_EXCESS_FIELDS.length);
if(Objects.isNull(excess.getAsOfDate())){
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[0]);
}
if(StringUtils.isEmpty(excess.getStatus())) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[1]);
}
if(Objects.isNull(excess.getLimit())) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[2]);
}
if(!Objects.isNull(excess.getLimit()) && Objects.isNull(excess.getLimit().getId())) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[3]);
}
if(!Objects.isNull(excess.getLimit()) && Objects.isNull(excess.getLimit().getAsOfDate())) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[4]);
}
if(Objects.isNull(excess.getExposure())) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[5]);
}
if(!Objects.isNull(excess.getExposure()) && Objects.isNull(excess.getExposure().getCoordinates())) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[6]);
}
if(!Objects.isNull(excess.getExposure()) && Objects.isNull(excess.getExposure().getValue())) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[7]);
}
if(StringUtils.isEmpty(excess.getLimitValue())) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[8]);
}
Do we have a better approach to reduce this boilerplate code or any design pattern or any new feature from Java-8 which I can leverage?
All the Object.isNull might be replaced with Optional object and its methods. Let's take example the line:
if (!Objects.isNull(excess.getLimit()) && Objects.isNull(excess.getLimit().getId())) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[3]);
}
Would be simplified to (and squeezed on 1 line remains readable):
Optional.ofNullable(excess.getLimit()) // check the Limit
.map(limit -> limit.getId()) // if not null, getId
.ifPresent(i -> builder.append(MANDATORY_EXCESS_FIELDS[3])); // Append if present
And for the String.isEmpty(s) check, you have to create Optional in this way:
Optional.ofNullable(excess.getStatus()).filter(s -> !StringUtils.isEmpty(s))
A short way would be to pass those Optional object into the map and use the index to iterate through them and perform an action. int count is a number of checkings:
Map<Integer, Optional<?>> map = new HashMap<>();
map.put(...);
map.put(1, Optional.ofNullable(excess.getStatus()).filter(s -> !StringUtils.isEmpty(s)));
map.put(...);
map.put(3, Optional.ofNullable(excess.getLimit()).map(limit -> limit.getId()));
map.put(...);
for (int index=0; index<count; index++) {
map.get(index).ifPresent(any -> mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[index]));
}
And the for-cycle might be simplified as well:
IntStream.range(0, count).forEach(index ->
map.get(index)
.ifPresent(any -> mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[index])));
Basically, there are two ways here:
As suggested by the comment, NonNull as offered by Project Lombok for example
Java bean validation
I would heavily recommend to look into bean validation:
Define your classes that carry information as beans. And then use the wide range of annotations to mark the corresponding fields. And then use some existing framework to do the validation for you. You can even define your own annotations there, that run your own code.
You can use javax.validator and hibernate.validator with #NotNull annotation on each field (or whichever field you want) on your excess POJO class. This combination provides an extensive pattern checking as well.
By this you don't have to do all the if checks explicitly. You can get ride of not only null checks but also pattern matching checks which can get scattered all over your code.
Basically the initialisation and assignments should not set any field to null.
If this is unopportune (a field being really logically optional), the field should probably be an Optional<...>, assigned with an Optional.ofNullable(...). This ensures that at usage the field is safely processed, but causes editing work of course.
Seeing the code now, here it seems that there is no easy refactoring.
The code could be refactored; somewhere a mapping of features is missing.
Predicate<Excess>[] parts = {
exc -> Objects.isNull(exc.getAsOfDate()),
exc -> StringUtils.isEmpty(exc.getStatus()),
...
};
for (int i = 0; i < parts.length; ++i) {
if (parts[i].test(excess)) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[i]);
}
}
Or such.
As easy refactoring you could introduce two helper methods :
private String createErrorMsgIfObjectNull(Object o, String errorMessage) {
return Objects.isNull(o) ? errorMessage : "";
}
private String createErrorMsgIfStringEmpty(String s, String errorMessage) {
return StringUtils.isEmpty(s) ? errorMessage : "";
}
And use them in this way :
StringBuilder mandatoryExcessFields = new StringBuilder(MANDATORY_EXCESS_FIELDS.length);
mandatoryExcessFields.append(createErrorMsgIfObjectNull(excess.getAsOfDate(), MANDATORY_EXCESS_FIELDS[0]))
.append(createErrorMsgIfStringEmpty(excess.getStatus(), MANDATORY_EXCESS_FIELDS[1]))
.append(createErrorMsgIfObjectNull(excess.getLimit(), MANDATORY_EXCESS_FIELDS[2]))
// ...
By checking the type of the object to test you could still go further. You would have a single helper method that will apply the processing according to the argument type :
private String createErrorMsgIfNullOrEmptyString(Object o, String errorMessage) {
if (o instanceof String) {
return StringUtils.isEmpty((String)o) ? errorMessage : "";
}
return Objects.isNull(o) ? errorMessage : "";
}
A Java 8 stream way would inline the helper in a filter and map() operations and would collect the String result :
List<SimpleImmutableEntry<Object, String>> objectAndErrorMessageList = new ArrayList<>();
int i = 0;
objectAndErrorMessageList.add(new SimpleImmutableEntry<>(excess.getAsOfDate(), MANDATORY_EXCESS_FIELDS[i++]));
objectAndErrorMessageList.add(new SimpleImmutableEntry<>(excess.getStatus(), MANDATORY_EXCESS_FIELDS[i++]));
// and so for
String globalErrorMsg =
objectAndErrorMessageList.stream()
.filter(e -> {
Object objectToValid = e.getKey();
if (objectToValid == null) {
return true;
}
if (objectToValid instanceof String && StringUtils.isEmpty(objectToValid)) {
return true;
}
return false;
})
.map(SimpleImmutableEntry::getValue)
.collect(Collectors.joining(""));
Other solution would be like this: same as #Nikolas answer.
Map<Integer, Predicate<Excess>> map = new HashMap<>();
Predicate<Excess> checkStatus = excess -> excess.getStatus().isEmpty();
Predicate<Excess> checkLimit = excess -> Objects.isNull(excess.getLimit());
Predicate<Excess> checkLimitId = excess -> Objects.isNull(excess.getLimit().getId());
Predicate<Excess> checkLimitAndId = checkLimit.and(checkLimitId);
// other predicates
map.put(1,checkStatus);
map.put(2,checkLimit);
map.put(3,checkLimitAndId);
// put other predicates ...
for (Map.Entry<Integer, Predicate<Excess>> m : map.entrySet()) {
if (m.getValue().test(excess)) {
mandatoryExcessFields.append(MANDATORY_EXCESS_FIELDS[m.getKey()]);
}
}
A little bit complicated, but I have a good solution because it's generic and can be used with any objects:
Excess excess = new Excess(new Limit());
Checker<Excess, Excess> checker = new Checker<>(
identity(),
List.of(
new CheckerValue<>("excess date is null", Excess::getAsOfDate),
new CheckerValue<>("limit is null", Excess::getLimit)
),
List.of(new Checker<>(Excess::getLimit, List.of(new CheckerValue<>("limit id is null", Limit::getId))))
);
System.out.println(checker.validate(excess));
This code will print:
excess date is null
limit id is null
The first class Checker contains:
sourceFunction - for getting the object
values - for checking each field from object obtained from sourceFunction
children - a list of Checker
class Checker<S, T> {
Function<S, T> sourceFunction;
List<CheckerValue<T>> values;
List<Checker<T, ?>> children = emptyList();
/*All args constructor; 2 args constructor*/
public String validate(S object) {
T value = sourceFunction.apply(object);
if(value != null) {
String valueString = values.stream().map(v -> v.validate(value)).filter(Optional::isPresent).map(Optional::get).collect(joining("\n"));
valueString += "\n\t";
valueString += children.stream().map(c -> c.validate(value)).collect(Collectors.joining("\n"));
return valueString;
}
return "";
}
}
and CheckerValue class:
class CheckerValue<T> {
String validationString;
Function<T, Object> fun;
/*all args constructor*/
public Optional<String> validate(T object) {
return fun.apply(object) != null ? Optional.empty() : Optional.of(validationString);
}
}
Related
I have a List of following objects:
public class OptionDetailResponse {
private long id;
private String flavor;
private String size;
private String status;
private String barcode;
}
I want to search in a List of those objects based on all 4 fields (except id):
flavor (input from a combobox)
size (input from a combobox)
status (input from a combobox)
barcode (input from a textfield)
This is my UI with the 4 input fields:
What I tried
I tried to use Predicate<OptionDetailResponse> for searching:
Predicate<OptionDetailResponse> selectFlavor = e -> e.getParentName().equals(flavor);
Predicate<OptionDetailResponse> selectSize = e -> e.getName().equals(size);
Predicate<OptionDetailResponse> selectStatus = e -> e.getStatus().equals(status);
Predicate<OptionDetailResponse> inputBarcode = e -> e.getBarcode().contains(barcode);
List<OptionDetailResponse> list = responseList.stream().filter(
selectFlavor.and(selectSize).and(selectStatus).and(inputBarcode))
.collect(Collectors.<OptionDetailResponse>toList());
But the list returned only a correct result when selected a value for in all search-fields.
Questions
How can I have all list when all field is empty using Predicate ?
Do have other ways to search by multiple fields ?
I think you can check on nullability or on specific value which shouldn't be checked inside each of your predicates depending on value you have in unselected field. I think it can look like this:
Predicate<OptionDetailResponse> selectFlavor = e -> flavor == null || e.getParentName().equals(flavor);
or
Predicate<OptionDetailResponse> selectFlavor = e -> flavor.equals("your unselected flavor value") || e.getParentName().equals(flavor);
.. and same for other predicates.
Bear in mind that when you use a Predicate in a filter method, the result will be the list of elements which match the "test" operation of the supplied predicate.
What you have done is to create a chain of Predicates in logical AND. This means that the result of the filter will be the list of the elements which match ALL given predicates.
if you need for a different result, you can create your chain by applying by using the specific Predicate functions, and thus realize eventually a more complex condition.
Other than and(Predicate<> target), for example you have the following method:
or(Predicate<> target): short-circuiting logical OR between two Predicate
negate(): logical negation of the current instance of Predicate
not(Predicate<> target): returns a predicate that is the negation of the supplied predicate
Probably you want to use following filter on each respective field:
if a search-parameter not supplied by UI (or left empty), then don't apply predicate: means predicate matches all regardless of object's field value
if a search-parameter is supplied by UI (or not empty), then apply the predicate
That said you could use a filter combined from all filled input-fields in stream:
// renamed your stream-result variable to indicate that it was filtered
List<OptionDetailResponse> filteredResult = responseList.stream()
.filter( buildPredicateFromInputFields(flavor, size, status, barcode) )
.collect(Collectors.toList());
where the predicate passed as argument to filter is combined from the 4 fields:
// you could name the method more specific: matchesAllNonEmptyInputs
Predicate<OptionDetailResponse> buildPredicateFromInputFields(
String flavor,
String size,
String status,
String barcode
) {
// build a set of field-matchers (predicate) based on given input fields
// null or empty (or blank) fields are excluded from the set
var givenFieldPredicates = new ArrayList<Predicate<OptionDetailResponse>>(4); // max 4 entries
if (flavor != null && !flavor.isBlank()) {
givenFieldPredicates.add(obj -> flavor.equals(obj.flavor))
}
if (size != null && !size.isBlank()) {
givenFieldPredicates.add(obj -> size.equals(obj.size))
}
if (status != null && !status.isBlank()) {
givenFieldPredicates.add(obj -> status.equals(obj.size))
}
// contained (partial match allowed)
if (barcode != null && !barcode.isBlank()) {
// will throw NullPointerException if object has null barcode!
givenFieldPredicates.add(obj -> obj.barcode.contains(barcode))
}
// combined them using AND: each field predicate must match
return givenFieldPredicates.stream().reduce(x -> true, Predicate::and);
}
See also:
Baeldung's Java Tutorial: Java 8 Predicate Chain, section "6. Combining a Collection of Predicates"
We could use Function, BiFunction and method reference and a pojo to hold the way to filter a field list to build something like
#Value
public static class Filter<T> {
private Function<OptionDetailResponse, T> getter;
private BiFunction<T, T, Boolean> filter;
public Predicate<OptionDetailResponse> toPredicate(OptionDetailResponse criteria) {
return o -> filter.apply(getter.apply(o), getter.apply(criteria));
}
}
public static List<Filter<?>> filters() {
List<Filter<?>> filterList = new ArrayList<>();
filterList.add(new Filter<>(OptionDetailResponse::getFlavor, Object::equals));
filterList.add(new Filter<>(OptionDetailResponse::getSize, Object::equals));
filterList.add(new Filter<>(OptionDetailResponse::getStatus, Object::equals));
filterList.add(new Filter<>(OptionDetailResponse::getBarcode, String::contains));
return filterList;
}
public static final List<Filter<?>> FILTERS = filters();
public Predicate<OptionDetailResponse> buildPredicate(OptionDetailResponse searchCriteria) {
return FILTERS
.stream()
.filter(f -> f.getGetter().apply(searchCriteria) != null)
.map(f -> f.toPredicate(searchCriteria))
.reduce(o -> true, Predicate::and);
}
public List<OptionDetailResponse> search(List<OptionDetailResponse> responseList,
OptionDetailResponse searchCriteria) {
return responseList.stream()
.filter(buildPredicate(searchCriteria))
.collect(Collectors.toList());
}
In the below code i am trying to create a Set based on some conditions. I want to use java 8 streams and replace this code involving for loops. Any code example/sample will be much appreciated.
Set<OwnerEntity> entities = new LinkedHashSet<>();
for(Account acct : accounts){
String acctNumber = acct.getAccountNumber();
OwnerEntity oEntity = new OwnerEntity();
oEntity.setAccount(acct);
oEntity.setName(acct.getName());
for (StatusModel sts : stsModels) {
if ( sts.egtStatus() != null &&
(acctNumber.equalsIgnoreCase(sts.getAccountNumber()))) {
oEntity.addStatusModel(sts);
}
}
entities.add(oEntity);
}
I would like to make in two steps, like so :
First Step
If you note, you have that block of code which is repeated each time :
for (StatusModel sts : stsModels) {
if ( sts.egtStatus() != null &&
(acctNumber.equalsIgnoreCase(sts.getAccountNumber()))) {
oEntity.addStatusModel(sts);
}
}
In this case, you can group by the status, then you can call with your map to get the list of StatusModel by accountNumber, to do this you can use :
Map<String, List<StatusModel>> map = stsModels.stream()
.filter(sts -> sts.getStatus() != null)
.collect(Collectors.groupingBy(StatusModel::getAccountNumber));
Or more precise, you can group with status in lowerCase :
.collect(Collectors.groupingBy(s -> s.getStatus().toLowerCase()));
Second Step
Then, you can loop over the accounts and create a new OwnerEntity like so :
Set<OwnerEntity> entities = accounts.stream()
.map(acct ->
new OwnerEntity(acct,
acct.getName(),
map.getOrDefault(acct.getAccountNumber().toLowerCase(), Collections.emptyList())))
.collect(Collectors.toSet());
Note: I used toLowerCase() in both side groupingBy and getOrDefault to avoid using looping over the list and check by using equalsIgnoreCase
It should look something like:
Set<OwnerEntity> entities = accounts.stream()
.map(acct -> {
String acctNumber = acct.getAccountNumber();
OwnerEntity oEntity = new OwnerEntity();
oEntity.setAccount(acct);
oEntity.setName(acct.getName());
stsModels.stream()
// You can do .filter as a two-parter with Objects::nonNull
.filter(sts ->
sts.egtStatus() != null &&
acctNumber.equalsIgnoreCase(sts.getAccountNumber())
)
.forEach(oEntity::addStatusModel);
})
.collect(Collectors.toCollection(LinkedHashSet::new));
It's not much of an improvement, but that's streams for you.
Having the following JEXL expression:
String expression = "myVar >= 12345 && mySecondVar <= 56789";
I can call createScript and getVariables to get myVar and mySecondVar as values, such as:
Set<List<String>> expressionVars = JEXL.createScript(expression).getVariables();
What I would like to know is, if given the same expression, I could call some other method that would return the values for those variables. Reason being is that I would like to validate the input of some of those values. I checked the docs and played around with JexlScript class but can't find an elegant way of doing it.
As JEXL is already doing the work of parsing my expression, it would be awesome to be able to retrieve this info, and not having to manually parse my expression to get this values.
Something in the line of script.getValue("myVar"); returning 12345
With JEXL, you evaluate a script/expression in a given context (JexlContext) that holds the variables and their values. JexlContext exposes the 'has' and 'get' methods that respectively check for the existence and get the value of variables.
In your case, you need to find out what your JexlContext is (or should be); from there, it is straightforward to iterate on your variables (extracted from your script) and check their values (from the context).
See:
http://commons.apache.org/proper/commons-jexl/apidocs/org/apache/commons/jexl3/JexlContext.html
http://commons.apache.org/proper/commons-jexl/apidocs/org/apache/commons/jexl3/JexlScript.html
For example (using JEXL 3.2 trunk from https://github.com/apache/commons-jexl):
/**
* Collect the global variables of a script and their values from a context.
* #param script the script
* #param context the context
* #return a map keyed by variable name of their contextual values
*/
Map<String, Object> collectVars(JexlScript script, JexlContext context) {
Set<List<String>> sls = script.getVariables();
Map<String, Object> vars = new TreeMap<String, Object>();
for(List<String> ls : sls) {
// build the 'antish' name by concatenating
StringBuilder strb = new StringBuilder();
for(String s : ls) {
if (strb.length() > 0) {
strb.append('.');
}
strb.append(s);
}
String name = strb.toString();
vars.put(name, context.get(name));
}
return vars;
}
#Test
public void testStckOvrflw() throws Exception {
JexlEngine jexl = new JexlBuilder().safe(false).create();
// a context with some variables
JexlContext context = new MapContext();
context.set("low", 15000);
context.set("high", 50000);
context.set("mid", 35000);
context.set("limit.low", 15042);
context.set("limit.high", 35042);
// an expression with 2 variables
JexlScript expr = jexl.createScript("low >= 12345 && high <= 56789");
// collecting the 2 variables, low and high
Map<String, Object> vars = collectVars(expr, context);
Assert.assertEquals(2, vars.size());
Assert.assertEquals(15000, vars.get("low"));
Assert.assertEquals(50000, vars.get("high"));
expr = jexl.createScript("limit.low >= 12345 && limit.high <= 56789");
vars = collectVars(expr, context);
Assert.assertEquals(2, vars.size());
Assert.assertEquals(15042, vars.get("limit.low"));
Assert.assertEquals(35042, vars.get("limit.high"));
}
You should implement your own context:
public class ZenContext implements JexlContext {
static private final Map<String, Object> reservedVars = new HashMap<String, Object>();
private final Map<String, Object> scriptDefinedVars = new HashMap<String, Object>();
static {
reservedVars.put("math", java.lang.Math.class);
reservedVars.put("stats", apache.commons.math3.stats.Stats);
// whatever else ...
}
public boolean has(String name) {
if (reservedVars .get(name) != null) return true;
return scriptDefinedVars.get(name) != null;
}
public boolean get (String name) {
Object value = null;
if ((value = reservedVars .get(name)) != null) return value;
return scriptDefinedVars.get(name);
}
public void set(String name, Object value) {
scriptDefinedVars.set(name, value);
}
public Map<String, Object> getReservedVars () {
return reservedVars;
}
public Map<String, Object> getScriptDefinedVars () {
return scriptDefinedVars ;
}
}
In this way, you would have
a map of reserved var names to contain Objects you do not allow scripts to change their values. e.g.,
a separate map of vars that could be set from script.
And then add these methods.
public Object execute(File scriptFile) {
JexlScript script = jexlEngine.createScript(scriptFile);
return script.execute(this); // supply this as the context
}
public Object execute (String scriptText) {
JexlScript script = jexlEngine.createScript(scriptText);
return script.execute(this); // supply this as the context
}
You could modify the set method I wrote here, to inspect the variables in the maps, before allowing them to set.
However, this will not work for local script vars
var greeting = 'The rain in Maine falls plainly insane';
Because local var relies on a different mechanism, org.apache.commons.jexl3.internal.Scope, using the getLocalVariables() method, which has a bug.
I have a List collection where each Metric contains several properties such as: metricName, namespace, fleet, type, component, firstSeenTime, lastSeenTime, etc. There are duplicates in this list such that all properties are same except for firstSeenTime and lastSeenTime. I am looking for an elegant way to filter this list and only return the metrics with the most recent lastSeenTime when there are such duplicates.
Something better than this:
private List<Metric> processResults(List<Metric metrics) {
List<Metric> results = new ArrayList<>();
for (Metric incomingMetric: metrics) {
// We need to implement "contains" below so that only properties
// other than the two dates are checked.
if (results.contains(incomingMetric) {
int index = results.indexOf(incomingMetric);
Metric existing = results.get(index);
if (incomingMetric.getLastSeen().after(existing.getLastSeen())) {
results.set(index, metricName);
} else {
// do nothing, metric in results is already the latest
}
} else {
// add incomingMetric to results for the first time
results.add(incomingMetric);
}
}
return results;
}
The results.contains check is done by iterating over all the Metrics in results and checking if each object matches the properties except for the two dates.
What could be a better approach that this for both elegance and performance?
In java the most elegant way to compare things is the Comparator interface. You should remove the duplicates using something like:
public List<Metric> removeDuplicates(List<Metric> metrics) {
List<Metric> copy = new ArrayList<>(metrics);
//first sort the metrics list from most recent to older
Collections.sort(copy, new SortComparator());
Set<Metric> set = new TreeSet<Metric>(new Comparator<Metric>() {
#Override
public int compare(Metric o1, Metric o2) {
int result = 0;
// compare the two metrics given your rules
return result;
}
});
for(Metric metric : copy) {
set.add(metric);
}
List<Metric> result = Arrays.asList(set.toArray());
return result;
}
class SortComparator implements Comparator<Metric> {
#Override
public int compare(Metric o1, Metric o2) {
int result = 0;
if(o2.getLastSeenTime() != null && o1.getLastSeenTime() != null) {
result = o2.getLastSeenTime().compareTo(o1.getLastSeenTime());
}
return result;
}
}
The strong of this approach is that you could write a family of comparators and provide a Factory to choose at runtime the best way to compare your metrics and remove or not instances as duplicates among the runtime conditions:
public void removeDuplicates(List<Metric> metrics, Comparator<Metric> comparator) {
List<Metric> copy = new ArrayList<>(metrics);
Collections.sort(copy, new SortComparator());
Set<Metric> set = new TreeSet<Metric>(comparator);
for(Metric metric : copy) {
set.add(metric);
}
List<Object> result = Arrays.asList(set.toArray());
return result;
}
I’m not sure how you are generating List<Metric>. But if you can maintain a Map<String, Metric> instead of that list you may can try the below approach.
So the key of this map will be a combination of all these values you need to compare. (except the date attributes.)
Key: “{metricName}${type}$.....”
For this you can maintain another attribute in Metric object with getter. When you call the getter it will return the key.
Then check the key is exist or not before you put into the map. If it’s exist, get the stored Metric in map for that key and do the date comparison to find the latest Metric object. If it’s the latest replace the map's stored object with new object.
PS : Do the execution time comparison for both cases. So you will find the best approach.
Thanks for the answers. I went with the map approach since it does not incur additional sorts and copies.
#VisibleForTesting
Set<Metric> removeDuplicates(List<Metric> metrics) {
Map<RawMetric, Metric> metricsMap = new HashMap<>();
for (Metric metric : metrics) {
RawMetric rawMetric = RawMetric.builder()
.metricName(metric.getName())
.metricType(metricName.getMetricType())
... // and more
.build();
// pick the latest updated metric (based on lastSeen date)
BiFunction<RawMetric, Metric, Metric> biFunction =
(k, v) -> Metric.builder()
.name(k.getMetricName())
.metricType(k.getMetricType())
... // and more
.lastSeen(v.getLastSeen().after(
metricName.getLastSeen()) ? v.getLastSeen() :
metricName.getLastSeen())
.firstSeen(v.getFirstSeen())
.build();
metricsMap.putIfAbsent(rawMetric, metric);
metricsMap.computeIfPresent(rawMetric, biFunction);
}
return ImmutableSet.copyOf(metricsMap.values());
}
#Value
#Builder
static class RawMetricName {
private String metricName;
private String metricType;
private String ad;
private String project;
private String fleet;
private String host;
private int granularity;
}
I am performing a scan on a DynamoDB table and I need to then add respective attributes from the returned items to a list of type User (User has a single constructor User(String uuid)). The code currently successfully scans the DB and returns a List of the scan results. However my iteration seems to return null for some reason.
AmazonDynamoDBClient client = dynamoClient.getDynamoClient();
DynamoDBMapper mapper = new DynamoDBMapper(client);
try {
DynamoDBScanExpression scanExpression = new DynamoDBScanExpression();
Map<String, Condition> scanFilter = new HashMap<String, Condition>();
Condition scanCondition =
new Condition().withComparisonOperator(ComparisonOperator.NOT_NULL);
scanFilter.put("uuid", scanCondition);
scanExpression.setScanFilter(scanFilter);
List scanResults = mapper.scan(UserAccounts.class, scanExpression);
for (Iterator it = scanResults.iterator(); it.hasNext();) {
//User user = (User) it.next();
allUserSummary.add(new User(scanResults.get(1).toString()));
}
} catch (Exception e) {
// TODO
}
I suggest you start using the modern and compact list iteration by means of The For-Each Loop, which helps to avoid many common errors when using the old iteration style:
[...]
The iterator is just clutter. Furthermore, it is an opportunity for
error. The iterator variable occurs three times in each loop: that is
two chances to get it wrong. The for-each construct gets rid of the
clutter and the opportunity for error. Here is how the example looks
with the for-each construct:
void cancelAll(Collection<TimerTask> c) {
for (TimerTask t : c)
t.cancel();
}
Applying this to your use case yields the following approximately:
List<UserAccounts> scanResults = mapper.scan(UserAccounts.class, scanExpression);
for (UserAccounts userAccounts : scanResults) {
allUserSummary.add(new User(userAccounts.toString()));
}
In case this doesn't work already, it could hint towards the actual error as well, insofar your code assumes the toString() of class UserAccounts to return the uuid, which may or may not be the case. The usual approach is to have a getKey() or getUuidAttribute() method and respective annotations #DynamoDBHashKey or #DynamoDBAttribute, as shown in the example for Class DynamoDBMapper, e.g.:
#DynamoDBTable(tableName = "UserAccounts")
public class UserAccounts{
private String key; // or uuid right away
#DynamoDBHashKey
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
// ...
}
This would obviously yield the following for your example:
List<UserAccounts> scanResults = mapper.scan(UserAccounts.class, scanExpression);
for (UserAccounts userAccounts : scanResults) {
allUserSummary.add(new User(userAccounts.getKey()));
}