Spring - get a different bean based on context - spring

I have a singleton bean that holds a registry of key -> value pairs. The key can change per Http request, and the value is used in dependency injection in a lot of places. I've made a request scoped bean that can return the appropriate value for any method called from within an Http request, as you would expect.
However, there is a new requirement that there is a background thread operating on the key value pairs - think running some callback on a timer. This of course has no access to the Http request, but it does have access to the value of the key.
Is there a way to set up a bean definition that will be aware of whether the code is request, and in that case run the existing method, or if it is not in the request scope, run some other way of getting the value.
This an example of the request scoped bean. The code is in Kotlin. The registry parameter is a singleton bean.
#Scope(value = "request", proxyMode = ScopedProxyMode.INTERFACES)
#Bean
fun getValue(request: HttpServletRequest, registry: ValueRegistry): Value {
val id = request.getSession(false).getAttribute(REGISTRY_ID) as String
return registry.get(id)
}
So I would like another bean that can step in in the event that the request is not available.
#Scope(value = "singleton")
#Bean
fun getValue2(request: HttpServletRequest, registry: ValueRegistry): Value {
return registry.first() ?: Value()
}

Related

How to set header variables in GraphQL-SPQR

I'm running a GraphQL API using GraphQL-SPQR and Spring Boot.
At the moment, I am throwing RuntimeExceptions to return GraphQL errors. I have a customExceptionHandler that implements DataFetcherExceptionHandler that returns errors in the correct format, as shown below:
class CustomExceptionHandler : DataFetcherExceptionHandler {
override fun onException(handlerParameters: DataFetcherExceptionHandlerParameters?): DataFetcherExceptionHandlerResult {
// get exception
var exception = handlerParameters?.exception
val locations = listOf(handlerParameters?.sourceLocation)
val path = listOf(handlerParameters?.path?.segmentName)
// create a GraphQLError from your exception
if (exception !is GraphQLError) {
exception = CustomGraphQLError(exception?.localizedMessage, locations, path)
}
// cast to GraphQLError
exception as CustomGraphQLError
exception.locations = locations
exception.path = path
val errors = listOf<GraphQLError>(exception)
return DataFetcherExceptionHandlerResult.Builder().errors(errors).build()
}
}
I use the CustomExceptionHandler as follows (in my main application class):
#Bean
fun graphQL(schema: GraphQLSchema): GraphQL {
return GraphQL.newGraphQL(schema)
.queryExecutionStrategy(AsyncExecutionStrategy(CustomExceptionHandler()))
.mutationExecutionStrategy(AsyncSerialExecutionStrategy(CustomExceptionHandler()))
.build()
}
I'd like to set a header variable for a UUID that corresponds to the exception, for logging purposes. How would I do that?
Even better, is it possible to create a Spring Bean that puts the UUID in the header for all queries and mutations?
Thanks!
when you're using spring boot, there's two options:
you're using the spring boot graphql spqr starter (which brings it's own controller to handle all graphQL requests)
you're using plain graphql-spqr and have your own controller to handle GraphQL requests
In any case, you've got a few options:
Making your CustomExceptionHandler a Spring Bean and Autowiring HttpServletResponse
That would probably be the easiest way to go - and it would probably work in any case: You could simply make your CustomExceptionHandler a Spring bean and have it autowire the HttpServletRequest - in the handler method, you could then set it to whatever you would like it to be. Here's some dummy code in Java (sorry, I am not proficient enough in Kotlin):
#Component
class CustomExceptionHandler implements DataFetcherExceptionHandler {
private final HttpServletResponse response;
public CustomExceptionHandler(HttpServletResponse response) {
this.response = response;
}
#Override
public DataFetcherExceptionHandlerResult onException(DataFetcherExceptionHandlerParameters handlerParameters) {
response.setHeader("X-Request-ID", UUID.randomUUID().toString());
// ... your actual error handling code
}
}
This is going to work because spring will realise that HttpServletRequest differs for each request. It will therefore inject a dynamic proxy into your error handler that will point to the actual HttpServletResponse instance for every request.
I would argue, that it's not the most elegant way, but it will certainly solve your problem.
for the graphql-spqr spring boot starter
There's a default controller implementation that is used in projects using this starter. That controller will handle every graphql request that you receive. You can customise it, by implementing your own GraphQLExecutor and making it a spring bean. That executor is responsible to call the GraphQL engine, pass the parameters in and output the response. Here's the default implementation, that you might want to base your work on.
Similarly to the previous solution, you could autowire the HttpServletResponse in that class and set a HTTP Response header.
That solution would allow you to decide, if you want to set a request id in all cases, or just in specific error cases. (graphql.execute returns an object from which you can get the information if and what errors existed)
when using graphql-spqr without the spring boot starter
Locate your GraphQL controller, add an argument to that method of type HttpServletRequest - and then add headers to that as you prefer (see previous section on some more specific suggestions)

How to pass data from business method to fallback method while using spring-boot circuit breaker (Hystrix)?

In official https://spring.io/guides/gs/circuit-breaker/ manual there are
business method (readingList)
fallback method (reliable)
#HystrixCommand(fallbackMethod = "reliable")
public String readingList() {
URI uri = URI.create("http://localhost:8090/recommended");
return this.restTemplate.getForObject(uri, String.class);
}
public String reliable() {
return "Cloud Native Java (O'Reilly)";
}
How to pass data from business method to fallback method? Use ThreadLocal, immutable collections, concurrent collections, any ideas/best practice?
Use ThreadLocal?
#HystrixCommand and the corresponding fallbackMethod are normally executed (together) in a separate thread because of the default execution.isolation.strategy which is ExecutionIsolationStrategy.THREAD.
So that means that if you use ThreadLocal to set any variables before #HystrixCommand is executed, they won't be available to the #HystrixCommand because the thread will be different.
If the above is necessary you can use a different isolation strategy - ExecutionIsolationStrategy.SEMAPHORE.
To override the default isolation strategy you can do it on the hystrix command definition (or in properties files):
#HystrixCommand(fallbackMethod = "reliable",
commandProperties = {
#HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE")
}
)
Passing input parameters data
Methods annotated with #HystrixCommand and the corresponding fallbackMethod need to have the same method signature (plus optional param in the fallback for exceptions thrown), so naturally the fallback method will have access to all input parameters to the #HystrixCommand.
Passing exceptions data
Adding Throwable in the fallback method signature will include the exception produced from the #HystrixCommand:
public String reliable(Throwable t) {
return "Cloud Native Java (O'Reilly)";
}
Passing execution data
It's not practical to expect any execution data to be passed from main method to the fallback method. You don't know when the main method will fail.
A key thing is to try and define better input parameters, which will be shared with the fallback anyway.
For example in the code that you've given the URL can become input parameter, so it will be available to the fallback method as well:
#HystrixCommand(fallbackMethod = "reliable")
public String readingList(String url) {
URI uri = URI.create(url);
return this.restTemplate.getForObject(uri, String.class);
}
public String reliable(String url, Throwable t) {
return "Cloud Native Java (O'Reilly)";
}

Spring tries to instantiate the interface when injecting a parameter

I hope you can help me with this.
I'm using Spring MVC (3.1.1) in my web application, and am facing a strange situation.
I have this simple #Controller, that makes use of the ServicioUsuario Service, and works fine:
#Controller
#RequestMapping("/ajax")
public class ControladorAjax extends ControladorGenerico {
#Autowired
ServicioUsuario servicioUsuario;
#RequestMapping("/check")
public ResponseEntity<String> check(#RequestParam String email) {
// Declarations and other operations omitted...
// Use servicioUsuario
servicioUsuario.doStuff();
return response;
}
}
However, if I remove the #Autowiring, and try to make Spring inject servicioUsuario as a parameter (i.e. by changing the method signature to: public ResponseEntity<String> check(#RequestParam String email, ServicioUsuario servicioUsuario)) the whole thing breaks, and I get this sort of exceptions in Tomcat's log:
javax.servlet.ServletException: NestedServletException in java.lang.Thread.getStackTrace:: Request processing failed; nested exception is org.springframework.beans.BeanInstantiationException: Could not instantiate bean class [com.package.ServicioUsuario]: Specified class is an interface
I have these interfaces:
com.package
|-> Servicio.java (interface)
|-> ServicioUsuario.java (interface that extends Servicio)
and these clases:
com.package.impl
|-> ServicioImpl.java (implements Servicio)
|-> ServicioUsuarioImpl.java (#Service("servicioUsuario") that extends ServicioImpl implements ServicioUsuario)
and configured Spring to scan both packages with:
<context:component-scan base-package="com.package
com.package.impl" />
Why is Spring trying to instantiate the interface and not the implementing class? Is it something I'm doing wrong?
According to 16.3.3.1 Supported method argument types of the official documentation, this is a complete list of what controller methods can take:
Request or response objects (Servlet API). [...] for example ServletRequest or HttpServletRequest.
Session object (Servlet API): of type HttpSession.
org.springframework.web.context.request.WebRequest or org.springframework.web.context.request.NativeWebRequest.
java.util.Locale for the current request locale [...]
java.io.InputStream / java.io.Reader for access to the request's content. [...]
java.io.OutputStream / java.io.Writer for generating the response's content. [...]
java.security.Principal containing the currently authenticated user.
#PathVariable annotated parameters for access to URI template variables. [...]
#RequestParam annotated parameters for access to specific Servlet request parameters. [...]
#RequestHeader annotated parameters for access to specific Servlet request HTTP headers. [...]
#RequestBody annotated parameters for access to the HTTP request body. [...]
#RequestPart annotated parameters for access to the content of a "multipart/form-data" request part. [...]
HttpEntity<?> parameters for access to the Servlet request HTTP headers and contents. [...]
java.util.Map / org.springframework.ui.Model / org.springframework.ui.ModelMap [...]
org.springframework.web.servlet.mvc.support.RedirectAttributes [...]
Command or form objects to bind request parameters to bean properties (via setters) or directly to fields, [...]
org.springframework.validation.Errors / org.springframework.validation.BindingResult [...]
org.springframework.web.bind.support.SessionStatus [...]
org.springframework.web.util.UriComponentsBuilder [...]
As you can see, Spring beans are not on this (quite impressive) list. Why would you inject services via controller method? They never change. It's enough to inject them once and assign them to a field.

Spring #RequestMapping consumes charset?

I'm trying to use #RequestMapping with the consumes-element. Reading the API-document it works on the Content-Type-header of the request. However, using
#RequestMapping(consumes = "application/x-www-form-urlencoded;charset=UTF-8", value = "/test")
public void test() {
:
}
or
#RequestMapping(consumes = "application/x-www-form-urlencoded;charset=ISO-8859-1", value = "/test")
public void test() {
:
}
doesn't make a difference. The header in the request can look like
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
or
Content-Type: application/x-www-form-urlencoded
test() will be called in all four possible constellations.
However, and this is proof to me that Spring sees and tries to use the charset-part, if I specify
#RequestMapping(consumes = "application/x-www-form-urlencoded;charset=UTF-x8", value = "/test")
public void test() {
:
}
I get an exception during startup (!) of the web-app:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping#0': Initialization of bean failed;
nested exception is java.nio.charset.UnsupportedCharsetException: UTF-x8
Note that the documentation on the produces-element also doesn't mention the use of charset, but according to Google some use it.
Any clues to what's happening here or what I'm doing wrong?
BTW, this is Spring 3.1.1.RELEASE.
I think you have already answered your question, so this is more of a confirmation, from a code point of view, as to why the charset is not taken into account when resolving mappings.
When digging into Spring code, the culprit seems to be the MediaType#includes(). method
More digging reveals that a RequestMappingInfo is created in association to the RequestMapping annotation of the method. This RequestMappingInfo stores a series of AbstractRequestCondition objects, one of them being the ConsumesRequestCondition which holds the MediaType defined in the consumes part of the annotation (i.e. application/x-www-form-urlencoded;charset=UTF-8).
Later when a request is made, this ConsumesRequestCondition has an inner ConsumeMediaTypeExpression class with a matchMediaType() method that extracts the MediaType of the HttpServletRequest and checks it against it's own MediaType to see if it's included.
If you look at the MediaType#includes() implementation (Lines 426 to 428), it returns true when type (i.e. application) and subtype (i.e. x-www-form-urlencoded) are equal, completely disregarding the parameters Map which in this case
holds the remnant "charset","UTF-8" combination.
Digging into the produces track seems to show similar results, but in this case it's the MediaType#isCompatibleWith() method involved, and again, it reaches only to type and subtype if they are equal.
If you found evidence on Google of the produces working for charset request mapping, I would doubt it (unless they changed core Spring stuff)
As to why it was designed this way, well that's another question :)

In spring, how to pass objects from one bean to another bean?

I have a row mapper class which implements RowMapper interface. I need to implement the mapRow method in that. The arguments of it are ResulSet and index. I would like to use this ResultSet object in another bean. How do i get it there ?
Set this object as instance variable. But i would never recommend that for ResultSet. Resultset will be useless once its closed by spring (as spring is managing it).
Better extract the data out of ResultSet store the data as some model bean in the instance variable (but keep in mind, by default the beans are singleton, and for each execution storing the data as instance variable would not make much sense either).
EDIT-- okay to refine a bit more, i am putting an example here
// this is singleton by default, if you want to store data in this bean
// mark it as bean either by annotation or thorugh xml
// so that it can be accessed through spring context
public class MyBeanRowMapper implements RowMapper
{
// you can store something here,
// but with each execution of mapRow, it will be updated
// not recommended to store execution result here
// or inject other bean here // SomeOtherBean someOtherbean;
public Object mapRow(ResultSet rs, int rowNum) throws SQLException {
MyBean myBean = new MyBean();
myBean.setId(rs.getInt("BEAN_ID"));
myBean.setName(rs.getString("NAME"));
// set this bean into gloablly accessible object here
// or to the bean in which you want to access the result
// something like -->// someOtherBean.setMyBean(myBean)
// again be careful if someOtherBean is singleton by default
return myBean;
}
}

Resources