Spring Method Level security Getting null Property EL1007E: Property or field 'price' cannot be found on null - spring

My Model Class is
#Data
public class Beer {
public Beer(int i, String string, float d) {
this.beerId = i;
this.beerName = string;
this.price = d;
}
public Beer() {
}
private int beerId;
private String beerName;
private float price;
}
And the Service Interface is
public interface IBeerService {
#Secured("ROLE_ADMIN")
#PreAuthorize("#beer.price > 0.0f")
Beer add(Beer beer);
#Secured("ROLE_USER")
#RolesAllowed("ROLE_USER")
List<Beer> getAll();
}
The interface is implemented as below.
#Service
public class BeerService implements IBeerService {
private List<Beer> beerRepository = new ArrayList<>();
#Override
public Beer add(Beer beerToAdd) {
if(!beerRepository.contains(beerToAdd)) {
beerToAdd.setBeerId(this.beerRepository.size()+1);
this.beerRepository.add(beerToAdd);
}
return beerToAdd;
}
#Override
public List<Beer> getAll() {
return beerRepository;
}
}
In the controller, I can able to see the object which is not null, in the debugger.
I am getting an error as
org.springframework.expression.spel.SpelEvaluationException: EL1007E: Property or field 'price' cannot be found on null
at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:213) ~[spring-expression-5.1.9.RELEASE.jar:5.1.9.RELEASE]
at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:104) ~[spring-expression-5.1.9.RELEASE.jar:5.1.9.RELEASE]

Related

Equivalent of Jackson's #JsonUnwrapped in Jsonb

I tried to implement the equivalent of Jacksons's #JsonUnwrapped in Jsonb (using Yasson) with this:
#Retention(RetentionPolicy.RUNTIME)
#JsonbTypeSerializer(UnwrappedJsonbSerializer.class)
public #interface JsonbUnwrapped {}
public class UnwrappedJsonbSerializer implements JsonbSerializer<Object> {
#Override
public void serialize(Object object, JsonGenerator generator, SerializationContext context) {
context.serialize(object, new UnwrappedJsonGenerator(generator));
}
}
public class UnwrappedJsonGenerator extends JsonGeneratorWrapper {
private int level;
public UnwrappedJsonGenerator(JsonGenerator delegate) {
super(delegate);
}
#Override
public JsonGenerator writeStartObject(String name) {
return level++ == 0 ? this : super.writeStartObject(name);
}
#Override
public JsonGenerator writeStartArray(String name) {
return level++ == 0 ? this : super.writeStartArray(name);
}
#Override
public JsonGenerator writeEnd() {
return --level == 0 ? this : super.writeEnd();
}
}
public class Person {
#JsonbUnwrapped
public Name getName() {
return new Name();
}
public static class Name {
public String getFirstName() {
return "John";
}
public String getLastName() {
return "Doe";
}
}
}
JsonbBuilder.create().toJson(new Person())
But this raises an exception javax.json.bind.JsonbException: Recursive reference has been found in class class Person$Name because my UnwrappedJsonbSerializer calls SerializationContext.serialize() with the same object that was initially passed.
Is there any other way to achieve that without resorting to custom serializers for Person or Name ?

Fix string contraints on JPA entity attribute

I am new in JPA,
I want to set only specific fix department names to attribute in entity as a fix string as constraints.I.e default values to attributes.
How to set it?
I think the best option is to use enumerated as indicated by Dinesh Dontha, try this:
Entity
#Entity
public class MyEntity implements Serializable(){
private MyEnum attribute;
}
Enum
public enum MyEnum {
NAME1("N1")
private String shortName;
private MyEnum(String shortName) {
this.shortName = shortName;
}
public String getShortName() {
return shortName;
}
public static MyEnum fromShortName(String shortName) {
switch (shortName) {
case "N1":
return NacionalidadEnum.NAME1;
default:
throw new IllegalArgumentException("ShortName [" + shortName
+ "] not supported.");
}
}
}
Converter
#Converter(autoApply = true)
public class MyEntityEnumConverter implements AttributeConverter<MyEnum, String> {
#Override
public String convertToDatabaseColumn(MyEnum myEnum) {
return myEnum.getShortName();
}
#Override
public MyEnum convertToEntityAttribute(String dbData) {
return MyEnum.fromShortName(dbData);
}
}

Converter works for RequestParameter but not for RequestBody field

I have the following converter:
#Component
public class CountryEnumConverter implements Converter<String, CountryEnum> {
#Override
public CountryEnum convert(String country) {
CountryEnum countryEnum = CountryEnum.getBySign(country);
if (countryEnum == null) {
throw new IllegalArgumentException(country + " - Country is not supported!");
}
return countryEnum;
}
}
Registered it is invoked when used for RequestParam
#GetMapping(value = RestApiEndpoints.RESULTS, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ResultDto> getResults(
Principal principal,
#RequestParam CountryEnum country) {
....
}
But this converter is never invoked when used for field in the RequstBody:
#GetMapping(value = RestApiEndpoints.RESULTS, produces = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<ResultDto> getResults(
Principal principal,
#RequestBody MyBody myBody) {
....
}
public class MyBody {
#NotNull
private CountryEnum country;
public MyBody() {
}
public CountryEnum getCountry() {
return country;
}
public void setCountry(CountryEnum country) {
this.country = country;
}
}
Your existing org.springframework.core.convert.converter.Converter instance will only work with data submitted as form encoded data. With #RequestBody you are sending JSON data which will be deserialized using using the Jackson library.
You can then create an instance of com.fasterxml.jackson.databind.util.StdConverter<IN, OUT>
public class StringToCountryTypeConverter extends StdConverter<String, CountryType> {
#Override
public CountryType convert(String value) {
//convert and return
}
}
and then apply this on the target property:
public class MyBody {
#NotNull
#JsonDeserialize(converter = StringToCountryTypeConverter.class)
private CountryEnum country;
}
Given the similarity of the 2 interfaces I would expect that you could create one class to handle both scenarios:
public class StringToCountryTypeConverter extends StdConverter<String, CountryType>
implements org.springframework.core.convert.converter.Converter<String, CountryType> {
#Override
public CountryType convert(String value) {
//convert and return
}
}
I found out that if I add the following code to my CountryEnum will do the trick.
#JsonCreator
public static CountryEnum fromString(String value) {
CountryEnumConverter converter = new CountryEnumConverter();
return converter.convert(value);
}

java.lang.ClassCastException: Entity A incompatible with Entity B

I'm trying to get proficient in generics in Java. I have some 100 entities that use the same findBy method in JPA interface. Almost all of them require a call to AwrSnapDetails so instead of adding
#ManyToOne private AwrSnapDetails awrSnapDetails; to each Entity, I've created a HelperEntity class and using #Embedded annotation. Now I have gotten to the point in coding where I can't figure out what I am doing wrong and how to go about resolving this error.
Entity
#Entity
public class AwrMemStats {
String description;
double begin_;
double end_;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
Long id;
#Embedded
private HelperEntity helperEntity;
public AwrMemStats() {
}
public AwrMemStats(String description, double begin_, double end_, AwrSnapDetails awrSnapDetails) {
this.description = description;
this.begin_ = begin_;
this.end_ = end_;
HelperEntity h = new HelperEntity(awrSnapDetails);
}
// getters/setters removed for clarity
}
Embedded Entity
#Embeddable
public class HelperEntity implements Serializable{
private static final long serialVersionUID = 1L;
#ManyToOne
AwrSnapDetails awrSnapDetails;
public HelperEntity() {
}
public HelperEntity(AwrSnapDetails awrSnapDetails) {
super();
this.awrSnapDetails = awrSnapDetails;
}
public AwrSnapDetails getAwrSnapDetails() {
return awrSnapDetails;
}
public AwrSnapDetails setAwrSnapDetails(AwrSnapDetails awrSnapDetails) {
return this.awrSnapDetails = awrSnapDetails;
}
}
Service Class
#Service
public class HelperService<T> {
#Autowired
private HelperRepository<T> repository;
public void add(T entity) {
repository.save(entity);
}
public void add(List<T> entities) {
repository.saveAll(entities);
}
public T get(T entity) {
T t = repository.findByHelperEntityAwrSnapDetailsStartSnapIdAndHelperEntityAwrSnapDetailsInstanceDetailDbNameAndHelperEntityAwrSnapDetailsInstanceDetailDbId(
((HelperEntity) entity).getAwrSnapDetails().getStartSnapId(),
((HelperEntity) entity).getAwrSnapDetails().getInstanceDetail().getDbName(),
((HelperEntity) entity).getAwrSnapDetails().getInstanceDetail().getDbId());
//((AwrMemStats) entity).getHelperEntity().getAwrSnapDetails().getStartSnapId(),
//((AwrMemStats) entity).getHelperEntity().getAwrSnapDetails().getInstanceDetail().getDbName(),
//((AwrMemStats) entity).getHelperEntity().getAwrSnapDetails().getInstanceDetail().getDbId());
if (t!= null) {
return t;
}
return null;
}
}
Controller
#RestController
public class HelperController<T> {
#Autowired
private HelperService<T> service;
public void add(T entity) {
service.add(entity);
}
public void add(List<T> entities) {
service.add(entities);
}
public T get(T entity) {
return service.get(entity);
}
}
Execution
getAwrSnapDetails() initilized in HelperLoader
#Component
public class LoadAwrMemStats extends HelperLoader{
#Autowired
private HelperController<AwrMemStats> controller;
public void doThis() {
AwrMemStats profile = new AwrMemStats("a",1.0,1.0,getAwrSnapDetails());
AwrMemStats s = controller.get(profile);
ANd finally the ERROR message
Exception in thread "restartedMain" java.lang.reflect.InvocationTargetException
...
...
Caused by: java.lang.ClassCastException: net.mharoon.perfmon.awr.entities.AwrMemStats incompatible with net.mharoon.perfmon.awr.entities.HelperEntity
at net.mharoon.perfmon.awr.service.HelperService.get(HelperService.java:27)
at net.mharoon.perfmon.awr.controller.HelperController.get(HelperController.java:24)
...
...
Update this code works but only for given class AwrMemStats.
public List<T> get(T entity) {
List<T> ts = repository.findByHelperEntityAwrSnapDetailsStartSnapIdAndHelperEntityAwrSnapDetailsInstanceDetailDbIdAndHelperEntityAwrSnapDetailsInstanceDetailDbName(
//((HelperEntity) entity).getAwrSnapDetails().getStartSnapId(),
//((HelperEntity) entity).getAwrSnapDetails().getInstanceDetail().getDbName(),
//((HelperEntity) entity).getAwrSnapDetails().getInstanceDetail().getDbId());
((AwrMemStats) entity).getHelperEntity().getAwrSnapDetails().getStartSnapId(),
((AwrMemStats) entity).getHelperEntity().getAwrSnapDetails().getInstanceDetail().getDbId(),
((AwrMemStats) entity).getHelperEntity().getAwrSnapDetails().getInstanceDetail().getDbName());
if (!ts.isEmpty()) {
return ts;
}
return null;
}
The reason is because you are returning an Object that is not AwrMemStats and assigning it to AwrMemStats.
A simple work around is to replace
public T get(T entity)
with
public <T extends AwrMemStats> T get(T entity)
EDIT : Another solution (which is more generic) is..
replace
public class AwrMemStats
with
public class AwrMemStats extends HelperEntity
then replace
AwrMemStats s = controller.get(profile);
with
AwrMemStats s = (AwrMemStats) controller.get(profile);

JPA repository fail with good name of property and works with wrong name

I have a class with property named isChecked with is boolean type. In Jpa repository I wrote a method to find all rows which has isChecked = false;
public interface ReservationReminderRepository extends JpaRepository<ReservationReminder, Integer> {
ReservationReminder findByReservationReminderId(Integer id);
//#Query("select r from ReservationReminder r where r.isChecked = :checked")
List<ReservationReminder> findByChecked(boolean checked);
}
While I tried to call a method findByChecked() in Jpa reporistory everything works, but when I tried to run a method with the proper named of property - as it is in jpa doc findByIsChecked() I got strange fails:
Caused by: java.lang.IllegalArgumentException: Unable to locate Attribute with the the given name [isChecked] on this ManagedType [com.carwash.domains.ReservationReminder]
at org.hibernate.jpa.internal.metamodel.AbstractManagedType.checkNotNull(AbstractManagedType.java:128)
at org.hibernate.jpa.internal.metamodel.AbstractManagedType.getAttribute(AbstractManagedType.java:113)
at org.springframework.data.jpa.repository.query.QueryUtils.toExpressionRecursively(QueryUtils.java:566)
at org.springframework.data.jpa.repository.query.JpaQueryCreator$PredicateBuilder.getTypedPath(JpaQueryCreator.java:334)
at org.springframework.data.jpa.repository.query.JpaQueryCreator$PredicateBuilder.build(JpaQueryCreator.java:277)
at org.springframework.data.jpa.repository.query.JpaQueryCreator.toPredicate(JpaQueryCreator.java:182)
at org.springframework.data.jpa.repository.query.JpaQueryCreator.create(JpaQueryCreator.java:109)
at org.springframework.data.jpa.repository.query.JpaQueryCreator.create(JpaQueryCreator.java:49)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createCriteria(AbstractQueryCreator.java:109)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:88)
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:73)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$QueryPreparer.<init>(PartTreeJpaQuery.java:118)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery$CountQueryPreparer.<init>(PartTreeJpaQuery.java:241)
at org.springframework.data.jpa.repository.query.PartTreeJpaQuery.<init>(PartTreeJpaQuery.java:68)
at org.springframework.data.jpa.repository.query.JpaQueryLookupStrategy$CreateQueryLookupStrategy.resolveQuery(JpaQueryLookupStrategy.java:103)
... 104 more
Can anyone tell me why I received that kind of fail? How the method name would looks like when I'd like to check with property checkedDate?
package com.carwash.domains;
import javax.persistence.*;
import java.util.Date;
/**
* Created by mbi on 01.03.2017.
*/
#Entity
public class ReservationReminder {
private int reservationReminderId;
private Reservation reservation;
private boolean isChecked;
private Date checkedDate;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int getReservationReminderId() {
return reservationReminderId;
}
public void setReservationReminderId(int reservationReminderId) {
this.reservationReminderId = reservationReminderId;
}
#OneToOne(mappedBy = "reservationReminder")
public Reservation getReservation() {
return reservation;
}
public void setReservation(Reservation reservation) {
this.reservation = reservation;
}
public boolean getChecked() {
return isChecked;
}
public void setChecked(Boolean checked) {
isChecked = checked;
}
public Date getCheckedDate() {
return checkedDate;
}
public void setCheckedDate(Date checkedDate) {
this.checkedDate = checkedDate;
}
#Override
public String toString() {
return "ReservationReminder{" +
"reviewId=" + reservationReminderId +
", isChecked=" + isChecked +
", checkedDate=" + checkedDate +
'}';
}
public ReservationReminder() {
}
public ReservationReminder(Boolean isChecked, Date checkedDate) {
this.isChecked = isChecked;
this.checkedDate = checkedDate;
}
public ReservationReminder(int reservationReminderId, Reservation reservation, boolean isChecked, Date checkedDate) {
this.reservationReminderId = reservationReminderId;
this.reservation = reservation;
this.isChecked = isChecked;
this.checkedDate = checkedDate;
}
}
It seems that the problem is related to the naming of that property.
As you are telling Spring to look for findByChecked and the property name is isChecked.
You can try to use findByIsChecked and change the getter to isChecked.
But actually i would change the property to checked, getter to isChecked and leave the jpa query method as it is.

Resources