My objective is to add a custom convenience method to a Spring Data REST API without creating my own controller.
According to the documentation here I have extended PagingAndSortingRepository in the following manner:
Repository:
#RepositoryRestResource
public interface PartyRestRepository extends PagingAndSortingRepository<Party, String>, CustomPartyRestRepository {
}
Interface with my method:
public interface CustomPartyRestRepository {
void dynamicPartyCreation(final String name);
}
Implementation:
public class CustomPartyRestRepositoryImpl implements CustomPartyRestRepository {
#Autowired
private PartyService partyService;
#Autowired
private PartyRepository partyRepository;
#Autowired
private HeroService heroService;
#Override
public void dynamicPartyCreation(final String name) {
final Party party = this.partyService.createParty(name);
final List<Hero> heroes = IntStream.range(0, 3)
.mapToObj(i -> this.heroService.createHero(String.format("Hero %d for %s", i, name)))
.collect(Collectors.toList());
party.setMembers(heroes);
this.partyRepository.save(party);
}
}
When I do GET localhost:8080/profile/parties/, I see that Spring has picked up my method and is exposing it:
<...cut...>
{
"name": "dynamicPartyCreation",
"type": "SAFE"
}
<..cut..>
But I can't seem to use it. GET localhost:8080/parties/dynamicPartyCreation/ results in 404, as does POST, with body or without, with query param or not. A PUT simply creates a party and ignores the /parties/dynamicPartyCreation/ part of the url (meaning, my method isn't called). I have tried a million combinations but I can't use it.
What am I doing wrong?
Well, interesting, I never thought it could work this way...
try
return this.partyRepository.save(party);
Although I don't understand how can it work at all, when you basically #autowired PartyRepositoryCustom into itself...
So maybe it should be something like this:
public class CustomPartyRestRepositoryImpl implements CustomPartyRestRepository {
#Autowired
private PartyService partyService;
#Autowired
private ListableBeanFactory beanFactory;
#Autowired
private HeroService heroService;
#Override
public Party dynamicPartyCreation(final String name) {
final Party party = this.partyService.createParty(name);
final List<Hero> heroes = IntStream.range(0, 3)
.mapToObj(i -> this.heroService.createHero(String.format("Hero %d for %s", i, name)))
.collect(Collectors.toList());
party.setMembers(heroes);
return beanFactory.getBean(PartyRepository.class).save(party);
}
}
If the server doesn't start, try accessing/initialising the PartyService and HeroService via beanFactory too.
Although it should work (I successfully tested a similar method in my current project), I still don't think it's a good idea to implement it this way.
It's not a search (even if it returns a new object)
It changes the DB, so this endpoint should be accessed via PUT method.
So I suggest creating a custom controller method instead, and moving the logic into the PartyService class.
Oh, btw. The URL you were looking for is
GET /parties/search/dynamicPartyCreation/
i have several interfaces which extend a single interface.
I need to add, during a #PostCostruct method, these interfaces to a Map.
The problem is that i need to retrieve the #Service class name from the DB and i don't know ho to put the interface in the map...
I'll try to explain it better
I have a general service interface
public interface IVehicleServiceGeneral{
//methods...
}
then i have several interfaces which extend the general one.
public interface IService1 extends IVehicleServiceGeneral{
}
public interface IService2 extends IVehicleServiceGeneral{
}
the concrete implementations of these classes are annotated with #Service("service1Name"), #Service("service2Name") and so on...
Then from the DB i retrieve my Suppliers
public class Supplier {
private long id;
private String serviceName;
//getters and setters
}
Finally i need to create the map, because i need to access the implementations at runtime based on the Supplier, i created a ContextAware class to get my beans by name, but the interfaces are not beans... I also tried to put the #Qualifier on the interface, but obviously it does not work... How can I put the interface in the map?
#PostConstruct
private void createServiceMap(){
serviceMap = new HashMap<OBUSupplier, IVehicleServiceGeneral>();
List<Supplier> suppliers = supplierService.findAll();
for(Supplier s : suppliers) {
serviceMap.put(s, contextAware.getBean(s.getServiceName()));
}
}
You can create IVehicleServiceGeneral instance map like this:
class SomeClass {
Map vehicleServiceGeneralInstanceMap = new HashMap();
SomeClass(Set<IVehicleServiceGeneral> instances) {
instances.forEach(i -> vehicleServiceGeneralInstanceMap.put(i.getServiceName(), i));
}
private void createServiceMap() {
Map serviceMap = new HashMap<OBUSupplier, IVehicleServiceGeneral>();
List<Supplier> suppliers = supplierService.findAll();
for(Supplier s : suppliers) {
serviceMap.put(s, vehicleServiceGeneralInstanceMap.get(s.getServiceName()));
}
}
The only thing you require is IVehicleServiceGeneral#getServiceName which your Service1, 2 need to override with proper names that present in DB.
I am using Spring version 4.3.3 and Jackson version 2.8.3. I am trying to filter out specific fields from an entity bean based on some custom logic that is determined at runtime. The #JsonFilter seems ideal for this type of functionality. The problem is that when I put it at the field or method level, my custom filter never gets invoked. If I put it at the class level, it gets invoked just fine. I don't want to use it at the class level though since then I would need to separately maintain the list of hardcoded field names that I want to apply the logic to. As of Jackson 2.3, the ability to put this annotation at the field level is supposed to exist.
Here is the most basic custom filter without any custom logic yet:
public class MyFilter extends SimpleBeanPropertyFilter {
#Override
protected boolean include(BeanPropertyWriter beanPropertyWriter) {
return true;
}
#Override
protected boolean include(PropertyWriter propertyWriter) {
return true;
}
}
Then I have the Jackson ObjectMapper configuration:
public class MyObjectMapper extends ObjectMapper {
public MyObjectMapper () {
SimpleFilterProvider filterProvider = new SimpleFilterProvider();
filterProvider.addFilter("myFilter", new MyFilter());
setFilterProvider(filterProvider);
}
}
Then finally I have my entity bean:
#Entity
public class Project implements Serializable {
private Long id;
private Long version;
#JsonFilter("myFilter") private String name;
#JsonFilter("myFilter") private String description;
// getters and setters
}
If I move the #JsonFilter annotation to the class level where #Entity is, the filter at least gets invoked, but when it is at the field level like in the example here, it never gets invoked.
I have the same need but after examining the unit tests I discovered that this is not the use-case covered by annotating a field.
Annotating a field invokes a filter on the value of the field not the instance containing the field. For example, imagine you have to classes, A and B, where A contains a field of type B.
class A {
#JsonFilter("myFilter") B foo;
}
Jackson applies "myFilter" to the fields in B not in A. Since your example contains fields of type String, which has no fields, Jackson never invokes your filter.
I have a need to exclude certain fields based on the caller's permissions. For example, an employee's profile may contain his taxpayer id, which is considered sensitive information and should only be serialized if the caller is a member of the Payrole department. Since I'm using Spring Security, I wish to integrate Jackson with the current security context.
public class EmployeeProfile {
private String givenName;
private String surname;
private String emailAddress;
#VisibleWhen("hasRole('PayroleSpecialist')")
private String taxpayerId;
}
The most obvious way to do this is to Jackson's filter mechanism but it has a few limitations:
Jackson does not support nested filters so adding an access filter prohibits using filters for any other purpose.
One cannot add Jackson annotations to existing, third-party classes.
Jackson filters are not designed to be generic. The intent is to write a custom filter for each class you wish to apply filtering. For example, I you need to filter classes A and B, then you have to write an AFilter and a BFilter.
For my use-case, the solution is to use a custom annotation introspector in conjunction with a chaining filter.
public class VisibilityAnnotationIntrospector extends JacksonAnnotationIntrospector {
private static final long serialVersionUID = 1L;
#Override
public Object findFilterId(Annotated a) {
Object result = super.findFilterId(a);
if (null != result) return result;
// By always returning a value, we cause Jackson to query the filter provider.
// A more sophisticated solution will introspect the annotated class and only
// return a value if the class contains annotated properties.
return a instanceof AnnotatedClass ? VisibilityFilterProvider.FILTER_ID : null;
}
}
This is basically a copy SimpleBeanProvider that replaces calls to include with calls to isVisible. I'll probably update this to use a Java 8 BiPredicate to make the solution more general but works for now.
This class also takes another filter as an argument and will delegate to it the final decision on whether to serialize the field if the field is visible.
public class AuthorizationFilter extends SimpleBeanPropertyFilter {
private final PropertyFilter antecedent;
public AuthorizationFilter() {
this(null);
}
public AuthorizationFilter(final PropertyFilter filter) {
this.antecedent = null != filter ? filter : serializeAll();
}
#Deprecated
#Override
public void serializeAsField(Object bean, JsonGenerator jgen, SerializerProvider provider, BeanPropertyWriter writer) throws Exception {
if (isVisible(bean, writer)) {
this.antecedent.serializeAsField(bean, jgen, provider, writer);
} else if (!jgen.canOmitFields()) { // since 2.3
writer.serializeAsOmittedField(bean, jgen, provider);
}
}
#Override
public void serializeAsField(Object pojo, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer) throws Exception {
if (isVisible(pojo, writer)) {
this.antecedent.serializeAsField(pojo, jgen, provider, writer);
} else if (!jgen.canOmitFields()) { // since 2.3
writer.serializeAsOmittedField(pojo, jgen, provider);
}
}
#Override
public void serializeAsElement(Object elementValue, JsonGenerator jgen, SerializerProvider provider, PropertyWriter writer) throws Exception {
if (isVisible(elementValue, writer)) {
this.antecedent.serializeAsElement(elementValue, jgen, provider, writer);
}
}
private static boolean isVisible(Object pojo, PropertyWriter writer) {
// Code to determine if the field should be serialized.
}
}
I then add a custom filter provider to each instance of ObjectMapper.
#SuppressWarnings("deprecation")
public class VisibilityFilterProvider extends SimpleFilterProvider {
private static final long serialVersionUID = 1L;
static final String FILTER_ID = "dummy-filter-id";
#Override
public BeanPropertyFilter findFilter(Object filterId) {
return super.findFilter(filterId);
}
#Override
public PropertyFilter findPropertyFilter(Object filterId, Object valueToFilter) {
if (FILTER_ID.equals(filterId)) {
// This implies that the class did not have an explict filter annotation.
return new AuthorizationFilter(null);
}
// The class has an explicit filter annotation so delegate to it.
final PropertyFilter antecedent = super.findPropertyFilter(filterId, valueToFilter);
return new VisibilityPropertyFilter(antecedent);
}
}
Finally, I have a Jackson module that automatically registers the custom annotaion introspector so I don't have to add it to each ObjectMapper instance manually.
public class FieldVisibilityModule extends SimpleModule {
private static final long serialVersionUID = 1L;
public FieldVisibilityModule() {
super(PackageVersion.VERSION);
}
#Override
public void setupModule(Module.SetupContext context) {
super.setupModule(context);
// Append after other introspectors (instead of before) since
// explicit annotations should have precedence
context.appendAnnotationIntrospector(new VisibilityAnnotationIntrospector());
}
}
There are more improvements that can be made and I still have more unit tests to write (e.g., handling arrays and collections) but this is the basic strategy I used.
You can try this approach for the same purpose:
#Entity
#Inheritance(
strategy = InheritanceType.SINGLE_TABLE
)
#DiscriminatorColumn(
discriminatorType = DiscriminatorType.STRING,
length = 2
)
#Table(
name = "project"
)
#JsonTypeInfo(
use = Id.CLASS,
include = As.PROPERTY,
property = "#class"
)
#JsonSubTypes({
#Type(
value = BasicProject.class,
name = "basicProject"
),
#Type(
value = AdvanceProject.class,
name = "advanceProject"
)})
public abstract class Project {
private Long id;
private Long version;
}
#Entity
#DiscriminatorValue("AD")
public class AdvanceProject extends Project {
private String name;
private String description;
}
#Entity
#DiscriminatorValue("BS")
public class BasicProject extends Project {
private String name;
}
I don't think you will make it work. I was trying and these are results of my investigation, maybe it will be helpful.
First of all, as #Faron noticed, the #JsonFilterannotation is applied for the class being annotated not a field.
Secondly, I see things this way. Let's imagine, somewhere in Jackson internals you are able to get the actual field. You can figure out if there is the annotation using Java Reflection API. You can even get the filter name. Then you get to the filter and pass the field value there. But it happens at runtime, how will you get the corresponding JsonSerializer of the field type if you decide to serialize the field? It is impossible because of type erasure.
The only alternative I see is to forget about dynamic logic. Then you can do the following things:
1) extend JacksonAnnotationIntrospector (almost the same as implement AnnotationIntrospector but no useless default code) overriding hasIgnoreMarker method. Take a look at this answer
2) criminal starts here. Kinda weird way taking into account your initial goal but still: extend BeanSerializerModifier and filter out fields there. An example can be found here. This way you can define serializer that actually doesn't serialize anything (again, I understand how strange it is but maybe one will find it helpful)
3) similar to the approach above: define useless serializer based on BeanDescription implementing ContextualSerializer's createContextual method. The example of this magic is here
Thanks to this really good blog, I was able to use #JsonView to filter out specific fields from an entity bean based on some custom logic that is determined at runtime.
Since the #JsonFilter does not apply for the fields within a class, I found this to be a cleaner workaround.
Here is the sample code:
#Data
#AllArgsConstructor
public class TestEntity {
private String a;
#JsonView(CustomViews.SecureAccess.class)
private Date b;
#JsonView(CustomViews.SecureAccess.class)
private Integer c;
private List<String> d;
}
public class CustomViews {
public static interface GeneralAccess {}
public static interface SecureAccess {}
public static class GeneralAccessClass implements GeneralAccess {}
public static class SecureAccessClass implements SecureAccess, GeneralAccess {}
public static Class getWriterView(final boolean hasSecureAccess) {
return hasSecureAccess
? SecureAccessClass.class
: GeneralAccessClass.class;
}
}
#Test
public void test() throws JsonProcessingException {
final boolean hasSecureAccess = false; // Custom logic resolved to a boolean value at runtime.
final TestEntity testEntity = new TestEntity("1", new Date(), 2, ImmutableList.of("3", "4", "5"));
final ObjectMapper objectMapper = new ObjectMapper().enable(MapperFeature.DEFAULT_VIEW_INCLUSION);
final String serializedValue = objectMapper
.writerWithView(CustomViews.getWriterView(hasSecureAccess))
.writeValueAsString(testEntity);
Assert.assertTrue(serializedValue.contains("a"));
Assert.assertFalse(serializedValue.contains("b"));
Assert.assertFalse(serializedValue.contains("c"));
Assert.assertTrue(serializedValue.contains("d"));
}
consider the following scenario: i have a bean that handles user-searches with a lot of parameters used on many pages with different urls. many users may spent a larger time with custom-searches and currently i am hitting the database to load those static lists everytime.
#ManagedBean
#ViewScoped
public class SearchBean extends DefaultBean {
private String searchPath; //seo: build a url-friendly path depending on search-parameters
private List<Currency>currencies;
private List<Country>countries;
private List<Market>markets;
private List<DrugTypes>drugTypes;
private List<Products>products;
/**
* ...15 other lists
*/
private List<ResultData>results;
#PostConstruct
public void init(){
this.currencies = Currency.getAll(); //jpa-entities
this.countries = Country.getAll();
this.markets = Markets.getAll();
this.drugTypes = DrugTypes.getAll();
this.products = Products.getAll();
}
public String search(){
this.results = ResultData.getByParameters(getSearchParams());
//
//e.g. localhost:8080/myApp/search/markets/germany/class-alpha-products/rhesus?faces-redirect=true
return searchPath;
}
public List<Currency> getCurrencies() { return currencies; }
public void setCurrencies(List<Currency> currencies) { this.currencies = currencies; }
public List<Country> getCountries() { return countries; }
public void setCountries(List<Country> countries) { this.countries = countries; }
public void setMarkets(List<Market> markets) { this.markets = markets; }
public List<Market> getMarkets() { return markets; }
public void setDrugTypes(List<DrugTypes> drugTypes) { this.drugTypes = drugTypes; }
public List<DrugTypes> getDrugTypes() { return drugTypes; }
public List<Products> getProducts() { return products; }
public void setProducts(List<Products> products) { this.products = products; }
}
what is the recommend way regarding to the headline? my small gripe is, that i see 20 jpa-queries on the console although the list-data which is build with <h:selectOneMenu> on client-side does not change on new pages but must be included on every subpage.
leave it as it is
put all those lists as session-attributes and remove them in #predestroy when user leaves.
put the whole bean as sessionbean (i already have 2 session-beans ("user" and "language" and i read that having more is not a good design)
store the list-data as a json-string in a cookie and recreate the list if the cookie exists?
other suggestions?
thanks for watching!
None of all. Caching DB entities isn't the responsibility of a front end (UI) framework. That's the responsibility of the persistence (DB) framework, which is thus JPA in your case.
JPA offers 2nd level caching possibilities. Main advantage as compared to all your proposals is that it knows precisely which entities are more queried and thus need to be cached, and when exactly to invalidate a cached entity because of an entity change. JSF as being a dumb HTML form based MVC framework which only delegates user interface events/data to business services knows nothing of this all.
See also:
How to configure L2 cache in Hibernate/JPA2?
Hibernate 5.1 User Guide - Chapter 12: Caching
EclipseLink Wiki - Examples / JPA / Caching
I am looking for transportation layer for gwt. I would like to create ajax request using generic method, f.e this is my DAO/service:
public class GenericDao<T extends GenericModel<T>> {
private Logger logger = LoggerFactory.getLogger(this.getClass().getCanonicalName());
#Transient protected Class<T> entityClass;
public GenericDao() {
super();
}
public GenericDao(Class<? extends GenericModel<T>> clazz) {
this.entityClass = (Class<T>) clazz;
}
public T getBy(Long id) {
return JPA.em().find(entityClass, id);
}
public List<GenericModel<T>> get() {
logger.error("trying to get data from db");
return getList();
}
public List<GenericModel<T>> getList() {
return JPA.em().createQuery("FROM " + entityClass.getSimpleName()).getResultList();
}
public void save(GenericModel<T> entityClass) {
JPA.em().getTransaction().begin();
JPA.em().persist(entityClass);
JPA.em().getTransaction().commit();
}
public void update(T entityClass) {
JPA.em().getTransaction().begin();
JPA.em().merge(entityClass);
JPA.em().getTransaction().commit();
}
public void delete(T entityClass) {
JPA.em().getTransaction().begin();
JPA.em().remove(entityClass);
JPA.em().getTransaction().commit();
}
}
GenericModel/Entity:
#MappedSuperclass
public class GenericModel<T extends GenericModel<T>> implements Identifiable, Versionable {
#Transient
protected Class<T> entityClass;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#Version
private Integer version;
// setter & getter
#Override
public Long getId() {return id;}
public void setId(Long id) {this.id = id;}
#Override
public Integer getVersion() {return version;}
public void setVersion(Integer version) {this.version = version;}
// constructor
public GenericModel() {
Class<?> obtainedClass = getClass();
Type genericSuperclass = null;
for (;;) {
genericSuperclass = obtainedClass.getGenericSuperclass();
if (genericSuperclass instanceof ParameterizedType) {
break;
}
obtainedClass = obtainedClass.getSuperclass();
}
ParameterizedType genericSuperclass_ = (ParameterizedType) genericSuperclass;
try {
entityClass = ((Class) ((Class) genericSuperclass_
.getActualTypeArguments()[0]));
} catch (ClassCastException e) {
entityClass = guessEntityClassFromTypeParametersClassTypedArgument();
}
}
public GenericModel(Long id) {
this();
this.id = id;
}
}
I am looking for mechanism that will allow me to use this generic service for all models on client side (each db entity have id- so I would like to downloads using ajax all my Entities this way, so I should have only one generic method for that on client side).
I've already checked:
GWT-RPC
RequestFactory
RestyGWT
But none of them support this feature.
I've found here:
https://www.mail-archive.com/google-web-toolkit#googlegroups.com/msg100095.html
information that: gwt-jackson supports generics and polymorphism. Unfortunately I didn't found any working example that. Can someone help, give an example, approved that information?
All entities have id and version parameter. So I would like to have one metod on client side RF that will allow me to get from server(service/dao/whatever) that entity by id- like this: Request getBy(Long id); But unfortunatelly I can't make it work. I like the RF way, so I've tried it first. Generally I don't wonna repeat code for downloading entity/proxy by id.
For better understanding, please look also on:
RequestFactory client-side inheritance of typed class with generics
I'm confused as to why you think RPC can't handle generics - according to your link, it can, but RestyGWT cannot. Granted, none of your JPA references make any sense in GWT, but those would live in a DAO on the server, not in the entity/model class themselves, or at least not in the client version. If you had a RPC method that returned T where <T extends GenericModel<T>>, then you would have serializers for every possible GenericModel<?> subtype, and any/all that are gwt-compatible could be sent over the wire.
Edit from update to question:
Your GenericModel class uses features of Java that cannot work in GWT, such as reflection. This cannot be compiled to GWT, since the compiler relies on removing reflection information to minimize your compiled size - leaving in general reflection information means leaving in details about all classes and members, even ones that it can't statically prove are in use, since some reflection might make use of them.
If there is a way to phrase your model object in a way that just deals with the data at hand, focus on that. Otherwise consider a DTO which is just the data to send over the wire - I'm not sure how you would plan to use the entityClass field on the client, or why that would be important to read from the superclass's generics instead of just using getClass().
RequestFactory will have a hard time dealing with generics - unlike RPC (and possibly RestyGWT) it cannot handle polymorphism the way you want, but will instead only send the fields for the declared type, not any arbitrary subtype. RPC will actually send the instance if it is something that the client can handle.