Threadlocal on tomcat misbehaviour after upgrading to JAVA 8 - java

I use thread local to store a user request specific features (ex. browser agent) it used to work fine on JAVA 7, but now after upgrading to JAVA 8 In some cases I see requests coming from android browser handled as if its coming from iOS browser even though it was detected correctly as android browser but later on while processing the request it was replaced with another thread local value! am not sure what am missing here can anyone help me? my environment setup (before/after) upgrade is:
tomcat 8 before and after.
JAVA upgraded from 7 to 8.
Spring upgraded from 4.1.7 to 4.2.5
Spring security upgraded from 3.2.3 to 4.03
I have a security filter that looks something like this:
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.web.filter.GenericFilterBean;
public class AuthenticationTokenProcessingFilter extends GenericFilterBean {
private final IdentityService identityService;
public AuthenticationTokenProcessingFilter(IdentityService userService) {
this.identityService = userService;
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
SecurityManager.manager().clearManager();
HttpServletRequest httpRequest = this.getAsHttpRequest(request);
String agent = httpRequest.getHeader("User-Agent");
SecurityManager.manager().setAgent(agent);
...
chain.doFilter(request, response);
}
}
And the Security manager looks like this:
import com.appseleon.platform.web.shared.CrossAppConstants;
public class SecurityManager {
private static SecurityManager manager;
private final ThreadLocal<String> agentContext = new ThreadLocal<String>();
private SecurityManager() {
manager = this;
}
public void clearManager() {
agentContext.set(null);
}
public static SecurityManager manager() {
return manager;
}
public String getAgent() {
String os = agentContext.get();
if (os == null) {
os = CrossAppConstants.DEFAULT_OS;
}
return os;
}
public void setAgent(String agent) {
System.out.println("### os detected: " + agent);
agentContext.set(agent);
}
}
Finally after setting the agent and in various areas of my code I call the SecurityManager to get the current user agent:
SecurityManager.manager().getAgent()
Can anyone help me figure out the cause of this issue, or even an alternative more reliable way to achieve this?
Thanks in advance :)

For starters your SecurityManager is flawed you should not get an instance but simply directly get/set the value on the ThreadLocal using static. Currently you might run into issues when things get loaded in a different class loader i.e. doesn't detect the singleton.
public abstract class SecurityManager {
private static final ThreadLocal<String> agentContext = new ThreadLocal<String>();
private SecurityManager() { }
public static void clearManager() {
agentContext.set(null);
}
public static String getAgent() {
String os = agentContext.get();
if (os == null) {
os = CrossAppConstants.DEFAULT_OS;
}
return os;
}
public static void setAgent(String agent) {
System.out.println("### os detected: " + agent);
agentContext.set(agent);
}
}
Then directly call the get/set methods on this.
In your filter you should wrap the filterChain.doFilter in a try / finally block in the finally always clear the thread local.
try {
chain.doFilter(request, response);
} finally {
SecurityManager.clearManager();
}
Also instead of extending GenericFilterBean you might want to extend OncePerRequestFilter which makes sure this functionality is only called once (especially useful if you have some forwards in your logic) and it works only for HttpServletRequest type of requests, saves you some code.
public class AuthenticationTokenProcessingFilter extends OncePerRequestFilter {
...
#Override
protected void doFilterInternal(HttpServletRequest req, HttpServletResponse ress, FilterChain chain) throws IOException, ServletException {
String agent = req.getHeader("User-Agent");
SecurityManager.setAgent(agent);
...
try {
chain.doFilter(request, response);
} finally {
SecurityManager.clearManager();
}
}
}
This is also the way Spring Security works and Springs Transaction management for instance (with the static methods and shared ThreadLocal).

Related

ThreadLocale value getting mixed up in Servlet Filter

I am working on a messy Struts 1 application that makes use of a custom context class to store values throughout the application. Basically it is only used to store session scope variables. I guess the reason that this custom class is used is so that other classes which do not have access to the http session can still get and set the session variables.
Anyways, for the most part this works just fine. The custom context is used throughout the Actions and service classes to share variables with no problem. However, I just discovered that things do not work out so nicely using this custom context inside of an Http Filter! It appears that it randomly will pull the value from a different session. And by session, I actually mean thread, since this custom context uses ThreadLocale to do it's dirty work.
Take a look
package com.zero.alpha.common;
import java.io.Serializable;
import java.util.Hashtable;
import java.util.Locale;
import java.util.Map;
public final class CustomContext implements Serializable {
private static final long serialVersionUID = 400312938676062620L;
private static ThreadLocal<CustomContext> local = new ThreadLocal() {
protected CustomContext initialValue() {
return new CustomContext("0", "0", Locale.getDefault());
}
};
private String dscId;
private String sessionId;
private Locale locale;
private Map<String, Serializable> generalArea;
public CustomContext(String dscId, String sessionId, Locale locale) {
this.dscId = dscId;
this.sessionId = sessionId;
if (locale != null) {
this.locale = locale;
} else {
this.locale = Locale.getDefault();
}
this.generalArea = new Hashtable();
}
public static CustomContext get() {
return ((CustomContext) local.get());
}
public static void set(CustomContext context) {
local.set(context);
}
public String getDscId() {
return this.dscId;
}
public String getSessionId() {
return this.sessionId;
}
public Locale getLocale() {
return this.locale;
}
public Serializable getGeneralArea(String key) {
return ((Serializable) this.generalArea.get(key));
}
public Serializable putGeneralArea(String key, Serializable value) {
return ((Serializable) this.generalArea.put(key, value));
}
public void clearGeneralArea() {
this.generalArea.clear();
}
public Serializable removeGeneralArea(String key) {
return ((Serializable) this.generalArea.remove(key));
}
}
Again, this seems to work just fine and dandy inside every other class other than a filter. Let me show you the filter where it messes up.
package com.zero.alpha.myapp.common.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.zero.alpha.common.CustomContext;
import com.zero.alpha.myapp.utility.CommonConstants;
import com.zero.alpha.myapp.utility.CommonHelpers;
import com.zero.alpha.myapp.UserDomain;
public class LoginFilter implements Filter {
public LoginFilter() {
}
public void init(FilterConfig config) throws ServletException {}
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
// Don't use the login filter during a login or logout request
if (req.getServletPath().equals("/login.do")
|| req.getServletPath().equals("/login-submit.do")
|| req.getServletPath().equals("/logout.do")) {
chain.doFilter(request, response);
} else {
doFilter(req, (HttpServletResponse) response, chain);
}
}
protected void doFilter(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpSession session = request.getSession(false);
// This is the problem right here. Sometimes this will grab the value of a different user currently logged in
UserDomain user = (UserDomain) CustomContext.get()
.getGeneralArea(CommonConstants.ContextKey.USER_SESSION);
if (session == null || user == null) {
// Unauthorized
response.sendRedirect(loginPage);
} else {
// Authorized
session.setAttribute("userInfo", CommonHelpers.getUserDisplay(user));
chain.doFilter(request, response);
}
}
}
When the custom context is used to grab the user in the doFilter method, it will randomly grab the user object from another logged in user. Obviously not a good situation!
The only time this happens is after some activity from a different logged in user. I could sit there all day and keep refreshing user A's session and there wouldn't be an issue. However, after taking some action as user B and then refreshing user A's session again, it will usually be swapped. But then if I refresh user A's session again, things are back to normal.
I've noticed this happens extremely more frequently when the application is actually deployed to a remote development tomcat server. It still happens when running locally, but not nearly as often as when deployed remotely. It happens almost 100% of the time remotely.
I have examined the session variable inside of the filter, and there doesn't appear to be an issue with the session itself. I have confirmed that the session id is still correct even when the incorrect user is pulled from the ThreadLocale variable.
Can anyone help me out? Thanks.
Your strategy is irretrievably flawed. Unless your servlet engine is single-threaded, you cannot rely on the same thread to handle every request in a given session. Moreover, and more relevant to the problem stated, even when the same thread to handle one request in a given session is also used to handle the next request in that session, it is not safe to assume that it does not handle a request belonging to a different session in between. There is no guarantee of thread / session binding.
Anyway, you seem to be missing the obvious. If you want to store session-scope variables, then store them in the session. That's what session attributes are for. See HttpSession.setAttribute() and HttpSession.getAttribute() -- you can use these to set and retrieve your context object.

"Publish" object through http

I have an object with state and non-serializable fields, like threads, and I would to invoke functions on it like one would do it through RMI but through http. I don't want to scale and I am in an isolated network. I am currently using Jetty, like this:
public class ObjectHandler extends AbstractHandler {
MyStatefulObject obj;
public void handle(String target, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
String action = request.getParameter("action");
switch (action) {
case "method1":
obj.method1(request.getParameter("some-parameter"));
break;
case "method2":
obj.method2(request.getParameter("some-other-parameter"));
break;
}
baseRequest.setHandled(true);
}
}
which is kind of weird. I would like to use something like Servlets, and use the different methods to tell apart the action to do, or use JAX-RS to use the calling url to tell apart the action to do. But both of those methods are stateless, that is, I cannot pass an object to a servlet, and, at least with jersey, the construction was made with the class, not with and instance of it, so I could not control the construction of the MyStatefulObject object. So, is there a library for, let's say, annotate an object and pass it to a server instance and start listening to requests? I would like to make something like this:
#Path("/")
public class MyStatefulObject {
MyStatefulObject(Parameter param1, Param) {
//some building stuff
}
#POST
#Path("/path1")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.APPLICATION_JSON + "; charset=UTF-8")
void method1(Parameter param) {}
#POST
#Path("/path2")
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.APPLICATION_JSON + "; charset=UTF-8")
Object method2(Parameter param) {
return new Object();
}
}
while outside I would have:
Server server = new Server(8081);
server.setHandler(new MyStatefulObject(param));
server.start();
server.join();
Is there a library that makes me able to do that? as I say before, I don't want to scale (this is running in a small network) and there is no security concerns. I just want to "publish" an object.
In the end, Jersey does allow stateful objects to be published, using the ResourceConfig class with an object (as opposed with a Class, which is the use I found more frequently). Funny cause in this question they want to do the exact opposite. We simply register an object in the ResourceConfig.
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;
import java.net.URI;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.glassfish.jersey.jetty.JettyHttpContainerFactory;
import org.glassfish.jersey.server.ResourceConfig;
import javax.inject.Singleton;
#Path("calculator")
public class Calculator {
int i = -1;
public Calculator(int i) {
this.i = i;
}
#GET
#Path("increment")
#Produces(MediaType.APPLICATION_JSON)
public String increment() {
i = i + 1;
return "" + i;
}
public static void main(String[] args) throws Exception {
ResourceConfig resourceConfig = new ResourceConfig();
resourceConfig.register(new Calculator(10));
Server server = JettyHttpContainerFactory.createServer(new URI("http://localhost:8080"), resourceConfig);
server.start();
}
}

Need more info when logging servlet exceptions through email

The environment
I'm running a number of applications that use Servlets, including those based on JSF and JAX-WS and some of my own custom servlets. I'm using Tomcat 7.x as my web container. I'm using java.util.logging for logging messages.
Current Situation
For logging exceptions, I have been using SMTPHandler which has worked very well. Here are the relevant excerpts from my logging.properties file:
handlers = {... other handlers ...},08SMTP.smtphandler.SMTPHandler
08SMTP.smtphandler.SMTPHandler.level=SEVERE
08SMTP.smtphandler.SMTPHandler.smtpHost=smtp.somedomain.com
08SMTP.smtphandler.SMTPHandler.to=developers#somedomain.com
08SMTP.smtphandler.SMTPHandler.from=developers#somedomain.com
08SMTP.smtphandler.SMTPHandler.subject=MyApplication error message
08SMTP.smtphandler.SMTPHandler.bufferSize=512
08SMTP.smtphandler.SMTPHandler.formatter=java.util.logging.SimpleFormatter
The only problem with this setup is that the email only contains an exception. There is no other information about the context in which the error happened.
What I'd Like To See
I'd like the email to contain other contextual information from the ServletRequest / HttpServletRequest object such as:
Who is the user that is logged in?
What was the queryString, URL, URI, ContextPath, ServletPath, and getMethod of the request?
What were the header parameters?
What were the parameters?
What were the attribute names/values?
The Attempted Solution
Logging Handlers configured by the logging.properties file don't have access to other parts of the application except through static variables, so I thought I'd try to create a logging Handler programatically. I've tried to make a handler, but there is not a way for it to know about the HttpServletRequest that is active at the time of the exception.
I've tried to create my own class that implements both ServletRequestListener and ServletContextListener, then registers a custom logging Handler that knows about a ThreadLocal<ServletRequest> variable, and then set and clear that ThreadLocal variable in the ServletRequestListener. After adding a <listener> reference in my web.xml file which correctly calls contextInitialized and requestInitialized, my logging Handler's publish method is never called when an exception happens.
The code for this is here.
public class LoggingWebListener
implements ServletRequestListener, ServletContextListener
{
public static class FtSmtpHandler
extends Handler
{
private final ServletContext sc;
private final ThreadLocal<ServletRequest> servletReqLocal;
public FtSmtpHandler(ServletContext servletContext, ThreadLocal<ServletRequest> servletReqLocal)
{
this.sc = servletContext;
this.servletReqLocal = servletReqLocal;
}
#Override public void publish(LogRecord record)
{
if (record.getLevel().intValue() < Level.WARNING.intValue())
return;
// Don't try to send email if the emailer fails and logs an exception
if (record.getLoggerName().equals(MyEmailHelper.class.getName()))
return;
// CODE TO SEND EMAIL GOES HERE
}
#Override public void flush()
{
}
#Override public void close()
throws SecurityException
{
}
}
public static final Logger glogger = Logger.getGlobal();
public static final Logger logger = Logger.getLogger(LoggingWebListener.class.getName());
private final ThreadLocal<ServletRequest> servletReqLocal = new ThreadLocal<>();
private FtSmtpHandler handler;
public LoggingWebListener()
{
}
#Override public void contextInitialized(ServletContextEvent evt)
{
logger.log(Level.INFO, "Initializing context for " + getClass().getName());
ServletContext servletContext = evt.getServletContext();
handler = new FtSmtpHandler(servletContext, servletReqLocal);
glogger.addHandler(handler);
}
#Override public void contextDestroyed(ServletContextEvent arg0)
{
glogger.removeHandler(handler);
handler = null;
}
#Override public void requestInitialized(ServletRequestEvent evt)
{
ServletRequest servletRequest = evt.getServletRequest();
// logger.log(Level.INFO, "Initializing request for request " + servletRequest);
servletReqLocal.set(servletRequest);
}
#Override public void requestDestroyed(ServletRequestEvent evt)
{
servletReqLocal.remove();
}
}
Is there a small mistake in what I'm doing? Is it the totally wrong approach? Is there an already existing module that will do what I want that I haven't found? Is there another way to do what I want to do?
This post suggests an approach similar to what I've taken, but does not have the details.
Create a custom servlet filter that will be triggered for all calls the the application. Then create a custom formatter that knows how to format the properties of your request. Inside the filter, capture the current request and send it over to the custom formatter that you installed on the SMTPHandler to gain access to the request object.
public class RequestContextFilter implements javax.servlet.Filter {
private static final String CLASS_NAME = MdcFilter.class.getName();
private static final Logger logger = Logger.getLogger("");
private volatile Handler emailer;
#Override
public void init(FilterConfig filterConfig) throws ServletException {
emailer = new SMTPHandler();
//etc...
emailer.setFormatter(new ContextFormatter());
logger.addHandler(emailer);
}
#Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
ContextFormatter.CTX.set(request);
try {
chain.doFilter(request, response);
} finally {
ContextFormatter.CTX.remove();
}
}
#Override
public void destroy() {
logger.removeHandler(emailer);
emailer.close();
}
private static class ContextFormatter extends Formatter {
static final ThreadLocal<ServletRequest> CTX = new ThreadLocal();
private final Formatter txt = new SimpleFormatter();
#Override
public String format(LogRecord record) {
HttpServletRequest req = (HttpServletRequest) CTX.get();
return req.getRequestURI() + " " + txt.format(record);
}
}
}
Since this is using a thread local it won't work if there is a thread handoff between the logger and filter.

Case-insensitive query string request parameters

My goal is that all below URI's should work
https://rest/xyz?sort=name
https://rest/xyz?Sort=name
https://rest/xyz?filter=name=value
https://rest/xyz?Filter=name=value
To achieve this, I have created custom filter that overrides the HttpServletRequest that is passed to the FilterChain. Below is the link for this approach:
http://forum.springsource.org/archive/index.php/t-87433.html
My code:
import java.io.IOException;
import java.util.Map;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
public class HttpCustomParamFilter implements Filter
{
private static class HttpServletRequestCustomeWrapper extends HttpServletRequestWrapper
{
private String[] parameterValues;
#Override
public String[] getParameterValues(String name)
{
Map<String, String[]> localParameterMap = super.getParameterMap();
// Handle case insensitivity of http request paramters like start, count, query, sort, filter etc.
if (localParameterMap != null && !localParameterMap.isEmpty())
{
parameterValues = new String[localParameterMap.size()];
for (String key : localParameterMap.keySet())
{
if (name.equalsIgnoreCase(key))
parameterValues = localParameterMap.get(key);
else
parameterValues = null;
}
}
return parameterValues;
}
public HttpServletRequestCustomWrapper(final ServletRequest request)
{
super((HttpServletRequest) request);
}
}
#Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// override the request passed to the FilterChain
chain.doFilter(new HttpServletRequestCustomWrapper(request), response);
}
#Override
public void init(FilterConfig filterConfig)
throws ServletException
{
// TODO Auto-generated method stub
}
#Override
public void destroy()
{
// TODO Auto-generated method stub
}
}
In this code, i have overriden getParameterValues(String name) method and achieved case-insensitivity of request paramters, but not sure if i need to override any other methods.
my doubts:
do i need to override other methods also like getParameter() and getParameterNames()?
what internal implementation is impacted with this?
which class i can see the code implementation of getParameter(), getParameterNames() and getParameterValues()?
First, let me say my peace: I don't think modifying the HttpServletRequestWrapper is the way to go. I am not even sure how you would go about using it, as my understanding is it's App Server specific. As a side note, this article has specifics on how to use the HttpServletRequest to get a case-insensitive query param without rolling your own.
But, in the spirit of answering your questions:
Do you need to override getParameter() and getParameterNames()? You could, as it would give you the ability to manipulate the case. In fact, I would say the safest way to make the query parameters case-insensitive would be to overwrite ONLY those methods. Make the getParameter() call do a case-insensitive equals on the string names. Not sure what you would do with getParameterNames(), maybe return every possible case, but this seems redundant.
What internal implementation is impacted by this? I am not certain. HttpServletRequest is so core to pretty much everything, there is no telling what you could introduce if your code is not 100% solid. For instance, Spring has a SecurityContextHolderAwareRequestWrapper, so does that mean you just broke Spring Security? No telling without a lot of testing.
Which class can I see the code implementation of getParameter(), getParameterNames(), and getParameterValues()? HttpServletRequestWrapper is the only implementation of HttpServletRequest interface, according to the JavaDocs. The actual implementation of this class is dependent on your application container. For instance, in my app its weblogic.servlet.internal.ServletRequestImpl, since I use Web Logic. Hopefully you are using an open-source app server that has the code readily available. The way I found this was to put a break in one of my Controller handler methods that has HttpServletRequest defined and viewing it's getClass() response in the debugger.

How to stop jersey client from throwing exception on Http 301?

I am using the Jersey client to run some integration tests against my service. However, one of my calls sends a redirect. I am expecting to get a redirect but when Jersey Client gets the redirect it errors out with a com.sun.jersey.api.client.UniformInterfaceException. Is there some way to make it accept the response with the redirect and just let me know what it got?
You can catch UniformInterfaceException which provides response field containing all the details.
You could additionally write some hamcrest matchers to express your expectations:
import static javax.ws.rs.core.Response.Status.*;
import static org.junit.rules.ExpectedException.*;
import javax.ws.rs.core.Response.Status;
import org.hamcrest.*;
import org.junit.*;
import org.junit.rules.ExpectedException;
import com.sun.jersey.api.client.*;
public class MyResourceShould {
#Rule
public ExpectedException unsuccessfulResponse = none();
private WebResource resource;
#Before
public void setUp() {
Client client = Client.create();
client.setFollowRedirects(false);
resource = client.resource("http://example.com");
}
#Test
public void reportMovedPermanently() {
unsuccessfulResponse.expect(statusCode(MOVED_PERMANENTLY));
resource.path("redirecting").get(String.class);
}
public static Matcher<UniformInterfaceException> statusCode(Status status) {
return new UniformInterfaceExceptionResponseStatusMatcher(status);
}
}
class UniformInterfaceExceptionResponseStatusMatcher extends TypeSafeMatcher<UniformInterfaceException> {
private final int statusCode;
public UniformInterfaceExceptionResponseStatusMatcher(Status status) {
this.statusCode = status.getStatusCode();
}
public void describeTo(Description description) {
description.appendText("response with status ").appendValue(statusCode);
}
#Override
protected boolean matchesSafely(UniformInterfaceException exception) {
return exception.getResponse().getStatus() == statusCode;
}
}
Also note that follow redirects (in setUp method) should be set to false in order to get UniformInterfaceException instead of following redirect (if one is specified in Location header).

Categories