ServletRequestAttributes skips well-known immutable values when updating accessed session attributes
Issue: SPR-11738
This commit is contained in:
@@ -248,7 +248,7 @@ public class ServletRequestAttributes extends AbstractRequestAttributes {
|
|||||||
String name = entry.getKey();
|
String name = entry.getKey();
|
||||||
Object newValue = entry.getValue();
|
Object newValue = entry.getValue();
|
||||||
Object oldValue = this.session.getAttribute(name);
|
Object oldValue = this.session.getAttribute(name);
|
||||||
if (oldValue == newValue) {
|
if (oldValue == newValue && !isImmutableSessionAttribute(name, newValue)) {
|
||||||
this.session.setAttribute(name, newValue);
|
this.session.setAttribute(name, newValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,6 +260,23 @@ public class ServletRequestAttributes extends AbstractRequestAttributes {
|
|||||||
this.sessionAttributesToUpdate.clear();
|
this.sessionAttributesToUpdate.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine whether the given value is to be considered as an immutable session
|
||||||
|
* attribute, that is, doesn't have to be re-set via {@code session.setAttribute}
|
||||||
|
* since its value cannot meaningfully change internally.
|
||||||
|
* <p>The default implementation returns {@code true} for {@code String},
|
||||||
|
* {@code Character}, {@code Boolean} and {@code Number} values.
|
||||||
|
* @param name the name of the attribute
|
||||||
|
* @param value the corresponding value to check
|
||||||
|
* @return {@code true} if the value is to be considered as immutable for the
|
||||||
|
* purposes of session attribute management; {@code false} otherwise
|
||||||
|
* @see #updateAccessedSessionAttributes()
|
||||||
|
*/
|
||||||
|
protected boolean isImmutableSessionAttribute(String name, Object value) {
|
||||||
|
return (value instanceof String || value instanceof Character ||
|
||||||
|
value instanceof Boolean || value instanceof Number);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register the given callback as to be executed after session termination.
|
* Register the given callback as to be executed after session termination.
|
||||||
* <p>Note: The callback object should be serializable in order to survive
|
* <p>Note: The callback object should be serializable in order to survive
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2002-2013 the original author or authors.
|
* Copyright 2002-2014 the original author or authors.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,10 +17,12 @@
|
|||||||
package org.springframework.web.context.request;
|
package org.springframework.web.context.request;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
import java.math.BigInteger;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
import javax.servlet.http.HttpSession;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import org.springframework.mock.web.test.MockHttpServletRequest;
|
import org.springframework.mock.web.test.MockHttpServletRequest;
|
||||||
import org.springframework.mock.web.test.MockHttpSession;
|
import org.springframework.mock.web.test.MockHttpSession;
|
||||||
|
|
||||||
@@ -39,23 +41,12 @@ public class ServletRequestAttributesTests {
|
|||||||
private static final Serializable VALUE = new Serializable() {
|
private static final Serializable VALUE = new Serializable() {
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@Test(expected = IllegalArgumentException.class)
|
@Test(expected = IllegalArgumentException.class)
|
||||||
public void ctorRejectsNullArg() throws Exception {
|
public void ctorRejectsNullArg() throws Exception {
|
||||||
new ServletRequestAttributes(null);
|
new ServletRequestAttributes(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void updateAccessedAttributes() throws Exception {
|
|
||||||
MockHttpSession session = new MockHttpSession();
|
|
||||||
session.setAttribute(KEY, VALUE);
|
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
|
||||||
request.setSession(session);
|
|
||||||
ServletRequestAttributes attrs = new ServletRequestAttributes(request);
|
|
||||||
Object value = attrs.getAttribute(KEY, RequestAttributes.SCOPE_SESSION);
|
|
||||||
assertSame(VALUE, value);
|
|
||||||
attrs.requestCompleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void setRequestScopedAttribute() throws Exception {
|
public void setRequestScopedAttribute() throws Exception {
|
||||||
MockHttpServletRequest request = new MockHttpServletRequest();
|
MockHttpServletRequest request = new MockHttpServletRequest();
|
||||||
@@ -162,4 +153,64 @@ public class ServletRequestAttributesTests {
|
|||||||
verify(request).getSession(false);
|
verify(request).getSession(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void updateAccessedAttributes() throws Exception {
|
||||||
|
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||||
|
HttpSession session = mock(HttpSession.class);
|
||||||
|
when(request.getSession(anyBoolean())).thenReturn(session);
|
||||||
|
when(session.getAttribute(KEY)).thenReturn(VALUE);
|
||||||
|
|
||||||
|
ServletRequestAttributes attrs = new ServletRequestAttributes(request);
|
||||||
|
assertSame(VALUE, attrs.getAttribute(KEY, RequestAttributes.SCOPE_SESSION));
|
||||||
|
attrs.requestCompleted();
|
||||||
|
|
||||||
|
verify(session, times(2)).getAttribute(KEY);
|
||||||
|
verify(session).setAttribute(KEY, VALUE);
|
||||||
|
verifyNoMoreInteractions(session);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void skipImmutableString() {
|
||||||
|
doSkipImmutableValue("someString");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void skipImmutableCharacter() {
|
||||||
|
doSkipImmutableValue(new Character('x'));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void skipImmutableBoolean() {
|
||||||
|
doSkipImmutableValue(Boolean.TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void skipImmutableInteger() {
|
||||||
|
doSkipImmutableValue(new Integer(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void skipImmutableFloat() {
|
||||||
|
doSkipImmutableValue(new Float(1.1));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void skipImmutableBigInteger() {
|
||||||
|
doSkipImmutableValue(new BigInteger("1"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doSkipImmutableValue(Object immutableValue) {
|
||||||
|
HttpServletRequest request = mock(HttpServletRequest.class);
|
||||||
|
HttpSession session = mock(HttpSession.class);
|
||||||
|
when(request.getSession(anyBoolean())).thenReturn(session);
|
||||||
|
when(session.getAttribute(KEY)).thenReturn(immutableValue);
|
||||||
|
|
||||||
|
ServletRequestAttributes attrs = new ServletRequestAttributes(request);
|
||||||
|
attrs.getAttribute(KEY, RequestAttributes.SCOPE_SESSION);
|
||||||
|
attrs.requestCompleted();
|
||||||
|
|
||||||
|
verify(session, times(2)).getAttribute(KEY);
|
||||||
|
verifyNoMoreInteractions(session);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user