I have developed a simple RESTful web service.
Root Resource Class:
#Path("/order")
#RequestScoped
public class CustOrderContainerResource {
//<editor-fold defaultstate="collapsed" desc="Instance Variable">
#Context
private UriInfo myUriInfo;
#Context
private ResourceContext myResourceContext;
#Context
private SecurityContext mySecurityContext;
#Inject
private CustOrderDAO myCustOrderDAO;
public CustOrderContainerResource() {
}
#GET
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_ATOM_XML})
public List<Custorder> ReadCustomerOrder(#QueryParam("min")int min,
#QueryParam("max")int max, #Context Request myRequest,
#Context HttpHeaders myHeader) {
int totalOrder = 0;
List<Custorder> resultList = null;
totalOrder = myCustOrderDAO.count();
if(min == 0 && max == 0) {
throw new QueryParamException("Order ID is empty");
}
else if(max > totalOrder) {
throw new QueryParamException("Order ID Range is invalid");
}
resultList = myCustOrderDAO.findRange(min, max, "findOrderIDRange");
return resultList;
}
#GET
#Produces(MediaType.APPLICATION_JSON)
public List<Custorder> ReadCustomerOrder() {
// Check conditional get here
return myCustOrderDAO.findAll();
}
#POST
#Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_FORM_URLENCODED})
public Response createOrder(Custorder myCustOrder) {
String orderID = null;
myCustOrder.setStatus("pending");
myCustOrder.setOrderdate(new Date());
myCustOrder.setTotal("");
// Persist
myCustOrderDAO.create(myCustOrder);
// Get Order ID
// Embedded created URL for new customer order in response
return Response.created(myUriInfo.getAbsolutePath().resolve(myCustOrder.getOrderid() + "/")).build();
}
#Path("{orderID}")
// #Produces(MediaType.TEXT_HTML)
public CustOrderResource ReadSingleCustomerOrder(#PathParam("orderID") String orderID) {
int userOrderID = Integer.parseInt(orderID);
int myOrderID = myCustOrderDAO.count();
CustOrderResource myCustorder = null;
if(userOrderID > myOrderID
|| myCustOrderDAO.find(orderID) == null) {
throw new OrderNotFoundException("Order ID Not Found");
}
if(!mySecurityContext.isUserInRole("admin")) {
// Propogates to specific resource class
myCustorder = myResourceContext.getResource(CustOrderResource.class);
myCustorder.setOrderID(orderID);
}
return myCustorder;
// return CustOrderResource.getInstance(myCustOrderDAO, orderID);
}
}
Sub Resource Locator Class :
#RequestScoped
public class CustOrderResource {
//<editor-fold defaultstate="collapsed" desc="Instance Variable">
#Inject
private CustOrderDAO myCustOrderDAO;
private String orderID;
private static final Logger myLogger = Logger.getLogger(CustOrderResource.class.getName());
//</editor-fold>
// ========================================================
public CustOrderResource() {
}
private CustOrderResource(String orderID) {
this.orderID = orderID;
}
public static Custorder getInstance(CustOrderDAO myCustOrderDAO, String orderID) {
return myCustOrderDAO.find(orderID);
}
#GET
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON, MediaType.APPLICATION_ATOM_XML})
public Custorder getCustomerOrder() {
return myCustOrderDAO.find(orderID);
}
#POST
#Consumes(MediaType.APPLICATION_XML)
public String updateCustomerOrder() {
return "so";
/*try {
myCustOrderDAO.update(myCustOrder);
}
catch(Exception e) {
myLogger.log(Level.ALL, e.toString());
throw new WebApplicationException(
Response.status(Status.INTERNAL_SERVER_ERROR)
.entity("Cust Order Update Failed").build());
}*/
}
#DELETE
// 415 Unsupported media type
public String deleteCustomerOrder() {
return "Deleted";
// myCustOrderDAO.delete(myCustOrder);
}
public String getOrderID() {
return orderID;
}
public void setOrderID(String orderID) {
this.orderID = orderID;
}
}
My question is
AFAIK, the resource context will propagate to specific resource class
when we specify it as an argument according to the HTTP method like
POST or DELETE. How do I pass the parameter from sub resource locator
method into sub resource class method?
I tried to update customer order using post method with XML data but unfortunately the JAX-RS runtime returns 415 Unsupported media type.
I am using the REST client from http://code.google.com/p/rest-client/ to test my application, by pasting an XML file into the body tab content. What is wrong with it?
Does the JAXB automatically convert to XML when I return a list of
objects? I have tested and it return xml format but just want
confirmation. Is it more flexible to return response object?
I wonder how to build a response object with list of object and list of URI or Atom XML with list of object (Apache Abdera).
How to find out a id of a newly persisted object into database in my
createCustomerOrder method ?
Thanks.
Please help.
Passing object into sub resource locator class is solve by using QueryParam annotation. Newly persisted object is finding using EntityManager util.
Related
I'm developing an app against a cloud application that has hard api rate limits in place. In order to have my team get a feeling for how close we are in regards to those limits I want to count all API calls made from our app in a meaningful way.
We use Feign as access layer, and I was hoping to be able to use the RequestInterceptor to count the different API endpoints we call:
RequestInterceptor ri = rq -> addStatistics(rq.url());
Now this does not work, as the resulting URLs almost always count "1" afterwards, as they already contain all resolved path variables, so I get counts for
1 - /something/id1valueverycryptic/get
1 - /something/anothercrypticidkey/get
and so on.
I was hoping to somehow get access to either the #ResuqestLine mapping value (GET /something/{id}/get) or at least the uri template pre-resolve (/somethine/{id}/get)
Is there a way to do this?
Thanks!
Maybe you could try using custom feign InvocationHandlerFactory.
I've managed to log RequestInterceptor using code like this:
change EnableFeignClients and add defaultConfiguration
#EnableFeignClients(defaultConfiguration = FeignConfig.class)
add default feign config
#Configuration
public class FeignConfig {
#Bean
#ConditionalOnMissingBean
public Retryer feignRetryer() {
return Retryer.NEVER_RETRY;
}
#Bean
#Scope("prototype")
#ConditionalOnMissingBean
public Feign.Builder feignBuilder(Retryer retryer) {
return Feign.builder()
.retryer(retryer)
.invocationHandlerFactory((target, dispatch) -> new CountingFeignInvocationHandler(target, dispatch));
}
}
create your invocation handler (code based on feign.ReflectiveFeign.FeignInvocationHandler)
public class CountingFeignInvocationHandler implements InvocationHandler {
private final Target target;
private final Map<Method, MethodHandler> dispatch;
public CountingFeignInvocationHandler(Target target, Map<Method, MethodHandler> dispatch) {
this.target = checkNotNull(target, "target");
this.dispatch = checkNotNull(dispatch, "dispatch for %s", target);
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if ("equals".equals(method.getName())) {
try {
Object otherHandler =
args.length > 0 && args[0] != null ? Proxy.getInvocationHandler(args[0]) : null;
return equals(otherHandler);
} catch (IllegalArgumentException e) {
return false;
}
} else if ("hashCode".equals(method.getName())) {
return hashCode();
} else if ("toString".equals(method.getName())) {
return toString();
}
RequestLine requestLine = method.getAnnotation(RequestLine.class);
addStatistics(requestLine.value());
return dispatch.get(method).invoke(args);
}
#Override
public boolean equals(Object obj) {
if (obj instanceof CountingFeignInvocationHandler) {
CountingFeignInvocationHandler other = (CountingFeignInvocationHandler) obj;
return target.equals(other.target);
}
return false;
}
#Override
public int hashCode() {
return target.hashCode();
}
#Override
public String toString() {
return target.toString();
}
}
Be careful and check if you feign configuration wasn't more complex and in that case extend classes as needed.
If you are using spring-cloud-starter-openfeign , You could do something like below
add the a primary contract bean
#Bean("YourContract")
#Primary
public Contract springpringContract() {
return (targetType) -> {
List<MethodMetadata> parseAndValidatateMetadata = new SpringMvcContract().parseAndValidatateMetadata(targetType);
parseAndValidatateMetadata.forEach(metadata -> {
RequestTemplate template = metadata.template();
template.header("unresolved_uri", template.path().replace("{", "[").replace("}", "]"));
});
return parseAndValidatateMetadata;
};
}
Add the contract to the feign client builder
#Bean
public <T> T feignBuilder(Class<T> feignInterface, String targetURL) {
return Feign.builder().client(getClient())
.contract(contract)
.
.
}
Once you are done with the above you should be able to access the unresolved path in the RequestTemplate
#component
public class FeignRequestFilter implements RequestInterceptor {
#Override
public void apply(RequestTemplate template) {
String unresolvedUri = template.headers().getOrDefault("unresolved_uri", Collections.singleton(template.path()))
.iterator().next();
}
}
Maybe you could try overwriting feign Logger.
Suppose we have a feign client,
#FeignClient(name = "demo-client", url = "http://localhost:8080/api", configuration = FeignConfig.class)
public interface DemoClient {
#GetMapping(value = "/test/{id}")
void test(#PathVariable(name = "id") Integer id) {
}
}
import feign.Logger;
import feign.Request;
import feign.Response;
import java.io.IOException;
public class CustomFeignRequestLogging extends Logger {
#Override
protected void logRequest(String configKey, Level logLevel, Request request) {
super.logRequest(configKey, logLevel, request);
// targetUrl = http://localhost:8080/api
String targetUrl = request.requestTemplate().feignTarget().url();
// path = /test/{id}
String path = request.requestTemplate().methodMetadata().template().path();
}
}
In Jersey 2, I'm trying to develop a method that allows me to pass a JSON list of couple (service, method) that representing the access path to a resource in a REST request and aggregate the result in a single response. So, the JSON list could be like this:
[
{
service : "customerService",
method : "getCustomer",
params : {
id:57
}
},
{
service : "customerService",
method : "getContacts",
params : {
idContact : 75
}
}
]
The corresponding command bean could be like this:
public class Command {
private String method;
private String service;
public Command() {
}
public Command(final String service, final String method) {
this.service = service;
this.method = method;
}
public String getMethod() {
return method;
}
public String getService() {
return service;
}
public void setMethod(final String method) {
this.method = method;
}
public void setService(final String service) {
this.service = service;
}
}
And the customer service class could be like this:
#Path("/customerService")
public class CustomerService {
#GET
#Path("/getCustomer/{id}")
#Produces(MediaType.APPLICATION_JSON)
public Customer getCustomer(#PathParam("id") final int id) {
...
}
#GET
#Path("/getContacts/{idCustomer}")
#Produces(MediaType.APPLICATION_JSON)
public List<Contact> getContacts(#PathParam("idCustomer") final int idCustomer) {
...
}
}
Thus, I could make one single Ajax call to the REST and get the the contacts list and the customer data and gain an Ajax call.
My question is How dispatch command in order to execute the methods of the service. I tried to do this:
#Context
ExtendedResourceContext context;
#POST
#Path("/exec")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.TEXT_PLAIN)
public String exec(List<Command> commands) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
final List<Resource> resources = context.getResourceModel().getRootResources();
for (final Command command : commands) {
for (final Resource serviceResource : resources) {
if (serviceResource.getPath().equals("/" + command.getService())) {
System.out.println("Service found " + serviceResource.getPath());
for (final Resource methodResource : serviceResource.getChildResources()) {
if (methodResource.getPath().equals("/" + command.getMethod())) {
for (ResourceModelComponent component : methodResource.getComponents()) {
if (component instanceof ResourceMethod) {
final ResourceMethod m = (ResourceMethod) component;
if (m.getHttpMethod().equals("GET") || m.getHttpMethod().equals("POST")) {
final Invocable invocable = m.getInvocable();
Method method = invocable.getHandlingMethod();
method.invoke(this);
}
}
}
}
}
}
}
}
return "ok";
}
But I can't instantiate some Jersey object like ExtendedResourceContext.
I've found this topic but it seems to be applied to version 1 of Jersey:
How to access multiple resources in a single request : Jersey Rest
Thank you for your answers and sorry for my bad english.
JSONObject jo=new JSONObject();
JSONObject jo1=new JSONObject();
JSONArray jarr=new JSONArray();
jo.put("service","customerService");
jo.put("method","getCustomer");
jo1.put("id","57");
jo.put("params",jo1);
jarr.put(jo);
By using the above example you can resolve your problem.hope this will helpful
I am trying to return {"status": its value}´in the case of routeD!=0 currently I am getting {"status":201,"routes":null} I would get the response in this form {"status":201} without "routes":null at the same time I dont want to lost the response of routeD==0 which is for example {"status":230,"routes":[1,9,3]}
I appeciate any help.
Receiver class:
#Path("/data")
public class Receiver {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public Response storeData(Data data) {
Database db = new Database();
String macD = data.getMac();
int routeD = data.getRoute();
double latD = data.getLatitude();
double longD = data.getLongitude();
double speedD = data.getSpeed();
// Jackson class to wrapper the data in JSON string.
SDBean bean = new SDBean();
if (routeD != 0) {
bean.status = db.insertData(macD, routeD, latD, longD);
return Response.status(bean.status).entity(bean.toJson()).build();
} else {
bean.routes = db.detectRoute(latD, longD);
return Response.status(230).entity(bean.toJson()).build();
}
}
}
SDBean class:
public class SDBean {
public int status;
public ArrayList<Integer> routes;
public SDBean(){
status = 230;
}
public String toJson() {
ObjectMapper mapper = new ObjectMapper();
String json = null;
try {
json = mapper.writeValueAsString(this);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
System.out.println(json);
return json;
}
}
Just use #JsonInclude(JsonInclude.Include.NON_NULL)
Annotation used to indicate when value of the annotated property (when used for a field, method or constructor parameter), or all properties of the annotated class, is to be serialized. Without annotation property values are always included, but by using this annotation one can specify simple exclusion rules to reduce amount of properties to write out.
import com.fasterxml.jackson.annotation.JsonInclude;
[...]
#JsonInclude(JsonInclude.Include.NON_NULL)
public class SDBean {
I'm using Jersy Rest, one of the requirement being is variable number of input from client to REST endpoint, with a JSON document that have N number of fields. I have been asked to write a #JsonParam annotation and to map the input json to Rest Endpoint method arguments.
Something like
public ResultWrapper addToCart(#JsonParam(key="cartId") String cartId)
{
....
}
Where input JSON might be
{
cartId:1233,
custId:123213,
itemId:3344
}
I have jackson in classpath and gave a solution where the entire input JSON is mapped to a java.util.Map and getting the values with map.get(key). But they are insisting on a #JsonParam as in map there is not type safety and i have to parse each of the input to the corresponding type.
I need some pointer on how to write a annotation processor for #JsonParam that will work with jersy.
Find below an example code based on the Jersey's 1.x InjectableProvder which inject a value to a parameter from a JSON string posted to a resource method.
The annotation class:
#Retention(RetentionPolicy.RUNTIME)
public #interface JsonParam {
String key();
}
The provider class:
#Provider
public class JsonParamProvider implements InjectableProvider<JsonParam, Parameter> {
#Override
public ComponentScope getScope() {
return ComponentScope.PerRequest;
}
#Override
public Injectable<Object> getInjectable(ComponentContext ic, final JsonParam jsonParam, Parameter parameter) {
return new AbstractHttpContextInjectable<Object>() {
#Override
public Object getValue(HttpContext c) {
// extract the entity one per request
Map<?, ?> entity = c.getRequest().getEntity(Map.class);
if (entity != null) {
// then store it a request property
c.getProperties().put("requestEntity", entity);
} else {
entity = (Map<?, ?>) c.getProperties().get("requestEntity");
}
if (entity == null) {
return null;
}
return entity.get(jsonParam.key());
}
};
}
}
The resource method declaration:
#POST
#Produces(MediaType.APPLICATION_JSON)
#Consumes(MediaType.APPLICATION_JSON)
public String foo(#JsonParam(key = "a") String param1, #JsonParam(key = "b") Integer param2) {
return "Params: " + param1 + " " + param2;
}
This is what I'm trying to do:
#Path("/finder")
public class Finder {
#Path("/{name}")
public Proxy find(#PathParam("name") String name) {
Object found = /* some object found by name */
return new Proxy(found);
}
}
public class Proxy {
private Object obj;
public Proxy(Object found) {
this.obj = found;
}
#GET
#Path("/")
public String info() {
return /* some meta-information about the object */
}
#Path("/")
public Object passthru() {
return this.obj;
}
}
I'm trying to enable:
GET /finder/alpha -> Proxy.info()
GET /finder/alpha/something -> obj.something()
Am I going the right way? Meanwhile, Jersey says:
WARNING: A sub-resource method, public final java.lang.String com.XXX.Proxy.info(),
with URI template, "/", is treated as a resource method
Everything is fine with the code above except that I don't need #Path("/") annotation at info().