Browse Source
The new type makes it easier providing multiple custom request mapping conditions via setters on RequestMappingHandlerMapping. Issue: SPR-9350pull/64/merge
6 changed files with 385 additions and 51 deletions
@ -0,0 +1,181 @@
@@ -0,0 +1,181 @@
|
||||
/* |
||||
* Copyright 2002-2012 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.servlet.mvc.condition; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.Collection; |
||||
import java.util.List; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
import org.springframework.util.Assert; |
||||
import org.springframework.util.ObjectUtils; |
||||
|
||||
import edu.emory.mathcs.backport.java.util.Collections; |
||||
|
||||
/** |
||||
* Implements the {@link RequestCondition} contract by delegating to multiple |
||||
* {@code RequestCondition} types and using a logical conjunction (' && ') to |
||||
* ensure all conditions match a given request. |
||||
* |
||||
* <p>When {@code CompositeRequestCondition} instances are combined or compared |
||||
* they are expected to (a) contain the same number of conditions and (b) that |
||||
* conditions in the respective index are of the same type. It is acceptable to |
||||
* provide {@code null} conditions or no conditions at all to the constructor. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
* @since 3.2 |
||||
*/ |
||||
public class CompositeRequestCondition extends AbstractRequestCondition<CompositeRequestCondition> { |
||||
|
||||
private final RequestConditionHolder[] requestConditions; |
||||
|
||||
/** |
||||
* Create an instance with 0 or more {@code RequestCondition} types. It is |
||||
* important to create {@code CompositeRequestCondition} instances with the |
||||
* same number of conditions so they may be compared and combined. |
||||
* It is acceptable to provide {@code null} conditions. |
||||
*/ |
||||
public CompositeRequestCondition(RequestCondition<?>... requestConditions) { |
||||
this.requestConditions = wrap(requestConditions); |
||||
} |
||||
|
||||
private RequestConditionHolder[] wrap(RequestCondition<?>... rawConditions) { |
||||
RequestConditionHolder[] wrappedConditions = new RequestConditionHolder[rawConditions.length]; |
||||
for (int i = 0; i < rawConditions.length; i++) { |
||||
wrappedConditions[i] = new RequestConditionHolder(rawConditions[i]); |
||||
} |
||||
return wrappedConditions; |
||||
} |
||||
|
||||
private CompositeRequestCondition(RequestConditionHolder[] requestConditions) { |
||||
this.requestConditions = requestConditions; |
||||
} |
||||
|
||||
/** |
||||
* Whether this instance contains 0 conditions or not. |
||||
*/ |
||||
public boolean isEmpty() { |
||||
return ObjectUtils.isEmpty(this.requestConditions); |
||||
} |
||||
|
||||
/** |
||||
* Return the underlying conditions, possibly empty but never {@code null}. |
||||
*/ |
||||
public List<RequestCondition<?>> getConditions() { |
||||
return unwrap(); |
||||
} |
||||
|
||||
private List<RequestCondition<?>> unwrap() { |
||||
List<RequestCondition<?>> result = new ArrayList<RequestCondition<?>>(); |
||||
for (RequestConditionHolder holder : this.requestConditions) { |
||||
result.add(holder.getCondition()); |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
@Override |
||||
protected Collection<?> getContent() { |
||||
return (isEmpty()) ? Collections.emptyList() : getConditions(); |
||||
} |
||||
|
||||
@Override |
||||
protected String getToStringInfix() { |
||||
return " && "; |
||||
} |
||||
|
||||
private int getLength() { |
||||
return this.requestConditions.length; |
||||
} |
||||
|
||||
/** |
||||
* If one instance is empty, return the other. |
||||
* If both instances have conditions, combine the individual conditions |
||||
* after ensuring they are of the same type and number. |
||||
*/ |
||||
public CompositeRequestCondition combine(CompositeRequestCondition other) { |
||||
if (isEmpty() && other.isEmpty()) { |
||||
return this; |
||||
} |
||||
else if (other.isEmpty()) { |
||||
return this; |
||||
} |
||||
else if (isEmpty()) { |
||||
return other; |
||||
} |
||||
else { |
||||
assertNumberOfConditions(other); |
||||
RequestConditionHolder[] combinedConditions = new RequestConditionHolder[getLength()]; |
||||
for (int i = 0; i < getLength(); i++) { |
||||
combinedConditions[i] = this.requestConditions[i].combine(other.requestConditions[i]); |
||||
} |
||||
return new CompositeRequestCondition(combinedConditions); |
||||
} |
||||
} |
||||
|
||||
private void assertNumberOfConditions(CompositeRequestCondition other) { |
||||
Assert.isTrue(getLength() == other.getLength(), |
||||
"Cannot combine CompositeRequestConditions with a different number of conditions. " |
||||
+ this.requestConditions + " and " + other.requestConditions); |
||||
} |
||||
|
||||
/** |
||||
* Delegate to <em>all</em> contained conditions to match the request and return the |
||||
* resulting "matching" condition instances. |
||||
* <p>An empty {@code CompositeRequestCondition} matches to all requests. |
||||
*/ |
||||
public CompositeRequestCondition getMatchingCondition(HttpServletRequest request) { |
||||
if (isEmpty()) { |
||||
return this; |
||||
} |
||||
RequestConditionHolder[] matchingConditions = new RequestConditionHolder[getLength()]; |
||||
for (int i = 0; i < getLength(); i++) { |
||||
matchingConditions[i] = this.requestConditions[i].getMatchingCondition(request); |
||||
if (matchingConditions[i] == null) { |
||||
return null; |
||||
} |
||||
} |
||||
return new CompositeRequestCondition(matchingConditions); |
||||
} |
||||
|
||||
/** |
||||
* If one instance is empty, the other "wins". If both instances have |
||||
* conditions, compare them in the order in which they were provided. |
||||
*/ |
||||
public int compareTo(CompositeRequestCondition other, HttpServletRequest request) { |
||||
if (isEmpty() && other.isEmpty()) { |
||||
return 0; |
||||
} |
||||
else if (isEmpty()) { |
||||
return 1; |
||||
} |
||||
else if (other.isEmpty()) { |
||||
return -1; |
||||
} |
||||
else { |
||||
assertNumberOfConditions(other); |
||||
for (int i = 0; i < getLength(); i++) { |
||||
int result = this.requestConditions[i].compareTo(other.requestConditions[i], request); |
||||
if (result != 0) { |
||||
return result; |
||||
} |
||||
} |
||||
return 0; |
||||
} |
||||
} |
||||
|
||||
} |
||||
@ -0,0 +1,141 @@
@@ -0,0 +1,141 @@
|
||||
/* |
||||
* Copyright 2002-2011 the original author or authors. |
||||
* |
||||
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
* you may not use this file except in compliance with the License. |
||||
* You may obtain a copy of the License at |
||||
* |
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* |
||||
* Unless required by applicable law or agreed to in writing, software |
||||
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
* See the License for the specific language governing permissions and |
||||
* limitations under the License. |
||||
*/ |
||||
|
||||
package org.springframework.web.servlet.mvc.condition; |
||||
|
||||
import static org.junit.Assert.assertEquals; |
||||
import static org.junit.Assert.assertNull; |
||||
import static org.junit.Assert.assertSame; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
import org.junit.Before; |
||||
import org.junit.Test; |
||||
import org.springframework.mock.web.MockHttpServletRequest; |
||||
import org.springframework.web.bind.annotation.RequestMethod; |
||||
|
||||
/** |
||||
* A test fixture for {@link CompositeRequestCondition} tests. |
||||
* |
||||
* @author Rossen Stoyanchev |
||||
*/ |
||||
public class CompositeRequestConditionTests { |
||||
|
||||
private ParamsRequestCondition param1; |
||||
private ParamsRequestCondition param2; |
||||
private ParamsRequestCondition param3; |
||||
|
||||
private HeadersRequestCondition header1; |
||||
private HeadersRequestCondition header2; |
||||
private HeadersRequestCondition header3; |
||||
|
||||
@Before |
||||
public void setup() { |
||||
this.param1 = new ParamsRequestCondition("param1"); |
||||
this.param2 = new ParamsRequestCondition("param2"); |
||||
this.param3 = this.param1.combine(this.param2); |
||||
|
||||
this.header1 = new HeadersRequestCondition("header1"); |
||||
this.header2 = new HeadersRequestCondition("header2"); |
||||
this.header3 = this.header1.combine(this.header2); |
||||
} |
||||
|
||||
@Test |
||||
public void combine() { |
||||
CompositeRequestCondition cond1 = new CompositeRequestCondition(this.param1, this.header1); |
||||
CompositeRequestCondition cond2 = new CompositeRequestCondition(this.param2, this.header2); |
||||
CompositeRequestCondition cond3 = new CompositeRequestCondition(this.param3, this.header3); |
||||
|
||||
assertEquals(cond3, cond1.combine(cond2)); |
||||
} |
||||
|
||||
@Test |
||||
public void combineEmpty() { |
||||
CompositeRequestCondition empty = new CompositeRequestCondition(); |
||||
CompositeRequestCondition notEmpty = new CompositeRequestCondition(this.param1); |
||||
|
||||
assertSame(empty, empty.combine(empty)); |
||||
assertSame(notEmpty, notEmpty.combine(empty)); |
||||
assertSame(notEmpty, empty.combine(notEmpty)); |
||||
} |
||||
|
||||
@Test(expected=IllegalArgumentException.class) |
||||
public void combineDifferentLength() { |
||||
CompositeRequestCondition cond1 = new CompositeRequestCondition(this.param1); |
||||
CompositeRequestCondition cond2 = new CompositeRequestCondition(this.param1, this.header1); |
||||
cond1.combine(cond2); |
||||
} |
||||
|
||||
@Test |
||||
public void match() { |
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/"); |
||||
request.setParameter("param1", "paramValue1"); |
||||
request.addHeader("header1", "headerValue1"); |
||||
|
||||
RequestCondition<?> getPostCond = new RequestMethodsRequestCondition(RequestMethod.GET, RequestMethod.POST); |
||||
RequestCondition<?> getCond = new RequestMethodsRequestCondition(RequestMethod.GET); |
||||
|
||||
CompositeRequestCondition condition = new CompositeRequestCondition(this.param1, getPostCond); |
||||
CompositeRequestCondition matchingCondition = new CompositeRequestCondition(this.param1, getCond); |
||||
|
||||
assertEquals(matchingCondition, condition.getMatchingCondition(request)); |
||||
} |
||||
|
||||
@Test |
||||
public void noMatch() { |
||||
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/"); |
||||
CompositeRequestCondition cond = new CompositeRequestCondition(this.param1); |
||||
|
||||
assertNull(cond.getMatchingCondition(request)); |
||||
} |
||||
|
||||
@Test |
||||
public void matchEmpty() { |
||||
CompositeRequestCondition empty = new CompositeRequestCondition(); |
||||
assertSame(empty, empty.getMatchingCondition(new MockHttpServletRequest())); |
||||
} |
||||
|
||||
@Test |
||||
public void compare() { |
||||
HttpServletRequest request = new MockHttpServletRequest(); |
||||
|
||||
CompositeRequestCondition cond1 = new CompositeRequestCondition(this.param1); |
||||
CompositeRequestCondition cond3 = new CompositeRequestCondition(this.param3); |
||||
|
||||
assertEquals(1, cond1.compareTo(cond3, request)); |
||||
assertEquals(-1, cond3.compareTo(cond1, request)); |
||||
} |
||||
|
||||
@Test |
||||
public void compareEmpty() { |
||||
HttpServletRequest request = new MockHttpServletRequest(); |
||||
|
||||
CompositeRequestCondition empty = new CompositeRequestCondition(); |
||||
CompositeRequestCondition notEmpty = new CompositeRequestCondition(this.param1); |
||||
|
||||
assertEquals(0, empty.compareTo(empty, request)); |
||||
assertEquals(-1, notEmpty.compareTo(empty, request)); |
||||
assertEquals(1, empty.compareTo(notEmpty, request)); |
||||
} |
||||
|
||||
@Test(expected=IllegalArgumentException.class) |
||||
public void compareDifferentLength() { |
||||
CompositeRequestCondition cond1 = new CompositeRequestCondition(this.param1); |
||||
CompositeRequestCondition cond2 = new CompositeRequestCondition(this.param1, this.header1); |
||||
cond1.compareTo(cond2, new MockHttpServletRequest()); |
||||
} |
||||
|
||||
} |
||||
Loading…
Reference in new issue