Would like to create the below hierarchical structure in REST with JAXRS and jersey as provider
#POST /origination/customers/
#PUT /origination/customers
#GET /origination/customers/{customerId}
#POST /origination/customers/{customerId}/inventory
#PUT /origination/customers/{customerId}/inventory
#GET /origination/customers/{customerId}/inventory/inventoryId
Currently all the services are written in a single class OriginationService, but for better encapsulation I would like to know if the services can be refractored like the customer origination in a seperate class called CustomerOriginationService and Inventory origination inside CustomerInventoryService (This is an example scenario, my problem is something similar)
Is it possible to achieve the above with JAXRS(Jersey) annotation
Definitely! Ant that's the standard way to assemble set of HTTP methods in different classes. You need to use #Path Example - #Path("/{parameter}").
Below code may be useful to you -
Controller Interface -
package com.teducate.api;
import java.io.UnsupportedEncodingException;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Response;
public interface TekEvents {
#GET
#Path("/{parameter}")
#Produces("application/json")
Response responseMsg( #PathParam("parameter") String parameter,
#DefaultValue("Nothing to say") #QueryParam("value") String value) throws UnsupportedEncodingException;
}
Implementation -
package com.teducate.api.impl;
import javax.ws.rs.Path;
import javax.ws.rs.core.Response;
import com.teducate.api.TekEvents;
import com.teducate.bo.TekEventsBO;
import com.teducate.bo.impl.TekEventBOImpl;
#Path("events")
public class TekEventsController implements TekEvents {
TekEventsBO tekEventsBO;
public TekEventsController() {
tekEventsBO = new TekEventBOImpl();
}
public Response responseMsg(String parameter, String value) {
String output = tekEventsBO.responseMsg(parameter, value);
return Response.status(200).entity(output).build();
}
}
Sub-resource locator is the keyword I was looking for.
The below article sums it up nicely
http://docs.oracle.com/javaee/6/tutorial/doc/gknav.html#gkrhr
Related
I'm trying to execute a request at another service that I own.
The guides I'm using to create the app are:
QUARKUS - Using the REST Client
QUARKUS - CDI Reference
QUARKUS - Workshop
I'm getting an error like:
org.jboss.resteasy.spi.UnhandledException: java.lang.RuntimeException: Error injecting com.easy.ecomm.core.product.ProductClient com.easy.ecomm.core.cart.CartService.productClient
org.eclipse.microprofile.rest.client.RestClientDefinitionException: Parameters and variables don't match on interface com.easy.ecomm.core.product.ProductClient::findProductById
Here's the class ProductClient
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
#Path("products")
#RegisterRestClient(configKey = "products-api")
public interface ProductClient {
#GET
#Path("{id}")
Product findProductById(String id);
}
Here's the Service Layer:
import org.eclipse.microprofile.rest.client.inject.RestClient;
import javax.enterprise.context.ApplicationScoped;
import javax.inject.Inject;
#ApplicationScoped
public class CartService {
#Inject
#RestClient
ProductClient productClient;
public void addItem(String cartId, String productId, Integer amount){
// Code to find the cart on a queue.
Product product = findProduct(productId);
cart.getItems().add(new CartItem(amount, product));
}
private Product findProduct(String productId) {
return productClient.findProductById(productId);
}
}
and the application.properties:
products-api/mp-rest/url=http://localhost:8060
products-api/mp-rest/scope=javax.inject.Singleton
The dependencies are the same as we have on the guides quarkus-rest-client and quarkus-rest-client-jackson
Things that I've already tried:
Remove the ConfigKey from the #RegisterRestClient and use the full path on the application.properties, add the Jandex Plugin on my POM.xml as described here.
But still no success. Each change gives me the same error message.
You forgot to annotate your path variable with #PathParam, that's why it can't instantiate the client:
import org.eclipse.microprofile.rest.client.inject.RegisterRestClient;
import javax.ws.rs.PathParam;
#Path("products")
#RegisterRestClient(configKey = "products-api")
public interface ProductClient {
#GET
#Path("{id}")
Product findProductById(#PathParam("id") String id);
}
This is my current dummy POST login function. I want to read the request headers inside public Response loginPost(). Is it possible? I tried for example changing the function arguments but I always get io.swagger.api.impl.LoginApiServiceImpl is not abstract and does not override abstract method loginPost(io.swagger.model.LoginPostRequestBody,javax.ws.rs.core.SecurityContext) in io.swagger.api.LoginApiService
package io.swagger.api.impl;
import io.swagger.api.*;
import io.swagger.model.*;
import io.swagger.model.LoginPost200Response;
import io.swagger.model.LoginPostRequestBody;
import java.util.Map;
import java.util.List;
import io.swagger.api.NotFoundException;
import java.io.InputStream;
import org.glassfish.jersey.media.multipart.FormDataContentDisposition;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.SecurityContext;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.HttpHeaders;
import javax.validation.constraints.*;
#javax.annotation.Generated(value = "io.swagger.codegen.v3.generators.java.JavaJerseyServerCodegen", date = "2020-01-21T12:31:41.554Z[GMT]")public class LoginApiServiceImpl extends LoginApiService {
#Override
public Response loginPost(LoginPostRequestBody body, SecurityContext securityContext) throws NotFoundException {
// do some magic!
}
}
The http headers cannot be accessed by this auto-generated class. Instead one has to go to src/gen/java/io/swagger/api/myApi.java and do the following
import javax.ws.rs.core.HttpHeaders;
edit the response function at the last part of the file so that it
also takes this argument #Context HttpHeaders requestHeaders
change the exception code at the end, the function there also
needs to take the above argument (without #Context in this case)
Then update the myApiService.java file in the same folder and of course the myApiServiceImpl.java file in src/main/java/io/swagger/api/impl/ so that they both import and take as argument the javax.ws.rs.core.HttpHeaders. Do not use #Context in these latter cases either.
The general idea is to first change the myApi.java file so that it passes request headers and then update all the files that use the request function (if you can't figure them all out, compiler errors will guide you)
The solution by Tasos is correct. However, modifying generated code is not my cup of tea. Therefore I would suggest modifying the Mustache templates:
API template and API service template (and the 'Impl' template, which is should follow suit).
I would suggest replacing SecurityContext securityContext in the service with a new container object storing all #Context objects that are needed.
E.g.:
// ... snippet from apiService.mustache ...
public abstract class {{classname}}Service {
public static class Context {
private final UriInfo uriInfo;
private final SecurityContext securityContext;
public Context(HttpHeaders httpHeaders, SecurityContext securityContext) {
this.httpHeaders = httpHeaders;
this.securityContext = securityContext;
}
// ... getters
}
{{#operation}}
public abstract Response {{nickname}}({{#allParams}}{{>serviceQueryParams}}{{>servicePathParams}}{{>serviceHeaderParams}}{{>serviceBodyParams}}{{>serviceFormParams}},{{/allParams}}Context context) throws NotFoundException;
{{/operation}}
}
{{/operations}}
The api.mustache file can be modified accordingly to fill the Context before calling the delegate method.
public Response {{nickname}}({{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{>formParams}},{{/allParams}}#Context SecurityContext securityContext,
#Context HttpHeaders httpHeaders) throws NotFoundException {
var context = new {{classname}}Service.Context(httpHeaders, securityContext);
return delegate.{{nickname}}({{#allParams}}{{#isFormParam}}{{#isFile}}{{paramName}}Bodypart{{/isFile}}{{/isFormParam}}{{^isFile}}{{paramName}}{{/isFile}}{{^isFormParam}}{{#isFile}}{{paramName}}{{/isFile}}{{/isFormParam}}, {{/allParams}}context);
}
The approach above allows the implementations that use the generated code to be consistent and require no modification after code generation.
I'm having trouble adding a custom Header to a RestTemplate using AOP in Spring.
What I have in mind is having some advice that will automatically modify execution of RestTemplate.execute(..) by adding this one Header. Other concern is targeting a particular RestTemplate instance that is a member of a Service which requires having this Header passed.
Here is the code of my Advice as it looks like now:
package com.my.app.web.controller.helper;
import com.my.app.web.HeaderContext;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RequestCallback;
import org.springframework.web.client.RestTemplate;
#Aspect
public class MyAppHeaderAspect {
private static final String HEADER_NAME = "X-Header-Name";
#Autowired
HeaderContext headerContext;
#Before("withinServiceApiPointcut() && executionOfRestTemplateExecuteMethodPointcut()")
public void addHeader(RequestCallback requestCallback){
if(requestCallback != null){
String header = headerContext.getHeader();
}
}
#Pointcut("within (com.my.app.service.NeedsHeaderService)")
public void withinServiceApiPointcut() {}
#Pointcut("execution (* org.springframework.web.client.RestTemplate.execute(..)) && args(requestCallback)")
public void executionOfRestTemplateExecuteMethodPointcut(RequestCallback requestCallback) {}
}
The problem that I'm having is how to modify RequestCallback to add my header. Being an interface it looks rather empty, on the other hand I'd rather not use a concrete implemntation because then I'd have to manually check if the implementation matches expected class. I'm beginning to wonder if this is really a correct method to do this. I found this answer Add my custom http header to Spring RestTemplate request / extend RestTemplate
But it uses RestTemplate.exchange() when I've checked that on my execution path RestTemplate.execute() is being used. Anyone has any ideas here?
Spring has htmlEscape to escape potentially unsafe HTML from user input. I'd like to apply htmlEscape as an annotation to the backend Spring based code (similar concept). This is to help prevent XSS attacks.
class MyCreateRequestDoc {
#NotBlank(message = 'A name must be specified')
#AsSafeHtml
String name
I believe this calls for an AOP pattern, but I don't know how to implement such a thing in Java Spring.
I started with:
package com.my.company.services..security
import groovy.util.logging.Slf4j
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.springframework.stereotype.Component
import org.springframework.web.util.HtmlUtils
#Aspect
#Slf4j
#Component
class AsSafeHtmlAspect {
#Around("#within(com.my.company.services.security.AsSafeHtml)")
void assertProperty(ProceedingJoinPoint pjp){
var input = 'get this from somewhere'
return toSafeHtml(input) //return?
}
private String toSafeHtml(input) {
return HtmlUtils.htmlEscape(input)
}
}
And the related annotation used in Around above:
package com.my.company.services.security
import java.lang.annotation.Retention
import java.lang.annotation.RetentionPolicy
#Retention(RetentionPolicy.RUNTIME)
#interface AsSafeHtml {
}
Is there a Extension Point for the Job REST API?
I want to add some information when http://server/jenkins/job/job_name/job_number/api/json is called.
Any hints?
OK, after a lot of research and tries, I've found the answer.
To expose additional data in the Job/Build REST API, the TransientActionFactory (http://javadoc.jenkins-ci.org/jenkins/model/TransientActionFactory.html) needs to be extended using the AbstractBuild (http://javadoc.jenkins-ci.org/hudson/model/AbstractBuild.html) as it type.
You'll have something like this:
import hudson.Extension;
import hudson.model.AbstractBuild;
import hudson.model.Action;
import java.util.Collection;
import java.util.Collections;
import jenkins.model.TransientActionFactory;
#Extension
public class MyTransientActionFactory extends TransientActionFactory<AbstractBuild> {
#Override
public Class<AbstractBuild> type() {
return AbstractBuild.class;
}
#Override
public Collection<? extends Action> createFor(AbstractBuild target) {
return Collections.singleton(new MyAction(target));
}
}
That will add MyAction to the AbstractBuild actions list which is shown in the REST API.