find how many requests are on queue in thread safe spring controller - java

I am using a spring controller which returns a string from a threadsafe method.
so i have made the controller also thread safe.
I want to know how many request are there in queue simultaneously which are calling to the spring controller

Here is my suggestion how you can solve your issue.
Imagine you have this #Controller:
#Controller
public class MyController {
#Autowired
private IMyService service;
#RequestMapping(value = "/myPathName", method = RequestMethod.GET)
public String home(HttpServletRequest request, Model model) {
// you synchronized call
service.callSynchronized(request, model);
return "someJsp";
}
// +edit
#ResponseBody
#RequestMapping(value = "/queueStatus", method = RequestMethod.GET)
public String home(HttpServletRequest request) {
//
return "inQueue: " + request.getAttribute("inQueue");
}
}
Now you can define an interceptor and count the requests before and after execution:
public class RequestsInWorkInterceptor extends HandlerInterceptorAdapter {
private static final Logger _logger = LoggerFactory.getLogger(RequestsInWorkInterceptor.class);
private final AtomicLong counter = new AtomicLong();
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String methodURI = request.getRequestURI();
long current;
if("myPathName".equals(methodURI){
current = counter.incrementAndGet();
_logger.debug("current {} clients in a queue", current);
} else {
current = counter.get(); // just get, no increment
}
// +edit: put the count in the request so you can get it in you controller
request.setAttribute("inQueue", current);
return super.preHandle(request, response, handler);
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
String methodURI = request.getRequestURI();
if("myPathName".equals(methodURI){
counter.decrementAndGet();
}
super.postHandle(request, response, handler, modelAndView);
}
}

You can get information about the number of active requests using Jetty's StatisticsHandler and JMX.
If you're using Jetty as an embedded container (the recommended approach), you can use an EmbeddedServletContainerCustomizer to set this up:
#Bean
public EmbeddedServletContainerCustomizer containerCustomizer() {
return new EmbeddedServletContainerCustomizer() {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
((JettyEmbeddedServletContainerFactory) container)
.addServerCustomizers(new JettyServerCustomizer() {
#Override
public void customize(Server server) {
MBeanContainer mbContainer = new MBeanContainer(
ManagementFactory.getPlatformMBeanServer());
server.addEventListener(mbContainer);
server.addBean(mbContainer);
StatisticsHandler statisticsHandler = new StatisticsHandler();
statisticsHandler.setHandler(server.getHandler());
server.setHandler(statisticsHandler);
}
});
}
};
}
You'll need to add a dependency on org.eclipse.jetty:jetty-jmx to get access to MBeanContainer.
You can try with spring boot actuator. On endpoint /metrics you should have field "httpsessions.active"
public class RequesInterceptor extends HandlerInterceptorAdapter {
private static Object lock = new Object();
private static int count = 0
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException {
sinchronize(lock){
count++;
}
}
}

Related

WebClient ExchangeFilterFunction caches value from ThreadLocal

I faced with the following issue while using Spring MVC with ThreadLocal and WebClient from Webflux.
My task is to:
Intercept the user's request to my application and get all the headers from it and save it in ThreadLocal.
After that, when my application makes a call to another service through the WebClient, intercept this request in ExchangeFilterFunction and supplement it with the Authorization header from p.1.
When I finish processing the user's request, I clear the context.
I use my custom class "RequestContext" to store headers in ThreadLocal:
public class RequestContext {
private HttpHeaders requestHeaders;
private String jwt;
private static final String BEARER_PREFIX = "Bearer ";
public RequestContext(HttpHeaders httpHeaders) {
this.requestHeaders = httpHeaders;
if (Objects.nonNull(httpHeaders)) {
init();
}
}
private void init() {
if (Objects.nonNull(requestHeaders)) {
extractJwt();
}
}
private void extractJwt() {
var jwtHeader = requestHeaders.getFirst(HttpHeaders.AUTHORIZATION);
if (StringUtils.isNotBlank(jwtHeader) && jwtHeader.startsWith(BEARER_PREFIX)) {
jwt = jwtHeader.substring(7);
}
}
}
I use my custom clas "RequestContextService" to deal with ThreadLocal:
public class RequestContextService {
private static final ThreadLocal<RequestContext> CONTEXT = new InheritableThreadLocal<>();
public void init(RequestContext requestContext) {
if (Objects.isNull(CONTEXT.get())) {
CONTEXT.set(requestContext);
} else {
log.error("#init: Context init error");
}
}
public RequestContext get() {
return CONTEXT.get();
}
public void clear() {
CONTEXT.remove();
}
}
My app is a WebMvc app. To complete step 1, I intercept the request with an HandlerInterceptor and set all headers to Threadlocal.
public class HeaderInterceptor implements HandlerInterceptor {
private final RequestContextService requestContextService;
#Override
public boolean preHandle(#NonNull HttpServletRequest request,
#NonNull HttpServletResponse response,
#NonNull Object handler) {
if (Objects.equals(request.getDispatcherType(), DispatcherType.REQUEST)) {
var headers = new ServletServerHttpRequest(request).getHeaders();
requestContextService.init(new RequestContext(headers));
}
return true;
}
#Override
public void afterCompletion(#NonNull HttpServletRequest request, #NonNull HttpServletResponse response,
#NonNull Object handler, Exception ex) {
requestContextService.clear();
}
}
As you can see, after every request I call "requestContextService.clear()" method to clear ThreadLocal.
To perform step two, I use the ExchangeFilterFunction, where I turn to the threadlocal and get the title from there.
public class SamlExchangeFilterFunction implements ExchangeFilterFunction {
private final RequestContextService requestContextService;
private static final ClientResponse UNAUTHORIZED_CLIENT_RESPONSE =
ClientResponse.create(HttpStatus.UNAUTHORIZED).build();
#Override
public #NotNull Mono<ClientResponse> filter(#NotNull ClientRequest request, #NotNull ExchangeFunction next) {
var jwt = requestContextService.get().getJwt();
if (StringUtils.isNoneBlank(jwt)) {
var clientRequest = ClientRequest.from(request)
.headers(httpHeaders -> httpHeaders.set(SAML_HEADER_NAME, jwt))
.build();
return next.exchange(clientRequest);
}
return Mono.just(UNAUTHORIZED_CLIENT_RESPONSE);
}
}
The problem is that the SamlExchangeFilterFunction works correctly only once.
On the first request to the application, everything works as it should. But with further requests with different authorization headers, the ExchangeFilterFunction seems to cache the value from the first request and substitutes it despite the fact that the threadlocal itself contains a completely different meaning of Authorization header.

Automatically setting model attribute in spring mvc

I want to populate a object as model attribute for each new request to my spring container.
I did the following.
-Added a new Interceptor within which am adding the object to request object
-Registered the interceptor
The interceptor is getting executed but the controller is returning a employee object with all values as null. Am i doing something wrong?
public class UserDetailsInterceptor extends AbstractInterceptor {
private static Class<? extends Annotation> annotationName = PopulateUserDetails.class;
public UserDetailsInterceptor() {
super(annotationName);
}
#Override
protected boolean handle(HttpServletRequest request, HttpServletResponse response, HandlerMethod handlerMethod)
throws Exception {
log.error("running my interceptor handle now");
Employee empoyee = populateEmployeeDetails();
request.setAttribute("employee",employee");
return true;
}
}
#Slf4j
#RestController
#CrossOrigin
public class TestController {
#RequestMapping(value = "/tst1", method = RequestMethod.GET)
#PopulateEmployee
public Employee getEmployee(#ModelAttribute("employee") Employee employee) {
return employee;
}
}
Instead of using AbstractInterceptor use HandlerInterceptorAdapter
UserDetailsInterceptor will be like this
public class UserDetailsInterceptor extends HandlerInterceptorAdapter
{
Employee empoyee = null;
#Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
log.error("running my interceptor handle now");
empoyee = populateEmployeeDetails();
return true;
}
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object object,
ModelAndView modelAndView) throws Exception {
request.setAttribute("employee",employee");
}
}

Unable to intercept and manipulate HttpServletResponse in Spring Boot

I have a requirement to Base64 decode every JSON request payload that my Spring Boot service receives. The JSON payload would have been Base64 encoded at the client before posting using the HTTP POST method. Further, I also need to Base64 encode the JSON response before presenting to the calling client application.
I am required to reduce boilerplate code by using handler interceptors.
I have already achieved the request/incoming leg of the operation by the use of interceptors but is yet to achieve this for the response leg.
I have posted the code snippets below. The code to intercept the response and base64 encode it is in the postHandle method of the interceptor class.
What am I doing wrong here?
Interceptor Class:
public class Base64ResponseEncodingInterceptor implements HandlerInterceptor {
private static final String DECODED_REQUEST_ATTRIB = "decodedRequest";
private static final String POST = "POST";
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView arg3) throws Exception {
try {
if (POST.equalsIgnoreCase(request.getMethod())) {
CharResponseWrapper res = new CharResponseWrapper(response);
res.getWriter();
byte[] encoded = Base64.encodeBase64(res.toString().getBytes());
byte[] encoded = Base64.encodeBase64(response.getHeader(ENCODED_RESPONSE_ATTRIB).getBytes());
response.getWriter().write(new String(encoded));
}
} catch (Exception e) {
throw new Exception(e.getMessage());
}
}
// preHandle and afterCompletion methods
// Omitted
}
The CharResponseWrapper Class used above:
public class CharResponseWrapper extends HttpServletResponseWrapper {
protected CharArrayWriter charWriter;
protected PrintWriter writer;
protected boolean getOutputStreamCalled;
protected boolean getWriterCalled;
public CharResponseWrapper(HttpServletResponse response) {
super(response);
charWriter = new CharArrayWriter();
}
#Override
public ServletOutputStream getOutputStream() throws IOException {
if (getWriterCalled) {
throw new IllegalStateException("getWriter already called");
}
getOutputStreamCalled = true;
return super.getOutputStream();
}
#Override
public PrintWriter getWriter() throws IOException {
if (writer != null) {
return writer;
}
if (getOutputStreamCalled) {
throw new IllegalStateException("getOutputStream already called");
}
getWriterCalled = true;
writer = new PrintWriter(charWriter);
return writer;
}
#Override
public String toString() {
String s = null;
if (writer != null) {
s = charWriter.toString();
}
return s;
}
}
JavaConfig Class where Interceptor is registered:
#Configuration
#EnableJpaRepositories(repositoryBaseClass = BaseRepositoryBean.class, basePackages = "")
#EntityScan(basePackages = { "com.companyname", "com.companyname.productname"})
public class RestConfig extends WebMvcConfigurerAdapter {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new Base64ResponseEncodingInterceptor());
}
}
The Controller Class, where the Interceptor is used (Only the working request leg is shown here):
#Autowired
HttpServletRequest request;
String decodedRequest = null;
#ModelAttribute("decodedRequest")
public void getDecodedParam(){
decodedRequest = (String) request.getAttribute("decodedRequest");
}
The code in the postHandle method does not work. It is either the HttpServletResponse is null or I get an exception message:
getOutputStream already called
Update: Work around solution to reading the response directly in the ResponseBodyAdvice
In the Controller Class, I added the following:
#RestController
#RequestMapping("/api/ipmanager")
public class IPProfileRestController extends AbstractRestController {
#Autowired
HttpServletResponse response;
String encodedResponse = null;
#ModelAttribute("encodedResponse")
public void getEncodedResponse(){
response.setHeader("encodedResponse", StringUtils.EMPTY);
}
#RequestMapping(value = "/time", method = { RequestMethod.POST }, produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.TEXT_PLAIN_VALUE }, consumes = {
MediaType.APPLICATION_JSON_VALUE })
public #ResponseBody String saveAccessClientTime(#RequestBody String ecodedRequest) {
// Some code here
String controllerResponse = prettyJson(iPProfileResponse);
response.setHeader("encodedResponse", controllerResponse);
return controllerResponse;
}
}
I have the following in the ResponseBodyAdvice
#ControllerAdvice
public class Base64EncodedResponseBodyAdvice implements ResponseBodyAdvice<Object> {
#Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
#Override
public Object beforeBodyWrite(Object body,
MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> converterType,
ServerHttpRequest request,
ServerHttpResponse response) {
String body1 = StringUtils.EMPTY;
// Encode the response and return
List<String> listOfHeaderValues = response.getHeaders().get("encodedResponse");
body1=new String(Base64.encodeBase64(listOfHeaderValues.get(0).getBytes()));
return body1;
}
}
As the Spring MVC documentation states:
the postHandle method of HandlerInterceptor is not always ideally
suited for use with #ResponseBody and ResponseEntity methods. In such
cases an HttpMessageConverter writes to and commits the response
before postHandle is called which makes it impossible to change the
response, for example to add a header. Instead an application can
implement ResponseBodyAdvice and either declare it as an
#ControllerAdvice bean or configure it directly on
RequestMappingHandlerAdapter.
With that being said:
What am I doing wrong here?
Since the response has been already committed, you can't change it. In order to change the response you should register a ResponseBodyAdvice<T> and put your response encoding logic there:
#ControllerAdvice
public class Base64EncodedResponseBodyAdvice implements ResponseBodyAdvice<Object> {
#Override
public boolean supports(MethodParameter returnType,
Class<? extends HttpMessageConverter<?>> converterType) {
return true;
}
#Override
public Object beforeBodyWrite(Object body,
MethodParameter returnType,
MediaType selectedContentType,
Class<? extends HttpMessageConverter<?>> converterType,
ServerHttpRequest request,
ServerHttpResponse response) {
// Encode the response and return
}
}
If your method is returning ResponseEntity<T> then write this code in postHandle() method of your HandlerInterceptor:
eg. response.addHeader("jwt_token", "kajdlakjd");
it will work!!

Why does FilterSecurityMetadataSource class to controller class take a longer time?

I have used Spring security oauth2 in our application. For some specific request, the time taken by FilterSecurityMetadataSource to controller is much higher.
FilterSecurityMetadataSource class:
public class FilterSecurityMetadataSource implements
FilterInvocationSecurityMetadataSource {
#Override
public List<ConfigAttribute> getAttributes(Object object) {
// ...
System.out.println("In FilterInvocation Class");
attributes = SecurityConfig.createListFromCommaDelimitedString(roles);
return attributes;
}
#Override
public Collection<ConfigAttribute> getAllConfigAttributes() {
return null;
}
#Override
public boolean supports(Class<?> clazz) {
return FilterInvocation.class.isAssignableFrom(clazz);
}
}
My controller class is like this:
#RestController
public class MobileController {
#RequestMapping(value = "/read/bills", method = RequestMethod.POST)
public Map method1(HttpServletRequest request, HttpServletResponse response) {
// ...
}
#RequestMapping(value = "/test/id", method = RequestMethod.GET)
public Map method2(HttpServletRequest request, HttpServletResponse response) {
// ...
}
#RequestMapping(value = "/test/item", method = RequestMethod.GET)
public Map method3(HttpServletRequest request, HttpServletResponse response) {
System.out.println("Test Method3");
// ...
}
}
On our server when I check, I found huge time difference in printing two SOUT .
What is be the reason?

Jersey: Use Provider in Resource Filter

Using Jersey 1.14 and Spring 3.1.2
I want to create a filter like this: https://gist.github.com/3031495
but in that filter I want access to a provider I created.
I'm getting an IllegalStateException. I suspect something in my lifecycle is hosed up. I can access #Context private HttpServletRequest and pull the session info I need from there, but then two classes have to know about where/how to get my "AuthUser" object.
Any help is appreciated!
My Provider:
#Component
#Provider
public class AuthUserProvider extends AbstractHttpContextInjectable<AuthUser> implements
InjectableProvider<Context, Type> {
private static final Logger LOG = LoggerFactory.getLogger(AuthUserProvider.class);
#Context
HttpServletRequest req;
public void init() {
LOG.debug("created");
}
#Override
// this may return a null AuthUser, which is what we want....remember, a
// null AuthUser means the user hasn't authenticated yet
public AuthUser getValue(HttpContext ctx) {
return (AuthUser) req.getSession().getAttribute(AuthUser.KEY);
}
// InjectableProvider implementation:
public ComponentScope getScope() {
return ComponentScope.Singleton;
}
public Injectable<AuthUser> getInjectable(ComponentContext ic, Context ctx, Type c) {
if (AuthUser.class.equals(c)) {
return this;
}
return null;
}
}
My Filter:
#Component
public class TodoFilter implements ResourceFilter {
private static final Logger LOG = LoggerFactory.getLogger(TodoFilter.class);
#Autowired
private JdbcTemplate todoTemplate;
// this works
#Context
private HttpServletRequest servletRequest;
// this throws a java.lang.IllegalStateException
// #Context
// private AuthUser authUser;
public void init() throws Exception {
LOG.debug("created");
LOG.debug(todoTemplate.getDataSource().getConnection().getMetaData()
.getDatabaseProductName());
}
#Override
public ContainerRequestFilter getRequestFilter() {
return new ContainerRequestFilter() {
#Override
public ContainerRequest filter(ContainerRequest request) {
LOG.debug("checking if {} is authorized to use {}", "my authenticated user",
request.getPath());
// String name = request.getUserPrincipal().getName();
// String[] admins = settings.getAdminUsers();
// for (String adminName : admins) {
// if (adminName.equals(name))
// return request;
// }
// if (authUser.getUsername().equals("jberk")) {
// return request;
// }
// return HTTP 403 if name is not found in admin users
throw new WebApplicationException(Response.status(Response.Status.FORBIDDEN)
.entity("You are not authorized!").build());
}
};
}
#Override
public ContainerResponseFilter getResponseFilter() {
return new ContainerResponseFilter() {
#Override
public ContainerResponse filter(ContainerRequest request,
ContainerResponse response) {
// do nothing
return response;
}
};
}
}
My Service (aka Resource):
#Component
#Path("/rs/todo")
#Produces(MediaType.APPLICATION_JSON)
#ResourceFilters(TodoFilter.class)
public class TodoService {
#GET / #POST methods
}
so I think I figured this out....
I added this to my ResourceFilter:
#Context
private HttpContext ctx;
#Autowired
private AuthUserProvider provider;
then I can do this in the filter method:
public ContainerRequest filter(ContainerRequest request) {
AuthUser authUser = provider.getValue(ctx);
// use authuser in some way
}
this might not be "correct"...but it's working and I don't have code duplication
public ComponentScope getScope() {
return ComponentScope.Singleton;
}
It should be
public ComponentScope getScope() {
return ComponentScope.PerRequest;
}

Categories

Resources