How to access multiple resources in a single request : Jersey Rest - rest

I am trying to a find a good design for the following scenario.
I have a POST rest service which will be given an array of services as data. And which should in turn be calling them one by one to aggregate results on the server and send them back to the client.
#Path("/resource1")
#Path("/resource2")
#Path("/collection")
Post data to /collection
{["serviceName": "resource1", "data":"test1"], ["serviceName":"resource2","data":"test2"]}
The reason i need the resource1 and resource2 are, because those services can be called standalone also. I want to reuse the same setup if possible.
Is there any way to do this.
I am using jersey with spring.

Not sure what these resources have in common. If the post method has the same signature for all of them, you could have an abstract class or interface they implement defining the post method and can try using ResourceContext.matchResource to do this. E.g. something like this:
public abstract class AbstractResource {
public abstract String post(Object data);
}
#Path("/resource1")
public class Resource1 extends AbstractResource {
#POST
public String post(String data) {
// do something
}
}
#Path("/collection")
public class CollectionResource {
#Context
private ResourceContext rc;
#POST
#Consumes("application/json")
public String post(List<PostRequest> postRequests) {
StringBuilder result = new StringBuilder();
for (PostRequest pr : postRequests) {
// should wrap this in try-catch
AbstractResource ar = rc.matchResource(pr.resource,
AbstractResource.class);
sb.append(ar.post(pr.data));
}
return result.toString();
}
}
#XmlRootElement
public class PostRequest {
public String resource;
public String data;
}
Hopefully you got the idea and will be able to play with it and tweak it to fit your needs.

Related

Implementing Projection with Specification in Spring Data JPA

I am trying to implement the projection with specification in Spring Data JPA via this implementation:
https://github.com/pramoth/specification-with-projection
Related classes are as follows:
Spec:
public class TopicSpec {
public static Specification<Topic> idEq(String id){
return (root, query, cb) -> cb.equal(root.get(Topic_.id),id);
}
}
Repository
#Repository
public interface TopicRepository extends JpaRepository<Topic,String>,JpaSpecificationExecutorWithProjection<Topic> {
public static interface TopicSimple{
String getId();
String getName();
}
List<TopicSimple> findById(String id);
}
Test
#Test
public void specificationWithProjection() {
Specification<Topic> where= Specifications.where(TopicSpec.idEq("Bir"));
List<Topic> all = topicRepository.findAll(where);
Assertions.assertThat(all).isNotEmpty();
}
I have this response from the Get method:
However the tests fail. Besides when I pull the github project of pramoth I can run the tests with success. Does anyone have any opinion about this issue?
The full project can be found here:
https://github.com/dengizik/projectionDemo
I have asked the same question to the developer of the project Pramoth Suwanpech, who was kind enough to check my code and give answer. My test class should've implement the test object like this:
#Before
public void init() {
Topic topic = new Topic();
topic.setId("İki");
topic.setName("Hello");
topicRepository.save(topic); }
With this setting the tests passed.

Problems when using EntityFilteringFeature and SelectableEntityFilteringFeature with Jersey 2

I'm new to Jersey 2 and JAX-RS, so probably I'm missing something.
What I'm trying to do is a test program to define a coding style in rest services developing.
The test was written in JAVA and uses JERSEY 2.22.2, JDK 1.8.31, MOXY AS JSON Provider.
I defined a Resource with GET methods to support LIST/DETAIL. Due to the size of my POJO, I used some filters and everything was fine.
// 1) First of all I defined the annotation.
#Target({ElementType.TYPE, ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#EntityFiltering
public #interface MyDetailView {
public static class Factory extends AnnotationLiteral<MyDetailView>
implements MyDetailView {
private Factory() {
}
public static MyDetailView get() {
return new Factory();
}
}
// 2) Once defined the annotation, I used to
// programmaticaly exclude the list of subItems in the response...
#XmlRootElement
public class MyPojo {
...
//*** THIS SHOULD BE FILTERED IF THE ANNOTATION IS NOT SPECIFIED IN THE RESPONSE ***
#MyDetailView
private List<SubItem> subItems = new ArrayList<SubItem>();
public List<SubItem> getSubItems() {
return subItems;
}
public void setSubItems(List<SubItem> subItems) {
this.subItems = subItems;
}
}
// 3) I registered the EntityFilteringFeature
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
....
register(EntityFilteringFeature.class);
}
// 4) Finally, I wrote the code to include/exclude the subItems
/*
The Resource class has getCollection() and getItem() methods...
getCollection() adds the annotation only if filterStyle="detail"
getItem() always add the annotation
*/
#Path(....)
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public class MyResource extends SecuredResource {
//filterStyle -> "detail" means MyDetailAnnotation
#GET
public Response getCollection(
#QueryParam("filterStyle") String filterStyle,
#Context UriInfo uriInfo) {
//THIS CODE AFFECTS THE RESPONSE
boolean detailedResponse = "detail".equals(filterStyle);
Annotation[] responseAnnotations = detailedResponse
? new Annotation[0]
: new Annotation[]{MyDetailView.Factory.get()};
//pojo collection...
MyPagedCollection myCollection = new MyPagedCollection();
//.....
ResponseBuilder builder = Response.ok();
return builder.entity(myCollection, responseAnnotations).build();
}
#GET
#Path("/{id}")
public Response getItem(#PathParam("{id}") String idS, #Context UriInfo uriInfo) {
MyPOJO pojo = ...
Annotation[] responseAnnotations = new Annotation[]{MyDetailView.Factory.get()};
return Response.ok().entity(pojo, responseAnnotations).build();
}
}
After the first test, I tried to use the SelectableEntityFilteringFeature to allow the client to ask for specific fields in the detail, so I changed the ApplicationConfig
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
....
register(EntityFilteringFeature.class);
register(SelectableEntityFilteringFeature.class);
property(SelectableEntityFilteringFeature.QUERY_PARAM_NAME, "fields");
}
and I've add the "fields" QueryParam to the Resource getItem() method...
#GET
#Path("/{id}")
public Response getDetail(#PathParam({id}) String id,
#QueryParam("fields") String fields,
#Context UriInfo uriInfo) {
....
But as long as I registered the SelectableEntityFilteringFeature class, the EntityFilteringFeature class stopped working. I tried to add "fields" parameter to one of the Resource methods, it worked perfectly. But the MyDetailAnnotation was completely useless.
I tried to register it using a DynamicFeature
public class MyDynamicFeature implements DynamicFeature {
#Override
public void configure(ResourceInfo resourceInfo, FeatureContext context) {
if ("MyResource".equals(resourceInfo.getResourceClass().getSimpleName())
&& "getItem".equals(resourceInfo.getResourceMethod().getName())) {
//*** IS THE CORRECT WAY TO BIND A FEATURE TO A METHOD? ***
//
context.register(SelectableEntityFilteringFeature.class);
context.property(SelectableEntityFilteringFeature.QUERY_PARAM_NAME, "fields");
}
}
Now the questions:
1) Why registering both the SelectableEntityFilteringFeature feature breaks the EntityFilteringFeature?
2) What is the correct way to bind a feature to a method with the DynamicFeature interface?
Thanks in advance.
This is my first post to Stack Overflow, I hope it was written complaining the rules.
Short answer: you can't. It appears to be a bug as of 2.25.1 and up to 2.26(that I tested with). https://github.com/jersey/jersey/issues/3523
SelectableEntityFilteringFeature implictily registers EntityFilteringFeature (As mentioned here). So I don't see a need to add this.
Since you need Annotation based filtering, you can exclude registering SelectableEntityFilteringFeature.
You can just do,
// Set entity-filtering scope via configuration.
.property(EntityFilteringFeature.ENTITY_FILTERING_SCOPE, new Annotation[] {MyDetailView.Factory.get()})
// Register the EntityFilteringFeature.
.register(EntityFilteringFeature.class)
// Further configuration of ResourceConfig.
You can refer to this example for usage and this example for registering the filter.
So you can remove SelectableEntityFilteringFeature and try just the above mentioned way to register it.

Could you explain me one OOP confusion?

I am creating a program for testing a website. Site has a registration process, which I am testing.
I have created a class named "Client", which should store the information about the client (name, family name, e-mail etc.).
Since I am testing, I use random number generator to generate a name for the client (I have the list of names, one of which is chosen randomly).
Obviously, I should create a method "generateName ()".
But the question is: in which class I should create it?
Lots of programmers would create the method in the Client class. And would do something like that:
client = new Client ();
client.generateName ();
But I have read, that this approach is incorrect - because the client does not generate name for himself. Program does.
Based on that information, I do as follows:
class Program
{
private void generateName ();
}
...
class Client
{
...
public void name ( String name )
{
this.name = name;
}
}
program = new Program ();
program.launch();
client = new Client ();
client.name ( program.generateName () );
But as I know, this approach is not used by the developers.
Could you clarify, how to know "what is right and what is wrong" here? And what information source should I use as an arbitrary for the cases of this kind?
It sounds like generateName() could be a static method in Client, since it's independent of instances:
class Client {
private String name;
public Client(String name) {
this.name = name;
}
public static String generateRandomName() {
String generatedName = ...;
return generatedName;
}
}
You could the simply pass it's value to the Client object as you currently are, or via the constructor:
client = new Client(Client.generateRandomName());
Otherwise, I'd suggest a ClientNameGenerator to handle name generation, to keep inline with SRP and to raise cohesion. This would be the better choice, seeing how you may be needing more name generation methods in the future:
class ClientNameGenerator {
public String generateRandomName() {
String generatedName = ...;
return generatedName;
}
//other name generation methods...
}
You can now use a ClientNameGenerator object to manage the generation of client names:
ClientNameGenerator nameGenerator = new ClientNameGenerator();
client = new Client(nameGenerator.generateRandomName());
Anytime you need to generate a name, simply use the ClientNameGenerator object you created.
There are a number of places which might be appropriate locations for this functionality.
You could have it as a private method on the Client class, used by a static factory method for generating Clients with a random name.
public class Client {
....
public static Client randomlyNamed() {
return new Client(randomName());
}
private static String randomName() {
return ...;
}
}
But that private method might be better extracted to a more appropriate class for generating random Strings...
public class Client {
private static final int defaultNameLength = 8;
....
public static Client randomlyNamed() {
return new Client(Strings.randomString(defaultNameLength));
}
}
public class Strings
private static String randomString(int length) {
return ...;
}
}
You could then expand the static method into a general purpose ClientBuilder class, with an instance method named something like 'withRandomName()'.
public class Client {
...
}
public class ClientBuilder {
private static final int defaultNameLength = 8;
...
public ClientBuilder randomlyNamed() {
this.name = Strings.randomString(defaultNameLength);
}
public Client build() {
return new Client(name);
}
}
public class Strings
private static String randomString(int length) {
return ...;
}
}
An alternative would be an implementation of a NamingStrategy (e.g. ``) object which is given to a ClientBuilder object.
public class RandomNames implements NamingStrategy {
private static final int defaultNameLength = 8;
public String name() {
return String.randomString(defaultNameLength);
}
}
public class ClientBuilder {
private final NamingStrategy nameSource;
public ClientBuilder(NamingStrategy nameSource) {
this.nameSource = nameSource;
}
public Client build() {
return new Client(nameSource.name());
}
}
The pure way to go would be to have a separate class ClientGenerator that produces clients. Because generating clients is not typical client behavior and in your application model a client is no more than a passive data container for client properties. However, generating clients is an activity in "the client domain". Therefor it would be defendable to create a static method Client.NewClient(), like the .NET feamework does with Guid. On the other hand, a guid's very nature is to be generated, it does not represent something in the real world. It IS a spawnable id. So the comparison may not be all that ligitimate.
A common similar mistake (or impurity if you wish) is a Save method on an object. Instead one should have a Persister class that does the job. Or a Manager. Because Save is something you can do to or with the class, not behavior of the class ifself.

Spring Data Rest CrudRepository vs ReadOnlyRepository

I noticed an anomaly in the way Spring Data Rest repositories are behaving. I have two types of entities. in my application - readonly entities (for reference data like statecodes, country codes, zip codes etc.). I don't want to let the end user change these. So I implemented the following ReadOnly repository.
#NoRepositoryBean
public interface ReadOnlyRepository<T, ID extends Serializable> extends Repository<T, ID> {
T findOne(ID id);
Iterable<T> findAll();
}
#Repository
public interface StateRepository extends ReadOnlyRepository<State, Long> {
}
Now, all other entities have CrudRepositories associated with them because they are editable entities like addresses which reference the states and zip codes.
Here's an example.
#Repository
public interface CustomerRepository extends CrudRepository<Address, Long> {
}
I have a controller for both readonly and editable entities, with a pass-through call to the repositories.
#RestController
#RequestMapping(value = "/addresses", produces = MediaType.APPLICATION_JSON_VALUE)
public class AddressController {
#Autowired
private AddressRepository addressRepository;
#RequestMapping(method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
public Iterable<Address> getAllAddresses() {
return addressRepository.findAll();
}
#RequestMapping(value = "/{id}", method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
public Address getAddress(#PathVariable("id") Long id) {
return addressRepository.findOne(id);
}
}
I have an identical Controller corresponding to the State entity.
Funnily enough, the request to StateController gives me a HATEOAS json response, while the request to Address gives me a non HATEOAS json response. What gives?
My bad. My application server did not redeploy certain repositories. This is a non-issue.
So for those running into these issues, you are likely using hot-code replace feature of your IDE. Consider restarting your app and it should be a non-issue.

Marshalling List<String> with JAX-RS

I'm used to working with jax-ws where a wsdl file is generated, and a client can then be generated based on this wsdl file and its xsd(s) using a maven plugin. Using this client is no hassle at at, and you don't have to really think about what happens in the background, like marshalling, http transfer and such.
I'm currently working on a jax-rs project using jaxb to unmarshal objects. One of the methods there returns a list of strings, but it seems that jaxb does not know how to marshal this, which is kinda surprising as it does know how to marshal a list of entities (ex, customers).
Also, I have written a client for the jax-rs service on my own, handling both http responses and unmarshalling of the payload using jaxb. Marshalling and unmarshalling with jaxb is a real hassle since it cannot automatically marshall or unmarshall list of entities that is added to its context, even less lists of strings.
I would like to know if there is some neat way to get all of this for free using restful webservices? This would have to be quite lightweight, and the clients must be easy to distribute.
Thanks!
Runar
The service method that is not working using jaxrs and jaxb:
#GET
#Path("/{customerId}")
#Produces(MediaType.APPLICATION_XML)
public List<String> isCustomerLocked(#PathParam("customerId") Long customerId) {
}
Client code that attempts to marshall/unmarshall text payload. Classes added to the jaxbcontext not shown:
javax.xml.bind.Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.marshal(obj, stringwriter)
javax.xml.bind.Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
unmarshaller.unmarshal(inputstream)
I'd use JAXB to wrap the data. For a simple List<String> this may look as overkill. But in most cases you want to un-/marshall Resource Representations, not simple objects.
Remember: REST ist not RPC!
If you really want tom un-/marshall List<String> write a JAX-RS Provider. But I'd prefer using JAXB.
S.java
#XmlRootElement
public class S {
private String s;
public S() {
}
public S(String s) {
this.s = s;
}
public String getS() {
return s;
}
public void setS(String s) {
this.s = s;
}
}
Ss.java
#XmlRootElement(name="ss-wrapper")
public class Ss {
private List<S> ss;
public List<S> getSs() {
return ss;
}
public void setSs(List<S> ss) {
this.ss = ss;
}
public Ss(List<S> ss) {
this.ss = ss;
}
public Ss() {
}
}
JAX-RS class
#Path("/strings")
#GET
#Produces(MediaType.APPLICATION_XML)
public Response getListOfStrings() {
S s1 = new S("foo");
S s2 = new S("bar");
List<S> strings = new ArrayList<S>();
strings.add(s1);
strings.add(s2);
Ss ss = new Ss(strings);
return Response.ok(ss).build();
}
XML
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ss-wrapper>
<ss>
<s>foo</s>
</ss>
<ss>
<s>bar</s>
</ss>
</ss-wrapper>