I'm having some trouble understanding this code. The content variable is an interface. I'm having trouble understanding how it's possible to call the method directly on the interface that isn't implemented by another class. Am I missing some java fundamental? Or does this have to do with the fact that it's autowired?
public class Utils {
#Autowired
private Content cContent;
#TraceMethod
public ConcurrentHashMap<String, String> getCMSContent(Locale locale, String businessId, String appId, String contentType, String contentId) {
CMContent content=null;
try {
content = cContent.getContent(locale.getValue(), businessId, appId, contentType, contentId);
} catch (Exception e) {
LOG.error("Error in content call");
}
...
...
}
The Interface
public interface Content {
CMContent getContent(String var1, String var2, String var3, String var4, String var5) throws Exception;
}
The code is designed to use a dependency injection framework, e.g. Spring.
When Spring creates an instance of Utils, it will find another object in its context which implements Content, and assign that to cContent. That's the meaning of the #Autowired annotation.
Making the type of the field an interface rather than a concrete type allows different implementations of the interface to be used in different circumstances. For instance, a mock can be used for unit testing the Utils class, or Spring could be using a factory to create the implementation, and provide a different implementation depending on how the application is configured.
Related
Given the following basic domain model:
abstract class BaseData { ... }
class DataA extends BaseData { ... }
class DataB extends BaseData { ... }
I want to write a Spring MVC controller endpoint thus ...
#PostMapping(path="/{typeOfData}", ...)
ResponseEntity<Void> postData(#RequestBody BaseData baseData) { ... }
The required concrete type of baseData can be inferred from the typeOfData in the path.
This allows me to have a single method that can handle multiple URLs with different body payloads. I would have a concrete type for each payload but I don't want to have to create multiple controller methods that all do the same thing (albeit each would do very little).
The challenge that I am facing is how to "inform" the deserialization process so that the correct concrete type is instantiated.
I can think of two ways to do this.
First use a custom HttpMessageConverter ...
#Bean
HttpMessageConverter httpMessageConverter() {
return new MappingJackson2HttpMessageConverter() {
#Override
public Object read(final Type type, final Class<?> contextClass, final HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException {
// TODO How can I set this dynamically ?
final Type subType = DataA.class;
return super.read(subType, contextClass, inputMessage);
}
};
}
... which gives me the challenge to determine the subType based on the HttpInputMessage. Possibly I could use a Filter to set a custom header earlier when the URL is available to me, or I could use a ThreadLocal also set via a Filter. Neither sounds ideal to me.
My second approach would be to again use a Filter and this time wrap the incoming payload in an outer object which would then provide the type in a way that enables Jackson to do the work via #JsonTypeInfo. At the moment this is probably my preferred approach.
I have investigated HandlerMethodArgumentResolver but if I try to register a custom one it is registered AFTER the RequestResponseBodyMethodProcessor and that class takes priority.
Hmm, so after typing all of that out I had a quick check of something in the RequestResponseBodyMethodProcessor before posting the question and found another avenue to explore, which worked neatly.
Excuse the #Configuration / #RestController / WebMvcConfigurer mash-up and public fields, all for brevity. Here's what worked for me and achieved exactly what I wanted:
#Configuration
#RestController
#RequestMapping("/dummy")
public class DummyController implements WebMvcConfigurer {
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#interface BaseData {}
public static class AbstractBaseData {}
public static class DataA extends AbstractBaseData {
public String a;
}
public static class DataB extends AbstractBaseData {
public String b;
}
private final MappingJackson2HttpMessageConverter converter;
DummyController(final MappingJackson2HttpMessageConverter converter) {
this.converter = converter;
}
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
resolvers.add(
new RequestResponseBodyMethodProcessor(Collections.singletonList(converter)) {
#Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(BaseData.class)
&& parameter.getParameterType() == AbstractBaseData.class;
}
#Override
protected <T> Object readWithMessageConverters(
NativeWebRequest webRequest, MethodParameter parameter, Type paramType)
throws IOException, HttpMediaTypeNotSupportedException,
HttpMessageNotReadableException {
final String uri =
webRequest.getNativeRequest(HttpServletRequest.class).getRequestURI();
return super.readWithMessageConverters(
webRequest, parameter, determineActualType(webRequest, uri));
}
private Type determineActualType(NativeWebRequest webRequest, String uri) {
if (uri.endsWith("data-a")) {
return DataA.class;
} else if (uri.endsWith("data-b")) {
return DataB.class;
}
throw new HttpMessageNotReadableException(
"Unable to determine actual type for request URI",
new ServletServerHttpRequest(
webRequest.getNativeRequest(HttpServletRequest.class)));
}
});
}
#PostMapping(
path = "/{type}",
consumes = MediaType.APPLICATION_JSON_VALUE,
produces = MediaType.APPLICATION_JSON_VALUE)
ResponseEntity<? extends AbstractBaseData> post(#BaseData AbstractBaseData baseData) {
return ResponseEntity.ok(baseData);
}
}
The key to this is that I stopped using #RequestBody because that is what was preventing me overriding the built-in behaviour. By using #BaseData instead I get a HandlerMethodArgumentResolver that uniquely supports the parameter.
Other than that it was a case of assembling the two objects that already did what I needed, so autowire a MappingJackson2HttpMessageConverter and instantiate a RequestResponseBodyMethodProcessor with that one converter. Then pick the right method to override so that I could control what parameter type was used at a point that I had access to the URI.
Quick test. Given the following payload for both requests ...
{
"a": "A",
"b": "B"
}
POST http://localhost:8081/dummy/data-a
... gives a response of ...
{
"a": "A"
}
POST http://localhost:8081/dummy/data-b
... gives a response of ...
{
"b": "B"
}
In our real-world example this means that we will be able to write one method each that supports the POST / PUT. We need to build the objects and configure the validation possibly - or alternatively if we use OpenAPI 3.0 which we are investigating we could generate the model and validate without writing any further code ... but that's a separate task ;)
Ok so I couldn't find any helpful materials on this topic, a big chunk of articles I found had one method that was annotated with #HystrixCommand and had defined a fallback method.
The other solution I found was using #DefaultProperties(defaultFallback = "fallbackMethod") but the problem with this is that the methods need to have compatible return types.
Unfortunately for me in my service I have many methods with completely different signatures and also I need to get hold of the throwable (in docs it is mentioned that you cannot have any parameters for a default fallback method). The methods look something like this:
#Service
#RequiredArgsConstructor
public class MyService {
private final FeignClient feignClient;
#Override
public String methodA(final CustomObjectA o, final String entity) {
...
}
#Override
public String methodB(final String collection, final Map<String, Object> requestBody) {
...
}
#Override
public String methodC(final String collection, final String id, final Map<String, Object> requestBody) {
...
}
}
And ofc I have more than 3 methods def in the service...
The thing I really want to avoid is making 20 hystrix default fallback methods.
Is there a way where I could def a standard fallback for all methods, no matter what the signatures they have, or am I stuck with defining a fallback method for every single method?
Thanks in advance!!
You will have to implement a fall back for each method.
However using the FallbackFactory might make this easier and allow each method to call one reusable method.
Maybe you don't really want hystrix fallbacks if they are the same for each method. All try catch might solve the same problem.
Let me share the code snippet used in my project.
To call an api like http://www.baidu.com/xxx, you have below steps to follow.
1.Api Definition (fallback = WebServiceApiFallback.class)
#Component
#FeignClient(value = "webServiceApi", configuration = FeignConfiguration.class, fallback = WebServiceApiFallback.class)
public interface WebServiceApi {
#Headers(value = {"Content-Type: application/json", "Accept-Encoding: gzip,deflate"})
#GetMapping(value = "/xxx")
BaseResponse<YourResponse> xxx(YourRequest request);
2.Fallback Definition
#Component
public class WebServiceApiFallback implements WebServiceApi {
#Override
public BaseResponse<YourResponse> xxx(YourRequest request) {
// Your Fallback Code here, when api request failed.
}
3.api host configuration, maybe application.properties...
webServiceApi.ribbon.listOfServers=http://www.baidu.com
4.use it
#Autowired
private WebServiceApi webServiceApi;
For any api, you can just define you request and response, and feign will do the request、 encode、and decode.
[Ref] https://github.com/spring-cloud/spring-cloud-netflix/issues/762
Say I have an annotation (#RequiresAccount) introduced in another library and I'm using it in my project, is there a way to conditionally apply it to a method, e.g. apply it when the customer is from website A and not apply when customer is from website B?
I've taken a look and the only possibility I've found was, creating a wrapper-Annotation:
#Aspect
#Component
public class RequiresAccountWrapperAspect {
#Autowired
private HttpServletRequest request;
private RequiresAccountAspect requiresAccountAspect = new RequiresAccountAspect();
#Around("#annotation(com.example.demo.components.RequiresAccountWrapper)")
public Object checkIfRequiresAccount(ProceedingJoinPoint joinPoint) throws Throwable {
String requestURL = request.getRequestURL().toString();
if (requestURL.startsWith("http://localhost")) {
requiresAccountAspect.checkAccount(joinPoint);
}
return joinPoint.proceed();
}
}
So everywhere you've used your RequiresAccount annotation, you can use this wrapper instead. For example:
#GetMapping("/test")
#RequiresAccountWrapper
public String h() {
return "test";
}
As you can see I'm creating a new instance of the aspect. I don't know if you have access to the Aspect-class itself but if you have you can then call the method in it and pass the joinPoint. To find the URL from the request you can inject the HttpServletRequest.
In my endpoint, I have some methods with #GET and some methods with #POST. #GETs are working fine, but #POSTs always return 404.
Here is some part from the endpoint's interface:
public interface TestEndpoint {
#GET
#Path("/ping")
Response ping();
#POST
#Path("/weather/{iata}/{pointType}")
Response updateWeather(#PathParam("iata") String iataCode,
#PathParam("pointType") String pointType,
String datapointJson);
#POST
#Path("/airport/{iata}/{lat}/{long}")
Response addAirport(#PathParam("iata") String iata,
#PathParam("lat") String latString,
#PathParam("long") String longString);
#GET
#Path("/exit")
Response exit();
}
Here is the server initialization part:
public class TestServer {
private static final String BASE_URL = "http://localhost:9090/";
public static void main(String[] args) {
try {
final ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(TestEndpointImpl.class);
HttpServer server = GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URL), resourceConfig, false);
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
server.shutdownNow();
}));
HttpServerProbe probe = new HttpServerProbe.Adapter() {
public void onRequestReceiveEvent(HttpServerFilter filter, Connection connection, Request request) {
System.out.println(request.getRequestURI());
}
};
server.getServerConfiguration().getMonitoringConfig().getWebServerConfig().addProbes(probe);
server.start();
Thread.currentThread().join();
server.shutdown();
} catch (IOException | InterruptedException ex) {
Logger.getLogger(TestServer.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
where, TestEndpointImpl is an implementation of TestEndpoint (as the name implies) with class-level annotation #Path("/collect").
When I perform GET requests, it works fine. But POSTs are problematic. Corresponding methods are not called.
As a side note, probe prints both GET and POST requests as expected, so I am sure that requests reach the server and paths are ok.
Is there any suggestion?
EDIT: Some snippet from the implementation:
#Path("/collect")
public class TestEndpointImpl implements TestEndpoint {
...
#Override
public Response updateWeather(#PathParam("iata") String iataCode, #PathParam("pointType") String pointType,
String datapointJson) {
System.out.println("TRACE: " + datapointJson);
// do something and return a Response
}
...
}
The registered probe prints /collect/weather/BOS/wind, but updateWeather is not called.
Short answer
Copy the #POST and the #Path annotations to the method implementation. It will do the trick.
Long answer
The section regarding annotation inheritance of the JAX-RS 2.0 specification (the specification which Jersey is the reference implementation) is pretty clear. See the quote below:
3.6 Annotation Inheritance
JAX-RS annotations may be used on the methods and method parameters of a super-class or an implemented interface. Such annotations are inherited by a corresponding sub-class or implementation class method provided that the method and its parameters do not have any JAX-RS annotations of their own. Annotations on a super-class take precedence over those on an implemented interface. The precedence over conflicting annotations defined in multiple implemented interfaces is implementation specific. Note that inheritance of class or interface annotations is not supported.
If a subclass or implementation method has any JAX-RS annotations then all of the annotations on the superclass or interface method are ignored. E.g.:
public interface ReadOnlyAtomFeed {
#GET
#Produces("application/atom+xml")
Feed getFeed();
}
#Path("feed")
public class ActivityLog implements ReadOnlyAtomFeed {
public Feed getFeed() {...}
}
In the above, ActivityLog.getFeed inherits the #GET and #Produces annotations from the interface. Conversely:
#Path("feed")
public class ActivityLog implements ReadOnlyAtomFeed {
#Produces("application/atom+xml")
public Feed getFeed() {...}
}
In the above, the #GET annotation on ReadOnlyAtomFeed.getFeed is not inherited by ActivityLog.getFeed and it would require its own request method designator since it redefines the #Produces annotation.
For consistency with other Java EE specifications, it is recommended to always repeat annotations instead of relying on annotation inheritance.
That can also happen if the url is not in the correct format; for example you could have sent a request without the correct path parameters.
This code works to access the uriInfo:
#Path("/testing")
public class Testing {
#javax.ws.rs.core.Context UriInfo uriInfo;
#POST
#Path("/test2")
#Produces(MediaType.TEXT_PLAIN)
public Response test2(
#FormParam("sessionId") String sessionId ) {
String currentUserId = Utils.setup(sessionId);
String accessPath = uriInfo.getAbsolutePath().toASCIIString();
System.out.println("The client used this URI to reach this resource method: " + uriInfo.getAbsolutePath().toASCIIString());
// Utils.test3("print this");
return Response.ok("Test 2 ended").build();
}
When I try to access the uriInfo in the called method Utils.test3("print this"); Here:
public class Utils {
#javax.ws.rs.core.Context static UriInfo uriInfo;
public static String setup(String sessionId) {
if (!com.retailapppartners.Utils.validSession(sessionId)) {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
String currentUserId = com.retailapppartners.Utils.getUserFromSession(sessionId);
MDC.put("user-id", currentUserId);
return currentUserId;
}
public static void test3(String message) {
System.out.println(message);
String path = uriInfo.getPath();
// System.out.println("The client used this URI: " + uriInfo.getAbsolutePath().toASCIIString());
return;
}
This fails with null pointer exception. I want to see the path uri in the called method to confirm security for all methods in my utils called method. I have searched hi and low for called examples of this. Thanks
Use the #Context annotation to inject an instance of UriInfo into an field variable or method parameter of your resource class
e.g. #1
public String find(#Context UriInfo uri){}
e.g. #2
public class RESTResource{
#Context
private UriInfo uri;
}
Continuing with my comment.. into an answer
Like I said, you can't just decide to inject it anywhere you want. The class being injected into needs to be managed by the JAX-RS runtime, as it's the one that will be doing the injecting. A resource class is managed, a filter provider is managed, that's why you can inject into them. You're utility class is not. And in any case, I don't think it would even work for a [static] "utility" class (even if you were to somehow get it managed) because of the static nature.
Let me just first mention, that UriInfo is specific to each request. static, by nature is "global". You cannot inject it as a static field.
One solution I can see is to make the Utils class (and methods) non-static, and use the underlying injection framework to inject an instance of the Utils class, where ever you need it. This way, if the Utils class is managed, then it should be able to inject the managed UriInfo instance. How this (getting the Utils class managed) will be achieved depends on the implementation you are using, and it's underlying injection framework.
For instance, with Jersey (2), I could do this
public class Utils {
#Context UriInfo uriInfo;
public String test(String s) {
return s + "=" +uriInfo.getAbsolutePath().toString();
}
}
#Path("some")
public class SomeResource {
#Inject
Utils utils;
#GET
public Response getSomething() {
return Response.ok(utils.test("Hello")).build();
}
}
public class JerseyApplication extends ResourceConfig {
public JerseyApplication() {
packages("stackoverflow.jersey.test");
register(new AbstractBinder(){
#Override
protected void configure() {
bind(Utils.class).to(Utils.class);
}
});
}
}
And this works just fine
C:\>curl -v http://localhost:8080/some
Result: Hello=http://localhost:8080/some
Jersey uses HK2 for its injection, so I am able to leverage it to injection of my Utils class.
Now this is probably not the answer you're looking for (as it defeats the purpose of a static utility class), but what you are trying too just can't be done. Either way you go, whether refactoring to pass the UriInfo to your static methods, or the solution above, you will probably have a lot of refactoring to do. I'm surprised you've already created 200 methods using this functionality, before making sure one worked :/