Spring Data JPA Invalid page.sort Parameters - java

In a web application that uses Spring Data JPA with Hibernate, we utilize the web pagination functionality to provide paging and sorting capabilities in various lists of entities.
#Controller
public class MyEntityController {
#RequestMapping(method = RequestMethod.GET)
public ModelAndView list(Pageable pageable) { ... }
}
#Configuration
public class MyWebMvcConfig extends WebMvcConfigurationSupport {
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
super.addArgumentResolvers(argumentResolvers);
argumentResolvers.add(new PageableArgumentResolver());
}
}
public interface MyEntityRepository extends PagingAndSortingRepository<MyEntity, String> {
Page<MyEntity> findByPropertyX(String propertyX, Pageable pagable);
}
This allows for entity properties to be defined in the rendered html as special sort request parameters, where the page.sort value actually matches a property in the entity upon which to sort.
<table>
<thead>
<tr>
<th>Property X</th>
<th>Property Y</th>
</tr>
</thead>
<tbody>...</tbody>
</table>
This produces a resulting URL such as:
http://host/context-root/entities/?page.sort=propertyX&page.sort.dir=asc
The problem is that users may modify the URL to use invalid page.sort properties that either reference non-existent column/property names, or worse, that use invalid JPA query characters that result in invalid syntax.
For example, if the URL is modified to sort on "noSuchProperty":
http://host/context-root/entities/?page.sort=noSuchProperty&page.sort.dir=asc
But this property doesn't exist, the following exception will be thrown:
java.lang.IllegalArgumentException: No property noSuchProperty found for type class com.my.company.MyEntity
at org.springframework.data.repository.query.parser.Property.<init>(Property.java:76)
. . .
at org.springframework.data.repository.query.parser.AbstractQueryCreator.createQuery(AbstractQueryCreator.java:86)
. . .
at $Proxy68.findByPropertyX(Unknown Source)
at com.my.company.MyEntityRepository.findByPropertyX(MyEntityRepository.java:17
Likewise, if the URL is modified to an invalid query syntax character, such as """:
http://host/context-root/entities/?page.sort=%22&page.sort.dir=asc
The following error will occur:
java.lang.StackOverflowError
java.util.regex.Pattern$GroupTail.match(Pattern.java:4227)
. . .
org.springframework.data.repository.query.parser.Property.create(Property.java:326)
org.springframework.data.repository.query.parser.Property.create(Property.java:326)
org.springframework.data.repository.query.parser.Property.create(Property.java:326)
org.springframework.data.repository.query.parser.Property.create(Property.java:326)
(There is also a third flavor of exceptions which results in a org.hibernate.QueryException when the #Query is explicitly defined on the Repository method.)
Spring Data JPA abstracts away the details of the sorting, paging, and handling of these parameters; however, it doesn't seem to gracefully handle these scenarios (i.e. where an invalid sort parameter is specified).
We could add in some additional custom logic to validate that the sort property actually exists on the entity; however, I'm wondering if there is a cleaner more centralized approach for doing this such that we don't lose the benefits and simplicity of the Spring Data JPA abstractions. We use this sorting capability throughout our app with many different entities, so ideally, we'd want more of a generic approach, rather than having to explicitly define or check the sort properties for every entity page requested.
Specifically, we actually extend the PageableArgumentResolver to accept an annotated sort default value that is provided in our controller (not illustrated in the code examples for simplicity), so we'd like to just fallback to this default sort order, or just the default sorting order for the entity, rather than throwing an exception.
Some ideas and attempts.. I could use a QueryCreationListener to intercept the query creation and get the sort parameter; however, I can't actually modify the query at that point. Or, I can extend and use a custom PageableArgumentResolver (we are already doing this) to grab the sort parameters; however, I don't have access to the entity at that point, nor the ability to determine if the entity actually has a property by that name. We could declare the supported properties explicitly; however, again, this defeats the idea of centrally and automatically handling this scenario without requiring specific or declared knowledge of the entities.
Is there any other type of interceptor or similar construct that I can utilize to centrally validate pageable sort parameters and modify if necessary before invoking the query? Or is there any type of configuration or way that Spring can automatically handle this scenario such that it more gracefully handles invalid sort params?

I was taking a look at the code and I think some more of the stack trace would be helpful. But from what I can see, I think there are two places you might want to tackle if you are in the mood to rewrite some Spring code.
There are two scenarios here, in the first one you are passing a sort field that doesn't exist in the object/table. What you really want is for that bad parameter to be silently ignored all the time, not just when passing in a 1PageableArgumentResolver]1. I'm thinking it should be an option on the AbstractQueryCreator (and hence, the JpaQueryCreator) to ignore bad parameters on a sort.
The second part that should be tackled is probably the PageableArgumentResolver. If you pass empty strings or something that doesn't make sense like %20 then it should ignore that parameter and not send it through to the PageRequest.
Happy hacking and good luck. Reading your post has made me realize that my site is vulnerable to the same problem and I really have no good solution.

I think improving the PageableArgumentResolver to gracefully handle those scenarios. It could try to create a PropertyPath instance from the String handed in as Sort and thus make sure it's a valid one. I am a bit torn on whether it makes sense to simply drop the invalid String by default, which would return the result not being sorted at all. This is probably the most seamless experience but might also lead to tedious attempts to find out why the result is not sorted.
However, it. Would be cool if you could raise a JIRA ticket for that against Spring Data Commons and just link this ticket here. Feel free to open a pull request in case you have fleshed out a viable implementation. Thanks for bringing this to the table already!

Related

What is the best way to implement a REST service that query the database using like statement using Spring Boot and JPA?

I want to query database for the items that are similar(sql like) to the given string which is later is going to be used for auto complete feature in my app. I want to use REST services but I wondering what is the best approach to implement it. I am also considering that might be later I need to introduce multiple parameters such as the way to sort(asc, desc) or search by multiple fields. Here is my current solution but I am not sure if it is the best way. My Rest service url looks something like this. I didn't want to use RequestParam in this case because it was not showing that this is going to be "like" operator instead of "equal":
https://localhost/rest/device/serial/like/lac
Looking for recommendations regarding best REST Resource Naming convention and ways to implement it in spring mvc.
#RestController
#RequestMapping({"rest/device"})
public class DeviceController {
private final ServiceManager serviceManager;
#GetMapping("/serial/like/{serial}")
#ResponseBody
public Iterable<Device> searchDevicesBySerialNumber(#PathVariable(value = "serial") String serial){
return this.serviceManager.getDeviceRepository().findBySerialNumberLike(serial);
}
}
This is also another way I can accomplish the same thing:
https://localhost/rest/device?serial-prefix=lac
#GetMapping("")
#ResponseBody
public Iterable<Device> searchDevice(#RequestParam(value = "serial-prefix",required = false) String serialPrefix){
if (serialPrefix!=null) {
return this.serviceManager.getDeviceRepository().findBySerialPrefix(serialPrefix);
}
return this.serviceManager.getDeviceRepository().findAll();
}
Your 2nd version is far better. Parameters for sorting, pagination or filtering should appear as request parameters in REST request.
You should also consider using Spring Data Rest. It has out-of-the-box solutions for standard REST endpoints for your entities, which includes paging and sorting too!
The search-feature you need can be implemented in literally 2 lines under Spring Data Rest.
In regards to naming in a REST API: there are many different approaches and not really an accepted best practice that I'm aware of. I'd say, just pick a combination of path variables and request parameters that make sense to you and in your application.
My personal recommendation would be not to overuse path variables. The path should just refer to the resource and (optionally) the operation that you're applying on the resource, but should not include parameters to that operation. That's what request parameters are for. In your case that would be something like your second example.
If you need to build a more complex search with sorting and search in several properties, take a look at this series of tutorials. There are several articles on different approaches to this with some very good ideas. Too many to sum it up here.
Finally, keep performance and security in mind. You probably don't want to give users access to everything, and they should probably not be ably to fetch all data from a table at once (i.e. you may need pagination).

A very specific usage of callbacks in Java

This question is about a specific usage of a callback pattern. By callback i mean an interface from which i can define method(s) that is (are) optionnaly (= with a default set to 'do nothing', thanks Java 8) called from a lower layer in my application. My "application" is in fact a product which may have a lot of changes between client projects, so i need to separates somethings in order to reuse what won't change (technical code, integration of technologies) from the rest (model, rules).
Let's take an example :
I developped a Search Service which is based upon Apache CXF JAX-RS Search.
This service parses a FIQL query which can only handle AND/OR condition with =/</&gt/LIKE/... condition to create a JPA criteria query. I can't use a a condition like 'isNull'.
Using a specific interface i can define a callback that will be called when i got the criteria query from apache CXF layer in my search service and add my condition to the existing ones before the query is executed. This condition are defined on the upper layer of my searchService (RestController). This is in order to reduce code duplicate, like retuning a criteria query and finalize it in every methods where i need it. And because using #Transactional in CXF JAX-RS controller does not work well Spring proxy and CXF work (some JAX-RS annotation are ignored);
First question : does this example seems to be a good idea in terms of design ?
Now another example : i have an object which have some basic fields created from a service layer. But i want to be able to set others non-nullable fields not related to the service's process before the entity is persisted. These fields may move from a projects to another so i'd like to not have to change the signature of my service's method every time we add / remove columns. So again i'm considering using a callback pattern to be able to set within the same transaction and before object is persisted by the Service layer.
Second question : What about this example ?
Global question : Except the classic usage of callback for events : is this a pratice to use this pattern for some specific usage or is there any better way to handle it ?
If you need some code sample ask me, i'll make some (can't post my current code).
I wouldn't say that what you've described is a very specific usage of "an interface from which i can define method(s) that is (are) optionally called from a lower layer". I think that it is reasonable and also quite common solution.
Your doubts may be due to the naming. I'd rather use the term command pattern here. It seems to me that it is less confusing. Your approach also resembles the strategy pattern i.e. you provide (inject) an object which performs some calculations. Depending, on the context you inject objects that behave in a different way (for example add different conditions to a query).
To sum up callbacks/commands are not only used for events. I'd even say that events are specific usage of them. Command/callback pattern is used whenever we need to encapsulate an operation within an object and transfer/pass it somehow (by the way, in Java there is no other way to do so but for example in C++ there are pointers to methods, in C# there are delegates...).
As to your second example. I'm not sure if I understand it correctly. Why can't you simply populate all required fields of an object before calling the service?

Spring Data Repository handle null results or entity reading permissions

I've managed to get Spring Data JPA up and running with my current project. Everything has been going fairly smooth up until now.
I was wondering, is there a way to handle Spring Data Repository responses inside the actual Repository/Base repository implementation? (Or perhaps in an interceptor?)
For instance: I have the need to check if any results were returned from my repository methods, and throw an exception if not like so:
(Preferably keeping the 'magic' of Spring data and not having to implement every repository method)
public interface MyRepository<T,ID> extends CrudRepository<T,ID> {
List<T> findByName(String name);
}
In this case I'd want to throw an exception if the resulting List<T> was null/empty.
Example 2: I have the need to check permission to read, to make sure I have the right to read the requested object from the database. Same thing goes here, I want to inspect / intercept the repository result and find out if the entity instance is the right one considering my context. (For security reasons)
TL;DR Is there any way of "intercept"/inspect the resulting entities/list from a repository method without having to implement ALL magic findBy... methods from all repositories? Perhaps in the base repository implementation or via an interceptor of some kind?
Regards
EDIT I have seen the answer in this particular SO question and I was wondering if there's another way of doing it, which doesn't involve the use of a custom implemented MethodInterceptor?
Regards

Migrate Struts-Tiles to Spring + tiles 3

I'm migrating to SpringMVC and Apache Tiles 3 from a Strut1 + Tiles project. I know only a little about Struts1+Tiles, it is too old and I'm stuck in Controller and ComponentContext in Struts-tiles. According to document from apache website, it was replaced by ViewPreparer and AttributeContext but I dont know the following line means:
ComponentContext compContext=(ComponentContext)pageContext.getAttribute(ComponentConstants.COMPONENT_CONTEXT,PageContext.REQUEST_SCOPE);
What is ComponentConstants.COMPONENT_CONTEXT? and how to change ComponentContext to AttributeContext
Please Help, Thanks.
Bidi, there are 2 ways of getting an AttributeContext:
The first one, like mck stated: through "org.apache.tiles.AttributeContext.STACK" key of request scope. However, the value is a STACK that contains 2 elements of AttributeContext type. The one we need is the first element. IMHO, this way is limited because since the data structure is a stack, getting also mean removing from the stack according to FIFO rule, so you can use the object for only once.
I am using the second way in my project. Because the execute() method of ViewPreparer already have a parameter of AttributeContext type, and this method is always called each time a page is rendered, so you can use this object to do the thing you want (or put it in request) when overriding the method.
AttributeContext is just a collection of key/value pairs. Normally, people use it to get access to some values which are attributes in the template, so fetching the values and putting them to the request can save the overhead. You can also create some static properties of the inheriting class and setting the values to them.
With the Spring-4 and Tiles-3 integration set up (there's spring docs on this as well as a number of good tutorials around) then the properties you put into spring's model map will be available in your jsps, this is not related to the AttributeContext.
AttributeContext only the other hand is (basically) only for holding the map of attributes. Attributes here are defined within a definition, used to identify template or string attributes (as is typically declared in you xml definitions), and come with properties of role, renderer, expression, and/or value.
If AttributeContext is what you are after: you can get hold of it through the current tilesContainer, and to get hold of the current container use the static TilesAccess, eg
TilesContainer tileContainer = TilesAccess.getCurrentContainer(request);
AttributeContext attributeContext = tilesContainer.getAttributeContext(request);
Bidi,
take a read of http://tiles.apache.org/framework/tutorial/advanced/runtime.html
particular the "Runtime Composition using APIs" section.
TilesContainer container = TilesAccess
.getContainer(request.getSession().getServletContext());
Request tilesRequest = new ServletRequest(
container.getApplicationContext(),
request,
response);
otherwise i suggest you take a dive into the Tiles codebase, it's not complicated code, especially the TilesAccess, Request, ApplicationContext stuff.

Provide data to a method using aspect oriented programming

I'm learning AOP and am comfortable with Pointcuts, Advices etc.
What am going to ask, am pretty sure is not possible, but want to ask anyways.
I have a method which takes a userId, fetches the user's record from a database and then does something to the record. I have like twenty different methods that do different things, but all of them take the userId as input and fetch the record from database. This to me looks like a cross cutting concern that can be pulled into an aspect.
But how? I know I can access the arguments (userId in this case), access the return value of the method and catch the methods exception. But how do I give the method something to work with (record in the database in this case?)
public String printUserDetails(String userId)
{
Record record = Database.fetchRecord(userId);
System.out.println(record.getDetails());
return record.getTitle();
}
So, is there a way to pull that database accessing code into an aspect?
One way I can think of is declare something like the following for input
class RequestObject
{
String userId;
Record record;
}
and inject the record in the Aspect and then call proceed(). But this somehow feels wrong.
IMO, resolving a user, using the userid, is not a cross-cutting concern and hence aspect is not the right way. The first landing page that receives a userId should actually resolve it to UserRecord and from then on, the userRecord should be the one moving around in the application.
A simple analogy I can draw to your scenario from one of my applications is, all authenticated servlets expect the servletRequest.getRemoteUser() to return the valid user login corresponding to the user sending the request. We decorated the HttpServletRequest to resolve this to a User object in our application and all the authenticated servlets downcast the HttpServletRequest to AuthenticatedServletRequest and extract this object. No one else within the application tries to resolve a user login anymore.
You cannot access a method's local variables from AspectJ if this is what you wanted to know.
The rest of the question is rather about design and the answer dependent on what you want to achieve. You can avoid code duplication in multiple methods using a template method design pattern. You can inject real or mock objects into classes if you refactor them to have a member instead of local variables. It is another question if you create the member by directly refactoring your classes or via AspectJ's (ITD)[http://www.eclipse.org/aspectj/doc/next/progguide/starting-aspectj.html#inter-type-declarations] mechanism. A third question would be if you possibly want to use an aspect for caching in order to avoid fetching the same object from the database multiple times.
I am not sure what exactly you want to achieve, so I cannot answer more specifically.

Categories

Resources