This is with respect to my solution of implementing Paging & Sorting in Domain Driven Design with intention of not to pollute the Domain models and repository contracts,
Base class for REST Request
public class AbstractQueryRequest {
...
private int startIndex;
private int offset;
...
}
Interceptor to retrieve the Query Meta data and store it in ThreadLocal container
public class QueryInterceptor extends HandlerInterceptorAdapter {
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
...
QueryMetaData.instance().setPageMetaData(/*Create and set the pageable*/);
}
}
Container for Query Meta data
public class QueryMetaData {
private static final ThreadLocal<QueryMetaData> instance = new ThreadLocal<>() {
protected QueryMetaData initialValue() {
return new QueryMetaData();
}
};
public static QueryMetaData instance() {
return instance.get();
}
private ThreadLocal<Pageable> pageMetadata = new ThreadLocal<>();
public void setPageMetaData(Pageable pageable) {
pageMetadata.set(pageable);
}
public Pageable getPageMetaData() {
return pageMetadata.get();
}
//clear threadlocal
}
I intend to retrieve this ThreadLocal value in the repository, if available use it with the queries to the datastore.
I hope this may not be a very dirty solution, but want to know is there better widely used pattern for this.
don't use your solution. It's quite complex and will be hard to debug and maintain. If you start using async methods it won't even work.
I don't understand why you can't have sorting/paging properties in your repository methods? If you want a cleaner solution you might create a new object which carries the sort and paging settings.
When you want to fetch data you typically do it through your application services. And those can use your repositories directly before converting the result to DTOs.
then you got something like:
public class YourAppService
{
public YourAppService(ISomeRepository repos)
{
}
public IList<UserDto> ListUsers(string sortColumn = "", bool ascending = true, int pageNumber = 1, pageSize = 30)
{
var querySettings = new QuerySettings(sortcolumn, ascending, pageNumber, pageSize);
var users = _repos.FindAll(querySettings);
return ConvertToDto(users);
}
}
you can read more about sorting and paging in my article here:
http://blog.gauffin.org/2013/01/11/repository-pattern-done-right/
Related
Following is a code snippet where we can use #ModelAttribute at method parameter level
#ReqestMapping(value = useruri)
public void submitInfo(#ModelAttribute User user) {
// Business logic
}
#ReqestMapping(value = personuri)
public void submitInfo(#ModelAttribute Person person) {
// Business logic
}
Can we do like following?
#RequestMapping(value = genericuri)
public void submitInfo(HttpServletRequest request, #PathVariable String type) {
if (type.equals("user")) {
User user = someSpringMvcMethod(request, User.class)
} else if (type.equals("person")) {
Person person = someSpringMvcMethod(request, Person.class)
}
//Business logic
}
Reason is, I am expecting different type of submitted data based on a type and I want to write a generic controller since only difference is conversion of request data to specific java class.
User and Person class has lot of different data and I don't think I can use inheritance/polymorphism to solve my use-case here
I don't recommend such a thing.
Look here
if (type.equals("user")) {
User user = someSpringMvcMethod(request, User.class)
} else if (type.equals("person")) {
Person person = someSpringMvcMethod(request, Person.class)
}
This is already wrong, imho. A single method managing multiple models.
What if you need another model's type? Another if branch.
For example, this is a lot better
#ReqestMapping("base-path/user")
public void submitInfo(#ModelAttribute final User user) {
commonLogic(user.valueOne, user.valueTwo);
}
#ReqestMapping("base-path/person")
public void submitInfo(#ModelAttribute final Person person) {
commonLogic(person.valueOne, person.valueTwo);
}
private void commonLogic(final String one, final String two) {
... // Business logic
}
commonLogic manages the common business logic between the models' types.
It centralizes the work.
You can even place commonLogic in a Service, which is where it should go anyway.
I'm creating a Java application using Elastic Search.
Here is the link for my project.
https://github.com/chanakaDe/ensembl-elastic-rest
In this project, I have implemented a rest controller to take data as JSON.
This is the controller class. Now it only has 2 methods. But I need to add some method like this.
#RequestMapping(value = "/find-by/{id}/{param1}/{param2}/{param3}", method = RequestMethod.GET)
public Iterable<Track> findAllWithParams(#PathVariable int id, #PathVariable String param1, #PathVariable String param2, #PathVariable String param3) {
return trackService.someMethodWithParams(id, param1, param2, param3);
}
What I need to do is take some values from user and send them into Elastic server and make a search. I just refered some of these links and got some idea.
https://www.elastic.co/guide/en/elasticsearch/client/java-api/current/java-search.html
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-terms-query.html
TrackService.java and TrackServiceImpl.java are implemented by TrackRepository.java and it's extended by ElasticsearchRepository default class. https://github.com/chanakaDe/ensembl-elastic-rest/blob/master/src/main/java/com/chanaka/book/repository/TrackRepository.java
I need to take values via REST URL and create an object like following and pass that to Elastic Server. How can I implement that with my current project configuration ?
{
"query": {
"constant_score" : {
"filter" : {
"terms" : { "user" : ["kimchy", "elasticsearch"]}
}
}
}
}
This is my TrackService.java interface.
public interface TrackService {
Track save(Track track);
Track findOne(int id);
Iterable<Track> findAll();
}
And also this is my TrackServiceImpl.java class implemented by TrackService.java.
public class TrackServiceImpl implements TrackService {
private TrackRepository trackRepository;
#Autowired
public void setTrackRepository(TrackRepository trackRepository) {this.trackRepository = trackRepository;}
#Override
public Track save(Track track) {
return trackRepository.save(track);
}
#Override
public Track findOne(int id) {
return trackRepository.findOne(id + "");
}
#Override
public Iterable<Track> findAll() {
return trackRepository.findAll();
}
}
Do I need to implement a custom method for that ? Or is there any default methods like findAll() and findOne() ?
Simply pass an object and get the value ?
I think, there's no such existing method and you need to create your own by using QueryBuilders.wrapperQuery(query.toString()) and ElasticsearchTemplate. Just to note, wrapperQuery supports only query not filter. But you can achieve filter context query with constant_score.
I'm unsuccessfully trying to conditionally and dynamically pick which property to serialize to respond to each request with Jersey (using Jackson). The idea behind this is to securely access to properties of objects within a REST API.
I have several objects that I return in API calls that should show/hide fields depending in the user who is authenticated.
For example, lets say I have an object Car
public class Car implements Serializable {
private Long id;
private String VIN;
private String color;
...
}
Lets say that if an user with the ROLE_ADMIN is authenticated, all properties should be returned, but if there isn't a logged user only the first two need to be shown.
I was thinking on building something that's annotation based. Something like:
public class Car implements Serializable {
private Long id;
private String VIN;
#Secured({AccessRole.ROLE_ADMIN})
private String color;
...
}
In this case, the color property should only be returned if the access role of the requesting user matches the ones passed via the annotation.
But I'm unable to get a hook on where should I implement this logic.
What I'm trying to implement is a sort of #JsonIgnore but that's conditional and dynamic. All solutions I found so far are static.
Is this even possible?
Jersey has support for Entity Filtering. Aside from general filtering, it also supports Role-based Entity Filtering using (javax.annotation.security) annotations.
So you can use the #RolesAllowed, #PermitAll, and #DenyAll annotations on the domain model properties
public static class Model {
private String secured;
#RolesAllowed({"ADMIN"})
public String getSecured() { return this.secured; }
}
To make this work though, you need to have set the SecurityContext inside of a request filter. Jersey will look up the SecurityContext to validate the roles. You can read more about it in this post (Note: the entity filtering is separate from any real authorization that is mentioned in that post. But the post does explain about the SecurityContext).
Basically you will have something like (notice the last line where you set the SecurityContext).
#PreMatching
public static class SimpleAuthFilter implements ContainerRequestFilter {
private static final Map<String, User> userStore = new ConcurrentHashMap<>();
static {
userStore.put("peeskillet", new User("peeskillet", Arrays.asList("ADMIN", "USER")));
userStore.put("paulski", new User("paulski", Arrays.asList("USER")));
}
#Override
public void filter(ContainerRequestContext request) throws IOException {
final String authHeader = request.getHeaderString("Authorization");
final String username = authHeader.split("=")[1];
final User user = userStore.get(username);
if (user == null) {
throw new NotAuthorizedException("No good.");
}
request.setSecurityContext(new SimpleSecurityContext(user));
}
}
Where the SimpleSecurityContext is just a class of your own, where you need to override the isUserInRole method and check if the user has the role
private static class SimpleSecurityContext implements SecurityContext {
private final User user;
SimpleSecurityContext(User user) {
this.user = user;
}
#Override
public Principal getUserPrincipal() {
return new Principal() {
#Override
public String getName() {
return user.getUsername();
}
};
}
#Override
public boolean isUserInRole(String role) {
return user.getRoles().contains(role);
}
#Override
public boolean isSecure() {
return false;
}
#Override
public String getAuthenticationScheme() {
return "simple";
}
}
That's pretty much it. You will also need to register the SecurityEntityFilteringFeature with the application to make it all work.
See a complete test case in this Gist
You can register a custom MessageBodyWriter https://jersey.java.net/documentation/latest/user-guide.html#d0e6951
The MessageBodyWriter will use your custom logic to decide what to write.
It can be done with #JsonView as #dnault suggested.
http://www.baeldung.com/jackson-json-view-annotation
Your MessageBodyWriter will hold a jackson mapper and you will apply the writerWithView with the matching view class as described in the above link.
EDIT: see this one - Jackson Json serialization: exclude property respect to the role of the logged user
Right now, I have some endpoints in a resource. These endpoints access some data and return it:
#Path("/v1/event")
#Produces({MediaType.APPLICATION_JSON})
public class EventResource {
private final DataStore dataStore;
// constructor stuff
#Timed
#GET
#Path("/all/total")
public String getAll(#Bind({Bind.Params.QUERY}) Params params) throws Exception {
return dataStore.getEventTotals(params);
}
}
We completely revamped how our data is stored so now I have a resource that accesses this new data store:
#Path("/v2/event")
#Produces({MediaType.APPLICATION_JSON})
public class NewEventResource {
private final NewDataStore newDataStore;
// constructor stuff
#Timed
#GET
#Path("/all/total")
public MyDataPojo getAll(#Bind({Bind.Params.QUERY}) Params params) throws Exception {
return newDataStore.getEventTotals(params);
}
}
What I would like to do now is somehow have the v1 endpoint use both these resources. Some object would decide which getAll method to use based on some parameters in the Params object that is passed in.
The reason is we have some customers that have data in the old data store, and other customers have data in the new data store. We also have a bunch of other projects that are using our endpoints. It's not feasible or realistic to go change all the other projects to use the v2 endpoint instead of the v1 endpoint.
A couple thoughts. I could do something like this:
#Path("/v1/event")
#Produces({MediaType.APPLICATION_JSON})
public class EventResource {
private final DataStore dataStore;
private final NewDataStore newDataStore;
// constructor stuff
#Timed
#GET
#Path("/all/total")
public String getAllEndpoint(#Bind({Bind.Params.QUERY}) Params params) throws Exception {
if (customerInNewDataStore(params.getCustomer())) {
return getEventTotalsNew(params);
} else {
return getEventTotalsOld(params);
}
}
private MyDataPojo getEventTotalsNew(Params params) throws Exception {
return newDataStore.getEventTotals(params);
}
private String getEventTotalsOld(Params params) throws Exception {
return dataStore.getEventTotals(params);
}
}
The problem with this is that getEvenTotalsNew and getEventTotalsOld return different types. How would I be able to merge this? Also, doing this would be sort of a pain to do for every endpoint as there are quite a few endpoints in our codebase.
I've been reading about filters and intercepters in Jersey: https://jersey.java.net/documentation/latest/filters-and-interceptors.html.
Would a ContainerRequestFilter be able to accomplish what I want to do? Would I be able to access my Params params object in the filter?
Any other better ways to do this? I'm open to all ideas.
Thanks!
I think I might have what you are looking for. You can use a pre-matching filter to modify the request.
This is based on your example stating that you have a v1 and v2 API, both of which are equal (apart from the versioning). I am using a custom header for routing.
Consider these two resources:
#Path("v1")
#Produces(MediaType.APPLICATION_JSON)
public class TestResource1 {
#GET
#Path("test")
public String get() {
return "Hello v1";
}
}
#Path("v2")
#Produces(MediaType.APPLICATION_JSON)
public class TestResource2 {
#GET
#Path("test")
public MyResultObj get() {
MyResultObj o = new MyResultObj();
o.name = "pandaa";
o.message = "Hello V2";
return o;
}
public static class MyResultObj {
#JsonProperty
String name;
#JsonProperty
String message;
}
}
These are both equal with the exception for the version type in the context and the return type. Note, the return type does not matter in this case. What ends up in the response is a json string regardless. In your case, you could also do something like:
Response.ok(myResultObject).build();
At that point all of your return types would just be Response.
Anyhow, Version 1 prints something, and version 2 returns an object.
The pre-matching (important, otherwise you can not change the URI) filter will look like this:
#PreMatching
public class RoutingFilter implements ContainerRequestFilter {
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
String headerString = requestContext.getHeaderString("IS_V2");
boolean v2 = headerString != null && headerString.equals("yes");
URI absolutePath = requestContext.getUriInfo().getAbsolutePath();
if(v2) {
URI v2Redirect = URI.create(absolutePath.toString().replace("v1", "v2"));
requestContext.setRequestUri(v2Redirect);
}
}
}
It simply evaluates the header and replaces the version in the URI. There is probably a better way to do this, but then again this is just an example of how to approach this.
Finally, the test:
artur#pandaadb:~/dev/vpn$ curl "localhost:9085/api/v1/test" --header "IS_V2: yes"
{"name":"pandaa","message":"Hello V2"}
artur#pandaadb:~/dev/vpn$ curl "localhost:9085/api/v1/test" --header "IS_V2: no"
Hello v1
Note how both are doing a request for V1. The first request though gets rerouted internally to v2.
You can write a more generic version (since you might need to be backwards compatible e.g. v1 -> v2 and v2 -> v1) so that it doesn't matter if people call v1 or v2.
Finally - I am not at all sure if this is a good solution :) Personally I would probably write a delegate as seen in your example.
I hope that helps!
Edit: finally - you should be able to use your params object. However this may result in you consuming the requests's input stream. I believe this can only be done once, so you may need to set a new stream after reading it as well.
Artur
I am trying to be able to define the following code:
public class MyObject {
private String name;
... // Other attributes
}
#Path(...)
#Stateless
public class MyRestResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response create(List<MyObject> myObjects) {
// Do some stuff there
}
}
I know that I need to use:
DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true
to setup correctly my object mapper to be able to accept single value as array on my rest resources. I succeed to setup that part.
My problem with this approach is that the following content is not differentiable:
{
"name": "a name",
... // other attributes
}
and
[{
"name": "a name",
... // other attributes
}]
will result into a list (List) of size one. Then, in the method create(List myObjects), I will not be able to do the difference between the List and the Single Object sent to the Rest Resource.
Then, my question is how to do something like that. The idea is to have only one #POST that accepts both Arrays and Single values?
Ideally, I will get rid of the configuration of the ObjectMapper to avoid letting the possibility to set Single Object into the other level of the JSON document. For example, I do not want to allow that:
{
...
"attributes": {
...
}
}
where normally this format should be mandatory:
{
...
"attributes": [{
...
}]
}
Based on that, I tried to put in place an object wrapper of my List to set if I am able to the difference between the list and the object. With something like that:
public class ObjectWrapper<T> {
private List<T> list;
private T object;
public boolean isObject() {
return list == null;
}
}
with the resource that becomes:
#Path(...)
#Stateless
public class MyRestResource {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response create(ObjectWrapper myObjects) {
// Do some stuff there
}
}
and trying to put in place the deserialization of my content through the JAX-RS/Jersey/Jackson mechanisms. If I let the solution as it is now, the deserialization fails due to the fact that the JSON format expected is the following:
{
"list": [{
"name": "a name",
... // other attributes
}]
}
Then I tried to write a custom deserializer but I am a bit lost in this task. I have something like that:
public class ObjectWrapperDeserializer<T> extends JsonDeserializer<T> {
#Override
public T deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, JsonProcessingException {
... // What to put there to deserialize Array or Object
}
}
I just want to deserialize the root level to set the content deserialized into the object wrapper. I also want to keep the feature configured in a class annotated with #ApplicationPath when the configuraiton of the different #Provider are done.
I hope that all the info will give a sufficient picture of what I want to do and what I already tested.
Waiting for suggestion on how to do a resource that accept Arrays or Objects on the same path.
Thanks a lot in advance.
Ok, finally I succeed to put in place a mechanism that do exactly what I am looking for. But, I am not sure if there are negative consequences such the performance or such things.
First, I defined a class that can accept both List or Single Object:
public class RootWrapper<T> {
private List<T> list;
private T object;
}
Then, I need a custom deserializer that is able to know which kind of T type to deserialize and to handle the collection or the single object.
public class RootWrapperDeserializer extends JsonDeserializer<CollectionWrapper<?>> {
private Class contentType;
public RootWrapperDeserializer(Class contentType) {
this.contentType = contentType;
}
#Override
public RootWrapper deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
// Retrieve the object mapper and read the tree.
ObjectMapper mapper = (ObjectMapper) jp.getCodec();
JsonNode root = mapper.readTree(jp);
RootWrapper wrapper = new RootWrapper();
// Check if the root received is an array.
if (root.isArray()) {
List list = new LinkedList();
// Deserialize each node of the array using the type expected.
Iterator<JsonNode> rootIterator = root.getElements();
while (rootIterator.hasNext()) {
list.add(mapper.readValue(rootIterator.next(), contentType));
}
wrapper.setList(list);
}
// Deserialize the single object.
else {
wrapper.setObject(mapper.readValue(root, contentType));
}
return wrapper;
}
}
As far as I know, I try to only deserialize the root level manually and then let Jackson take the next operations in charge. I only have to know which real type I expect to be present in the Wrapper.
At this stage, I need a way to tell Jersey/Jackson which deserializer to use. One way I found for that is to create a sort of deserializer registry where are stored the type to deserialize with the right deserializer. I extended the Deserializers.Base class for that.
public class CustomDeserializers extends Deserializers.Base {
// Deserializers caching
private Map<Class, RootWrapperDeserializer> deserializers = new HashMap<>();
#Override
public JsonDeserializer<?> findBeanDeserializer(JavaType type,
DeserializationConfig config, DeserializerProvider provider,
BeanDescription beanDesc, BeanProperty property) throws JsonMappingException {
// Check if we have to provide a deserializer
if (type.getRawClass() == RootWrapper.class) {
// Check the deserializer cache
if (deserializers.containsKey(type.getRawClass())) {
return deserializers.get(type.getRawClass());
}
else {
// Create the new deserializer and cache it.
RootWrapperDeserializer deserializer =
new RootWrapperDeserializer(type.containedType(0).getRawClass());
deserializers.put(type.getRawClass(), deserializer);
return deserializer;
}
}
return null;
}
}
Ok, then I have my deserializers registry that create new deserializer only on demand and keep them once created. What I am not sure about that approach is if there is any concurrency issue. I know that Jackson do a lot of caching and do not call every time the findBeanDeserializer once it was called a first time on a specific deserialization context.
Now I have created my different classes, I need to do some plumbing to combine everything together. In a provider where I create the ObjectMapper, I can setup the deserializers registry to the created object mapper like below:
#Provider
#Produces(MediaType.APPLICATION_JSON)
public class JsonObjectMapper implements ContextResolver<ObjectMapper> {
private ObjectMapper jacksonObjectMapper;
public JsonObjectMapper() {
jacksonObjectMapper = new ObjectMapper();
// Do some custom configuration...
// Configure a new deserializer registry
jacksonObjectMapper.setDeserializerProvider(
jacksonObjectMapper.getDeserializerProvider().withAdditionalDeserializers(
new RootArrayObjectDeserializers()
)
);
}
#Override
public ObjectMapper getContext(Class<?> arg0) {
return jacksonObjectMapper;
}
}
Then, I can also define my #ApplicationPath that is my REST application like following:
public abstract class AbstractRestApplication extends Application {
private Set<Class<?>> classes = new HashSet<>();
public AbstractRestApplication() {
classes.add(JacksonFeature.class);
classes.add(JsonObjectMapper.class);
addResources(classes);
}
#Override
public Set<Class<?>> getClasses() {
return classes;
}
#Override
public Set<Object> getSingletons() {
final Set<Object> singletons = new HashSet<>(1);
singletons.add(new JacksonJsonProvider());
return singletons;
}
private void addResources(Set<Class<?>> classes) {
classes.add(SomeRestResource.class);
// ...
}
}
Now, everything is in place and I can write a REST resource method like that:
#POST
#Path("somePath")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public Response create(RootWrapper<SpecificClass> wrapper) {
if (wrapper.isObject()) {
// Do something for one single object
SpecificClass sc = wrapper.getObject();
// ...
return Response.ok(resultSingleObject).build();
}
else {
// Do something for list of objects
for (SpecificClass sc = wrapper.getList()) {
// ...
}
return Response.ok(resultList).build();
}
}
That's all. Do not hesitate to comment the solution. Feedbacks are really welcome especially around the way of deserialization process where I am really not sure that it is safe for performance and concurrency.