Hold HTTP servlet request threads for reloading business config object - java

I need to Hot load or Reload business configuration / object at runtime in http servlet.
Config object is generated by reading a file during servlet init(). I need to reload this object when file is changed / updated..
public void init() {
Config config = initializeConfigFile();
}
Task 1 would be to have a thread periodically monitor file contents and if changed re-create config object from changed file.. I think this can be achieved by having some hash like md5 on file contents.. check if new hash changed..
public void run() {
// Read file
// Generate hash of file contents
// Compare with previous hash
// If different set a flag to hold threads
// configFileChanged = true;
}
Task 2 hold request threads until new config object is created. As this happens at runtime there might be request threads already accessing old config object and I can't just swap the config object as this might produce unexpected behavior. So I need to wait until all the threads already accessing the old config are done.
So I need suggestions for task 2.. I have not explored much of java advanced threading and queue based API's
For now thinking of some queue which holds request threads.
public void doGet(HttpServletRequest req, HttpServletResponse resp) {
if (configFileChanged) {
synchronized (ActionServlet.class) {
while (configFileChanged) {
if (queue.isEmpty) {
config = initializeConfigFile();
configFileChanged = false;
} else {
sleep(2000);
}
}
}
}
queue.insert(Thread.currentThread());
// perform task using config object
queue.remove();
}
Also suggest improvizations for task 1, if any..
Thanks..

Bad way for both tasks:
For task 1:
Checking file regularry is not a logical process while you can have an observer/monitor for the file, in other word, check the new config once the file is changed(file monitor using java.nio.file)
For task 2:
If and if the container is not configured/overridden to create new instance for each request. --> The default/std behavior is having ONE object of the Servlet, and invoke the instance for each request via a thread, so for this a synchronized void reload() method would help.
During the reload() method run, because it locks the Servlet object, All new requests will be hold till the reload() finishes it's job.
public void doGet(HttpServletRequest req, HttpServletResponse resp) {
//servlet business
}
//the reload is called by the file monitor
//The synchronized method will hold all new requests
synchronized void reload(){/*applying new configs*/}
Besides having a file(external) observer might not look safe and logical, I suggest having an external applciation which monitors the file and informs the web context by calling a servlet(maybe /friends/reload_config) where there is a servlet mapped to /friends/reload_config which will call reload() method.
You may check the request ip in /friends/reload_config servlet in order to ensure the request is generated by local app(not from net)

Related

Safe way to find if we are within a thread bound request without checking for an Exception

Problem
So we have a Request Interceptor (Feign) that checks an autowired HttpServletRequest for headers, and then propagates/copies these to the outgoing request. Our interceptor's job is to propogate headers from microservice to microservice so that even the last micro-service in the graph has the information about who initiated the request (e.g. tenant).
Sometimes we call feign as a result of an HTTP request thread, and sometimes we call it on start-up or from a scheduled thread.
In the case of a scheduled thread, we would like to be able to detect whether a request exists without needing to do a try/catch. This is the case where we are the initiating party and we don't need to copy anything.
I was expecting the following to work, but we get a proxy object that throws an exception:
The following check fails because this.request is not null:
this.request!=null && this.request.getHeader("X-Application")
With the following error:
No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
I understand the error. I would like to avoid doing the obvious workaround which would be something like:
Current Workaround - clumsy and bad
//TODO: Review this
boolean requestExists = true;
try{
request.getHeader(APPLICATION_HEADER);
}catch (IllegalStateException e ){
requestExists = false;
}
Current Code Causing the Problem
public class ServiceNameFeignInterceptor implements RequestInterceptor {
private static final Logger log = LoggerFactory.getLogger(ServiceNameFeignInterceptor.class);
final TenantIdResolver tenantResolver;
final ApplicationNameResolver appResolver;
private final String APPLICATION_HEADER = "X-Application";
private final String TENANT_ID = "X-Tenant-Id";
...
#Autowired
HttpServletRequest request;
public void apply(RequestTemplate requestTemplate) {
...
if (this.request!=null && this.request.getHeader("X-Application") != null) {
log.info("Application header found in the request !!!");
requestTemplate.header("X-Application", new String[]{this.request.getHeader("X-Application")});
requestTemplate.header("X-Tenant-Id", new String[]{this.request.getHeader("X-Tenant-Id")});
} else {
log.info("Setting {} as {} for URL {} ", new Object[]{"X-Application", appName, requestTemplate.url()});
requestTemplate.header("X-Application", new String[]{appName});
requestTemplate.header("X-Tenant-Id", new String[]{appName});
}
}
Current Options
Please correct me on the following points or propose better options if possible.
I currently have three options in mind:
Use the try/catch solution ( least favored)
Check threadlocal variables for the existence of the request
Pass our own additional thread local variable that will be a flag ( that we are not within a request context ).
Problems
I don't like 1, because catching exceptions are expensive, and because they would possibly mask any real errors.
I don't like 2, because if the spring implementation changed maybe implementation details would change (e.g. key) and our implementation in our starter would break. But in any case whenever upgrading spring boot various minor or major things need to be fixed.
Option 3 I like because it is a conscious action to set a flag before call our feign client. So there is no risk of errors going un-noticed.
Opinions, options, solutions?
Update
One of the team members suggests we use: new NamedThreadLocal("Request attributes");
They suggest this because of the implementation at:
https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/context/request/RequestContextHolder.java#L50
https://github.com/spring-projects/spring-framework/blob/master/spring-web/src/main/java/org/springframework/web/context/request/RequestContextHolder.java#L107
So we would use something like:
ThreadLocal<RequestAttributes> requestAttributesHolder = new NamedThreadLocal("Request attributes");
RequestAttributes attributes = (RequestAttributes)requestAttributesHolder.get();
boolean requestExists = attributes != null;
But this is quite dependent on the internals of spring and them continuing to use "Request attributes".
I have a similar problem and I use RequestContextHolder to check if the request is bound to the thread. According to the doc, getRequestAttributes returns null if no RequestAttributes bound to the thread.
RequestAttributes attrs = RequestContextHolder.getRequestAttributes();
if (attrs == null) {
return;
}

How to make a servlet respond to multiple requests?

I have a task that I want to wrap in a servlet to provide the ability to run the task remotely, by http request.
I know I can achieve this with REST API, but currently I assume (and please correct me if I'm wrong) that a simple servlet will do.
One of the things I want to achieve is that if a request to the servlet is made while another request is still processed, I'll get an appropriate response - "Task is already running".
I've built a simple servlet, using servlet-3.0, that calls the jar I want to run, but when I make 2 requests, the second one is not processed until the first one is finished.
EDIT:
My servlet is a simple http serlvet. service method overriden.
I have a system.out.println("a") in the start.
when I call the servlet in debug mode and then (while stopped at breakpoint) call it again, the message is printed only one time and printed the second time when I release the breakpoint and the first run finishes.
First of all, this does not seems like REST at all. If you really just want to spawn a (single) background task, make sure you do it in a separate worker thread, not the request thread.
Maybe you need a lock:
public class Task extends HttpServlet {
// for singleton
//private volatile boolean running = false;
// or try this:
public static boolean running = false;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException
{
if(running){
PrintWriter out = response.getWriter();
out.println("running");
return;
}
synchronized(Task.class){
if(!running){
running = true;
// run the task
running = false;
}
}
}
}

ThreadLocal in Tomcat servlets

I'm having a problem where Exceptions are popping up in my production system but I really don't have good information about who is causing them. The person's username is stored as a variable in their tomcat session, which I have access to in my doPost or doGet method obviously, but unless I pass that information down as a parameter to each of my business objects, I don't have access to the session. For obvious reasons, I'd like to tack the username into the logging message so I have an idea of what is going on.
So my solution is to do something like this
public class ExceptionUtil {
private ExceptionUtil() { } // no instantiation
private static final ThreadLocal<String> local = new ThreadLocal<String>();
public static void set(String user) { local.set(user); }
public static String get() { return local.get(); }
}
Then in my posts/gets, I can do this
String username = request.getSession().getAttribute("username");
ExceptionUtil.set(username);
Then in my exceptions, I might do this (contrived, bad practice example)
catch(SQLException e) {
logger.error(ExceptionUtil.get() + " did something dumb in sql", e);
throw e;
}
The only problem I'm concerned about is how Tomcat will manage my threads. What if they keep the threads? Will they persist? Will the ThreadLocal values also persist? If I was storing the entire Session in the ThreadLocal instead of just a String, that would be a serious memory leak potential. It also means if someone forgot to re-set (or forgets to clear when done) the username/session on a thread that persisted for multiple requests, there might be stale data in there.
Call my cynical, but I don't want to have to rely on programmers (even, especially myself!) not forgetting to do things for a program's correctness. If I can idiot-proof my code, I'd like to. And that means getting a better understanding of how Tomcat will use the threads.
So, the question in a single-sentence form:
If I use ThreadLocal in a webapp running on Tomcat (7.0.27), do I run
the risk of a Thread being used for multiple requests, and with it
data from a previous request being persisted?
I should note that even though they don't answer the exact question of "Tomcat/ThreadLocal shenanigans", I am open to alternative solutions that allow me to elegantly access session variables for logging purposes. I am also open to commentary about potential pitfalls of my solution. I have a business problem to solve, and I'm not married to any one solution. I just want to know who keeps causing the exceptions on my prod system :)
Yes, tomcat uses the ThreadPool concept , that means the threads are being reused and hence as you suggested "Your Thread Local retains the values" ,
alternatives what i would suggest could be
clean up threads after you are done, somewhere in the view controller
Write a Request Filter and on start of filter Clean up everything and push new values,
and assign this to every url pattern on ur server.
for the approach you are following instead of saving certain values in classes,
Store the request in Thread Local and then use the request to pull values out of session using a homemade util class, that takes request and then returns you desired value, that way you save yourself of saving session in Thread and get the value, but please ensure that u add fresh every time and clean up request after you are done(use 2nd option for that ) .
You don't need to reinvent the wheel, the log system does it for you.
If logback/log4j is your logger implementation, then Mapped Diagnostic Context(MDC) is definitely your answer.
MDC is logically like ThreadLocal, but it's better:
MDC handle thread-safe and synchronization transparently
A child thread automatically inherits a copy of the mapped diagnostic context of its parent. So even you using multi-thread to process request, it's still ok.
So set MDC in servlet filter like this, to achieve your goal:
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
boolean successfulRegistration = false;
HttpServletRequest req = (HttpServletRequest) request;
Principal principal = req.getUserPrincipal();
// Please note that we could have also used a cookie to
// retrieve the user name
if (principal != null) {
String username = principal.getName();
successfulRegistration = registerUsername(username);
}
try {
chain.doFilter(request, response);
} finally {
if (successfulRegistration) {
MDC.remove(USER_KEY);
}
}
}
private boolean registerUsername(String username) {
if (username != null && username.trim().length() > 0) {
MDC.put(USER_KEY, username);
return true;
}
return false;
}
Then in your log configuration, add %X{USER_KEY} in your pattern layout to use the value you set in MDC.
In logback, there are out-of-box filter MDCInsertingServletFilter can log more information like remoteHost/requestUrl and etc, very useful information for logging.
Check the logback document on MDC http://logback.qos.ch/manual/mdc.html

How do you to detect the end of a thread via ThreadId?

Talking Java Servlets here... I'm working on creating my own "Per Request Context" and I was looking to tie the "Per Request Context" object to the Thread.currentThread().getId() value.
Instead of passing around this context object everywhere I was planning on checking the current threadid when a user calls a function that is Per Request based and automatically getting the Context object out of a hashtable for that threadId.
I would use the code this like..
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
MyFramework.EnterContext();
try {
// do stuff here that leads to other classes on the same thread
// Access current context via static MyFramework.getCurrentContext()
}
finally { MyFramework.ExitContext(); }
}
However I would like to protect my application automatically from any potential user that does not call ExitContext(). In C# there is an event handler on the thread object for onexit...(think I wrong on this) is there some way to detect or poll when a thread exits? I'm currently storing only the threadId (long).
Any ideas?
unfortunatelly, there is no such feature built in for threads in Java. Besides, thread id is only guaranteed to be unique at any one time, but may be reused eventually when the thread dies (from the docs). however, the servlet framework that you are using may be implementing such feature (just a speculation).
i would recommend you implement a servlet filter, and tell your users to include it in their web.xml. with this you can be sure the client code always gets correctly wraped in your thread context.
A ThreadLocal seems to fit your use perfectly. A ThreadLocal object can provide a way to store a variable per thread. The internal workings of this class are very much of what you describe, it uses a map to give thread-local variables.
Something like this should do the trick:
private static final ThreadLocal<UserContext> userContext = new ThreadLocal<UserContext>();
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
MyFramework.EnterContext();
try {
UserContext context = userContext.get();
//if you used the set method in this thread earlier
//a thread local context would be returned using get
}
finally { MyFramework.ExitContext(); }
}
As for your other problem, you can use an observer pattern and notify when the thread completes its task.

in servlet I want to have a response.print() while another thread is running

I have a cumbersome object which has to work in background , I create a servlet which will call this object and for running the object I've implemented the Runnable interface inside my servlet like following :
public class myObject extends HttpServlet implements Runnable
{
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
res.print("<br/> running...");
res.flush(); //doens't help
Thread t = new Thread(this);
t.run();
}
public void run()
{
res.print("<br/> running...");
res.flush();//doesn't helper either
cumbersomeObject.workHard();
}
}
How can I print this simple line and then send 200 status to user and my servlet work in background happily ever after?
Change t.run() to t.start(), your thread is not running in background, but blocking in foreground. BTW how does this compile? How can run() access res variable?
However it looks like you are trying to send some data to a user via res response object from a thread. This won't work, see HttpServletResponse seems to periodically send prematurely for a detailed discussion.
You're making a fundamental mistake. The HTTP servlet request and response objects are tied to the current HTTP request thread. Once this thread finishes (i.e. when the HTTP request/response finished), then the associated HTTP servlet request and response objects are trashed and completely useless. So if you use those objects in a different thread after the initial HTTP request/response finishes, then you'll get exceptions in all colors because they point nowhere to.
Manually spawning new thread on a per-request basis is also a terribly bad idea. Threads are not cheap and definitely not unlimited available. Rather use a pool of 5~10 threads or something. If you have such a thread pool, then you could just submit a task to it, associate it with some unique key in the HTTP session or maybe even in a database along the logged-in user ID. You can then just poll in every subsequent request if the task is finished or not (i.e. it has stored the result there where you'd expect to see it).

Categories