I am getting a compile time error when trying to implement the hateos , It is not showing me the methodOn method - spring-boot

I am trying to implement the Hateoas using spring boot.
In my UserController class i have used the below code
#GetMapping("/users/{id}")
public Resource<User> retrieveUser(#PathVariable int id) {
User user = service.findOne(id);
Resource<User> resource = new Resource<User>(user);
ControllerLinkBuilder linkTo=linkTo(methodOn(this.getClass()).retrieveAllUsers());
I am getting a compile time error on line where i am using the - methodOn().
Compiler is not able to find the methodOn using the controller link builder.
I have used the below import to implement hateoas
import org.springframework.hateoas.mvc.ControllerLinkBuilder.*;

Methods you are using here are static, so to use them you need access using class reference unless you import them using static imports. See more details here

Related

Spring inject component into non-spring managed interface/abstract class and its subclasses

TLDR: I need an interface/abstract class and all classes implementing it to have access to a Spring managed bean. Can Spring inject a bean into an interface/abstract-class and its subclasses simply via #Autowired ?
I am working on an API built with Spring Webflux + Cloud Gateway that depending on the cookie JWT authorized party, identifies the User's policy group and assign an Attribute ENUM "InterfaceID" to the ServerWebExchange via exchange.getAttribute().put("InterfaceID",InterfaceID.A) after the JWT is validated, and currently uses "InterfaceID" to represent the different groups of users/different interface the user entered from.
JWTValidationFilter.java [Current]
switch(JWTValidator.validate(jwt).get("AZP")){
//if user is from company A or its partners
case "a":
case "aa":
exchange.getAttribute().put(InterfaceID.COMPANY_A_ACCESS);
break;
case "b":
exchange.getAttribute().put(InterfaceID.NORMAL_ACCESS);
...
}
For certain API endpoints (say /api/getSessionDocument), different "InterfaceID" fetches data from different DB/apis, as well as have different permission checking on top of that.
RequestController.java [Current]
#Autowired
APICallerUtil apiCallerUtil;
switch(exchange.getAttribute.get(InterfaceID)){
case "NORMAL_ACCESS":
apiCallerUtil.getDataFromApiA();
break;
case "COMPANY_A_ACCESS":
// call api B but check for permission from api D first
...
}
The endpoint's controller now has another switch statement, and to many code analyzers this have been a code smell. I have been trying to refactor this entire bit of code to use polymorphism to handle the different "getSessionDocument" flows, but i run into issues regarding the injection of util classes that calls specific APIs.
APICallerUtil.java class, exisiting class from the project, would prefer not to refactor this.
#Component
public class APICallerUtil{
#Value("${some uri to some API}") //different by environment and therefore cant be static final
private String uri1;
#Value("${some auth to some API}") //confidential
private String uri1AuthHeader;
//...
public JSONObject getDataFromApiA(String somekey){ //cant be static since uri1 is not static
//Some code that uses uri1 and apache httpclient
return data;
}
...
}
IBaseAccess.java
interface IBaseAccess{
default Mono<JSONObject> getSesssionDocument(ServerWebExchange e){return Mono.error("not implemented");}
}
RequestController.java [new]
#Autowired
APICallerUtil apiCallerUtil;
return exchange.getAttribute.get(InterfaceID).getSessionDocument(exchange);
NormalAccess.java
public class NormalAccess implements IBaseAccess{
//can i autowire APICallerUtil here?
//use constructor to pass the Util class reference here?
Mono<JSONObject> getSesssionDocument(ServerWebExchange e){
//need to call ApiA here
//need to call ApiC here
}
}
NormalAccess needs to call APICaller.getDataFromApiA(), but it needs a reference to the Spring managed instance of APICaller. What would be the "correct" way to pass the reference/autowire API caller into NormalAccess, or even better IBaseAccess (so that the implementing classes can use the Util bean)?
JWTValidationFilter.java [new]
switch(JWTValidator.validate(jwt).get("AZP")){
//if user is from company A or its partners
case "a":
case "aa":
exchange.getAttribute().put("InterfaceID",new CompanyAAccess(/*pass the util class here?*/));
break;
case "b":
exchange.getAttribute().put("InterfaceID",new NormalAccess(/*pass the util class here?*/));
...
}
I have tried several methods, but either I lack the knowledge on the specific Spring feature, or that method is deeemed a bad design choice by some, including:
Making the methods and fields in APICallerUtil static, via suggestions from Spring: How to inject a value to static field? and Assigning private static final field member using spring injection , then the Access classes can call the static methods.
Creating a contructor for IBaseAccess that consumes the APICallerUtil reference and store it inside. The JWTfilter would hold an autowired APICallerUtil and pass it in when the attribute is assigned.
Create a static class that provides the application context and Access classes use applicationContext.getBean("APICallerUtil"); to obtain the bean.
Use the #Configurable annotation? I could not find much documentation on how this works for interfaces/abstract-class.
I understand that there might not exist an absolute answer for this question, but regardless I'd like suggestion/feedback on which of these approaches are viable/good. Especailly concerning whether the APIUtil class should be static or not.

Spring Graphql - How to use a custom DataFetcherExceptionHandler and override the default one?

I'm new to spring graphql and I was trying to implement my own DataFetcherExceptionHandler so I can wrap all exceptions with my custom one.
I've implemented my custom class that implements DataFetcherExceptionHandler but it seems like it still uses the default one, the SimpleDataFetcherExceptionHandler.
How can I make my custom DataFetcherExceptionHandler the default one for the graphql exceptions?
My class:
#Slf4j
#AllArgsConstructor
#Component
public class GraphqlExceptionHandler implements DataFetcherExceptionHandler {
public DataFetcherExceptionHandlerResult onException(DataFetcherExceptionHandlerParameters handlerParameters) {
Throwable exception = handlerParameters.getException();
SourceLocation sourceLocation = handlerParameters.getSourceLocation();
ResultPath path = handlerParameters.getPath();
MyCustomException error = exposedException(exception, sourceLocation, path);
return DataFetcherExceptionHandlerResult.newResult().error(error).build();
}
#Override
public CompletableFuture<DataFetcherExceptionHandlerResult> handleException(DataFetcherExceptionHandlerParameters handlerParameters) {
return CompletableFuture.completedFuture(this.onException(handlerParameters));
}
Note: I'm not sure if I can use my custom exception like that, but I'm not able to test it while I can't make the exception handler the default one.
With Spring for GraphQL you can implement a DataFetcherExceptionResolver or more specifically a DataFetcherExceptionResolverAdapter that you can for example annotate with #Component to register it automatically.
The DataFetcherExceptionHandler from graphql-java is used by Spring for GraphQL internally to delegate to your DataFetcherExceptionResolver classes.
Inside your own DataFetcherExceptionResolverAdapter, you can get the informations that are available as DataFetcherExceptionHandlerParameters (Path, SourceLocation and so on) in a DataFetcherExceptionHandler from the DataFetchingEnvironment that is passed to DataFetcherExceptionResolverAdapter resolveToSingleError and resolveToMultipleErrors methods.
See here for more informations: https://docs.spring.io/spring-graphql/docs/current/reference/html/#execution-exceptions
You can find an example implementation here: https://github.com/nilshartmann/spring-graphql-training/blob/main/app/publy-backend/src/main/java/nh/publy/backend/graphql/runtime/PublyGraphQLExceptionResolver.java

How to have dynamic base URL with Quarkus MicroProfile Rest Client?

Quarkus using Rest Client, explains how to use the MicroProfile REST Client. For Base URL application.properties can be used.
org.acme.restclient.CountriesService/mp-rest/url=https://restcountries.eu/rest #
With above approach, cant have dynamic base URL.
Able to achieve it by using RestClientBuilder as explained in MicroProfile Rest Client. Downside of this approach is not having auto-negotiation capability.
SimpleGetApi simpleGetApi = RestClientBuilder.newBuilder().baseUri(getApplicationUri()).build(SimpleGetApi.class);
Is there other or better way to achieve this? Thanks.
While it is true, that the MP Rest CLient does not allow you to set the BaseUri dynamically when you use declarative/Injected clients, there are some (albeit hacky) ways how to achieve that.
One is to use standard ClientRequestFilter which can modify the URL:
#Provider
#Slf4j
public class Filter implements ClientRequestFilter {
#Inject RequestScopeHelper helper;
#Override
public void filter(ClientRequestContext requestContext) throws IOException {
if (helper.getUrl() != null) {
URI newUri = URI.create(requestContext.getUri().toString().replace("https://originalhost.com", helper.getUrl()));
requestContext.setUri(newUri);
}
}
}
Where RequestScopeHelper is some help class (e.g. request scoped bean) through which you can pass the dynamic url, for example:
#Inject
RequestScopeHelper helper;
#Inject
#RestClient
TestIface myApiClient;
public void callSomeAPIWithDynamicBaseUri(String dynamic) {
helper.setUrl(dynamic);
myApiClient.someMethod();
}
Second is to use MP rest client SPI, namely the RestClientListener which allows you to modify the rest clients after they are built.
For this to work, you have to set the scope of your rest client to RequestScoped so that new instance is created for each request(if you use singleton for example, then the client is only created once and your listener will only be called once). This you can do via quarkus properties:
quarkus.rest-client."com.example.MyRestIface".scope=javax.enterprise.context.RequestScoped
public class MyListener implements RestClientListener {
#Override
public void onNewClient(Class<?> serviceInterface, RestClientBuilder builder) {
String newUri = //obtain dynamic URI from somewhere e.g. again request scope bean lookup, or maybe dynamic config source (create new in-memory ConfigSource, before you invoke your rest client set the corresponding rest client url property to your dynamic value, then inside this listener use ConfigProvider.getConfig().getProperty...)
builder.baseUri(URI.create(newUri));
}
}
Don't forget to register this listener as service provider(META-INF/services/org.eclipse.microprofile.rest.client.spi.RestClientListener)
Another option is to use custom CDI producer that would produce the Rest client instances for you; then you could control all client config yourself. You can use the RestClientBase from Quarkus rest client which is exactly what Quarkus uses under the hood during deployment phase to construct client instances. You will however have to duplicate all the logic related to registration of handlers, interceptors etc.
Do keep in mind, that any of these solutions will make the debugging and problem analysis more challenging - because you will now have multiple places, where the URI is controlled(MP config/quarkus properties, env vars, your custom impl...), so you need to be careful with your approach and maybe add some explicit log messages when you override the URI manually.
MicroProfile REST Client in Quarkus does allow you to use dynamic base URL with that simple "hack" :
Just put an empty String in #Path annotations for you API interface like that :
import javax.ws.rs.GET;
import javax.ws.rs.Path;
#Path("")
public interface SimpleGetApi {
#Path("")
#GET
String callWithDynmamicUrl(); //it can be String or any return type you want
}
After that you are ready to call your dynamic base URL :
import org.eclipse.microprofile.rest.client.RestClientBuilder;
import java.net.URI;
public class Example {
public static void main(String[] args) {
URI anyDynamicUrl = URI.create("http://restcountries.eu/rest/some/dynamic/path");
SimpleGetApi simpleGetApi = RestClientBuilder.newBuilder().baseUri(anyDynamicUrl)
.build(SimpleGetApi.class);
simpleGetApi.callWithDynmamicUrl();
}
}

Configuring Spring MockMvc to use custom argument resolver before built-in ones

I have a straightforward test case. I have a controller which has a parameter of a type Spring doesn't support by default, so I wrote a custom resolver.
I create the mock mvc instance I'm using like so:
mvc = MockMvcBuilders.standaloneSetup(controller).setCustomArgumentResolvers(new GoogleOAuthUserResolver()).build();
However, Spring is also registering almost 30 other argument resolvers, one of which is general enough that it is getting used to resolve the argument before mine. How can I set or sort the resolvers so that mine is invoked first?
This worked for me without reflection:
#RequiredArgsConstructor
#Configuration
public class CustomerNumberArgumentResolverRegistration {
private final RequestMappingHandlerAdapter requestMappingHandlerAdapter;
#PostConstruct
public void prioritizeCustomArgumentResolver () {
final List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>(Objects.requireNonNull(requestMappingHandlerAdapter.getArgumentResolvers()));
argumentResolvers.add(0, new CustomerNumberArgumentResolver());
requestMappingHandlerAdapter.setArgumentResolvers(argumentResolvers);
}
}
The issue was that the People class the Google OAuth library I am using extends Map and the mock servlet API provides no way to manipulate the order in which the handlers are registered.
I ended up using reflection to reach into the mocks guts and remove the offending handler.

Issue with importing JcrTagImpl class in OSGI

I need to import JcrTagImpl class which is located in CQ Day Communique 5 Tagging jar file(com.day.cq.cq-tagging)
Then I tried to add the above jar in my pom.xml's dependency as below then I can import the whole package as com.day.cq.tagging.*;
<dependency>
<groupId>com.day.cq</groupId>
<artifactId>cq-tagging</artifactId>
<version>5.7.18</version>
<scope>provided</scope>
</dependency>
UPDATE:
I need to call getTagID method which is located in com.day.cq.tagging.JcrTagImpl class.
AEM uses com.day.cq.tagging.TagCommandServlet to display the TagID in tagging console. TagCommandServlet is importing JcrTagImpl class and calling getTagID method.
I have my own servlet and I wanted to call getTagID. I could not call directly getTagID of JcrTagImpl implementation since it is not exposed. Can it done by any annotation?Can you please guide me how to call getTagId method.
You are trying to call the implementation directly instead of the service. Generally, the implementation is not exposed and you would have to use the service instead.
I guess, TagManager is available as a Sling Service, using which you can work on Tags. Use #Reference to inject it in your service or use sling.adaptTo() to adapt your resource.
EDIT:
Like i mentioned earlier, you cannot access an implementation class directly, as it wouldn't be exported by the bundle.
However to get the tag ID you can use any of the below methods according to your requirements.
If you have the path to the tag, you can get the resource and adaptTo Tag.class and retrieve the tagID
You can adaptTo TagManager.class from a ResourceResolver object and then resolve the path of the tag to get the Tag object
Use the JcrTagManagerFactory service to obtain the tag manager and then resolve the path of the tag.
#SlingServlet({ //config })
public class MyServlet extends SlingSafeMethodsServlet {
#Reference
private JcrTagManagerFactory jcrTagManagerFactory;
protected void doGet(SlingHttpServletRequest req, SlingHttpServletResponse res) {
//First Method
ResourceResolver resolver = req.getResourceResolver();
Resource tagResource = resolver.resolve("<<path to the tag>>");
Tag tag1 = tagResource.adaptTo(Tag.class);
tag1.getTagID();
//Second Method
TagManager tagManager = resolver.adaptTo(TagManager.class);
Tag tag2 = tagManager.resolve("<<path to tag>>");
tag2.getTagID();
//Third Approach
Session session = resolver.adaptTo(Session.class);
TagManager tagManager = jcrTagManagerFactory.getTagManager(session);
Tag tag3 = tagManager.resolve("<<path to tag>>");
tag3.getTagID();
}
}
Using the TagManager, you can fetch the tags set on the current resource or query for tags etc.

Resources