I am trying to write simple validation rules with SpEL
I want to be able to say something like, "Do any elements in the List have a value greater then x". Simple case works fine, but I wanted to extend it to get the absolute value first and then do the greater than test
If I try to call methods or functions as part of the criteria to select from a collection. it seems to loose the context that I am working with an item in the collection
import java.util.Arrays;
import java.util.List;
public class BeanClass {
private final List<ListOf> list;
public BeanClass(ListOf... list){
this.list = Arrays.asList(list);
}
public List<ListOf> getList(){
return list;
}
}
public class ListOf {
private final double value;
public ListOf(double v) {
this.value = v;
}
public double getValue() {
return value;
}
}
import org.junit.Test;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import static org.junit.Assert.assertTrue;
public class SpelTest {
#Test
public void test(){
ExpressionParser parser = new SpelExpressionParser();
// works as expected
Expression expression1 = parser.parseExpression("list.?[ value>2 ].size()!=0");
assertTrue(expression1.getValue(new BeanClass(new ListOf(1.1), new ListOf(2.2)), Boolean.class));
Expression expression2 = parser.parseExpression("list.?[ T(java.lang.Math).abs(value) > 2 ].size()!=0");
// get this error:
//org.springframework.expression.spel.SpelEvaluationException: EL1008E:(pos 5): Property or field 'value' cannot be found on object of type 'BeanClass' - maybe not public?
assertTrue(expression2.getValue(new BeanClass(new ListOf(1.1), new ListOf(-2.2)), Boolean.class));
}
}
I have played with defining my own function and registering it
Experimented with #this as well
Looks like an issue, though: https://jira.spring.io/browse/SPR-12035
As a cunclusion: you can't use method invocation within selectionExpression for items.
You need to add #this to refer the current item, for example:
Expression expression2 = parser.parseExpression("list.?[ T(java.lang.Math).abs(#this.getValue()) > 2 ].size()!=0");
Related
I would like to newer have nulls for my fields which are type of "list"
As I understead below are points where object are initializated, so in these we should do something to initializate empty list in case of null.
Controller (When object is comming from frontend)
Initialization (new AnyObject() or AnyObject.toBuilder - lombok)
FeginClient - Calls between Apis
Is there some framework/annotation which in case of null will set empty list?
Below is what I have currently done
public class TestMapin {
public static void main(String[] args) throws IllegalAccessException {
Test test = new Test();
notNull(test);
System.out.println(test);
}
public static void notNull(Object test) throws IllegalAccessException {
for (Field field : test.getClass().getDeclaredFields()) {
boolean access = field.canAccess(test);
field.setAccessible(true);
if (field.get(test) == null) {
if (field.getType().isAssignableFrom(List.class)) {
field.set(test, Collections.emptyList());
} else if (field.getType().isAssignableFrom(Map.class)) {
field.set(test, Collections.emptyMap());
}
} else if (field.getType().getPackageName().contains("org.owozniak.selfestem")) {
notNull(field);
}
field.setAccessible(access);
}
}
}
#Setter
#ToString
class Test {
private ArrayList<String> stringList;
private Box box = Box.builder().build();
private Magazine magazine;
}
So, I need to use
- initializating via toBuilder - #Singular annotation
- Controllers/Feign clients - inject this code snippet to spring filters
- Initialization via constructor - Use static factory method which will use this code snipped and return "enchanced" instance
Any more ideas/frameworks?
I suggest to use #Singular annotation along with #Builder. This will initialize collection with a non-null List. By annotating one of the parameters (if annotating a method or constructor with #Builder) or fields (if annotating a class with #Builder) with the #Singular annotation, lombok will treat that builder node as a collection, and it generates 2 ‘adder’ methods instead of a ‘setter’ method. One which adds a single element to the collection, and one which adds all elements of another collection to the collection. No setter to just set the collection (replacing whatever was already added) will be generated. A ‘clear’ method is also generated. You can read full details on my recent article https://technicalsand.com/using-lombok-advanced-features/
import lombok.Builder;
import lombok.Singular;
import java.util.Set;
#Builder
public class BuilderExample {
#Builder.Default private long created = System.currentTimeMillis();
private String name;
private int age;
#Singular
private Set<String> occupations;
}
I have the following function in my service.
public boolean checkNameUnique(String name) {
QEntity qEntity = QEntity.entity;
BooleanExpression nameUniquePredicate = qEntity.name.eq(name);
long count = entityReadRepository.count(nameUniquePredicate);
return count == 0;
}
It just checks if the name already exists in db. That needs to be unique, so it returns true if does not already exist and false if it does.
Now how do I write a mockito unit test case for this? I am new to Mockito and writing unit test cases, hence the question.
My reading on Mockito has lead me to write something on the lines of
when(entityReadRepository.count(nameUniquePredicate)).thenReturn(1);
and then call the function to be tested. But that doesn't make any sense.
Entity is Hibernate entity which corresponds to a table in the DB
entityReadRepository extends JpaRepository and QueryDslPredicateExecutor. QEntity is the Q object generated by QueryDsl's plugin.
A unit test would normally mock out any external dependencies, in your case entityReadRepository. If you want to do actual db call it would be classed as integration test.
Your method should return two different values depending on the entityReadRepository response and this is what you would stub in order to unit test it. You were on a good path trying:
when(entityReadRepository.count(any(BooleanExpression.class))).thenReturn(1l);
The problem you have is that you have a lot of static calls and objects in your method and that can't be handled gracefully. One option is to use tools like Powermockito where you can mock behaviour of static methods. If you prefer to stick with mockito you could extract static piece of code to a separate method and create a spy of your class under test:
package com.slavpilus;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.runners.MockitoJUnitRunner;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;
#RunWith(MockitoJUnitRunner.class)
public class TPresenterTest {
#InjectMocks
#Spy
private ClassUnderTest target = new ClassUnderTest();
#Before
public void setUp() {
doReturn(null).when(target).getUniqueNamePredicate();
}
#Mock
private YourRepositoryDependency entityReadRepository;
#Test
public void checkNameUniqueShouldBeTrueIfNameNotInDatabase() {
when(entityReadRepository.count(any())).thenReturn(0l);
boolean isUnique = target.checkNameUnique("anyName");
Assert.assertTrue(isUnique);
}
#Test
public void checkNameUniqueShouldBeFalseIfNameFoundInDatabase() {
when(entityReadRepository.count(any())).thenReturn(1l);
boolean isUnique = target.checkNameUnique("anyName");
Assert.assertFalse(isUnique);
}
}
and your production code would look something like that:
public boolean checkNameUnique(String name) {
BooleanExpression nameUniquePredicate = getUniqueNamePredicate();
long count = entityReadRepository.count(nameUniquePredicate);
return count == 0;
}
protected BooleanExpression getUniqueNamePredicate() {
QEntity qEntity = QEntity.entity;
return qEntity.name.eq(name);
}
This approach however leaves you with some code untested as getUniqueNamePredicate method is skipped entirely during the test execution.
Here is my requirement in Java 6: I am using Eclipse JUNO.
Annotate a method with a custom annotation.
During compilation, raise warning message if a method is calling the
annotated method.
I am looking for something like #Deprecated annotation.
This is what I have done:
Wrote a custom annotation.
Wrote an annotation processor to read and process the methods with
the annotation.
Created a jar and added it in annotation processor path. My sample code (see below) raises the warning message in the annotated method. But it is not my requirement.
What I couldn’t do:
I could not get the calling methods. I want to raise the warning
message in those calling methods.
My sample code:
Custom annotation:
package tool.apichecks;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.SOURCE)
#Target({ ElementType.METHOD })
public #interface HighCostMethod {
String altMethod();
}
Annotation Processor:
package tool.apichecks;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;
#SupportedAnnotationTypes({ "tool.apichecks.HighCostMethod" })
public class MethodProcessor extends AbstractProcessor {
private enum MethodType {
HIGH_COST(HighCostMethod.class.getName());
private String name;
private MethodType(String name) {
this.name = name;
}
private static MethodType getMethodType(String name) {
MethodType methodType = null;
for (MethodType methodType2 : MethodType.values()) {
if (methodType2.name.equals(name)) {
methodType = methodType2;
break;
}
}
return methodType;
}
}
private ProcessingEnvironment processingEnvironment;
#Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
this.processingEnvironment = processingEnvironment;
}
#Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnvironment) {
if (!roundEnvironment.processingOver()) {
for (TypeElement annotation : annotations) {
final Set<? extends Element> elements = roundEnvironment
.getElementsAnnotatedWith(annotation);
MethodType methodType = MethodType.getMethodType(annotation
.toString());
for (Element element : elements) {
switch (methodType) {
case HIGH_COST: {
processHighCostMethod(element);
break;
}
}
}
}
}
return true;
}
protected void processHighCostMethod(Element element) {
HighCostMethod highCostMethod = element
.getAnnotation(HighCostMethod.class);
/* TODO This warns the annotated method itself. I don't want this. I want to warn the methods that calls this method */
processingEnvironment
.getMessager()
.printMessage(
Kind.WARNING,
String.format(
"Do not use high cost method %s. Instead use %s method.",
element, highCostMethod.altMethod()), element);
}
}
Using an AnnotationProcessor will only work on the files containing the annotations or overriding methods, but not calling methods. Maybe there's a way around this, but then you will probably be limited by projects, because the processor only looks at one project at a time.
I guess you need to write an Eclipse plugin with a builder, that analyses code in all files and checks called methods for annotations.
That a lot more work than an annotation processor, but you also have more options. E.g. you could implement a quick fix for the error markers.
I found the following example in core JSF book, this example use data model to finding the selected row then delete it.
But i found an error in this line names.remove(nameToDelete);
can you help me how can i solve this?
package com.jsf.model;
import java.io.Serializable;
import javax.faces.model.ArrayDataModel;
import javax.faces.model.DataModel;
public class TableData implements Serializable {
private static final Names[] names = new Names[] {
new Names("William", "Dupont"),
new Names("Anna", "Keeney"),
new Names("Mariko", "Randor"),
new Names("John", "Wilson")
};
private DataModel<Names> model = new ArrayDataModel<Names>(names);
public DataModel<Names> getNames() { return model; }
public String deleteRow() {
Names nameToDelete = model.getRowData();
names.remove(nameToDelete);
return null;
}
}
thanks all
You are calling remove() on names, an array of Names. Arrays do not have the remove function specified - you want to call it on model.
I have the following class:
public static class TestSomething {
Integer test;
public TestSomething(Integer test) {
this.test = test;
}
// getter and setter for test
}
Ok, now create a collection of this class and serialize it with Gson:
Collection<TestSomething> tests = Arrays.asList(
new TestSomething(1),
new TestSomething(2),
new TestSomething(3)
);
String json = new Gson().toJson(tests, new TypeToken<Collection<TestSomething>>() {}.getType());
After this, the String json is set to
[{"test":1},{"test":2},{"test":3}]
Which is great.
But now, all of my model classes inherit from a generic type Identifiable<T> which provides just two methods T getId() and void setId(T). So I change the TestSomething-class from above to
public static class TestSomething extends Identifiable<Long> {
// same as above
}
When I try to put this through Gson.toJson(), Gson ends up with the following Exception:
java.lang.UnsupportedOperationException: Expecting parameterized type, got class path.to.TestSomething.
Are you missing the use of TypeToken idiom?
See http://sites.google.com/site/gson/gson-user-guide#TOC-Serializing-and-Deserializing-Gener
at com.google.gson.TypeInfoFactory.getActualType(TypeInfoFactory.java:97)
...
So, what do I have to do to get this work?
I don't know the answer, but I know that generic type resolution is a tricky thing to get right: specifically full type resolution from interface with type parameter T up through to generic parameter declaration (T=Long). In these cases it is not enough to check for Method object's parameters but also resolve generic type parameters. This is most likely what causes issues; it may be a bug in Gson.
Since you are serializing things, perhaps you could just omit any type declarations? Although your TypeToken is correct for the use case, maybe it confuses Gson.
But just in case you could not make Gson work with this, I know that of other JSON libraries Jackson can handle such cases correctly.
Perhaps this issue was resolved in one of the Gson releases newer than what the original questioner was using, because the example in the original question now serializes as expected.
// output:
// [{"test":1},{"test":2},{"test":3}]
import java.util.Arrays;
import java.util.Collection;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
public class Foo
{
public static void main(String[] args)
{
Collection<TestSomething> tests = Arrays.asList(
new TestSomething(1),
new TestSomething(2),
new TestSomething(3));
String json = new Gson().toJson(tests, new TypeToken<Collection<TestSomething>>() {}.getType());
System.out.println(json);
}
}
class TestSomething extends Identifiable<Long>
{
Integer test;
public TestSomething(Integer test)
{
this.test = test;
}
#Override
Long getId()
{
return new Long(test);
}
#Override
void setId(Long t)
{
this.test = (int)(t.longValue());
}
}
abstract class Identifiable<T>
{
abstract T getId();
abstract void setId(T t);
}