();
+ jp.nextToken();
+ String nextFieldName = jp.getCurrentName();
+ while (!nextFieldName.equals(ROWS_FIELD_NAME)) {
+ jp.nextToken();
+ map.put(nextFieldName, Integer.valueOf(jp.getIntValue()));
+ jp.nextToken();
+ nextFieldName = jp.getCurrentName();
+ }
+ return map;
+ }
+
+ @Override
+ protected void writeInternal(Object o, HttpOutputMessage outputMessage)
+ throws IOException, HttpMessageNotWritableException {
+
+ JsonEncoding encoding = getEncoding(outputMessage.getHeaders()
+ .getContentType());
+ JsonGenerator jsonGenerator = this.objectMapper.getJsonFactory()
+ .createJsonGenerator(outputMessage.getBody(), encoding);
+ try {
+ if (this.prefixJson) {
+ jsonGenerator.writeRaw("{} && ");
+ }
+ this.objectMapper.writeValue(jsonGenerator, o);
+ } catch (JsonGenerationException ex) {
+ throw new HttpMessageNotWritableException("Could not write JSON: "
+ + ex.getMessage(), ex);
+ }
+ }
+
+ private JsonEncoding getEncoding(MediaType contentType) {
+ if (contentType != null && contentType.getCharSet() != null) {
+ Charset charset = contentType.getCharSet();
+ for (JsonEncoding encoding : JsonEncoding.values()) {
+ if (charset.name().equals(encoding.getJavaName())) {
+ return encoding;
+ }
+ }
+ }
+ return JsonEncoding.UTF8;
+ }
+
+ private static class ParseState {
+ boolean inRow;
+ String docFieldName = "";
+ }
}
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/AbstractMonitor.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/AbstractMonitor.java
index fc9d66c90..67e03ae41 100644
--- a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/AbstractMonitor.java
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/AbstractMonitor.java
@@ -15,32 +15,27 @@
*/
package org.springframework.data.document.couchdb.monitor;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
import org.springframework.web.client.RestTemplate;
/**
* Base class to encapsulate common configuration settings when connecting to a CouchDB database
- *
- * @author Mark Pollack
*
+ * @author Mark Pollack
*/
public abstract class AbstractMonitor {
-
- protected RestTemplate restTemplate;
- protected String databaseUrl;
-
- /**
- * Gets the databaseUrl used to connect to CouchDB
- * @return
- */
- public String getDatabaseUrl() {
- return this.databaseUrl;
- }
-
+ protected RestTemplate restTemplate;
+ protected String databaseUrl;
+
+ /**
+ * Gets the databaseUrl used to connect to CouchDB
+ *
+ * @return
+ */
+ public String getDatabaseUrl() {
+ return this.databaseUrl;
+ }
-
}
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/ServerInfo.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/ServerInfo.java
index 80159a669..d45546ab3 100644
--- a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/ServerInfo.java
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/monitor/ServerInfo.java
@@ -24,40 +24,39 @@ import org.springframework.jmx.export.annotation.ManagedResource;
import org.springframework.web.client.RestTemplate;
/**
- * Expose basic server information via JMX
- *
- * @author Mark Pollack
+ * Expose basic server information via JMX
*
+ * @author Mark Pollack
*/
-@ManagedResource(description="Server Information")
+@ManagedResource(description = "Server Information")
public class ServerInfo extends AbstractMonitor {
-
-
- public ServerInfo(String databaseUrl) {
- this.databaseUrl = databaseUrl;
- this.restTemplate = new RestTemplate();
- }
-
-
- @ManagedOperation(description="Server host name")
- public String getHostName() throws UnknownHostException {
- return InetAddress.getLocalHost().getHostName();
- }
- @ManagedOperation(description="CouchDB Server Version")
- public String getVersion() {
- return (String) getRoot().get("version");
- }
-
- @ManagedOperation(description="Message of the day")
- public String getMotd() {
- return (String) getRoot().get("greeting");
- }
-
- public Map getRoot() {
- Map map = restTemplate.getForObject(getDatabaseUrl(),Map.class);
- return map;
- }
+ public ServerInfo(String databaseUrl) {
+ this.databaseUrl = databaseUrl;
+ this.restTemplate = new RestTemplate();
+ }
+
+
+ @ManagedOperation(description = "Server host name")
+ public String getHostName() throws UnknownHostException {
+ return InetAddress.getLocalHost().getHostName();
+ }
+
+
+ @ManagedOperation(description = "CouchDB Server Version")
+ public String getVersion() {
+ return (String) getRoot().get("version");
+ }
+
+ @ManagedOperation(description = "Message of the day")
+ public String getMotd() {
+ return (String) getRoot().get("greeting");
+ }
+
+ public Map getRoot() {
+ Map map = restTemplate.getForObject(getDatabaseUrl(), Map.class);
+ return map;
+ }
}
diff --git a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/support/CouchUtils.java b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/support/CouchUtils.java
index 823454ca9..fb42d6486 100644
--- a/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/support/CouchUtils.java
+++ b/spring-data-couchdb/src/main/java/org/springframework/data/document/couchdb/support/CouchUtils.java
@@ -17,11 +17,10 @@
package org.springframework.data.document.couchdb.support;
import org.springframework.dao.DataAccessException;
-import org.springframework.data.document.UncategorizedDocumentStoreException;
/**
* Helper class featuring helper methods for internal CouchDB classes.
- *
+ *
* Mainly intended for internal use within the framework.
*
* @author Thomas Risberg
@@ -30,49 +29,53 @@ import org.springframework.data.document.UncategorizedDocumentStoreException;
*/
public abstract class CouchUtils {
- /**
- * Convert the given runtime exception to an appropriate exception from the
- * org.springframework.dao hierarchy.
- * Return null if no translation is appropriate: any other exception may
- * have resulted from user code, and should not be translated.
- * @param ex runtime exception that occurred
- * @return the corresponding DataAccessException instance,
- * or null if the exception should not be translated
- */
- public static DataAccessException translateCouchExceptionIfPossible(RuntimeException ex) {
+ /**
+ * Convert the given runtime exception to an appropriate exception from the
+ * org.springframework.dao hierarchy.
+ * Return null if no translation is appropriate: any other exception may
+ * have resulted from user code, and should not be translated.
+ *
+ * @param ex runtime exception that occurred
+ * @return the corresponding DataAccessException instance,
+ * or null if the exception should not be translated
+ */
+ public static DataAccessException translateCouchExceptionIfPossible(RuntimeException ex) {
- return null;
- }
-
- /**
- * Adds an id variable to a URL
- * @param url the URL to modify
- * @return the modified URL
- */
- public static String addId(String url) {
- return ensureTrailingSlash(url) + "{id}";
- }
-
-
- /**
- * Adds a 'changes since' variable to a URL
- * @param url
- * @return
- */
- public static String addChangesSince(String url) {
- return ensureTrailingSlash(url) + "_changes?since={seq}";
- }
-
- /**
- * Ensures that a URL ends with a slash.
- * @param url the URL to modify
- * @return the modified URL
- */
- public static String ensureTrailingSlash(String url) {
- if (!url.endsWith("/")) {
- url += "/";
- }
- return url;
+ return null;
+ }
+
+ /**
+ * Adds an id variable to a URL
+ *
+ * @param url the URL to modify
+ * @return the modified URL
+ */
+ public static String addId(String url) {
+ return ensureTrailingSlash(url) + "{id}";
+ }
+
+
+ /**
+ * Adds a 'changes since' variable to a URL
+ *
+ * @param url
+ * @return
+ */
+ public static String addChangesSince(String url) {
+ return ensureTrailingSlash(url) + "_changes?since={seq}";
+ }
+
+ /**
+ * Ensures that a URL ends with a slash.
+ *
+ * @param url the URL to modify
+ * @return the modified URL
+ */
+ public static String ensureTrailingSlash(String url) {
+ if (!url.endsWith("/")) {
+ url += "/";
}
+ return url;
+ }
}
diff --git a/spring-data-couchdb/src/main/resources/org/springframework/data/document/couchdb/config/spring-couch-1.0.xsd b/spring-data-couchdb/src/main/resources/org/springframework/data/document/couchdb/config/spring-couch-1.0.xsd
index d4e21345b..ccf257511 100644
--- a/spring-data-couchdb/src/main/resources/org/springframework/data/document/couchdb/config/spring-couch-1.0.xsd
+++ b/spring-data-couchdb/src/main/resources/org/springframework/data/document/couchdb/config/spring-couch-1.0.xsd
@@ -1,33 +1,33 @@
+ xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+ xmlns:tool="http://www.springframework.org/schema/tool"
+ xmlns:context="http://www.springframework.org/schema/context"
+ xmlns:repository="http://www.springframework.org/schema/data/repository"
+ targetNamespace="http://www.springframework.org/schema/data/couch"
+ elementFormDefault="qualified" attributeFormDefault="unqualified">
-
-
-
+
+
+
-
-
-
+
+
-
-
-
-
-
+
+
+
+
-
-
-
-
-
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/DummyDocument.java b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/DummyDocument.java
index d43677696..43368f470 100644
--- a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/DummyDocument.java
+++ b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/DummyDocument.java
@@ -16,62 +16,62 @@
package org.springframework.data.document.couchdb;
-import org.codehaus.jackson.annotate.JsonIgnoreProperties;
-
import java.util.Date;
+import org.codehaus.jackson.annotate.JsonIgnoreProperties;
+
/**
* @author Tareq Abedrabbo (tareq.abedrabbo@opencredo.com)
* @since 13/01/2011
*/
-@JsonIgnoreProperties(ignoreUnknown=true)
+@JsonIgnoreProperties(ignoreUnknown = true)
public class DummyDocument {
- private String message;
+ private String message;
- private String timestamp = new Date().toString();
+ private String timestamp = new Date().toString();
- public DummyDocument() {
- }
+ public DummyDocument() {
+ }
- public DummyDocument(String message) {
- this.message = message;
- }
+ public DummyDocument(String message) {
+ this.message = message;
+ }
- public String getMessage() {
- return message;
- }
+ public String getMessage() {
+ return message;
+ }
- public void setMessage(String message) {
- this.message = message;
- }
+ public void setMessage(String message) {
+ this.message = message;
+ }
- public String getTimestamp() {
- return timestamp;
- }
+ public String getTimestamp() {
+ return timestamp;
+ }
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
- DummyDocument document = (DummyDocument) o;
+ DummyDocument document = (DummyDocument) o;
- if (message != null ? !message.equals(document.message) : document.message != null) return false;
+ if (message != null ? !message.equals(document.message) : document.message != null) return false;
- return true;
- }
+ return true;
+ }
- @Override
- public int hashCode() {
- return message != null ? message.hashCode() : 0;
- }
+ @Override
+ public int hashCode() {
+ return message != null ? message.hashCode() : 0;
+ }
- @Override
- public String toString() {
- return "DummyDocument{" +
- "message='" + message + '\'' +
- ", timestamp=" + timestamp +
- '}';
- }
+ @Override
+ public String toString() {
+ return "DummyDocument{" +
+ "message='" + message + '\'' +
+ ", timestamp=" + timestamp +
+ '}';
+ }
}
diff --git a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/IsBodyEqual.java b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/IsBodyEqual.java
index 8f690bcc2..bb43591ab 100644
--- a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/IsBodyEqual.java
+++ b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/IsBodyEqual.java
@@ -24,28 +24,29 @@ import org.springframework.http.HttpEntity;
/**
* Matches the content of the body of an HttpEntity.
+ *
* @author Tareq Abedrabbo
* @since 31/01/2011
*/
public class IsBodyEqual extends TypeSafeMatcher {
- private Object object;
+ private Object object;
- public IsBodyEqual(Object object) {
- this.object = object;
- }
+ public IsBodyEqual(Object object) {
+ this.object = object;
+ }
- @Override
- public boolean matchesSafely(HttpEntity httpEntity) {
- return httpEntity.getBody().equals(object);
- }
+ @Override
+ public boolean matchesSafely(HttpEntity httpEntity) {
+ return httpEntity.getBody().equals(object);
+ }
- public void describeTo(Description description) {
- description.appendText("body equals ").appendValue(object);
- }
+ public void describeTo(Description description) {
+ description.appendText("body equals ").appendValue(object);
+ }
- @Factory
- public static Matcher bodyEqual(Object object) {
- return new IsBodyEqual(object);
- }
+ @Factory
+ public static Matcher bodyEqual(Object object) {
+ return new IsBodyEqual(object);
+ }
}
diff --git a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/admin/CouchAdminIntegrationTests.java b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/admin/CouchAdminIntegrationTests.java
index 93c13de54..31d603083 100644
--- a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/admin/CouchAdminIntegrationTests.java
+++ b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/admin/CouchAdminIntegrationTests.java
@@ -18,22 +18,21 @@ package org.springframework.data.document.couchdb.admin;
import java.util.List;
import junit.framework.Assert;
-
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.data.document.couchdb.core.CouchConstants;
public class CouchAdminIntegrationTests {
- @Test
- @Ignore("until CI has couch server running")
- public void dbLifecycle() {
-
- CouchAdmin admin = new CouchAdmin(CouchConstants.COUCHDB_URL);
- admin.deleteDatabase("foo");
- List dbs = admin.listDatabases();
- admin.createDatabase("foo");
- List newDbs = admin.listDatabases();
- Assert.assertEquals(dbs.size()+1, newDbs.size());
- }
+ @Test
+ @Ignore("until CI has couch server running")
+ public void dbLifecycle() {
+
+ CouchAdmin admin = new CouchAdmin(CouchConstants.COUCHDB_URL);
+ admin.deleteDatabase("foo");
+ List dbs = admin.listDatabases();
+ admin.createDatabase("foo");
+ List newDbs = admin.listDatabases();
+ Assert.assertEquals(dbs.size() + 1, newDbs.size());
+ }
}
diff --git a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/AbstractCouchTemplateIntegrationTests.java b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/AbstractCouchTemplateIntegrationTests.java
index 98fa48750..7d114e307 100644
--- a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/AbstractCouchTemplateIntegrationTests.java
+++ b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/AbstractCouchTemplateIntegrationTests.java
@@ -16,28 +16,22 @@
package org.springframework.data.document.couchdb.core;
-import org.apache.commons.logging.Log;
-import org.apache.commons.logging.LogFactory;
-import org.junit.Before;
-import org.junit.BeforeClass;
-import org.junit.runner.RunWith;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
-import org.springframework.http.client.ClientHttpResponse;
-import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
-import org.springframework.web.client.DefaultResponseErrorHandler;
-import org.springframework.web.client.RestClientException;
-import org.springframework.web.client.RestTemplate;
+import static org.junit.Assume.assumeNoException;
+import static org.junit.Assume.assumeTrue;
+import static org.springframework.http.HttpStatus.OK;
import java.io.IOException;
import java.util.UUID;
-import static org.junit.Assume.assumeNoException;
-import static org.junit.Assume.assumeTrue;
-import static org.springframework.http.HttpStatus.OK;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.springframework.http.*;
+import org.springframework.http.client.ClientHttpResponse;
+import org.springframework.web.client.DefaultResponseErrorHandler;
+import org.springframework.web.client.RestClientException;
+import org.springframework.web.client.RestTemplate;
/**
* Base class for CouchDB integration tests. Checks whether CouchDB is available before running each test,
@@ -50,75 +44,75 @@ import static org.springframework.http.HttpStatus.OK;
public abstract class AbstractCouchTemplateIntegrationTests {
- protected static final Log log = LogFactory.getLog(AbstractCouchTemplateIntegrationTests.class);
+ protected static final Log log = LogFactory.getLog(AbstractCouchTemplateIntegrationTests.class);
- protected static final RestTemplate restTemplate = new RestTemplate();
+ protected static final RestTemplate restTemplate = new RestTemplate();
- /**
- * This methods ensures that the database is running. Otherwise, the test is ignored.
- */
- @BeforeClass
- public static void assumeDatabaseIsUpAndRunning() {
- try {
- ResponseEntity responseEntity = restTemplate.getForEntity(CouchConstants.COUCHDB_URL, String.class);
- assumeTrue(responseEntity.getStatusCode().equals(OK));
- log.debug("CouchDB is running on " + CouchConstants.COUCHDB_URL +
- " with status " + responseEntity.getStatusCode());
- } catch (RestClientException e) {
- log.debug("CouchDB is not running on " + CouchConstants.COUCHDB_URL);
- assumeNoException(e);
- }
+ /**
+ * This methods ensures that the database is running. Otherwise, the test is ignored.
+ */
+ @BeforeClass
+ public static void assumeDatabaseIsUpAndRunning() {
+ try {
+ ResponseEntity responseEntity = restTemplate.getForEntity(CouchConstants.COUCHDB_URL, String.class);
+ assumeTrue(responseEntity.getStatusCode().equals(OK));
+ log.debug("CouchDB is running on " + CouchConstants.COUCHDB_URL +
+ " with status " + responseEntity.getStatusCode());
+ } catch (RestClientException e) {
+ log.debug("CouchDB is not running on " + CouchConstants.COUCHDB_URL);
+ assumeNoException(e);
}
+ }
- @Before
- public void setUpTestDatabase() throws Exception {
- RestTemplate template = new RestTemplate();
- template.setErrorHandler(new DefaultResponseErrorHandler(){
- @Override
- public void handleError(ClientHttpResponse response) throws IOException {
- // do nothing, error status will be handled in the switch statement
- }
- });
- ResponseEntity response = template.getForEntity(CouchConstants.TEST_DATABASE_URL, String.class);
- HttpStatus statusCode = response.getStatusCode();
- switch (statusCode) {
- case NOT_FOUND:
- createNewTestDatabase();
- break;
- case OK:
- deleteExisitingTestDatabase();
- createNewTestDatabase();
- break;
- default:
- throw new IllegalStateException("Unsupported http status [" + statusCode + "]");
- }
+ @Before
+ public void setUpTestDatabase() throws Exception {
+ RestTemplate template = new RestTemplate();
+ template.setErrorHandler(new DefaultResponseErrorHandler() {
+ @Override
+ public void handleError(ClientHttpResponse response) throws IOException {
+ // do nothing, error status will be handled in the switch statement
+ }
+ });
+ ResponseEntity response = template.getForEntity(CouchConstants.TEST_DATABASE_URL, String.class);
+ HttpStatus statusCode = response.getStatusCode();
+ switch (statusCode) {
+ case NOT_FOUND:
+ createNewTestDatabase();
+ break;
+ case OK:
+ deleteExisitingTestDatabase();
+ createNewTestDatabase();
+ break;
+ default:
+ throw new IllegalStateException("Unsupported http status [" + statusCode + "]");
}
+ }
- private void deleteExisitingTestDatabase() {
- restTemplate.delete(CouchConstants.TEST_DATABASE_URL);
- }
+ private void deleteExisitingTestDatabase() {
+ restTemplate.delete(CouchConstants.TEST_DATABASE_URL);
+ }
- private void createNewTestDatabase() {
- restTemplate.put(CouchConstants.TEST_DATABASE_URL, null);
- }
+ private void createNewTestDatabase() {
+ restTemplate.put(CouchConstants.TEST_DATABASE_URL, null);
+ }
- /**
- * Reads a CouchDB document and converts it to the expected type.
- */
- protected T getDocument(String id, Class expectedType) {
- String url = CouchConstants.TEST_DATABASE_URL + "{id}";
- return restTemplate.getForObject(url, expectedType, id);
- }
+ /**
+ * Reads a CouchDB document and converts it to the expected type.
+ */
+ protected T getDocument(String id, Class expectedType) {
+ String url = CouchConstants.TEST_DATABASE_URL + "{id}";
+ return restTemplate.getForObject(url, expectedType, id);
+ }
- /**
- * Writes a CouchDB document
- */
- protected String putDocument(Object document) {
- HttpHeaders headers = new HttpHeaders();
- headers.setContentType(MediaType.APPLICATION_JSON);
- HttpEntity request = new HttpEntity(document, headers);
- String id = UUID.randomUUID().toString();
- restTemplate.put(CouchConstants.TEST_DATABASE_URL + "{id}", request, id);
- return id;
- }
+ /**
+ * Writes a CouchDB document
+ */
+ protected String putDocument(Object document) {
+ HttpHeaders headers = new HttpHeaders();
+ headers.setContentType(MediaType.APPLICATION_JSON);
+ HttpEntity request = new HttpEntity(document, headers);
+ String id = UUID.randomUUID().toString();
+ restTemplate.put(CouchConstants.TEST_DATABASE_URL + "{id}", request, id);
+ return id;
+ }
}
diff --git a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchConstants.java b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchConstants.java
index 14412b947..1ae934684 100644
--- a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchConstants.java
+++ b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchConstants.java
@@ -17,11 +17,11 @@ package org.springframework.data.document.couchdb.core;
public abstract class CouchConstants {
- public static final String COUCHDB_URL = "http://127.0.0.1:5984/";
- public static final String TEST_DATABASE_URL = COUCHDB_URL + "si_couchdb_test/";
+ public static final String COUCHDB_URL = "http://127.0.0.1:5984/";
+ public static final String TEST_DATABASE_URL = COUCHDB_URL + "si_couchdb_test/";
- public CouchConstants() {
- // TODO Auto-generated constructor stub
- }
+ public CouchConstants() {
+ // TODO Auto-generated constructor stub
+ }
}
diff --git a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateIntegrationTests.java b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateIntegrationTests.java
index 893f32471..31302ea25 100644
--- a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateIntegrationTests.java
+++ b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateIntegrationTests.java
@@ -19,22 +19,21 @@ package org.springframework.data.document.couchdb.core;
import java.util.UUID;
import junit.framework.Assert;
-
import org.junit.Ignore;
import org.junit.Test;
import org.springframework.data.document.couchdb.DummyDocument;
public class CouchTemplateIntegrationTests extends AbstractCouchTemplateIntegrationTests {
-
- @Test
- @Ignore("until CI has couch server running")
- public void saveAndFindTest() {
- CouchTemplate template = new CouchTemplate(CouchConstants.TEST_DATABASE_URL);
- DummyDocument document = new DummyDocument("hello");
- String id = UUID.randomUUID().toString();
- template.save(id, document);
- DummyDocument foundDocument = template.findOne(id, DummyDocument.class);
- Assert.assertEquals(document.getMessage(), foundDocument.getMessage());
- }
+
+ @Test
+ @Ignore("until CI has couch server running")
+ public void saveAndFindTest() {
+ CouchTemplate template = new CouchTemplate(CouchConstants.TEST_DATABASE_URL);
+ DummyDocument document = new DummyDocument("hello");
+ String id = UUID.randomUUID().toString();
+ template.save(id, document);
+ DummyDocument foundDocument = template.findOne(id, DummyDocument.class);
+ Assert.assertEquals(document.getMessage(), foundDocument.getMessage());
+ }
}
diff --git a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateTests.java b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateTests.java
index b0163aa43..da4afe30c 100644
--- a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateTests.java
+++ b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/core/CouchTemplateTests.java
@@ -20,13 +20,11 @@ import org.junit.Test;
/**
* Unit tests for CouchTemplate with mocks
- *
- *
*/
public class CouchTemplateTests {
- @Test
- public void foo() {
-
- }
+ @Test
+ public void foo() {
+
+ }
}
diff --git a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/monitor/JmxServer.java b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/monitor/JmxServer.java
index 07cf47222..e846ea8d2 100644
--- a/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/monitor/JmxServer.java
+++ b/spring-data-couchdb/src/test/java/org/springframework/data/document/couchdb/monitor/JmxServer.java
@@ -20,17 +20,17 @@ import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Server application to test JMX functionality.
- *
+ *
* @author Mark Pollack
*/
public class JmxServer {
- public static void main(String[] args) {
- new JmxServer().run();
- }
+ public static void main(String[] args) {
+ new JmxServer().run();
+ }
- public void run() {
- new ClassPathXmlApplicationContext(new String[] {"server-jmx.xml"} );
- }
+ public void run() {
+ new ClassPathXmlApplicationContext(new String[]{"server-jmx.xml"});
+ }
}
diff --git a/spring-data-couchdb/src/test/resources/server-jmx.xml b/spring-data-couchdb/src/test/resources/server-jmx.xml
index 30c1933fe..d5c9028d5 100644
--- a/spring-data-couchdb/src/test/resources/server-jmx.xml
+++ b/spring-data-couchdb/src/test/resources/server-jmx.xml
@@ -1,25 +1,24 @@
-
-
+
-
+
+
+
+
+
+
-
-
-
-
-
\ No newline at end of file
diff --git a/spring-data-document-core/.classpath b/spring-data-document-core/.classpath
deleted file mode 100644
index 6019b8317..000000000
--- a/spring-data-document-core/.classpath
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/spring-data-document-core/.project b/spring-data-document-core/.project
deleted file mode 100644
index 903baba7a..000000000
--- a/spring-data-document-core/.project
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
- spring-data-document-core
-
-
-
-
-
- org.eclipse.wst.common.project.facet.core.builder
-
-
-
-
- org.eclipse.jdt.core.javabuilder
-
-
-
-
- org.maven.ide.eclipse.maven2Builder
-
-
-
-
- org.eclipse.wst.validation.validationbuilder
-
-
-
-
-
- org.eclipse.jem.workbench.JavaEMFNature
- org.eclipse.wst.common.modulecore.ModuleCoreNature
- org.eclipse.jdt.core.javanature
- org.maven.ide.eclipse.maven2Nature
- org.eclipse.wst.common.project.facet.core.nature
-
-
diff --git a/spring-data-document-core/.settings/org.eclipse.jdt.core.prefs b/spring-data-document-core/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644
index aa62685f0..000000000
--- a/spring-data-document-core/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,9 +0,0 @@
-#Wed Mar 09 13:51:17 EST 2011
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
-org.eclipse.jdt.core.compiler.compliance=1.6
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
-org.eclipse.jdt.core.compiler.source=1.6
diff --git a/spring-data-document-core/.settings/org.eclipse.wst.common.component b/spring-data-document-core/.settings/org.eclipse.wst.common.component
deleted file mode 100644
index 8eb900276..000000000
--- a/spring-data-document-core/.settings/org.eclipse.wst.common.component
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/spring-data-document-core/.settings/org.eclipse.wst.common.project.facet.core.xml b/spring-data-document-core/.settings/org.eclipse.wst.common.project.facet.core.xml
deleted file mode 100644
index 5c9bd7532..000000000
--- a/spring-data-document-core/.settings/org.eclipse.wst.common.project.facet.core.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/spring-data-document-core/.settings/org.maven.ide.eclipse.prefs b/spring-data-document-core/.settings/org.maven.ide.eclipse.prefs
deleted file mode 100644
index 5545b44b1..000000000
--- a/spring-data-document-core/.settings/org.maven.ide.eclipse.prefs
+++ /dev/null
@@ -1,9 +0,0 @@
-#Wed Oct 06 14:49:46 EDT 2010
-activeProfiles=
-eclipse.preferences.version=1
-fullBuildGoals=process-test-resources
-includeModules=false
-resolveWorkspaceProjects=true
-resourceFilterGoals=process-resources resources\:testResources
-skipCompilerPlugin=true
-version=1
diff --git a/spring-data-document-core/src/main/java/org/springframework/data/document/AbstractDocumentStoreTemplate.java b/spring-data-document-core/src/main/java/org/springframework/data/document/AbstractDocumentStoreTemplate.java
index 97439420b..dfea53c99 100644
--- a/spring-data-document-core/src/main/java/org/springframework/data/document/AbstractDocumentStoreTemplate.java
+++ b/spring-data-document-core/src/main/java/org/springframework/data/document/AbstractDocumentStoreTemplate.java
@@ -18,16 +18,15 @@ package org.springframework.data.document;
public abstract class AbstractDocumentStoreTemplate {
-
- public abstract C getConnection();
- public T execute(DocumentStoreConnectionCallback action) {
- try {
- return action.doInConnection(getConnection());
- }
- catch (Exception e) {
- throw new UncategorizedDocumentStoreException("Failure executing using datastore connection", e);
- }
- }
-
+ public abstract C getConnection();
+
+ public T execute(DocumentStoreConnectionCallback action) {
+ try {
+ return action.doInConnection(getConnection());
+ } catch (Exception e) {
+ throw new UncategorizedDocumentStoreException("Failure executing using datastore connection", e);
+ }
+ }
+
}
diff --git a/spring-data-document-core/src/main/java/org/springframework/data/document/DocumentMapper.java b/spring-data-document-core/src/main/java/org/springframework/data/document/DocumentMapper.java
index 48cf9919b..dc50394a5 100644
--- a/spring-data-document-core/src/main/java/org/springframework/data/document/DocumentMapper.java
+++ b/spring-data-document-core/src/main/java/org/springframework/data/document/DocumentMapper.java
@@ -23,7 +23,7 @@ package org.springframework.data.document;
* @since 1.0
*/
public interface DocumentMapper {
-
- T mapDocument(D document);
+
+ T mapDocument(D document);
}
diff --git a/spring-data-document-core/src/main/java/org/springframework/data/document/DocumentStoreConnectionCallback.java b/spring-data-document-core/src/main/java/org/springframework/data/document/DocumentStoreConnectionCallback.java
index 5711adaa6..d6cb0e232 100644
--- a/spring-data-document-core/src/main/java/org/springframework/data/document/DocumentStoreConnectionCallback.java
+++ b/spring-data-document-core/src/main/java/org/springframework/data/document/DocumentStoreConnectionCallback.java
@@ -18,5 +18,5 @@ package org.springframework.data.document;
public interface DocumentStoreConnectionCallback {
- T doInConnection(C con) throws Exception;
+ T doInConnection(C con) throws Exception;
}
diff --git a/spring-data-document-core/src/main/java/org/springframework/data/document/InvalidDocumentStoreApiUsageException.java b/spring-data-document-core/src/main/java/org/springframework/data/document/InvalidDocumentStoreApiUsageException.java
index 205d93ef4..19eacb1c7 100644
--- a/spring-data-document-core/src/main/java/org/springframework/data/document/InvalidDocumentStoreApiUsageException.java
+++ b/spring-data-document-core/src/main/java/org/springframework/data/document/InvalidDocumentStoreApiUsageException.java
@@ -20,12 +20,12 @@ import org.springframework.dao.InvalidDataAccessApiUsageException;
public class InvalidDocumentStoreApiUsageException extends InvalidDataAccessApiUsageException {
- public InvalidDocumentStoreApiUsageException(String msg) {
- super(msg);
- }
+ public InvalidDocumentStoreApiUsageException(String msg) {
+ super(msg);
+ }
- public InvalidDocumentStoreApiUsageException(String msg, Throwable cause) {
- super(msg, cause);
- }
+ public InvalidDocumentStoreApiUsageException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
}
diff --git a/spring-data-document-core/src/main/java/org/springframework/data/document/UncategorizedDocumentStoreException.java b/spring-data-document-core/src/main/java/org/springframework/data/document/UncategorizedDocumentStoreException.java
index 44ee8b879..f7fe00595 100644
--- a/spring-data-document-core/src/main/java/org/springframework/data/document/UncategorizedDocumentStoreException.java
+++ b/spring-data-document-core/src/main/java/org/springframework/data/document/UncategorizedDocumentStoreException.java
@@ -20,8 +20,8 @@ import org.springframework.dao.UncategorizedDataAccessException;
public class UncategorizedDocumentStoreException extends UncategorizedDataAccessException {
- public UncategorizedDocumentStoreException(String msg, Throwable cause) {
- super(msg, cause);
- }
+ public UncategorizedDocumentStoreException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
}
diff --git a/spring-data-document-core/src/main/java/org/springframework/data/document/analytics/ControllerCounter.java b/spring-data-document-core/src/main/java/org/springframework/data/document/analytics/ControllerCounter.java
index 459feeaed..77c2696b8 100644
--- a/spring-data-document-core/src/main/java/org/springframework/data/document/analytics/ControllerCounter.java
+++ b/spring-data-document-core/src/main/java/org/springframework/data/document/analytics/ControllerCounter.java
@@ -1,45 +1,43 @@
package org.springframework.data.document.analytics;
-import java.util.Date;
import java.util.Map;
public class ControllerCounter {
-
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
+ public String getName() {
+ return name;
+ }
- public double getCount() {
- return count;
- }
+ public void setName(String name) {
+ this.name = name;
+ }
- public void setCount(double count) {
- this.count = count;
- }
+ public double getCount() {
+ return count;
+ }
- public Map getMethods() {
- return methods;
- }
-
-
+ public void setCount(double count) {
+ this.count = count;
+ }
- public void setMethods(Map methods) {
- this.methods = methods;
- }
+ public Map getMethods() {
+ return methods;
+ }
- private String name;
-
- private double count;
-
- private Map methods;
-
- @Override
- public String toString() {
- return "ControllerCounter [name=" + name + ", count=" + count
- + ", methods=" + methods + "]";
- }
+
+ public void setMethods(Map methods) {
+ this.methods = methods;
+ }
+
+ private String name;
+
+ private double count;
+
+ private Map methods;
+
+ @Override
+ public String toString() {
+ return "ControllerCounter [name=" + name + ", count=" + count
+ + ", methods=" + methods + "]";
+ }
}
diff --git a/spring-data-document-core/src/main/java/org/springframework/data/document/analytics/MvcEvent.java b/spring-data-document-core/src/main/java/org/springframework/data/document/analytics/MvcEvent.java
index a63fe2ee6..fea3bc4e5 100644
--- a/spring-data-document-core/src/main/java/org/springframework/data/document/analytics/MvcEvent.java
+++ b/spring-data-document-core/src/main/java/org/springframework/data/document/analytics/MvcEvent.java
@@ -4,81 +4,80 @@ import java.util.Date;
public class MvcEvent {
- private String controller;
-
- private String action;
-
- private Parameters parameters;
-
- private Date date;
-
- private String requestUri;
-
- private String requestAddress;
-
- private String remoteUser;
-
- private String view;
+ private String controller;
- public String getController() {
- return controller;
- }
+ private String action;
- public void setController(String controller) {
- this.controller = controller;
- }
+ private Parameters parameters;
- public String getAction() {
- return action;
- }
+ private Date date;
- public void setAction(String action) {
- this.action = action;
- }
+ private String requestUri;
- public Parameters getParameters() {
- return parameters;
- }
+ private String requestAddress;
- public void setParameters(Parameters parameters) {
- this.parameters = parameters;
- }
+ private String remoteUser;
- public Date getDate() {
- return date;
- }
+ private String view;
- public void setDate(Date date) {
- this.date = date;
- }
+ public String getController() {
+ return controller;
+ }
- public String getRequestUri() {
- return requestUri;
- }
+ public void setController(String controller) {
+ this.controller = controller;
+ }
- public void setRequestUri(String requestUri) {
- this.requestUri = requestUri;
- }
+ public String getAction() {
+ return action;
+ }
- public String getRequestAddress() {
- return requestAddress;
- }
+ public void setAction(String action) {
+ this.action = action;
+ }
- public void setRequestAddress(String requestAddress) {
- this.requestAddress = requestAddress;
- }
+ public Parameters getParameters() {
+ return parameters;
+ }
+
+ public void setParameters(Parameters parameters) {
+ this.parameters = parameters;
+ }
+
+ public Date getDate() {
+ return date;
+ }
+
+ public void setDate(Date date) {
+ this.date = date;
+ }
+
+ public String getRequestUri() {
+ return requestUri;
+ }
+
+ public void setRequestUri(String requestUri) {
+ this.requestUri = requestUri;
+ }
+
+ public String getRequestAddress() {
+ return requestAddress;
+ }
+
+ public void setRequestAddress(String requestAddress) {
+ this.requestAddress = requestAddress;
+ }
+
+ public String getRemoteUser() {
+ return remoteUser;
+ }
+
+ public void setRemoteUser(String remoteUser) {
+ this.remoteUser = remoteUser;
+ }
+
+ //TODO
+ //Map sessionAttributes
- public String getRemoteUser() {
- return remoteUser;
- }
- public void setRemoteUser(String remoteUser) {
- this.remoteUser = remoteUser;
- }
-
- //TODO
- //Map sessionAttributes
-
-
-
}
diff --git a/spring-data-document-core/src/main/java/org/springframework/data/document/analytics/Parameters.java b/spring-data-document-core/src/main/java/org/springframework/data/document/analytics/Parameters.java
index a8c9eaef0..0fe1a4f71 100644
--- a/spring-data-document-core/src/main/java/org/springframework/data/document/analytics/Parameters.java
+++ b/spring-data-document-core/src/main/java/org/springframework/data/document/analytics/Parameters.java
@@ -2,34 +2,34 @@ package org.springframework.data.document.analytics;
public class Parameters {
- private String p1;
-
- private String p2;
-
- private String p3;
+ private String p1;
- public String getP1() {
- return p1;
- }
+ private String p2;
- public void setP1(String p1) {
- this.p1 = p1;
- }
+ private String p3;
- public String getP2() {
- return p2;
- }
+ public String getP1() {
+ return p1;
+ }
- public void setP2(String p2) {
- this.p2 = p2;
- }
+ public void setP1(String p1) {
+ this.p1 = p1;
+ }
- public String getP3() {
- return p3;
- }
+ public String getP2() {
+ return p2;
+ }
+
+ public void setP2(String p2) {
+ this.p2 = p2;
+ }
+
+ public String getP3() {
+ return p3;
+ }
+
+ public void setP3(String p3) {
+ this.p3 = p3;
+ }
- public void setP3(String p3) {
- this.p3 = p3;
- }
-
}
diff --git a/spring-data-document-core/src/main/java/org/springframework/data/document/web/bind/annotation/support/HandlerMethodInvoker.java b/spring-data-document-core/src/main/java/org/springframework/data/document/web/bind/annotation/support/HandlerMethodInvoker.java
index a5d7c4416..0e6e4119a 100644
--- a/spring-data-document-core/src/main/java/org/springframework/data/document/web/bind/annotation/support/HandlerMethodInvoker.java
+++ b/spring-data-document-core/src/main/java/org/springframework/data/document/web/bind/annotation/support/HandlerMethodInvoker.java
@@ -18,67 +18,29 @@
package org.springframework.data.document.web.bind.annotation.support;
import java.lang.annotation.Annotation;
-import java.lang.reflect.Array;
-import java.lang.reflect.GenericArrayType;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.lang.reflect.*;
+import java.util.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.core.BridgeMethodResolver;
-import org.springframework.core.Conventions;
-import org.springframework.core.GenericTypeResolver;
-import org.springframework.core.MethodParameter;
-import org.springframework.core.ParameterNameDiscoverer;
+import org.springframework.core.*;
import org.springframework.core.annotation.AnnotationUtils;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpInputMessage;
-import org.springframework.http.HttpOutputMessage;
-import org.springframework.http.MediaType;
+import org.springframework.http.*;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.Model;
-import org.springframework.util.Assert;
-import org.springframework.util.ClassUtils;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
-import org.springframework.util.ReflectionUtils;
+import org.springframework.util.*;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.WebDataBinder;
-import org.springframework.web.bind.annotation.CookieValue;
-import org.springframework.web.bind.annotation.InitBinder;
-import org.springframework.web.bind.annotation.ModelAttribute;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestHeader;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ValueConstants;
+import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.support.HandlerMethodInvocationException;
import org.springframework.web.bind.annotation.support.HandlerMethodResolver;
-import org.springframework.web.bind.support.DefaultSessionAttributeStore;
-import org.springframework.web.bind.support.SessionAttributeStore;
-import org.springframework.web.bind.support.SessionStatus;
-import org.springframework.web.bind.support.SimpleSessionStatus;
-import org.springframework.web.bind.support.WebArgumentResolver;
-import org.springframework.web.bind.support.WebBindingInitializer;
-import org.springframework.web.bind.support.WebRequestDataBinder;
+import org.springframework.web.bind.support.*;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.multipart.MultipartFile;
@@ -87,812 +49,773 @@ import org.springframework.web.multipart.MultipartRequest;
/**
* Support class for invoking an annotated handler method. Operates on the introspection results of a {@link
* HandlerMethodResolver} for a specific handler type.
- *
+ *
* Used by {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter} and {@link
* org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter}.
*
* @author Juergen Hoeller
* @author Arjen Poutsma
- * @since 2.5.2
* @see #invokeHandlerMethod
+ * @since 2.5.2
*/
public class HandlerMethodInvoker {
- private static final String MODEL_KEY_PREFIX_STALE = SessionAttributeStore.class.getName() + ".STALE.";
-
- /** We'll create a lot of these objects, so we don't want a new logger every time. */
- private static final Log logger = LogFactory.getLog(HandlerMethodInvoker.class);
-
- private final HandlerMethodResolver methodResolver;
-
- private final WebBindingInitializer bindingInitializer;
-
- private final SessionAttributeStore sessionAttributeStore;
-
- private final ParameterNameDiscoverer parameterNameDiscoverer;
-
- private final WebArgumentResolver[] customArgumentResolvers;
-
- private final HttpMessageConverter[] messageConverters;
-
- private final SimpleSessionStatus sessionStatus = new SimpleSessionStatus();
-
-
- public HandlerMethodInvoker(HandlerMethodResolver methodResolver) {
- this(methodResolver, null);
- }
-
- public HandlerMethodInvoker(HandlerMethodResolver methodResolver, WebBindingInitializer bindingInitializer) {
- this(methodResolver, bindingInitializer, new DefaultSessionAttributeStore(), null, null, null);
- }
-
- public HandlerMethodInvoker(HandlerMethodResolver methodResolver, WebBindingInitializer bindingInitializer,
- SessionAttributeStore sessionAttributeStore, ParameterNameDiscoverer parameterNameDiscoverer,
- WebArgumentResolver[] customArgumentResolvers, HttpMessageConverter[] messageConverters) {
-
- this.methodResolver = methodResolver;
- this.bindingInitializer = bindingInitializer;
- this.sessionAttributeStore = sessionAttributeStore;
- this.parameterNameDiscoverer = parameterNameDiscoverer;
- this.customArgumentResolvers = customArgumentResolvers;
- this.messageConverters = messageConverters;
- }
-
-
- public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
- NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
-
- Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
- try {
- boolean debug = logger.isDebugEnabled();
- for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
- Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
- if (attrValue != null) {
- implicitModel.addAttribute(attrName, attrValue);
- }
- }
- for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
- Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
- Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
- if (debug) {
- logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
- }
- String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value();
- if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
- continue;
- }
- ReflectionUtils.makeAccessible(attributeMethodToInvoke);
- Object attrValue = attributeMethodToInvoke.invoke(handler, args);
- if ("".equals(attrName)) {
- Class resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
- attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
- }
- if (!implicitModel.containsAttribute(attrName)) {
- implicitModel.addAttribute(attrName, attrValue);
- }
- }
- Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
- if (debug) {
- logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
- }
- ReflectionUtils.makeAccessible(handlerMethodToInvoke);
- return handlerMethodToInvoke.invoke(handler, args);
- }
- catch (IllegalStateException ex) {
- // Internal assertion failed (e.g. invalid signature):
- // throw exception with full handler method context...
- throw new HandlerMethodInvocationException(handlerMethodToInvoke, ex);
- }
- catch (InvocationTargetException ex) {
- // User-defined @ModelAttribute/@InitBinder/@RequestMapping method threw an exception...
- ReflectionUtils.rethrowException(ex.getTargetException());
- return null;
- }
- }
-
- public final void updateModelAttributes(Object handler, Map mavModel,
- ExtendedModelMap implicitModel, NativeWebRequest webRequest) throws Exception {
-
- if (this.methodResolver.hasSessionAttributes() && this.sessionStatus.isComplete()) {
- for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
- this.sessionAttributeStore.cleanupAttribute(webRequest, attrName);
- }
- }
-
- // Expose model attributes as session attributes, if required.
- // Expose BindingResults for all attributes, making custom editors available.
- Map model = (mavModel != null ? mavModel : implicitModel);
- if (model != null) {
- try {
- String[] originalAttrNames = model.keySet().toArray(new String[model.size()]);
- for (String attrName : originalAttrNames) {
- Object attrValue = model.get(attrName);
- boolean isSessionAttr = this.methodResolver.isSessionAttribute(
- attrName, (attrValue != null ? attrValue.getClass() : null));
- if (isSessionAttr) {
- if (this.sessionStatus.isComplete()) {
- implicitModel.put(MODEL_KEY_PREFIX_STALE + attrName, Boolean.TRUE);
- }
- else if (!implicitModel.containsKey(MODEL_KEY_PREFIX_STALE + attrName)) {
- this.sessionAttributeStore.storeAttribute(webRequest, attrName, attrValue);
- }
- }
- if (!attrName.startsWith(BindingResult.MODEL_KEY_PREFIX) &&
- (isSessionAttr || isBindingCandidate(attrValue))) {
- String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attrName;
- if (mavModel != null && !model.containsKey(bindingResultKey)) {
- WebDataBinder binder = createBinder(webRequest, attrValue, attrName);
- initBinder(handler, attrName, binder, webRequest);
- mavModel.put(bindingResultKey, binder.getBindingResult());
- }
- }
- }
- }
- catch (InvocationTargetException ex) {
- // User-defined @InitBinder method threw an exception...
- ReflectionUtils.rethrowException(ex.getTargetException());
- }
- }
- }
-
-
- private Object[] resolveHandlerArguments(Method handlerMethod, Object handler,
- NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
-
- Class[] paramTypes = handlerMethod.getParameterTypes();
- Object[] args = new Object[paramTypes.length];
-
- for (int i = 0; i < args.length; i++) {
- MethodParameter methodParam = new MethodParameter(handlerMethod, i);
- methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
- GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
- String paramName = null;
- String headerName = null;
- boolean requestBodyFound = false;
- String cookieName = null;
- String pathVarName = null;
- String attrName = null;
- boolean required = false;
- String defaultValue = null;
- boolean validate = false;
- int annotationsFound = 0;
- Annotation[] paramAnns = methodParam.getParameterAnnotations();
-
- for (Annotation paramAnn : paramAnns) {
- if (RequestParam.class.isInstance(paramAnn)) {
- RequestParam requestParam = (RequestParam) paramAnn;
- paramName = requestParam.value();
- required = requestParam.required();
- defaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
- annotationsFound++;
- }
- else if (RequestHeader.class.isInstance(paramAnn)) {
- RequestHeader requestHeader = (RequestHeader) paramAnn;
- headerName = requestHeader.value();
- required = requestHeader.required();
- defaultValue = parseDefaultValueAttribute(requestHeader.defaultValue());
- annotationsFound++;
- }
- else if (RequestBody.class.isInstance(paramAnn)) {
- requestBodyFound = true;
- annotationsFound++;
- }
- else if (CookieValue.class.isInstance(paramAnn)) {
- CookieValue cookieValue = (CookieValue) paramAnn;
- cookieName = cookieValue.value();
- required = cookieValue.required();
- defaultValue = parseDefaultValueAttribute(cookieValue.defaultValue());
- annotationsFound++;
- }
- else if (PathVariable.class.isInstance(paramAnn)) {
- PathVariable pathVar = (PathVariable) paramAnn;
- pathVarName = pathVar.value();
- annotationsFound++;
- }
- else if (ModelAttribute.class.isInstance(paramAnn)) {
- ModelAttribute attr = (ModelAttribute) paramAnn;
- attrName = attr.value();
- annotationsFound++;
- }
- else if (Value.class.isInstance(paramAnn)) {
- defaultValue = ((Value) paramAnn).value();
- }
- else if ("Valid".equals(paramAnn.annotationType().getSimpleName())) {
- validate = true;
- }
- }
-
- if (annotationsFound > 1) {
- throw new IllegalStateException("Handler parameter annotations are exclusive choices - " +
- "do not specify more than one such annotation on the same parameter: " + handlerMethod);
- }
-
- if (annotationsFound == 0) {
- Object argValue = resolveCommonArgument(methodParam, webRequest);
- if (argValue != WebArgumentResolver.UNRESOLVED) {
- args[i] = argValue;
- }
- else if (defaultValue != null) {
- args[i] = resolveDefaultValue(defaultValue);
- }
- else {
- Class paramType = methodParam.getParameterType();
- if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
- args[i] = implicitModel;
- }
- else if (SessionStatus.class.isAssignableFrom(paramType)) {
- args[i] = this.sessionStatus;
- }
- else if (HttpEntity.class.isAssignableFrom(paramType)) {
- args[i] = resolveHttpEntityRequest(methodParam, webRequest);
- }
- else if (Errors.class.isAssignableFrom(paramType)) {
- throw new IllegalStateException("Errors/BindingResult argument declared " +
- "without preceding model attribute. Check your handler method signature!");
- }
- else if (BeanUtils.isSimpleProperty(paramType)) {
- paramName = "";
- }
- else {
- attrName = "";
- }
- }
- }
-
- if (paramName != null) {
- args[i] = resolveRequestParam(paramName, required, defaultValue, methodParam, webRequest, handler);
- }
- else if (headerName != null) {
- args[i] = resolveRequestHeader(headerName, required, defaultValue, methodParam, webRequest, handler);
- }
- else if (requestBodyFound) {
- args[i] = resolveRequestBody(methodParam, webRequest, handler);
- }
- else if (cookieName != null) {
- args[i] = resolveCookieValue(cookieName, required, defaultValue, methodParam, webRequest, handler);
- }
- else if (pathVarName != null) {
- args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler);
- }
- else if (attrName != null) {
- WebDataBinder binder =
- resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
- boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
- if (binder.getTarget() != null) {
- doBind(binder, webRequest, validate, !assignBindingResult);
- }
- args[i] = binder.getTarget();
- if (assignBindingResult) {
- args[i + 1] = binder.getBindingResult();
- i++;
- }
- implicitModel.putAll(binder.getBindingResult().getModel());
- }
- }
-
- return args;
- }
-
- protected void initBinder(Object handler, String attrName, WebDataBinder binder, NativeWebRequest webRequest)
- throws Exception {
-
- if (this.bindingInitializer != null) {
- this.bindingInitializer.initBinder(binder, webRequest);
- }
- if (handler != null) {
- Set initBinderMethods = this.methodResolver.getInitBinderMethods();
- if (!initBinderMethods.isEmpty()) {
- boolean debug = logger.isDebugEnabled();
- for (Method initBinderMethod : initBinderMethods) {
- Method methodToInvoke = BridgeMethodResolver.findBridgedMethod(initBinderMethod);
- String[] targetNames = AnnotationUtils.findAnnotation(initBinderMethod, InitBinder.class).value();
- if (targetNames.length == 0 || Arrays.asList(targetNames).contains(attrName)) {
- Object[] initBinderArgs =
- resolveInitBinderArguments(handler, methodToInvoke, binder, webRequest);
- if (debug) {
- logger.debug("Invoking init-binder method: " + methodToInvoke);
- }
- ReflectionUtils.makeAccessible(methodToInvoke);
- Object returnValue = methodToInvoke.invoke(handler, initBinderArgs);
- if (returnValue != null) {
- throw new IllegalStateException(
- "InitBinder methods must not have a return value: " + methodToInvoke);
- }
- }
- }
- }
- }
- }
-
- private Object[] resolveInitBinderArguments(Object handler, Method initBinderMethod,
- WebDataBinder binder, NativeWebRequest webRequest) throws Exception {
-
- Class[] initBinderParams = initBinderMethod.getParameterTypes();
- Object[] initBinderArgs = new Object[initBinderParams.length];
-
- for (int i = 0; i < initBinderArgs.length; i++) {
- MethodParameter methodParam = new MethodParameter(initBinderMethod, i);
- methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
- GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
- String paramName = null;
- boolean paramRequired = false;
- String paramDefaultValue = null;
- String pathVarName = null;
- Annotation[] paramAnns = methodParam.getParameterAnnotations();
-
- for (Annotation paramAnn : paramAnns) {
- if (RequestParam.class.isInstance(paramAnn)) {
- RequestParam requestParam = (RequestParam) paramAnn;
- paramName = requestParam.value();
- paramRequired = requestParam.required();
- paramDefaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
- break;
- }
- else if (ModelAttribute.class.isInstance(paramAnn)) {
- throw new IllegalStateException(
- "@ModelAttribute is not supported on @InitBinder methods: " + initBinderMethod);
- }
- else if (PathVariable.class.isInstance(paramAnn)) {
- PathVariable pathVar = (PathVariable) paramAnn;
- pathVarName = pathVar.value();
- }
- }
-
- if (paramName == null && pathVarName == null) {
- Object argValue = resolveCommonArgument(methodParam, webRequest);
- if (argValue != WebArgumentResolver.UNRESOLVED) {
- initBinderArgs[i] = argValue;
- }
- else {
- Class paramType = initBinderParams[i];
- if (paramType.isInstance(binder)) {
- initBinderArgs[i] = binder;
- }
- else if (BeanUtils.isSimpleProperty(paramType)) {
- paramName = "";
- }
- else {
- throw new IllegalStateException("Unsupported argument [" + paramType.getName() +
- "] for @InitBinder method: " + initBinderMethod);
- }
- }
- }
-
- if (paramName != null) {
- initBinderArgs[i] =
- resolveRequestParam(paramName, paramRequired, paramDefaultValue, methodParam, webRequest, null);
- }
- else if (pathVarName != null) {
- initBinderArgs[i] = resolvePathVariable(pathVarName, methodParam, webRequest, null);
- }
- }
-
- return initBinderArgs;
- }
-
- @SuppressWarnings("unchecked")
- private Object resolveRequestParam(String paramName, boolean required, String defaultValue,
- MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
- throws Exception {
-
- Class> paramType = methodParam.getParameterType();
- if (Map.class.isAssignableFrom(paramType) && paramName.length() == 0) {
- return resolveRequestParamMap((Class extends Map>) paramType, webRequest);
- }
- if (paramName.length() == 0) {
- paramName = getRequiredParameterName(methodParam);
- }
- Object paramValue = null;
- MultipartRequest multipartRequest = webRequest.getNativeRequest(MultipartRequest.class);
- if (multipartRequest != null) {
- List files = multipartRequest.getFiles(paramName);
- if (!files.isEmpty()) {
- if (files.size() == 1 && !paramType.isArray() && !Collection.class.isAssignableFrom(paramType)) {
- paramValue = files.get(0);
- }
- else {
- paramValue = files;
- }
- }
- }
- if (paramValue == null) {
- String[] paramValues = webRequest.getParameterValues(paramName);
- if (paramValues != null) {
- if (paramValues.length == 1 && !paramType.isArray() && !Collection.class.isAssignableFrom(paramType)) {
- paramValue = paramValues[0];
- }
- else {
- paramValue = paramValues;
- }
- }
- }
- if (paramValue == null) {
- if (defaultValue != null) {
- paramValue = resolveDefaultValue(defaultValue);
- }
- else if (required) {
- raiseMissingParameterException(paramName, paramType);
- }
- paramValue = checkValue(paramName, paramValue, paramType);
- }
- WebDataBinder binder = createBinder(webRequest, null, paramName);
- initBinder(handlerForInitBinderCall, paramName, binder, webRequest);
- return binder.convertIfNecessary(paramValue, paramType, methodParam);
- }
-
- private Map resolveRequestParamMap(Class extends Map> mapType, NativeWebRequest webRequest) {
- Map parameterMap = webRequest.getParameterMap();
- if (MultiValueMap.class.isAssignableFrom(mapType)) {
- MultiValueMap result = new LinkedMultiValueMap(parameterMap.size());
- for (Map.Entry entry : parameterMap.entrySet()) {
- for (String value : entry.getValue()) {
- result.add(entry.getKey(), value);
- }
- }
- return result;
- }
- else {
- Map result = new LinkedHashMap(parameterMap.size());
- for (Map.Entry entry : parameterMap.entrySet()) {
- if (entry.getValue().length > 0) {
- result.put(entry.getKey(), entry.getValue()[0]);
- }
- }
- return result;
- }
- }
-
- @SuppressWarnings("unchecked")
- private Object resolveRequestHeader(String headerName, boolean required, String defaultValue,
- MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
- throws Exception {
-
- Class> paramType = methodParam.getParameterType();
- if (Map.class.isAssignableFrom(paramType)) {
- return resolveRequestHeaderMap((Class extends Map>) paramType, webRequest);
- }
- if (headerName.length() == 0) {
- headerName = getRequiredParameterName(methodParam);
- }
- Object headerValue = null;
- String[] headerValues = webRequest.getHeaderValues(headerName);
- if (headerValues != null) {
- headerValue = (headerValues.length == 1 ? headerValues[0] : headerValues);
- }
- if (headerValue == null) {
- if (defaultValue != null) {
- headerValue = resolveDefaultValue(defaultValue);
- }
- else if (required) {
- raiseMissingHeaderException(headerName, paramType);
- }
- headerValue = checkValue(headerName, headerValue, paramType);
- }
- WebDataBinder binder = createBinder(webRequest, null, headerName);
- initBinder(handlerForInitBinderCall, headerName, binder, webRequest);
- return binder.convertIfNecessary(headerValue, paramType, methodParam);
- }
-
- private Map resolveRequestHeaderMap(Class extends Map> mapType, NativeWebRequest webRequest) {
- if (MultiValueMap.class.isAssignableFrom(mapType)) {
- MultiValueMap result;
- if (HttpHeaders.class.isAssignableFrom(mapType)) {
- result = new HttpHeaders();
- }
- else {
- result = new LinkedMultiValueMap();
- }
- for (Iterator iterator = webRequest.getHeaderNames(); iterator.hasNext();) {
- String headerName = iterator.next();
- for (String headerValue : webRequest.getHeaderValues(headerName)) {
- result.add(headerName, headerValue);
- }
- }
- return result;
- }
- else {
- Map result = new LinkedHashMap();
- for (Iterator iterator = webRequest.getHeaderNames(); iterator.hasNext();) {
- String headerName = iterator.next();
- String headerValue = webRequest.getHeader(headerName);
- result.put(headerName, headerValue);
- }
- return result;
- }
- }
-
- /**
- * Resolves the given {@link RequestBody @RequestBody} annotation.
- */
- protected Object resolveRequestBody(MethodParameter methodParam, NativeWebRequest webRequest, Object handler)
- throws Exception {
-
- return readWithMessageConverters(methodParam, createHttpInputMessage(webRequest), methodParam.getParameterType());
- }
-
- private HttpEntity resolveHttpEntityRequest(MethodParameter methodParam, NativeWebRequest webRequest)
- throws Exception {
-
- HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
- Class> paramType = getHttpEntityType(methodParam);
- Object body = readWithMessageConverters(methodParam, inputMessage, paramType);
- return new HttpEntity(body, inputMessage.getHeaders());
- }
-
- private Object readWithMessageConverters(MethodParameter methodParam, HttpInputMessage inputMessage, Class paramType)
- throws Exception {
-
- MediaType contentType = inputMessage.getHeaders().getContentType();
- if (contentType == null) {
- StringBuilder builder = new StringBuilder(ClassUtils.getShortName(methodParam.getParameterType()));
- String paramName = methodParam.getParameterName();
- if (paramName != null) {
- builder.append(' ');
- builder.append(paramName);
- }
- throw new HttpMediaTypeNotSupportedException(
- "Cannot extract parameter (" + builder.toString() + "): no Content-Type found");
- }
-
- List allSupportedMediaTypes = new ArrayList();
- if (this.messageConverters != null) {
- for (HttpMessageConverter> messageConverter : this.messageConverters) {
- allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
- if (messageConverter.canRead(paramType, contentType)) {
- if (logger.isDebugEnabled()) {
- logger.debug("Reading [" + paramType.getName() + "] as \"" + contentType
- +"\" using [" + messageConverter + "]");
- }
- return messageConverter.read(paramType, inputMessage);
- }
- }
- }
- throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);
- }
-
- private Class> getHttpEntityType(MethodParameter methodParam) {
- Assert.isAssignable(HttpEntity.class, methodParam.getParameterType());
- ParameterizedType type = (ParameterizedType) methodParam.getGenericParameterType();
- if (type.getActualTypeArguments().length == 1) {
- Type typeArgument = type.getActualTypeArguments()[0];
- if (typeArgument instanceof Class) {
- return (Class>) typeArgument;
- }
- else if (typeArgument instanceof GenericArrayType) {
- Type componentType = ((GenericArrayType) typeArgument).getGenericComponentType();
- if (componentType instanceof Class) {
- // Surely, there should be a nicer way to do this
- Object array = Array.newInstance((Class>) componentType, 0);
- return array.getClass();
- }
- }
- }
- throw new IllegalArgumentException(
- "HttpEntity parameter (" + methodParam.getParameterName() + ") is not parameterized");
-
- }
-
- private Object resolveCookieValue(String cookieName, boolean required, String defaultValue,
- MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
- throws Exception {
-
- Class> paramType = methodParam.getParameterType();
- if (cookieName.length() == 0) {
- cookieName = getRequiredParameterName(methodParam);
- }
- Object cookieValue = resolveCookieValue(cookieName, paramType, webRequest);
- if (cookieValue == null) {
- if (defaultValue != null) {
- cookieValue = resolveDefaultValue(defaultValue);
- }
- else if (required) {
- raiseMissingCookieException(cookieName, paramType);
- }
- cookieValue = checkValue(cookieName, cookieValue, paramType);
- }
- WebDataBinder binder = createBinder(webRequest, null, cookieName);
- initBinder(handlerForInitBinderCall, cookieName, binder, webRequest);
- return binder.convertIfNecessary(cookieValue, paramType, methodParam);
- }
-
- /**
- * Resolves the given {@link CookieValue @CookieValue} annotation.
- * Throws an UnsupportedOperationException by default.
- */
- protected Object resolveCookieValue(String cookieName, Class paramType, NativeWebRequest webRequest)
- throws Exception {
-
- throw new UnsupportedOperationException("@CookieValue not supported");
- }
-
- private Object resolvePathVariable(String pathVarName, MethodParameter methodParam,
- NativeWebRequest webRequest, Object handlerForInitBinderCall) throws Exception {
-
- Class> paramType = methodParam.getParameterType();
- if (pathVarName.length() == 0) {
- pathVarName = getRequiredParameterName(methodParam);
- }
- String pathVarValue = resolvePathVariable(pathVarName, paramType, webRequest);
- WebDataBinder binder = createBinder(webRequest, null, pathVarName);
- initBinder(handlerForInitBinderCall, pathVarName, binder, webRequest);
- return binder.convertIfNecessary(pathVarValue, paramType, methodParam);
- }
-
- /**
- * Resolves the given {@link PathVariable @PathVariable} annotation.
- *
Throws an UnsupportedOperationException by default.
- */
- protected String resolvePathVariable(String pathVarName, Class paramType, NativeWebRequest webRequest)
- throws Exception {
-
- throw new UnsupportedOperationException("@PathVariable not supported");
- }
-
- private String getRequiredParameterName(MethodParameter methodParam) {
- String name = methodParam.getParameterName();
- if (name == null) {
- throw new IllegalStateException(
- "No parameter name specified for argument of type [" + methodParam.getParameterType().getName() +
- "], and no parameter name information found in class file either.");
- }
- return name;
- }
-
- private Object checkValue(String name, Object value, Class paramType) {
- if (value == null) {
- if (boolean.class.equals(paramType)) {
- return Boolean.FALSE;
- }
- else if (paramType.isPrimitive()) {
- throw new IllegalStateException("Optional " + paramType + " parameter '" + name +
- "' is not present but cannot be translated into a null value due to being declared as a " +
- "primitive type. Consider declaring it as object wrapper for the corresponding primitive type.");
- }
- }
- return value;
- }
-
- private WebDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam,
- ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler) throws Exception {
-
- // Bind request parameter onto object...
- String name = attrName;
- if ("".equals(name)) {
- name = Conventions.getVariableNameForParameter(methodParam);
- }
- Class> paramType = methodParam.getParameterType();
- Object bindObject;
- if (implicitModel.containsKey(name)) {
- bindObject = implicitModel.get(name);
- }
- else if (this.methodResolver.isSessionAttribute(name, paramType)) {
- bindObject = this.sessionAttributeStore.retrieveAttribute(webRequest, name);
- if (bindObject == null) {
- raiseSessionRequiredException("Session attribute '" + name + "' required - not found in session");
- }
- }
- else {
- bindObject = BeanUtils.instantiateClass(paramType);
- }
- WebDataBinder binder = createBinder(webRequest, bindObject, name);
- initBinder(handler, name, binder, webRequest);
- return binder;
- }
-
-
- /**
- * Determine whether the given value qualifies as a "binding candidate", i.e. might potentially be subject to
- * bean-style data binding later on.
- */
- protected boolean isBindingCandidate(Object value) {
- return (value != null && !value.getClass().isArray() && !(value instanceof Collection) &&
- !(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass()));
- }
-
- protected void raiseMissingParameterException(String paramName, Class paramType) throws Exception {
- throw new IllegalStateException("Missing parameter '" + paramName + "' of type [" + paramType.getName() + "]");
- }
-
- protected void raiseMissingHeaderException(String headerName, Class paramType) throws Exception {
- throw new IllegalStateException("Missing header '" + headerName + "' of type [" + paramType.getName() + "]");
- }
-
- protected void raiseMissingCookieException(String cookieName, Class paramType) throws Exception {
- throw new IllegalStateException(
- "Missing cookie value '" + cookieName + "' of type [" + paramType.getName() + "]");
- }
-
- protected void raiseSessionRequiredException(String message) throws Exception {
- throw new IllegalStateException(message);
- }
-
- protected WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName)
- throws Exception {
-
- return new WebRequestDataBinder(target, objectName);
- }
-
- private void doBind(WebDataBinder binder, NativeWebRequest webRequest, boolean validate, boolean failOnErrors)
- throws Exception {
-
- doBind(binder, webRequest);
- if (validate) {
- binder.validate();
- }
- if (failOnErrors && binder.getBindingResult().hasErrors()) {
- throw new BindException(binder.getBindingResult());
- }
- }
-
- protected void doBind(WebDataBinder binder, NativeWebRequest webRequest) throws Exception {
- ((WebRequestDataBinder) binder).bind(webRequest);
- }
-
- /**
- * Return a {@link HttpInputMessage} for the given {@link NativeWebRequest}.
- *
Throws an UnsupportedOperation1Exception by default.
- */
- protected HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest) throws Exception {
- throw new UnsupportedOperationException("@RequestBody not supported");
- }
-
- /**
- * Return a {@link HttpOutputMessage} for the given {@link NativeWebRequest}.
- *
Throws an UnsupportedOperationException by default.
- */
- protected HttpOutputMessage createHttpOutputMessage(NativeWebRequest webRequest) throws Exception {
- throw new UnsupportedOperationException("@ResponseBody not supported");
- }
-
- protected String parseDefaultValueAttribute(String value) {
- return (ValueConstants.DEFAULT_NONE.equals(value) ? null : value);
- }
-
- protected Object resolveDefaultValue(String value) {
- return value;
- }
-
- protected Object resolveCommonArgument(MethodParameter methodParameter, NativeWebRequest webRequest)
- throws Exception {
-
- // Invoke custom argument resolvers if present...
- if (this.customArgumentResolvers != null) {
- for (WebArgumentResolver argumentResolver : this.customArgumentResolvers) {
- Object value = argumentResolver.resolveArgument(methodParameter, webRequest);
- if (value != WebArgumentResolver.UNRESOLVED) {
- return value;
- }
- }
- }
-
- // Resolution of standard parameter types...
- Class paramType = methodParameter.getParameterType();
- Object value = resolveStandardArgument(paramType, webRequest);
- if (value != WebArgumentResolver.UNRESOLVED && !ClassUtils.isAssignableValue(paramType, value)) {
- throw new IllegalStateException("Standard argument type [" + paramType.getName() +
- "] resolved to incompatible value of type [" + (value != null ? value.getClass() : null) +
- "]. Consider declaring the argument type in a less specific fashion.");
- }
- return value;
- }
-
- protected Object resolveStandardArgument(Class> parameterType, NativeWebRequest webRequest) throws Exception {
- if (WebRequest.class.isAssignableFrom(parameterType)) {
- return webRequest;
- }
- return WebArgumentResolver.UNRESOLVED;
- }
-
- protected final void addReturnValueAsModelAttribute(Method handlerMethod, Class handlerType,
- Object returnValue, ExtendedModelMap implicitModel) {
-
- ModelAttribute attr = AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class);
- String attrName = (attr != null ? attr.value() : "");
- if ("".equals(attrName)) {
- Class resolvedType = GenericTypeResolver.resolveReturnType(handlerMethod, handlerType);
- attrName = Conventions.getVariableNameForReturnType(handlerMethod, resolvedType, returnValue);
- }
- implicitModel.addAttribute(attrName, returnValue);
- }
+ private static final String MODEL_KEY_PREFIX_STALE = SessionAttributeStore.class.getName() + ".STALE.";
+
+ /**
+ * We'll create a lot of these objects, so we don't want a new logger every time.
+ */
+ private static final Log logger = LogFactory.getLog(HandlerMethodInvoker.class);
+
+ private final HandlerMethodResolver methodResolver;
+
+ private final WebBindingInitializer bindingInitializer;
+
+ private final SessionAttributeStore sessionAttributeStore;
+
+ private final ParameterNameDiscoverer parameterNameDiscoverer;
+
+ private final WebArgumentResolver[] customArgumentResolvers;
+
+ private final HttpMessageConverter[] messageConverters;
+
+ private final SimpleSessionStatus sessionStatus = new SimpleSessionStatus();
+
+
+ public HandlerMethodInvoker(HandlerMethodResolver methodResolver) {
+ this(methodResolver, null);
+ }
+
+ public HandlerMethodInvoker(HandlerMethodResolver methodResolver, WebBindingInitializer bindingInitializer) {
+ this(methodResolver, bindingInitializer, new DefaultSessionAttributeStore(), null, null, null);
+ }
+
+ public HandlerMethodInvoker(HandlerMethodResolver methodResolver, WebBindingInitializer bindingInitializer,
+ SessionAttributeStore sessionAttributeStore, ParameterNameDiscoverer parameterNameDiscoverer,
+ WebArgumentResolver[] customArgumentResolvers, HttpMessageConverter[] messageConverters) {
+
+ this.methodResolver = methodResolver;
+ this.bindingInitializer = bindingInitializer;
+ this.sessionAttributeStore = sessionAttributeStore;
+ this.parameterNameDiscoverer = parameterNameDiscoverer;
+ this.customArgumentResolvers = customArgumentResolvers;
+ this.messageConverters = messageConverters;
+ }
+
+
+ public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
+ NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
+
+ Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
+ try {
+ boolean debug = logger.isDebugEnabled();
+ for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
+ Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
+ if (attrValue != null) {
+ implicitModel.addAttribute(attrName, attrValue);
+ }
+ }
+ for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
+ Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
+ Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
+ if (debug) {
+ logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
+ }
+ String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value();
+ if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
+ continue;
+ }
+ ReflectionUtils.makeAccessible(attributeMethodToInvoke);
+ Object attrValue = attributeMethodToInvoke.invoke(handler, args);
+ if ("".equals(attrName)) {
+ Class resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
+ attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
+ }
+ if (!implicitModel.containsAttribute(attrName)) {
+ implicitModel.addAttribute(attrName, attrValue);
+ }
+ }
+ Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
+ if (debug) {
+ logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
+ }
+ ReflectionUtils.makeAccessible(handlerMethodToInvoke);
+ return handlerMethodToInvoke.invoke(handler, args);
+ } catch (IllegalStateException ex) {
+ // Internal assertion failed (e.g. invalid signature):
+ // throw exception with full handler method context...
+ throw new HandlerMethodInvocationException(handlerMethodToInvoke, ex);
+ } catch (InvocationTargetException ex) {
+ // User-defined @ModelAttribute/@InitBinder/@RequestMapping method threw an exception...
+ ReflectionUtils.rethrowException(ex.getTargetException());
+ return null;
+ }
+ }
+
+ public final void updateModelAttributes(Object handler, Map mavModel,
+ ExtendedModelMap implicitModel, NativeWebRequest webRequest) throws Exception {
+
+ if (this.methodResolver.hasSessionAttributes() && this.sessionStatus.isComplete()) {
+ for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
+ this.sessionAttributeStore.cleanupAttribute(webRequest, attrName);
+ }
+ }
+
+ // Expose model attributes as session attributes, if required.
+ // Expose BindingResults for all attributes, making custom editors available.
+ Map model = (mavModel != null ? mavModel : implicitModel);
+ if (model != null) {
+ try {
+ String[] originalAttrNames = model.keySet().toArray(new String[model.size()]);
+ for (String attrName : originalAttrNames) {
+ Object attrValue = model.get(attrName);
+ boolean isSessionAttr = this.methodResolver.isSessionAttribute(
+ attrName, (attrValue != null ? attrValue.getClass() : null));
+ if (isSessionAttr) {
+ if (this.sessionStatus.isComplete()) {
+ implicitModel.put(MODEL_KEY_PREFIX_STALE + attrName, Boolean.TRUE);
+ } else if (!implicitModel.containsKey(MODEL_KEY_PREFIX_STALE + attrName)) {
+ this.sessionAttributeStore.storeAttribute(webRequest, attrName, attrValue);
+ }
+ }
+ if (!attrName.startsWith(BindingResult.MODEL_KEY_PREFIX) &&
+ (isSessionAttr || isBindingCandidate(attrValue))) {
+ String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attrName;
+ if (mavModel != null && !model.containsKey(bindingResultKey)) {
+ WebDataBinder binder = createBinder(webRequest, attrValue, attrName);
+ initBinder(handler, attrName, binder, webRequest);
+ mavModel.put(bindingResultKey, binder.getBindingResult());
+ }
+ }
+ }
+ } catch (InvocationTargetException ex) {
+ // User-defined @InitBinder method threw an exception...
+ ReflectionUtils.rethrowException(ex.getTargetException());
+ }
+ }
+ }
+
+
+ private Object[] resolveHandlerArguments(Method handlerMethod, Object handler,
+ NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
+
+ Class[] paramTypes = handlerMethod.getParameterTypes();
+ Object[] args = new Object[paramTypes.length];
+
+ for (int i = 0; i < args.length; i++) {
+ MethodParameter methodParam = new MethodParameter(handlerMethod, i);
+ methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
+ GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
+ String paramName = null;
+ String headerName = null;
+ boolean requestBodyFound = false;
+ String cookieName = null;
+ String pathVarName = null;
+ String attrName = null;
+ boolean required = false;
+ String defaultValue = null;
+ boolean validate = false;
+ int annotationsFound = 0;
+ Annotation[] paramAnns = methodParam.getParameterAnnotations();
+
+ for (Annotation paramAnn : paramAnns) {
+ if (RequestParam.class.isInstance(paramAnn)) {
+ RequestParam requestParam = (RequestParam) paramAnn;
+ paramName = requestParam.value();
+ required = requestParam.required();
+ defaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
+ annotationsFound++;
+ } else if (RequestHeader.class.isInstance(paramAnn)) {
+ RequestHeader requestHeader = (RequestHeader) paramAnn;
+ headerName = requestHeader.value();
+ required = requestHeader.required();
+ defaultValue = parseDefaultValueAttribute(requestHeader.defaultValue());
+ annotationsFound++;
+ } else if (RequestBody.class.isInstance(paramAnn)) {
+ requestBodyFound = true;
+ annotationsFound++;
+ } else if (CookieValue.class.isInstance(paramAnn)) {
+ CookieValue cookieValue = (CookieValue) paramAnn;
+ cookieName = cookieValue.value();
+ required = cookieValue.required();
+ defaultValue = parseDefaultValueAttribute(cookieValue.defaultValue());
+ annotationsFound++;
+ } else if (PathVariable.class.isInstance(paramAnn)) {
+ PathVariable pathVar = (PathVariable) paramAnn;
+ pathVarName = pathVar.value();
+ annotationsFound++;
+ } else if (ModelAttribute.class.isInstance(paramAnn)) {
+ ModelAttribute attr = (ModelAttribute) paramAnn;
+ attrName = attr.value();
+ annotationsFound++;
+ } else if (Value.class.isInstance(paramAnn)) {
+ defaultValue = ((Value) paramAnn).value();
+ } else if ("Valid".equals(paramAnn.annotationType().getSimpleName())) {
+ validate = true;
+ }
+ }
+
+ if (annotationsFound > 1) {
+ throw new IllegalStateException("Handler parameter annotations are exclusive choices - " +
+ "do not specify more than one such annotation on the same parameter: " + handlerMethod);
+ }
+
+ if (annotationsFound == 0) {
+ Object argValue = resolveCommonArgument(methodParam, webRequest);
+ if (argValue != WebArgumentResolver.UNRESOLVED) {
+ args[i] = argValue;
+ } else if (defaultValue != null) {
+ args[i] = resolveDefaultValue(defaultValue);
+ } else {
+ Class paramType = methodParam.getParameterType();
+ if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
+ args[i] = implicitModel;
+ } else if (SessionStatus.class.isAssignableFrom(paramType)) {
+ args[i] = this.sessionStatus;
+ } else if (HttpEntity.class.isAssignableFrom(paramType)) {
+ args[i] = resolveHttpEntityRequest(methodParam, webRequest);
+ } else if (Errors.class.isAssignableFrom(paramType)) {
+ throw new IllegalStateException("Errors/BindingResult argument declared " +
+ "without preceding model attribute. Check your handler method signature!");
+ } else if (BeanUtils.isSimpleProperty(paramType)) {
+ paramName = "";
+ } else {
+ attrName = "";
+ }
+ }
+ }
+
+ if (paramName != null) {
+ args[i] = resolveRequestParam(paramName, required, defaultValue, methodParam, webRequest, handler);
+ } else if (headerName != null) {
+ args[i] = resolveRequestHeader(headerName, required, defaultValue, methodParam, webRequest, handler);
+ } else if (requestBodyFound) {
+ args[i] = resolveRequestBody(methodParam, webRequest, handler);
+ } else if (cookieName != null) {
+ args[i] = resolveCookieValue(cookieName, required, defaultValue, methodParam, webRequest, handler);
+ } else if (pathVarName != null) {
+ args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler);
+ } else if (attrName != null) {
+ WebDataBinder binder =
+ resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
+ boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
+ if (binder.getTarget() != null) {
+ doBind(binder, webRequest, validate, !assignBindingResult);
+ }
+ args[i] = binder.getTarget();
+ if (assignBindingResult) {
+ args[i + 1] = binder.getBindingResult();
+ i++;
+ }
+ implicitModel.putAll(binder.getBindingResult().getModel());
+ }
+ }
+
+ return args;
+ }
+
+ protected void initBinder(Object handler, String attrName, WebDataBinder binder, NativeWebRequest webRequest)
+ throws Exception {
+
+ if (this.bindingInitializer != null) {
+ this.bindingInitializer.initBinder(binder, webRequest);
+ }
+ if (handler != null) {
+ Set initBinderMethods = this.methodResolver.getInitBinderMethods();
+ if (!initBinderMethods.isEmpty()) {
+ boolean debug = logger.isDebugEnabled();
+ for (Method initBinderMethod : initBinderMethods) {
+ Method methodToInvoke = BridgeMethodResolver.findBridgedMethod(initBinderMethod);
+ String[] targetNames = AnnotationUtils.findAnnotation(initBinderMethod, InitBinder.class).value();
+ if (targetNames.length == 0 || Arrays.asList(targetNames).contains(attrName)) {
+ Object[] initBinderArgs =
+ resolveInitBinderArguments(handler, methodToInvoke, binder, webRequest);
+ if (debug) {
+ logger.debug("Invoking init-binder method: " + methodToInvoke);
+ }
+ ReflectionUtils.makeAccessible(methodToInvoke);
+ Object returnValue = methodToInvoke.invoke(handler, initBinderArgs);
+ if (returnValue != null) {
+ throw new IllegalStateException(
+ "InitBinder methods must not have a return value: " + methodToInvoke);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private Object[] resolveInitBinderArguments(Object handler, Method initBinderMethod,
+ WebDataBinder binder, NativeWebRequest webRequest) throws Exception {
+
+ Class[] initBinderParams = initBinderMethod.getParameterTypes();
+ Object[] initBinderArgs = new Object[initBinderParams.length];
+
+ for (int i = 0; i < initBinderArgs.length; i++) {
+ MethodParameter methodParam = new MethodParameter(initBinderMethod, i);
+ methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
+ GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
+ String paramName = null;
+ boolean paramRequired = false;
+ String paramDefaultValue = null;
+ String pathVarName = null;
+ Annotation[] paramAnns = methodParam.getParameterAnnotations();
+
+ for (Annotation paramAnn : paramAnns) {
+ if (RequestParam.class.isInstance(paramAnn)) {
+ RequestParam requestParam = (RequestParam) paramAnn;
+ paramName = requestParam.value();
+ paramRequired = requestParam.required();
+ paramDefaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
+ break;
+ } else if (ModelAttribute.class.isInstance(paramAnn)) {
+ throw new IllegalStateException(
+ "@ModelAttribute is not supported on @InitBinder methods: " + initBinderMethod);
+ } else if (PathVariable.class.isInstance(paramAnn)) {
+ PathVariable pathVar = (PathVariable) paramAnn;
+ pathVarName = pathVar.value();
+ }
+ }
+
+ if (paramName == null && pathVarName == null) {
+ Object argValue = resolveCommonArgument(methodParam, webRequest);
+ if (argValue != WebArgumentResolver.UNRESOLVED) {
+ initBinderArgs[i] = argValue;
+ } else {
+ Class paramType = initBinderParams[i];
+ if (paramType.isInstance(binder)) {
+ initBinderArgs[i] = binder;
+ } else if (BeanUtils.isSimpleProperty(paramType)) {
+ paramName = "";
+ } else {
+ throw new IllegalStateException("Unsupported argument [" + paramType.getName() +
+ "] for @InitBinder method: " + initBinderMethod);
+ }
+ }
+ }
+
+ if (paramName != null) {
+ initBinderArgs[i] =
+ resolveRequestParam(paramName, paramRequired, paramDefaultValue, methodParam, webRequest, null);
+ } else if (pathVarName != null) {
+ initBinderArgs[i] = resolvePathVariable(pathVarName, methodParam, webRequest, null);
+ }
+ }
+
+ return initBinderArgs;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Object resolveRequestParam(String paramName, boolean required, String defaultValue,
+ MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
+ throws Exception {
+
+ Class> paramType = methodParam.getParameterType();
+ if (Map.class.isAssignableFrom(paramType) && paramName.length() == 0) {
+ return resolveRequestParamMap((Class extends Map>) paramType, webRequest);
+ }
+ if (paramName.length() == 0) {
+ paramName = getRequiredParameterName(methodParam);
+ }
+ Object paramValue = null;
+ MultipartRequest multipartRequest = webRequest.getNativeRequest(MultipartRequest.class);
+ if (multipartRequest != null) {
+ List files = multipartRequest.getFiles(paramName);
+ if (!files.isEmpty()) {
+ if (files.size() == 1 && !paramType.isArray() && !Collection.class.isAssignableFrom(paramType)) {
+ paramValue = files.get(0);
+ } else {
+ paramValue = files;
+ }
+ }
+ }
+ if (paramValue == null) {
+ String[] paramValues = webRequest.getParameterValues(paramName);
+ if (paramValues != null) {
+ if (paramValues.length == 1 && !paramType.isArray() && !Collection.class.isAssignableFrom(paramType)) {
+ paramValue = paramValues[0];
+ } else {
+ paramValue = paramValues;
+ }
+ }
+ }
+ if (paramValue == null) {
+ if (defaultValue != null) {
+ paramValue = resolveDefaultValue(defaultValue);
+ } else if (required) {
+ raiseMissingParameterException(paramName, paramType);
+ }
+ paramValue = checkValue(paramName, paramValue, paramType);
+ }
+ WebDataBinder binder = createBinder(webRequest, null, paramName);
+ initBinder(handlerForInitBinderCall, paramName, binder, webRequest);
+ return binder.convertIfNecessary(paramValue, paramType, methodParam);
+ }
+
+ private Map resolveRequestParamMap(Class extends Map> mapType, NativeWebRequest webRequest) {
+ Map parameterMap = webRequest.getParameterMap();
+ if (MultiValueMap.class.isAssignableFrom(mapType)) {
+ MultiValueMap result = new LinkedMultiValueMap(parameterMap.size());
+ for (Map.Entry entry : parameterMap.entrySet()) {
+ for (String value : entry.getValue()) {
+ result.add(entry.getKey(), value);
+ }
+ }
+ return result;
+ } else {
+ Map result = new LinkedHashMap(parameterMap.size());
+ for (Map.Entry entry : parameterMap.entrySet()) {
+ if (entry.getValue().length > 0) {
+ result.put(entry.getKey(), entry.getValue()[0]);
+ }
+ }
+ return result;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private Object resolveRequestHeader(String headerName, boolean required, String defaultValue,
+ MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
+ throws Exception {
+
+ Class> paramType = methodParam.getParameterType();
+ if (Map.class.isAssignableFrom(paramType)) {
+ return resolveRequestHeaderMap((Class extends Map>) paramType, webRequest);
+ }
+ if (headerName.length() == 0) {
+ headerName = getRequiredParameterName(methodParam);
+ }
+ Object headerValue = null;
+ String[] headerValues = webRequest.getHeaderValues(headerName);
+ if (headerValues != null) {
+ headerValue = (headerValues.length == 1 ? headerValues[0] : headerValues);
+ }
+ if (headerValue == null) {
+ if (defaultValue != null) {
+ headerValue = resolveDefaultValue(defaultValue);
+ } else if (required) {
+ raiseMissingHeaderException(headerName, paramType);
+ }
+ headerValue = checkValue(headerName, headerValue, paramType);
+ }
+ WebDataBinder binder = createBinder(webRequest, null, headerName);
+ initBinder(handlerForInitBinderCall, headerName, binder, webRequest);
+ return binder.convertIfNecessary(headerValue, paramType, methodParam);
+ }
+
+ private Map resolveRequestHeaderMap(Class extends Map> mapType, NativeWebRequest webRequest) {
+ if (MultiValueMap.class.isAssignableFrom(mapType)) {
+ MultiValueMap result;
+ if (HttpHeaders.class.isAssignableFrom(mapType)) {
+ result = new HttpHeaders();
+ } else {
+ result = new LinkedMultiValueMap();
+ }
+ for (Iterator iterator = webRequest.getHeaderNames(); iterator.hasNext();) {
+ String headerName = iterator.next();
+ for (String headerValue : webRequest.getHeaderValues(headerName)) {
+ result.add(headerName, headerValue);
+ }
+ }
+ return result;
+ } else {
+ Map result = new LinkedHashMap();
+ for (Iterator iterator = webRequest.getHeaderNames(); iterator.hasNext();) {
+ String headerName = iterator.next();
+ String headerValue = webRequest.getHeader(headerName);
+ result.put(headerName, headerValue);
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Resolves the given {@link RequestBody @RequestBody} annotation.
+ */
+ protected Object resolveRequestBody(MethodParameter methodParam, NativeWebRequest webRequest, Object handler)
+ throws Exception {
+
+ return readWithMessageConverters(methodParam, createHttpInputMessage(webRequest), methodParam.getParameterType());
+ }
+
+ private HttpEntity resolveHttpEntityRequest(MethodParameter methodParam, NativeWebRequest webRequest)
+ throws Exception {
+
+ HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
+ Class> paramType = getHttpEntityType(methodParam);
+ Object body = readWithMessageConverters(methodParam, inputMessage, paramType);
+ return new HttpEntity(body, inputMessage.getHeaders());
+ }
+
+ private Object readWithMessageConverters(MethodParameter methodParam, HttpInputMessage inputMessage, Class paramType)
+ throws Exception {
+
+ MediaType contentType = inputMessage.getHeaders().getContentType();
+ if (contentType == null) {
+ StringBuilder builder = new StringBuilder(ClassUtils.getShortName(methodParam.getParameterType()));
+ String paramName = methodParam.getParameterName();
+ if (paramName != null) {
+ builder.append(' ');
+ builder.append(paramName);
+ }
+ throw new HttpMediaTypeNotSupportedException(
+ "Cannot extract parameter (" + builder.toString() + "): no Content-Type found");
+ }
+
+ List allSupportedMediaTypes = new ArrayList();
+ if (this.messageConverters != null) {
+ for (HttpMessageConverter> messageConverter : this.messageConverters) {
+ allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
+ if (messageConverter.canRead(paramType, contentType)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Reading [" + paramType.getName() + "] as \"" + contentType
+ + "\" using [" + messageConverter + "]");
+ }
+ return messageConverter.read(paramType, inputMessage);
+ }
+ }
+ }
+ throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);
+ }
+
+ private Class> getHttpEntityType(MethodParameter methodParam) {
+ Assert.isAssignable(HttpEntity.class, methodParam.getParameterType());
+ ParameterizedType type = (ParameterizedType) methodParam.getGenericParameterType();
+ if (type.getActualTypeArguments().length == 1) {
+ Type typeArgument = type.getActualTypeArguments()[0];
+ if (typeArgument instanceof Class) {
+ return (Class>) typeArgument;
+ } else if (typeArgument instanceof GenericArrayType) {
+ Type componentType = ((GenericArrayType) typeArgument).getGenericComponentType();
+ if (componentType instanceof Class) {
+ // Surely, there should be a nicer way to do this
+ Object array = Array.newInstance((Class>) componentType, 0);
+ return array.getClass();
+ }
+ }
+ }
+ throw new IllegalArgumentException(
+ "HttpEntity parameter (" + methodParam.getParameterName() + ") is not parameterized");
+
+ }
+
+ private Object resolveCookieValue(String cookieName, boolean required, String defaultValue,
+ MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
+ throws Exception {
+
+ Class> paramType = methodParam.getParameterType();
+ if (cookieName.length() == 0) {
+ cookieName = getRequiredParameterName(methodParam);
+ }
+ Object cookieValue = resolveCookieValue(cookieName, paramType, webRequest);
+ if (cookieValue == null) {
+ if (defaultValue != null) {
+ cookieValue = resolveDefaultValue(defaultValue);
+ } else if (required) {
+ raiseMissingCookieException(cookieName, paramType);
+ }
+ cookieValue = checkValue(cookieName, cookieValue, paramType);
+ }
+ WebDataBinder binder = createBinder(webRequest, null, cookieName);
+ initBinder(handlerForInitBinderCall, cookieName, binder, webRequest);
+ return binder.convertIfNecessary(cookieValue, paramType, methodParam);
+ }
+
+ /**
+ * Resolves the given {@link CookieValue @CookieValue} annotation.
+ * Throws an UnsupportedOperationException by default.
+ */
+ protected Object resolveCookieValue(String cookieName, Class paramType, NativeWebRequest webRequest)
+ throws Exception {
+
+ throw new UnsupportedOperationException("@CookieValue not supported");
+ }
+
+ private Object resolvePathVariable(String pathVarName, MethodParameter methodParam,
+ NativeWebRequest webRequest, Object handlerForInitBinderCall) throws Exception {
+
+ Class> paramType = methodParam.getParameterType();
+ if (pathVarName.length() == 0) {
+ pathVarName = getRequiredParameterName(methodParam);
+ }
+ String pathVarValue = resolvePathVariable(pathVarName, paramType, webRequest);
+ WebDataBinder binder = createBinder(webRequest, null, pathVarName);
+ initBinder(handlerForInitBinderCall, pathVarName, binder, webRequest);
+ return binder.convertIfNecessary(pathVarValue, paramType, methodParam);
+ }
+
+ /**
+ * Resolves the given {@link PathVariable @PathVariable} annotation.
+ *
Throws an UnsupportedOperationException by default.
+ */
+ protected String resolvePathVariable(String pathVarName, Class paramType, NativeWebRequest webRequest)
+ throws Exception {
+
+ throw new UnsupportedOperationException("@PathVariable not supported");
+ }
+
+ private String getRequiredParameterName(MethodParameter methodParam) {
+ String name = methodParam.getParameterName();
+ if (name == null) {
+ throw new IllegalStateException(
+ "No parameter name specified for argument of type [" + methodParam.getParameterType().getName() +
+ "], and no parameter name information found in class file either.");
+ }
+ return name;
+ }
+
+ private Object checkValue(String name, Object value, Class paramType) {
+ if (value == null) {
+ if (boolean.class.equals(paramType)) {
+ return Boolean.FALSE;
+ } else if (paramType.isPrimitive()) {
+ throw new IllegalStateException("Optional " + paramType + " parameter '" + name +
+ "' is not present but cannot be translated into a null value due to being declared as a " +
+ "primitive type. Consider declaring it as object wrapper for the corresponding primitive type.");
+ }
+ }
+ return value;
+ }
+
+ private WebDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam,
+ ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler) throws Exception {
+
+ // Bind request parameter onto object...
+ String name = attrName;
+ if ("".equals(name)) {
+ name = Conventions.getVariableNameForParameter(methodParam);
+ }
+ Class> paramType = methodParam.getParameterType();
+ Object bindObject;
+ if (implicitModel.containsKey(name)) {
+ bindObject = implicitModel.get(name);
+ } else if (this.methodResolver.isSessionAttribute(name, paramType)) {
+ bindObject = this.sessionAttributeStore.retrieveAttribute(webRequest, name);
+ if (bindObject == null) {
+ raiseSessionRequiredException("Session attribute '" + name + "' required - not found in session");
+ }
+ } else {
+ bindObject = BeanUtils.instantiateClass(paramType);
+ }
+ WebDataBinder binder = createBinder(webRequest, bindObject, name);
+ initBinder(handler, name, binder, webRequest);
+ return binder;
+ }
+
+
+ /**
+ * Determine whether the given value qualifies as a "binding candidate", i.e. might potentially be subject to
+ * bean-style data binding later on.
+ */
+ protected boolean isBindingCandidate(Object value) {
+ return (value != null && !value.getClass().isArray() && !(value instanceof Collection) &&
+ !(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass()));
+ }
+
+ protected void raiseMissingParameterException(String paramName, Class paramType) throws Exception {
+ throw new IllegalStateException("Missing parameter '" + paramName + "' of type [" + paramType.getName() + "]");
+ }
+
+ protected void raiseMissingHeaderException(String headerName, Class paramType) throws Exception {
+ throw new IllegalStateException("Missing header '" + headerName + "' of type [" + paramType.getName() + "]");
+ }
+
+ protected void raiseMissingCookieException(String cookieName, Class paramType) throws Exception {
+ throw new IllegalStateException(
+ "Missing cookie value '" + cookieName + "' of type [" + paramType.getName() + "]");
+ }
+
+ protected void raiseSessionRequiredException(String message) throws Exception {
+ throw new IllegalStateException(message);
+ }
+
+ protected WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName)
+ throws Exception {
+
+ return new WebRequestDataBinder(target, objectName);
+ }
+
+ private void doBind(WebDataBinder binder, NativeWebRequest webRequest, boolean validate, boolean failOnErrors)
+ throws Exception {
+
+ doBind(binder, webRequest);
+ if (validate) {
+ binder.validate();
+ }
+ if (failOnErrors && binder.getBindingResult().hasErrors()) {
+ throw new BindException(binder.getBindingResult());
+ }
+ }
+
+ protected void doBind(WebDataBinder binder, NativeWebRequest webRequest) throws Exception {
+ ((WebRequestDataBinder) binder).bind(webRequest);
+ }
+
+ /**
+ * Return a {@link HttpInputMessage} for the given {@link NativeWebRequest}.
+ *
Throws an UnsupportedOperation1Exception by default.
+ */
+ protected HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest) throws Exception {
+ throw new UnsupportedOperationException("@RequestBody not supported");
+ }
+
+ /**
+ * Return a {@link HttpOutputMessage} for the given {@link NativeWebRequest}.
+ *
Throws an UnsupportedOperationException by default.
+ */
+ protected HttpOutputMessage createHttpOutputMessage(NativeWebRequest webRequest) throws Exception {
+ throw new UnsupportedOperationException("@ResponseBody not supported");
+ }
+
+ protected String parseDefaultValueAttribute(String value) {
+ return (ValueConstants.DEFAULT_NONE.equals(value) ? null : value);
+ }
+
+ protected Object resolveDefaultValue(String value) {
+ return value;
+ }
+
+ protected Object resolveCommonArgument(MethodParameter methodParameter, NativeWebRequest webRequest)
+ throws Exception {
+
+ // Invoke custom argument resolvers if present...
+ if (this.customArgumentResolvers != null) {
+ for (WebArgumentResolver argumentResolver : this.customArgumentResolvers) {
+ Object value = argumentResolver.resolveArgument(methodParameter, webRequest);
+ if (value != WebArgumentResolver.UNRESOLVED) {
+ return value;
+ }
+ }
+ }
+
+ // Resolution of standard parameter types...
+ Class paramType = methodParameter.getParameterType();
+ Object value = resolveStandardArgument(paramType, webRequest);
+ if (value != WebArgumentResolver.UNRESOLVED && !ClassUtils.isAssignableValue(paramType, value)) {
+ throw new IllegalStateException("Standard argument type [" + paramType.getName() +
+ "] resolved to incompatible value of type [" + (value != null ? value.getClass() : null) +
+ "]. Consider declaring the argument type in a less specific fashion.");
+ }
+ return value;
+ }
+
+ protected Object resolveStandardArgument(Class> parameterType, NativeWebRequest webRequest) throws Exception {
+ if (WebRequest.class.isAssignableFrom(parameterType)) {
+ return webRequest;
+ }
+ return WebArgumentResolver.UNRESOLVED;
+ }
+
+ protected final void addReturnValueAsModelAttribute(Method handlerMethod, Class handlerType,
+ Object returnValue, ExtendedModelMap implicitModel) {
+
+ ModelAttribute attr = AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class);
+ String attrName = (attr != null ? attr.value() : "");
+ if ("".equals(attrName)) {
+ Class resolvedType = GenericTypeResolver.resolveReturnType(handlerMethod, handlerType);
+ attrName = Conventions.getVariableNameForReturnType(handlerMethod, resolvedType, returnValue);
+ }
+ implicitModel.addAttribute(attrName, returnValue);
+ }
}
diff --git a/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/ActionExecutedContext.java b/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/ActionExecutedContext.java
index 33726acd2..97123fdbe 100644
--- a/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/ActionExecutedContext.java
+++ b/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/ActionExecutedContext.java
@@ -22,40 +22,37 @@ import org.springframework.web.servlet.ModelAndView;
public class ActionExecutedContext extends ActionExecutingContext {
- private ModelAndView modelAndView;
-
- private Exception exception;
-
- public ActionExecutedContext(ActionExecutingContext actionExecutingContext, ModelAndView modelAndView, Exception exception) {
- super(actionExecutingContext.getServletWebRequest(), actionExecutingContext.getHandler(),
- actionExecutingContext.getHandlerMethod(), actionExecutingContext.getHandlerParameters(),
- actionExecutingContext.getImplicitModel());
- this.modelAndView = modelAndView;
- this.exception = exception;
- }
+ private ModelAndView modelAndView;
- @Override
- public String toString() {
- return "ActionExecutedContext [handler=" + getHandler()
- + ", servletWebRequest=" + getServletWebRequest()
- + ", implicitModel=" + getImplicitModel() + ", handlerMethod="
- + getHandlerMethod() + ", handlerParameters="
- + Arrays.toString(getHandlerParameters()) + ",modelAndView=" + modelAndView
- + ", exception=" + exception + "]";
- }
-
+ private Exception exception;
+
+ public ActionExecutedContext(ActionExecutingContext actionExecutingContext, ModelAndView modelAndView, Exception exception) {
+ super(actionExecutingContext.getServletWebRequest(), actionExecutingContext.getHandler(),
+ actionExecutingContext.getHandlerMethod(), actionExecutingContext.getHandlerParameters(),
+ actionExecutingContext.getImplicitModel());
+ this.modelAndView = modelAndView;
+ this.exception = exception;
+ }
+
+ @Override
+ public String toString() {
+ return "ActionExecutedContext [handler=" + getHandler()
+ + ", servletWebRequest=" + getServletWebRequest()
+ + ", implicitModel=" + getImplicitModel() + ", handlerMethod="
+ + getHandlerMethod() + ", handlerParameters="
+ + Arrays.toString(getHandlerParameters()) + ",modelAndView=" + modelAndView
+ + ", exception=" + exception + "]";
+ }
- public ModelAndView getModelAndView() {
- return modelAndView;
- }
+ public ModelAndView getModelAndView() {
+ return modelAndView;
+ }
- public Exception getException() {
- return exception;
- }
+ public Exception getException() {
+ return exception;
+ }
-
-
}
diff --git a/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/ActionExecutingContext.java b/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/ActionExecutingContext.java
index 45c839c6a..f87c63884 100644
--- a/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/ActionExecutingContext.java
+++ b/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/ActionExecutingContext.java
@@ -17,7 +17,6 @@ package org.springframework.data.document.web.servlet;
import java.lang.reflect.Method;
import java.util.Arrays;
-
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@@ -26,67 +25,65 @@ import org.springframework.web.context.request.ServletWebRequest;
public class ActionExecutingContext {
-
- private Object handler;
-
- private ServletWebRequest servletWebRequest;
-
- private ExtendedModelMap implicitModel;
-
- private Method handlerMethod;
-
- private Object[] handlerParameters;
-
- public ActionExecutingContext(ServletWebRequest servletWebRequest,
- Object handler, Method handlerMethod, Object[] handlerParameters,
- ExtendedModelMap implicitModel) {
- super();
- this.servletWebRequest = servletWebRequest;
- this.handler = handler;
- this.handlerMethod = handlerMethod;
- this.handlerParameters = handlerParameters;
- this.implicitModel = implicitModel;
- }
+ private Object handler;
- public HttpServletRequest getHttpServletRequest() {
- return servletWebRequest.getRequest();
- }
+ private ServletWebRequest servletWebRequest;
- public HttpServletResponse getHttpServletResponse() {
- return servletWebRequest.getResponse();
- }
+ private ExtendedModelMap implicitModel;
- public Object getHandler() {
- return handler;
- }
+ private Method handlerMethod;
- public ServletWebRequest getServletWebRequest() {
- return servletWebRequest;
- }
+ private Object[] handlerParameters;
- public ExtendedModelMap getImplicitModel() {
- return implicitModel;
- }
- public Method getHandlerMethod() {
- return handlerMethod;
- }
+ public ActionExecutingContext(ServletWebRequest servletWebRequest,
+ Object handler, Method handlerMethod, Object[] handlerParameters,
+ ExtendedModelMap implicitModel) {
+ super();
+ this.servletWebRequest = servletWebRequest;
+ this.handler = handler;
+ this.handlerMethod = handlerMethod;
+ this.handlerParameters = handlerParameters;
+ this.implicitModel = implicitModel;
+ }
+
+ public HttpServletRequest getHttpServletRequest() {
+ return servletWebRequest.getRequest();
+ }
+
+ public HttpServletResponse getHttpServletResponse() {
+ return servletWebRequest.getResponse();
+ }
+
+ public Object getHandler() {
+ return handler;
+ }
+
+ public ServletWebRequest getServletWebRequest() {
+ return servletWebRequest;
+ }
+
+ public ExtendedModelMap getImplicitModel() {
+ return implicitModel;
+ }
+
+ public Method getHandlerMethod() {
+ return handlerMethod;
+ }
+
+ public Object[] getHandlerParameters() {
+ return handlerParameters;
+ }
+
+ @Override
+ public String toString() {
+ return "ActionExecutingContext [handler=" + handler
+ + ", servletWebRequest=" + servletWebRequest
+ + ", implicitModel=" + implicitModel + ", handlerMethod="
+ + handlerMethod + ", handlerParameters="
+ + Arrays.toString(handlerParameters) + "]";
+ }
- public Object[] getHandlerParameters() {
- return handlerParameters;
- }
-
- @Override
- public String toString() {
- return "ActionExecutingContext [handler=" + handler
- + ", servletWebRequest=" + servletWebRequest
- + ", implicitModel=" + implicitModel + ", handlerMethod="
- + handlerMethod + ", handlerParameters="
- + Arrays.toString(handlerParameters) + "]";
- }
-
-
-
}
diff --git a/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/ActionInterceptor.java b/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/ActionInterceptor.java
index 30f440540..6327c2605 100644
--- a/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/ActionInterceptor.java
+++ b/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/ActionInterceptor.java
@@ -17,9 +17,9 @@ package org.springframework.data.document.web.servlet;
public interface ActionInterceptor {
- boolean preHandle(ActionExecutingContext actionExecutingContext);
-
- void postHandle(ActionExecutedContext actionExecutedContext);
-
- void afterCompletion(ActionExecutedContext actionExecutedContext);
+ boolean preHandle(ActionExecutingContext actionExecutingContext);
+
+ void postHandle(ActionExecutedContext actionExecutedContext);
+
+ void afterCompletion(ActionExecutedContext actionExecutedContext);
}
diff --git a/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java b/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java
index 5038ed1cd..8761e5101 100644
--- a/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java
+++ b/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/mvc/annotation/AnnotationMethodHandlerAdapter.java
@@ -16,26 +16,11 @@
package org.springframework.data.document.web.servlet.mvc.annotation;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.io.Reader;
-import java.io.Writer;
+import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.Principal;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
@@ -47,32 +32,19 @@ import javax.servlet.http.HttpSession;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanExpressionContext;
import org.springframework.beans.factory.config.BeanExpressionResolver;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.core.BridgeMethodResolver;
-import org.springframework.core.Conventions;
-import org.springframework.core.GenericTypeResolver;
-import org.springframework.core.LocalVariableTableParameterNameDiscoverer;
-import org.springframework.core.Ordered;
-import org.springframework.core.ParameterNameDiscoverer;
+import org.springframework.core.*;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.document.web.servlet.ActionExecutedContext;
import org.springframework.data.document.web.servlet.ActionExecutingContext;
import org.springframework.data.document.web.servlet.ActionInterceptor;
import org.springframework.data.document.web.servlet.mvc.annotation.support.InterceptingHandlerMethodInvoker;
-import org.springframework.format.support.FormattingConversionService;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpInputMessage;
-import org.springframework.http.HttpOutputMessage;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.MediaType;
-import org.springframework.http.ResponseEntity;
+import org.springframework.http.*;
import org.springframework.http.converter.ByteArrayHttpMessageConverter;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.StringHttpMessageConverter;
@@ -84,13 +56,7 @@ import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.Model;
-import org.springframework.util.AntPathMatcher;
-import org.springframework.util.Assert;
-import org.springframework.util.ClassUtils;
-import org.springframework.util.ObjectUtils;
-import org.springframework.util.PathMatcher;
-import org.springframework.util.ReflectionUtils;
-import org.springframework.util.StringUtils;
+import org.springframework.util.*;
import org.springframework.validation.support.BindingAwareModelMap;
import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
@@ -98,14 +64,7 @@ import org.springframework.web.HttpSessionRequiredException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.bind.WebDataBinder;
-import org.springframework.web.bind.annotation.InitBinder;
-import org.springframework.web.bind.annotation.ModelAttribute;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RequestMethod;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ResponseBody;
-import org.springframework.web.bind.annotation.ResponseStatus;
-import org.springframework.web.bind.annotation.SessionAttributes;
+import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.support.HandlerMethodInvocationException;
import org.springframework.web.bind.annotation.support.HandlerMethodInvoker;
import org.springframework.web.bind.annotation.support.HandlerMethodResolver;
@@ -134,1178 +93,1153 @@ import org.springframework.web.util.WebUtils;
* Implementation of the {@link org.springframework.web.servlet.HandlerAdapter} interface
* that maps handler methods based on HTTP paths, HTTP methods and request parameters
* expressed through the {@link RequestMapping} annotation.
- *
+ *
* Supports request parameter binding through the {@link RequestParam} annotation.
* Also supports the {@link ModelAttribute} annotation for exposing model attribute
* values to the view, as well as {@link InitBinder} for binder initialization methods
* and {@link SessionAttributes} for automatic session management of specific attributes.
- *
+ *
* This adapter can be customized through various bean properties.
* A common use case is to apply shared binder initialization logic through
* a custom {@link #setWebBindingInitializer WebBindingInitializer}.
*
* @author Juergen Hoeller
* @author Arjen Poutsma
- * @since 2.5
* @see #setPathMatcher
* @see #setMethodNameResolver
* @see #setWebBindingInitializer
* @see #setSessionAttributeStore
+ * @since 2.5
*/
public class AnnotationMethodHandlerAdapter extends WebContentGenerator
- implements HandlerAdapter, Ordered, BeanFactoryAware {
+ implements HandlerAdapter, Ordered, BeanFactoryAware {
-
- private ActionInterceptor[] actionInterceptors;
-
- /**
- * Log category to use when no mapped handler is found for a request.
- * @see #pageNotFoundLogger
- */
- public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
- /**
- * Additional logger to use when no mapped handler is found for a request.
- * @see #PAGE_NOT_FOUND_LOG_CATEGORY
- */
- protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);
-
-
- private UrlPathHelper urlPathHelper = new UrlPathHelper();
-
- private PathMatcher pathMatcher = new AntPathMatcher();
-
- private MethodNameResolver methodNameResolver = new InternalPathMethodNameResolver();
-
- private WebBindingInitializer webBindingInitializer;
-
- private SessionAttributeStore amhaSessionAttributeStore = new DefaultSessionAttributeStore();
-
- private int cacheSecondsForSessionAttributeHandlers = 0;
-
- private boolean synchronizeOnSession = false;
-
- private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
-
- private WebArgumentResolver[] customArgumentResolvers;
-
- private ModelAndViewResolver[] customModelAndViewResolvers;
-
- private HttpMessageConverter>[] messageConverters;
-
- private int order = Ordered.LOWEST_PRECEDENCE;
-
- private ConfigurableBeanFactory beanFactory;
-
- private BeanExpressionContext expressionContext;
-
- private final Map, ServletHandlerMethodResolver> methodResolverCache =
- new ConcurrentHashMap, ServletHandlerMethodResolver>();
-
-
- public AnnotationMethodHandlerAdapter() {
- // no restriction of HTTP methods by default
- // MLP
- super(false);
-
- // See SPR-7316
- StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
- stringHttpMessageConverter.setWriteAcceptCharset(false);
- messageConverters = new HttpMessageConverter[]{new ByteArrayHttpMessageConverter(), stringHttpMessageConverter,
- new SourceHttpMessageConverter(), new XmlAwareFormHttpMessageConverter()};
- }
-
-
- /**
- * Set if URL lookup should always use the full path within the current servlet
- * context. Else, the path within the current servlet mapping is used if applicable
- * (that is, in the case of a ".../*" servlet mapping in web.xml).
- * Default is "false".
- * @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath
- */
- public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
- this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath);
- }
-
- /**
- * Set if context path and request URI should be URL-decoded. Both are returned
- * undecoded by the Servlet API, in contrast to the servlet path.
- *
Uses either the request encoding or the default encoding according
- * to the Servlet spec (ISO-8859-1).
- * @see org.springframework.web.util.UrlPathHelper#setUrlDecode
- */
- public void setUrlDecode(boolean urlDecode) {
- this.urlPathHelper.setUrlDecode(urlDecode);
- }
-
- /**
- * Set the UrlPathHelper to use for resolution of lookup paths.
- *
Use this to override the default UrlPathHelper with a custom subclass,
- * or to share common UrlPathHelper settings across multiple HandlerMappings and HandlerAdapters.
- */
- public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
- Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
- this.urlPathHelper = urlPathHelper;
- }
-
- /**
- * Set the PathMatcher implementation to use for matching URL paths against registered URL patterns.
- *
Default is {@link org.springframework.util.AntPathMatcher}.
- */
- public void setPathMatcher(PathMatcher pathMatcher) {
- Assert.notNull(pathMatcher, "PathMatcher must not be null");
- this.pathMatcher = pathMatcher;
- }
-
- /**
- * Set the MethodNameResolver to use for resolving default handler methods
- * (carrying an empty @RequestMapping annotation).
- *
Will only kick in when the handler method cannot be resolved uniquely
- * through the annotation metadata already.
- */
- public void setMethodNameResolver(MethodNameResolver methodNameResolver) {
- this.methodNameResolver = methodNameResolver;
- }
-
- /**
- * Specify a WebBindingInitializer which will apply pre-configured
- * configuration to every DataBinder that this controller uses.
- */
- public void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) {
- this.webBindingInitializer = webBindingInitializer;
- }
-
- /**
- * Specify the strategy to store session attributes with.
- *
Default is {@link org.springframework.web.bind.support.DefaultSessionAttributeStore},
- * storing session attributes in the HttpSession, using the same attribute name as in the model.
- */
- public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore) {
- Assert.notNull(sessionAttributeStore, "SessionAttributeStore must not be null");
- this.amhaSessionAttributeStore = sessionAttributeStore;
- }
-
- /**
- * Cache content produced by @SessionAttributes annotated handlers
- * for the given number of seconds. Default is 0, preventing caching completely.
- *
In contrast to the "cacheSeconds" property which will apply to all general handlers
- * (but not to @SessionAttributes annotated handlers), this setting will
- * apply to @SessionAttributes annotated handlers only.
- * @see #setCacheSeconds
- * @see org.springframework.web.bind.annotation.SessionAttributes
- */
- public void setCacheSecondsForSessionAttributeHandlers(int cacheSecondsForSessionAttributeHandlers) {
- this.cacheSecondsForSessionAttributeHandlers = cacheSecondsForSessionAttributeHandlers;
- }
-
- /**
- * Set if controller execution should be synchronized on the session,
- * to serialize parallel invocations from the same client.
- *
More specifically, the execution of the handleRequestInternal
- * method will get synchronized if this flag is "true". The best available
- * session mutex will be used for the synchronization; ideally, this will
- * be a mutex exposed by HttpSessionMutexListener.
- *
The session mutex is guaranteed to be the same object during
- * the entire lifetime of the session, available under the key defined
- * by the SESSION_MUTEX_ATTRIBUTE constant. It serves as a
- * safe reference to synchronize on for locking on the current session.
- *
In many cases, the HttpSession reference itself is a safe mutex
- * as well, since it will always be the same object reference for the
- * same active logical session. However, this is not guaranteed across
- * different servlet containers; the only 100% safe way is a session mutex.
- * @see org.springframework.web.util.HttpSessionMutexListener
- * @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession)
- */
- public void setSynchronizeOnSession(boolean synchronizeOnSession) {
- this.synchronizeOnSession = synchronizeOnSession;
- }
-
- /**
- * Set the ParameterNameDiscoverer to use for resolving method parameter names if needed
- * (e.g. for default attribute names).
- *
Default is a {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer}.
- */
- public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
- this.parameterNameDiscoverer = parameterNameDiscoverer;
- }
-
- /**
- * Set a custom WebArgumentResolvers to use for special method parameter types.
- *
Such a custom WebArgumentResolver will kick in first, having a chance to resolve
- * an argument value before the standard argument handling kicks in.
- */
- public void setCustomArgumentResolver(WebArgumentResolver argumentResolver) {
- this.customArgumentResolvers = new WebArgumentResolver[] {argumentResolver};
- }
-
- /**
- * Set one or more custom WebArgumentResolvers to use for special method parameter types.
- *
Any such custom WebArgumentResolver will kick in first, having a chance to resolve
- * an argument value before the standard argument handling kicks in.
- */
- public void setCustomArgumentResolvers(WebArgumentResolver[] argumentResolvers) {
- this.customArgumentResolvers = argumentResolvers;
- }
-
- /**
- * Set a custom ModelAndViewResolvers to use for special method return types.
- *
Such a custom ModelAndViewResolver will kick in first, having a chance to resolve
- * a return value before the standard ModelAndView handling kicks in.
- */
- public void setCustomModelAndViewResolver(ModelAndViewResolver customModelAndViewResolver) {
- this.customModelAndViewResolvers = new ModelAndViewResolver[] {customModelAndViewResolver};
- }
-
- /**
- * Set one or more custom ModelAndViewResolvers to use for special method return types.
- *
Any such custom ModelAndViewResolver will kick in first, having a chance to resolve
- * a return value before the standard ModelAndView handling kicks in.
- */
- public void setCustomModelAndViewResolvers(ModelAndViewResolver[] customModelAndViewResolvers) {
- this.customModelAndViewResolvers = customModelAndViewResolvers;
- }
-
- /**
- * Set the message body converters to use.
- *
These converters are used to convert from and to HTTP requests and responses.
- */
- public void setMessageConverters(HttpMessageConverter>[] messageConverters) {
- this.messageConverters = messageConverters;
- }
-
- /**
- * Return the message body converters that this adapter has been configured with.
- */
- public HttpMessageConverter>[] getMessageConverters() {
- return messageConverters;
- }
-
- public void setActionInterceptors(ActionInterceptor[] actionInterceptors) {
- this.actionInterceptors = actionInterceptors;
- }
-
- /**
- * Specify the order value for this HandlerAdapter bean.
- *
Default value is Integer.MAX_VALUE, meaning that it's non-ordered.
- * @see org.springframework.core.Ordered#getOrder()
- */
- public void setOrder(int order) {
- this.order = order;
- }
-
- public int getOrder() {
- return this.order;
- }
-
- public void setBeanFactory(BeanFactory beanFactory) {
- if (beanFactory instanceof ConfigurableBeanFactory) {
- this.beanFactory = (ConfigurableBeanFactory) beanFactory;
- this.expressionContext = new BeanExpressionContext(this.beanFactory, new RequestScope());
- }
- }
-
-
- public boolean supports(Object handler) {
- return getMethodResolver(handler).hasHandlerMethods();
- }
-
- public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
- throws Exception {
-
- if (AnnotationUtils.findAnnotation(handler.getClass(), SessionAttributes.class) != null) {
- // Always prevent caching in case of session attribute management.
- checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
- // Prepare cached set of session attributes names.
- }
- else {
- // Uses configured default cacheSeconds setting.
- checkAndPrepare(request, response, true);
- }
-
- // Execute invokeHandlerMethod in synchronized block if required.
- if (this.synchronizeOnSession) {
- HttpSession session = request.getSession(false);
- if (session != null) {
- Object mutex = WebUtils.getSessionMutex(session);
- synchronized (mutex) {
- return invokeHandlerMethod(request, response, handler);
- }
- }
- }
-
- return invokeHandlerMethod(request, response, handler);
- }
-
- protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
- throws Exception {
-
- ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
- Method handlerMethod = methodResolver.resolveHandlerMethod(request);
- ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver, actionInterceptors);
- ServletWebRequest webRequest = new ServletWebRequest(request, response);
- ExtendedModelMap implicitModel = new BindingAwareModelMap();
-
-
- return methodInvoker.interceptingInvokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
- //Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
- //ModelAndView mav =
- // methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
- // methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
- // return mav;
- }
-
- public long getLastModified(HttpServletRequest request, Object handler) {
- return -1;
- }
-
- /**
- * Build a HandlerMethodResolver for the given handler type.
- */
- private ServletHandlerMethodResolver getMethodResolver(Object handler) {
- Class handlerClass = ClassUtils.getUserClass(handler);
- ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass);
- if (resolver == null) {
- resolver = new ServletHandlerMethodResolver(handlerClass);
- this.methodResolverCache.put(handlerClass, resolver);
- }
- return resolver;
- }
-
-
- /**
- * Template method for creating a new ServletRequestDataBinder instance.
- *
The default implementation creates a standard ServletRequestDataBinder.
- * This can be overridden for custom ServletRequestDataBinder subclasses.
- * @param request current HTTP request
- * @param target the target object to bind onto (or null
- * if the binder is just used to convert a plain parameter value)
- * @param objectName the objectName of the target object
- * @return the ServletRequestDataBinder instance to use
- * @throws Exception in case of invalid state or arguments
- * @see ServletRequestDataBinder#bind(javax.servlet.ServletRequest)
- * @see ServletRequestDataBinder#convertIfNecessary(Object, Class, org.springframework.core.MethodParameter)
- */
- protected ServletRequestDataBinder createBinder(HttpServletRequest request, Object target, String objectName)
- throws Exception {
- return new ServletRequestDataBinder(target, objectName);
- }
-
- /**
- * Template method for creating a new HttpInputMessage instance.
- *
The default implementation creates a standard {@link ServletServerHttpRequest}.
- * This can be overridden for custom {@code HttpInputMessage} implementations
- * @param servletRequest current HTTP request
- * @return the HttpInputMessage instance to use
- * @throws Exception in case of errors
- */
- protected HttpInputMessage createHttpInputMessage(HttpServletRequest servletRequest) throws Exception {
- return new ServletServerHttpRequest(servletRequest);
- }
-
- /**
- * Template method for creating a new HttpOuputMessage instance.
- *
The default implementation creates a standard {@link ServletServerHttpResponse}.
- * This can be overridden for custom {@code HttpOutputMessage} implementations
- * @param servletResponse current HTTP response
- * @return the HttpInputMessage instance to use
- * @throws Exception in case of errors
- */
- protected HttpOutputMessage createHttpOutputMessage(HttpServletResponse servletResponse) throws Exception {
- return new ServletServerHttpResponse(servletResponse);
- }
-
-
- /**
- * Servlet-specific subclass of {@link HandlerMethodResolver}.
- */
- private class ServletHandlerMethodResolver extends HandlerMethodResolver {
-
- private final Map mappings = new HashMap();
-
- private ServletHandlerMethodResolver(Class> handlerType) {
- init(handlerType);
- }
-
- @Override
- protected boolean isHandlerMethod(Method method) {
- if (this.mappings.containsKey(method)) {
- return true;
- }
- RequestMapping mapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
- if (mapping != null) {
- RequestMappingInfo mappingInfo = new RequestMappingInfo();
- mappingInfo.patterns = mapping.value();
- if (!hasTypeLevelMapping() || !Arrays.equals(mapping.method(), getTypeLevelMapping().method())) {
- mappingInfo.methods = mapping.method();
- }
- if (!hasTypeLevelMapping() || !Arrays.equals(mapping.params(), getTypeLevelMapping().params())) {
- mappingInfo.params = mapping.params();
- }
- if (!hasTypeLevelMapping() || !Arrays.equals(mapping.headers(), getTypeLevelMapping().headers())) {
- mappingInfo.headers = mapping.headers();
- }
- this.mappings.put(method, mappingInfo);
- return true;
- }
- return false;
- }
-
- public Method resolveHandlerMethod(HttpServletRequest request) throws ServletException {
- String lookupPath = urlPathHelper.getLookupPathForRequest(request);
- Comparator pathComparator = pathMatcher.getPatternComparator(lookupPath);
- Map targetHandlerMethods = new LinkedHashMap();
- Set allowedMethods = new LinkedHashSet(7);
- String resolvedMethodName = null;
- for (Method handlerMethod : getHandlerMethods()) {
- RequestMappingInfo mappingInfo = this.mappings.get(handlerMethod);
- boolean match = false;
- if (mappingInfo.hasPatterns()) {
- List matchingPatterns = new ArrayList(mappingInfo.patterns.length);
- for (String pattern : mappingInfo.patterns) {
- if (!hasTypeLevelMapping() && !pattern.startsWith("/")) {
- pattern = "/" + pattern;
- }
- String combinedPattern = getCombinedPattern(pattern, lookupPath, request);
- if (combinedPattern != null) {
- if (mappingInfo.matches(request)) {
- match = true;
- matchingPatterns.add(combinedPattern);
- }
- else {
- if (!mappingInfo.matchesRequestMethod(request)) {
- allowedMethods.addAll(mappingInfo.methodNames());
- }
- break;
- }
- }
- }
- Collections.sort(matchingPatterns, pathComparator);
- mappingInfo.matchedPatterns = matchingPatterns;
- }
- else {
- // No paths specified: parameter match sufficient.
- match = mappingInfo.matches(request);
- if (match && mappingInfo.methods.length == 0 && mappingInfo.params.length == 0 &&
- resolvedMethodName != null && !resolvedMethodName.equals(handlerMethod.getName())) {
- match = false;
- }
- else {
- if (!mappingInfo.matchesRequestMethod(request)) {
- allowedMethods.addAll(mappingInfo.methodNames());
- }
- }
- }
- if (match) {
- Method oldMappedMethod = targetHandlerMethods.put(mappingInfo, handlerMethod);
- if (oldMappedMethod != null && oldMappedMethod != handlerMethod) {
- if (methodNameResolver != null && mappingInfo.patterns.length == 0) {
- if (!oldMappedMethod.getName().equals(handlerMethod.getName())) {
- if (resolvedMethodName == null) {
- resolvedMethodName = methodNameResolver.getHandlerMethodName(request);
- }
- if (!resolvedMethodName.equals(oldMappedMethod.getName())) {
- oldMappedMethod = null;
- }
- if (!resolvedMethodName.equals(handlerMethod.getName())) {
- if (oldMappedMethod != null) {
- targetHandlerMethods.put(mappingInfo, oldMappedMethod);
- oldMappedMethod = null;
- }
- else {
- targetHandlerMethods.remove(mappingInfo);
- }
- }
- }
- }
- if (oldMappedMethod != null) {
- throw new IllegalStateException(
- "Ambiguous handler methods mapped for HTTP path '" + lookupPath + "': {" +
- oldMappedMethod + ", " + handlerMethod +
- "}. If you intend to handle the same path in multiple methods, then factor " +
- "them out into a dedicated handler class with that path mapped at the type level!");
- }
- }
- }
- }
- if (!targetHandlerMethods.isEmpty()) {
- List matches = new ArrayList(targetHandlerMethods.keySet());
- RequestMappingInfoComparator requestMappingInfoComparator =
- new RequestMappingInfoComparator(pathComparator, request);
- Collections.sort(matches, requestMappingInfoComparator);
- RequestMappingInfo bestMappingMatch = matches.get(0);
- String bestMatchedPath = bestMappingMatch.bestMatchedPattern();
- if (bestMatchedPath != null) {
- extractHandlerMethodUriTemplates(bestMatchedPath, lookupPath, request);
- }
- return targetHandlerMethods.get(bestMappingMatch);
- }
- else {
- if (!allowedMethods.isEmpty()) {
- throw new HttpRequestMethodNotSupportedException(request.getMethod(),
- StringUtils.toStringArray(allowedMethods));
- }
- throw new NoSuchRequestHandlingMethodException(lookupPath, request.getMethod(),
- request.getParameterMap());
- }
- }
-
- /**
- * Determines the combined pattern for the given methodLevelPattern and path.
- * Uses the following algorithm:
- * If there is a type-level mapping with path information, it is {@linkplain
- * PathMatcher#combine(String, String) combined} with the method-level pattern.
- * If there is a {@linkplain HandlerMapping#BEST_MATCHING_PATTERN_ATTRIBUTE best matching pattern} in the
- * request, it is combined with the method-level pattern.
- * Otherwise, the method-level pattern is returned.
- *
- */
- private String getCombinedPattern(String methodLevelPattern, String lookupPath, HttpServletRequest request) {
- if (hasTypeLevelMapping() && (!ObjectUtils.isEmpty(getTypeLevelMapping().value()))) {
- String[] typeLevelPatterns = getTypeLevelMapping().value();
- for (String typeLevelPattern : typeLevelPatterns) {
- if (!typeLevelPattern.startsWith("/")) {
- typeLevelPattern = "/" + typeLevelPattern;
- }
- String combinedPattern = pathMatcher.combine(typeLevelPattern, methodLevelPattern);
- if (isPathMatchInternal(combinedPattern, lookupPath)) {
- return combinedPattern;
- }
- }
- return null;
- }
- String bestMatchingPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
- if (StringUtils.hasText(bestMatchingPattern) && bestMatchingPattern.endsWith("*")) {
- String combinedPattern = pathMatcher.combine(bestMatchingPattern, methodLevelPattern);
- if (!combinedPattern.equals(bestMatchingPattern) &&
- (isPathMatchInternal(combinedPattern, lookupPath))) {
- return combinedPattern;
- }
- }
- if (isPathMatchInternal(methodLevelPattern, lookupPath)) {
- return methodLevelPattern;
- }
- return null;
- }
-
- private boolean isPathMatchInternal(String pattern, String lookupPath) {
- if (pattern.equals(lookupPath) || pathMatcher.match(pattern, lookupPath)) {
- return true;
- }
- boolean hasSuffix = pattern.indexOf('.') != -1;
- if (!hasSuffix && pathMatcher.match(pattern + ".*", lookupPath)) {
- return true;
- }
- boolean endsWithSlash = pattern.endsWith("/");
- if (!endsWithSlash && pathMatcher.match(pattern + "/", lookupPath)) {
- return true;
- }
- return false;
- }
-
- @SuppressWarnings("unchecked")
- private void extractHandlerMethodUriTemplates(String mappedPattern,
- String lookupPath,
- HttpServletRequest request) {
-
- Map variables =
- (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
-
- int patternVariableCount = StringUtils.countOccurrencesOf(mappedPattern, "{");
-
- if ( (variables == null || patternVariableCount != variables.size())
- && pathMatcher.match(mappedPattern, lookupPath)) {
- variables = pathMatcher.extractUriTemplateVariables(mappedPattern, lookupPath);
- request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, variables);
- }
- }
- }
-
-
- /**
- * Servlet-specific subclass of {@link HandlerMethodInvoker}.
- */
- private class ServletHandlerMethodInvoker extends InterceptingHandlerMethodInvoker {
-
- private boolean responseArgumentUsed = false;
-
- private ServletHandlerMethodInvoker(HandlerMethodResolver resolver, ActionInterceptor[] actionInterceptors) {
- super(resolver, webBindingInitializer, amhaSessionAttributeStore, parameterNameDiscoverer,
- customArgumentResolvers, messageConverters, actionInterceptors);
- }
-
- public ModelAndView interceptingInvokeHandlerMethod(Method handlerMethod, Object handler,
- NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
-
- int interceptorIndex = -1;
- ModelAndView mav = null;
- ActionExecutingContext executingContext = null;
- Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
- try {
- boolean debug = logger.isDebugEnabled();
- for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
- Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
- if (attrValue != null) {
- implicitModel.addAttribute(attrName, attrValue);
- }
- }
- for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
- Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
- Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
- if (debug) {
- logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
- }
- String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value();
- if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
- continue;
- }
- ReflectionUtils.makeAccessible(attributeMethodToInvoke);
- Object attrValue = attributeMethodToInvoke.invoke(handler, args);
- if ("".equals(attrName)) {
- Class resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
- attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
- }
- if (!implicitModel.containsAttribute(attrName)) {
- implicitModel.addAttribute(attrName, attrValue);
- }
- }
- Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
- if (debug) {
- logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
- }
- ReflectionUtils.makeAccessible(handlerMethodToInvoke);
-
- executingContext = new ActionExecutingContext((ServletWebRequest)webRequest, handler, handlerMethodToInvoke, args, implicitModel);
-
- // Apply preHandle methods of registered interceptors.
- ActionInterceptor[] interceptors = getActionInterceptors();
- if (interceptors != null) {
- for (int i = 0; i < interceptors.length; i++) {
- ActionInterceptor interceptor = interceptors[i];
- if (!interceptor.preHandle(executingContext)) {
- triggerAfterCompletion(executingContext, interceptorIndex, null, null);
- return null; //TODO verify null is ok
- }
- interceptorIndex = i;
- }
- }
-
- // Actually invoke the handler
- Object result = handlerMethodToInvoke.invoke(handler, args);
-
-
- mav = getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, (ServletWebRequest)webRequest);
- updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
-
- // Trigger after-completion for successful outcome
- triggerAfterCompletion(executingContext, interceptorIndex, mav, null);
-
- return mav;
- }
- catch (IllegalStateException ex) {
- // Internal assertion failed (e.g. invalid signature):
- // throw exception with full handler method context...
- triggerAfterCompletion(executingContext, interceptorIndex, mav, ex);
- throw new HandlerMethodInvocationException(handlerMethodToInvoke, ex);
- }
- catch (InvocationTargetException ex) {
- // User-defined @ModelAttribute/@InitBinder/@RequestMapping method threw an exception...
- triggerAfterCompletion(executingContext, interceptorIndex, mav, ex);
- ReflectionUtils.rethrowException(ex.getTargetException());
- return null;
- }
- }
-
- private void triggerAfterCompletion(ActionExecutingContext executingContext,
- int interceptorIndex,
- ModelAndView modelAndView,
- Exception ex) throws Exception {
-
- // Apply afterCompletion methods of registered interceptors.
- if (executingContext.getHandler() != null) {
- //TODO should be passed in;
- ActionInterceptor[] interceptors = getActionInterceptors();
- if (interceptors != null) {
- for (int i = interceptorIndex; i >= 0; i--) {
- ActionInterceptor interceptor = interceptors[i];
- ActionExecutedContext actionExecutedContext = new ActionExecutedContext(executingContext, modelAndView, ex);
- try {
- interceptor.afterCompletion(actionExecutedContext);
- }
- catch (Throwable ex2) {
- logger.error("ActionInterceptor threw exception", ex2);
- }
- }
- }
- }
- }
-
-
- @Override
- protected void raiseMissingParameterException(String paramName, Class paramType) throws Exception {
- throw new MissingServletRequestParameterException(paramName, paramType.getSimpleName());
- }
-
- @Override
- protected void raiseSessionRequiredException(String message) throws Exception {
- throw new HttpSessionRequiredException(message);
- }
-
- @Override
- protected WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName)
- throws Exception {
-
- return AnnotationMethodHandlerAdapter.this.createBinder(
- webRequest.getNativeRequest(HttpServletRequest.class), target, objectName);
- }
-
- @Override
- protected void doBind(WebDataBinder binder, NativeWebRequest webRequest) throws Exception {
- ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
- servletBinder.bind(webRequest.getNativeRequest(ServletRequest.class));
- }
-
- @Override
- protected HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest) throws Exception {
- HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
- return AnnotationMethodHandlerAdapter.this.createHttpInputMessage(servletRequest);
- }
-
- @Override
- protected HttpOutputMessage createHttpOutputMessage(NativeWebRequest webRequest) throws Exception {
- HttpServletResponse servletResponse = (HttpServletResponse) webRequest.getNativeResponse();
- return AnnotationMethodHandlerAdapter.this.createHttpOutputMessage(servletResponse);
- }
-
- @Override
- protected Object resolveDefaultValue(String value) {
- if (beanFactory == null) {
- return value;
- }
- String placeholdersResolved = beanFactory.resolveEmbeddedValue(value);
- BeanExpressionResolver exprResolver = beanFactory.getBeanExpressionResolver();
- if (exprResolver == null) {
- return value;
- }
- return exprResolver.evaluate(placeholdersResolved, expressionContext);
- }
-
- @Override
- protected Object resolveCookieValue(String cookieName, Class paramType, NativeWebRequest webRequest)
- throws Exception {
-
- HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
- Cookie cookieValue = WebUtils.getCookie(servletRequest, cookieName);
- if (Cookie.class.isAssignableFrom(paramType)) {
- return cookieValue;
- }
- else if (cookieValue != null) {
- return urlPathHelper.decodeRequestString(servletRequest, cookieValue.getValue());
- }
- else {
- return null;
- }
- }
-
- @Override
- @SuppressWarnings({"unchecked"})
- protected String resolvePathVariable(String pathVarName, Class paramType, NativeWebRequest webRequest)
- throws Exception {
-
- HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
- Map uriTemplateVariables =
- (Map) servletRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
- if (uriTemplateVariables == null || !uriTemplateVariables.containsKey(pathVarName)) {
- throw new IllegalStateException(
- "Could not find @PathVariable [" + pathVarName + "] in @RequestMapping");
- }
- return uriTemplateVariables.get(pathVarName);
- }
-
- @Override
- protected Object resolveStandardArgument(Class> parameterType, NativeWebRequest webRequest) throws Exception {
- HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
- HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
-
- if (ServletRequest.class.isAssignableFrom(parameterType) ||
- MultipartRequest.class.isAssignableFrom(parameterType)) {
- Object nativeRequest = webRequest.getNativeRequest(parameterType);
- if (nativeRequest == null) {
- throw new IllegalStateException(
- "Current request is not of type [" + parameterType.getName() + "]: " + request);
- }
- return nativeRequest;
- }
- else if (ServletResponse.class.isAssignableFrom(parameterType)) {
- this.responseArgumentUsed = true;
- Object nativeResponse = webRequest.getNativeResponse(parameterType);
- if (nativeResponse == null) {
- throw new IllegalStateException(
- "Current response is not of type [" + parameterType.getName() + "]: " + response);
- }
- return nativeResponse;
- }
- else if (HttpSession.class.isAssignableFrom(parameterType)) {
- return request.getSession();
- }
- else if (Principal.class.isAssignableFrom(parameterType)) {
- return request.getUserPrincipal();
- }
- else if (Locale.class.equals(parameterType)) {
- return RequestContextUtils.getLocale(request);
- }
- else if (InputStream.class.isAssignableFrom(parameterType)) {
- return request.getInputStream();
- }
- else if (Reader.class.isAssignableFrom(parameterType)) {
- return request.getReader();
- }
- else if (OutputStream.class.isAssignableFrom(parameterType)) {
- this.responseArgumentUsed = true;
- return response.getOutputStream();
- }
- else if (Writer.class.isAssignableFrom(parameterType)) {
- this.responseArgumentUsed = true;
- return response.getWriter();
- }
- return super.resolveStandardArgument(parameterType, webRequest);
- }
-
- @SuppressWarnings("unchecked")
- public ModelAndView getModelAndView(Method handlerMethod, Class handlerType, Object returnValue,
- ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception {
-
- ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
- if (responseStatusAnn != null) {
- HttpStatus responseStatus = responseStatusAnn.value();
- String reason = responseStatusAnn.reason();
- if (!StringUtils.hasText(reason)) {
- webRequest.getResponse().setStatus(responseStatus.value());
- }
- else {
- webRequest.getResponse().sendError(responseStatus.value(), reason);
- }
-
- // to be picked up by the RedirectView
- webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, responseStatus);
-
- responseArgumentUsed = true;
- }
-
- // Invoke custom resolvers if present...
- if (customModelAndViewResolvers != null) {
- for (ModelAndViewResolver mavResolver : customModelAndViewResolvers) {
- ModelAndView mav = mavResolver.resolveModelAndView(
- handlerMethod, handlerType, returnValue, implicitModel, webRequest);
- if (mav != ModelAndViewResolver.UNRESOLVED) {
- return mav;
- }
- }
- }
-
- if (returnValue instanceof HttpEntity) {
- handleHttpEntityResponse((HttpEntity>) returnValue, webRequest);
- return null;
- }
- else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
- handleResponseBody(returnValue, webRequest);
- return null;
- }
- else if (returnValue instanceof ModelAndView) {
- ModelAndView mav = (ModelAndView) returnValue;
- mav.getModelMap().mergeAttributes(implicitModel);
- return mav;
- }
- else if (returnValue instanceof Model) {
- return new ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap());
- }
- else if (returnValue instanceof View) {
- return new ModelAndView((View) returnValue).addAllObjects(implicitModel);
- }
- else if (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) {
- addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
- return new ModelAndView().addAllObjects(implicitModel);
- }
- else if (returnValue instanceof Map) {
- return new ModelAndView().addAllObjects(implicitModel).addAllObjects((Map) returnValue);
- }
- else if (returnValue instanceof String) {
- return new ModelAndView((String) returnValue).addAllObjects(implicitModel);
- }
- else if (returnValue == null) {
- // Either returned null or was 'void' return.
- if (this.responseArgumentUsed || webRequest.isNotModified()) {
- return null;
- }
- else {
- // Assuming view name translation...
- return new ModelAndView().addAllObjects(implicitModel);
- }
- }
- else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
- // Assume a single model attribute...
- addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
- return new ModelAndView().addAllObjects(implicitModel);
- }
- else {
- throw new IllegalArgumentException("Invalid handler method return value: " + returnValue);
- }
- }
-
- private void handleResponseBody(Object returnValue, ServletWebRequest webRequest)
- throws Exception {
- if (returnValue == null) {
- return;
- }
- HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
- HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest);
- writeWithMessageConverters(returnValue, inputMessage, outputMessage);
- }
-
- private void handleHttpEntityResponse(HttpEntity> responseEntity, ServletWebRequest webRequest)
- throws Exception {
- if (responseEntity == null) {
- return;
- }
- HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
- HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest);
- if (responseEntity instanceof ResponseEntity && outputMessage instanceof ServerHttpResponse) {
- ((ServerHttpResponse)outputMessage).setStatusCode(((ResponseEntity) responseEntity).getStatusCode());
- }
- HttpHeaders entityHeaders = responseEntity.getHeaders();
- if (!entityHeaders.isEmpty()) {
- outputMessage.getHeaders().putAll(entityHeaders);
- }
- Object body = responseEntity.getBody();
- if (body != null) {
- writeWithMessageConverters(body, inputMessage, outputMessage);
- }
- else {
- // flush headers
- outputMessage.getBody();
- }
- }
-
- @SuppressWarnings("unchecked")
- private void writeWithMessageConverters(Object returnValue,
- HttpInputMessage inputMessage, HttpOutputMessage outputMessage)
- throws IOException, HttpMediaTypeNotAcceptableException {
- List acceptedMediaTypes = inputMessage.getHeaders().getAccept();
- if (acceptedMediaTypes.isEmpty()) {
- acceptedMediaTypes = Collections.singletonList(MediaType.ALL);
- }
- MediaType.sortByQualityValue(acceptedMediaTypes);
- Class> returnValueType = returnValue.getClass();
- List allSupportedMediaTypes = new ArrayList();
- if (getMessageConverters() != null) {
- for (MediaType acceptedMediaType : acceptedMediaTypes) {
- for (HttpMessageConverter messageConverter : getMessageConverters()) {
- if (messageConverter.canWrite(returnValueType, acceptedMediaType)) {
- messageConverter.write(returnValue, acceptedMediaType, outputMessage);
- if (logger.isDebugEnabled()) {
- MediaType contentType = outputMessage.getHeaders().getContentType();
- if (contentType == null) {
- contentType = acceptedMediaType;
- }
- logger.debug("Written [" + returnValue + "] as \"" + contentType +
- "\" using [" + messageConverter + "]");
- }
- this.responseArgumentUsed = true;
- return;
- }
- }
- }
- for (HttpMessageConverter messageConverter : messageConverters) {
- allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
- }
- }
- throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);
- }
-
- }
-
-
- /**
- * Holder for request mapping metadata. Allows for finding a best matching candidate.
- */
- static class RequestMappingInfo {
-
- String[] patterns = new String[0];
-
- List matchedPatterns = Collections.emptyList();
-
- RequestMethod[] methods = new RequestMethod[0];
-
- String[] params = new String[0];
-
- String[] headers = new String[0];
-
- public boolean hasPatterns() {
- return patterns.length > 0;
- }
-
- public String bestMatchedPattern() {
- return (!this.matchedPatterns.isEmpty() ? this.matchedPatterns.get(0) : null);
- }
-
- public boolean matches(HttpServletRequest request) {
- return matchesRequestMethod(request) && matchesParameters(request) && matchesHeaders(request);
- }
-
- public boolean matchesHeaders(HttpServletRequest request) {
- return ServletAnnotationMappingUtils.checkHeaders(this.headers, request);
- }
-
- public boolean matchesParameters(HttpServletRequest request) {
- return ServletAnnotationMappingUtils.checkParameters(this.params, request);
- }
-
- public boolean matchesRequestMethod(HttpServletRequest request) {
- return ServletAnnotationMappingUtils.checkRequestMethod(this.methods, request);
- }
-
- public Set methodNames() {
- Set methodNames = new LinkedHashSet(methods.length);
- for (RequestMethod method : methods) {
- methodNames.add(method.name());
- }
- return methodNames;
- }
-
- @Override
- public boolean equals(Object obj) {
- RequestMappingInfo other = (RequestMappingInfo) obj;
- return (Arrays.equals(this.patterns, other.patterns) && Arrays.equals(this.methods, other.methods) &&
- Arrays.equals(this.params, other.params) && Arrays.equals(this.headers, other.headers));
- }
-
- @Override
- public int hashCode() {
- return (Arrays.hashCode(this.patterns) * 23 + Arrays.hashCode(this.methods) * 29 +
- Arrays.hashCode(this.params) * 31 + Arrays.hashCode(this.headers));
- }
-
- @Override
- public String toString() {
- StringBuilder builder = new StringBuilder();
- builder.append(Arrays.asList(patterns));
- if (methods.length > 0) {
- builder.append(',');
- builder.append(Arrays.asList(methods));
- }
- if (headers.length > 0) {
- builder.append(',');
- builder.append(Arrays.asList(headers));
- }
- if (params.length > 0) {
- builder.append(',');
- builder.append(Arrays.asList(params));
- }
- return builder.toString();
- }
- }
-
-
- /**
- * Comparator capable of sorting {@link RequestMappingInfo}s (RHIs) so that sorting a list with this comparator will
- * result in:
- *
- * RHIs with {@linkplain RequestMappingInfo#matchedPatterns better matched paths} take prescedence
- * over those with a weaker match (as expressed by the {@linkplain PathMatcher#getPatternComparator(String) path
- * pattern comparator}.) Typically, this means that patterns without wild cards and uri templates will be ordered
- * before those without.
- * RHIs with one single {@linkplain RequestMappingInfo#methods request method} will be
- * ordered before those without a method, or with more than one method.
- * RHIs with more {@linkplain RequestMappingInfo#params request parameters} will be ordered before those with
- * less parameters
- *
- */
- static class RequestMappingInfoComparator implements Comparator {
-
- private final Comparator pathComparator;
-
- private final ServerHttpRequest request;
-
- RequestMappingInfoComparator(Comparator pathComparator, HttpServletRequest request) {
- this.pathComparator = pathComparator;
- this.request = new ServletServerHttpRequest(request);
- }
-
- public int compare(RequestMappingInfo info1, RequestMappingInfo info2) {
- int pathComparison = pathComparator.compare(info1.bestMatchedPattern(), info2.bestMatchedPattern());
- if (pathComparison != 0) {
- return pathComparison;
- }
- int info1ParamCount = info1.params.length;
- int info2ParamCount = info2.params.length;
- if (info1ParamCount != info2ParamCount) {
- return info2ParamCount - info1ParamCount;
- }
- int info1HeaderCount = info1.headers.length;
- int info2HeaderCount = info2.headers.length;
- if (info1HeaderCount != info2HeaderCount) {
- return info2HeaderCount - info1HeaderCount;
- }
- int acceptComparison = compareAcceptHeaders(info1, info2);
- if (acceptComparison != 0) {
- return acceptComparison;
- }
- int info1MethodCount = info1.methods.length;
- int info2MethodCount = info2.methods.length;
- if (info1MethodCount == 0 && info2MethodCount > 0) {
- return 1;
- }
- else if (info2MethodCount == 0 && info1MethodCount > 0) {
- return -1;
- }
- else if (info1MethodCount == 1 & info2MethodCount > 1) {
- return -1;
- }
- else if (info2MethodCount == 1 & info1MethodCount > 1) {
- return 1;
- }
- return 0;
- }
-
- private int compareAcceptHeaders(RequestMappingInfo info1, RequestMappingInfo info2) {
- List requestAccepts = request.getHeaders().getAccept();
- MediaType.sortByQualityValue(requestAccepts);
-
- List info1Accepts = getAcceptHeaderValue(info1);
- List info2Accepts = getAcceptHeaderValue(info2);
-
- for (MediaType requestAccept : requestAccepts) {
- int pos1 = indexOfIncluded(info1Accepts, requestAccept);
- int pos2 = indexOfIncluded(info2Accepts, requestAccept);
- if (pos1 != pos2) {
- return pos2 - pos1;
- }
- }
- return 0;
- }
-
- private int indexOfIncluded(List infoAccepts, MediaType requestAccept) {
- for (int i = 0; i < infoAccepts.size(); i++) {
- MediaType info1Accept = infoAccepts.get(i);
- if (requestAccept.includes(info1Accept)) {
- return i;
- }
- }
- return -1;
- }
-
- private List getAcceptHeaderValue(RequestMappingInfo info) {
- for (String header : info.headers) {
- int separator = header.indexOf('=');
- if (separator != -1) {
- String key = header.substring(0, separator);
- String value = header.substring(separator + 1);
- if ("Accept".equalsIgnoreCase(key)) {
- return MediaType.parseMediaTypes(value);
- }
- }
- }
- return Collections.emptyList();
- }
- }
+ private ActionInterceptor[] actionInterceptors;
+
+ /**
+ * Log category to use when no mapped handler is found for a request.
+ *
+ * @see #pageNotFoundLogger
+ */
+ public static final String PAGE_NOT_FOUND_LOG_CATEGORY = "org.springframework.web.servlet.PageNotFound";
+
+ /**
+ * Additional logger to use when no mapped handler is found for a request.
+ *
+ * @see #PAGE_NOT_FOUND_LOG_CATEGORY
+ */
+ protected static final Log pageNotFoundLogger = LogFactory.getLog(PAGE_NOT_FOUND_LOG_CATEGORY);
+
+
+ private UrlPathHelper urlPathHelper = new UrlPathHelper();
+
+ private PathMatcher pathMatcher = new AntPathMatcher();
+
+ private MethodNameResolver methodNameResolver = new InternalPathMethodNameResolver();
+
+ private WebBindingInitializer webBindingInitializer;
+
+ private SessionAttributeStore amhaSessionAttributeStore = new DefaultSessionAttributeStore();
+
+ private int cacheSecondsForSessionAttributeHandlers = 0;
+
+ private boolean synchronizeOnSession = false;
+
+ private ParameterNameDiscoverer parameterNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
+
+ private WebArgumentResolver[] customArgumentResolvers;
+
+ private ModelAndViewResolver[] customModelAndViewResolvers;
+
+ private HttpMessageConverter>[] messageConverters;
+
+ private int order = Ordered.LOWEST_PRECEDENCE;
+
+ private ConfigurableBeanFactory beanFactory;
+
+ private BeanExpressionContext expressionContext;
+
+ private final Map, ServletHandlerMethodResolver> methodResolverCache =
+ new ConcurrentHashMap, ServletHandlerMethodResolver>();
+
+
+ public AnnotationMethodHandlerAdapter() {
+ // no restriction of HTTP methods by default
+ // MLP
+ super(false);
+
+ // See SPR-7316
+ StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
+ stringHttpMessageConverter.setWriteAcceptCharset(false);
+ messageConverters = new HttpMessageConverter[]{new ByteArrayHttpMessageConverter(), stringHttpMessageConverter,
+ new SourceHttpMessageConverter(), new XmlAwareFormHttpMessageConverter()};
+ }
+
+
+ /**
+ * Set if URL lookup should always use the full path within the current servlet
+ * context. Else, the path within the current servlet mapping is used if applicable
+ * (that is, in the case of a ".../*" servlet mapping in web.xml).
+ * Default is "false".
+ *
+ * @see org.springframework.web.util.UrlPathHelper#setAlwaysUseFullPath
+ */
+ public void setAlwaysUseFullPath(boolean alwaysUseFullPath) {
+ this.urlPathHelper.setAlwaysUseFullPath(alwaysUseFullPath);
+ }
+
+ /**
+ * Set if context path and request URI should be URL-decoded. Both are returned
+ * undecoded by the Servlet API, in contrast to the servlet path.
+ *
Uses either the request encoding or the default encoding according
+ * to the Servlet spec (ISO-8859-1).
+ *
+ * @see org.springframework.web.util.UrlPathHelper#setUrlDecode
+ */
+ public void setUrlDecode(boolean urlDecode) {
+ this.urlPathHelper.setUrlDecode(urlDecode);
+ }
+
+ /**
+ * Set the UrlPathHelper to use for resolution of lookup paths.
+ *
Use this to override the default UrlPathHelper with a custom subclass,
+ * or to share common UrlPathHelper settings across multiple HandlerMappings and HandlerAdapters.
+ */
+ public void setUrlPathHelper(UrlPathHelper urlPathHelper) {
+ Assert.notNull(urlPathHelper, "UrlPathHelper must not be null");
+ this.urlPathHelper = urlPathHelper;
+ }
+
+ /**
+ * Set the PathMatcher implementation to use for matching URL paths against registered URL patterns.
+ *
Default is {@link org.springframework.util.AntPathMatcher}.
+ */
+ public void setPathMatcher(PathMatcher pathMatcher) {
+ Assert.notNull(pathMatcher, "PathMatcher must not be null");
+ this.pathMatcher = pathMatcher;
+ }
+
+ /**
+ * Set the MethodNameResolver to use for resolving default handler methods
+ * (carrying an empty @RequestMapping annotation).
+ *
Will only kick in when the handler method cannot be resolved uniquely
+ * through the annotation metadata already.
+ */
+ public void setMethodNameResolver(MethodNameResolver methodNameResolver) {
+ this.methodNameResolver = methodNameResolver;
+ }
+
+ /**
+ * Specify a WebBindingInitializer which will apply pre-configured
+ * configuration to every DataBinder that this controller uses.
+ */
+ public void setWebBindingInitializer(WebBindingInitializer webBindingInitializer) {
+ this.webBindingInitializer = webBindingInitializer;
+ }
+
+ /**
+ * Specify the strategy to store session attributes with.
+ *
Default is {@link org.springframework.web.bind.support.DefaultSessionAttributeStore},
+ * storing session attributes in the HttpSession, using the same attribute name as in the model.
+ */
+ public void setSessionAttributeStore(SessionAttributeStore sessionAttributeStore) {
+ Assert.notNull(sessionAttributeStore, "SessionAttributeStore must not be null");
+ this.amhaSessionAttributeStore = sessionAttributeStore;
+ }
+
+ /**
+ * Cache content produced by @SessionAttributes annotated handlers
+ * for the given number of seconds. Default is 0, preventing caching completely.
+ *
In contrast to the "cacheSeconds" property which will apply to all general handlers
+ * (but not to @SessionAttributes annotated handlers), this setting will
+ * apply to @SessionAttributes annotated handlers only.
+ *
+ * @see #setCacheSeconds
+ * @see org.springframework.web.bind.annotation.SessionAttributes
+ */
+ public void setCacheSecondsForSessionAttributeHandlers(int cacheSecondsForSessionAttributeHandlers) {
+ this.cacheSecondsForSessionAttributeHandlers = cacheSecondsForSessionAttributeHandlers;
+ }
+
+ /**
+ * Set if controller execution should be synchronized on the session,
+ * to serialize parallel invocations from the same client.
+ *
More specifically, the execution of the handleRequestInternal
+ * method will get synchronized if this flag is "true". The best available
+ * session mutex will be used for the synchronization; ideally, this will
+ * be a mutex exposed by HttpSessionMutexListener.
+ *
The session mutex is guaranteed to be the same object during
+ * the entire lifetime of the session, available under the key defined
+ * by the SESSION_MUTEX_ATTRIBUTE constant. It serves as a
+ * safe reference to synchronize on for locking on the current session.
+ *
In many cases, the HttpSession reference itself is a safe mutex
+ * as well, since it will always be the same object reference for the
+ * same active logical session. However, this is not guaranteed across
+ * different servlet containers; the only 100% safe way is a session mutex.
+ *
+ * @see org.springframework.web.util.HttpSessionMutexListener
+ * @see org.springframework.web.util.WebUtils#getSessionMutex(javax.servlet.http.HttpSession)
+ */
+ public void setSynchronizeOnSession(boolean synchronizeOnSession) {
+ this.synchronizeOnSession = synchronizeOnSession;
+ }
+
+ /**
+ * Set the ParameterNameDiscoverer to use for resolving method parameter names if needed
+ * (e.g. for default attribute names).
+ *
Default is a {@link org.springframework.core.LocalVariableTableParameterNameDiscoverer}.
+ */
+ public void setParameterNameDiscoverer(ParameterNameDiscoverer parameterNameDiscoverer) {
+ this.parameterNameDiscoverer = parameterNameDiscoverer;
+ }
+
+ /**
+ * Set a custom WebArgumentResolvers to use for special method parameter types.
+ *
Such a custom WebArgumentResolver will kick in first, having a chance to resolve
+ * an argument value before the standard argument handling kicks in.
+ */
+ public void setCustomArgumentResolver(WebArgumentResolver argumentResolver) {
+ this.customArgumentResolvers = new WebArgumentResolver[]{argumentResolver};
+ }
+
+ /**
+ * Set one or more custom WebArgumentResolvers to use for special method parameter types.
+ *
Any such custom WebArgumentResolver will kick in first, having a chance to resolve
+ * an argument value before the standard argument handling kicks in.
+ */
+ public void setCustomArgumentResolvers(WebArgumentResolver[] argumentResolvers) {
+ this.customArgumentResolvers = argumentResolvers;
+ }
+
+ /**
+ * Set a custom ModelAndViewResolvers to use for special method return types.
+ *
Such a custom ModelAndViewResolver will kick in first, having a chance to resolve
+ * a return value before the standard ModelAndView handling kicks in.
+ */
+ public void setCustomModelAndViewResolver(ModelAndViewResolver customModelAndViewResolver) {
+ this.customModelAndViewResolvers = new ModelAndViewResolver[]{customModelAndViewResolver};
+ }
+
+ /**
+ * Set one or more custom ModelAndViewResolvers to use for special method return types.
+ *
Any such custom ModelAndViewResolver will kick in first, having a chance to resolve
+ * a return value before the standard ModelAndView handling kicks in.
+ */
+ public void setCustomModelAndViewResolvers(ModelAndViewResolver[] customModelAndViewResolvers) {
+ this.customModelAndViewResolvers = customModelAndViewResolvers;
+ }
+
+ /**
+ * Set the message body converters to use.
+ *
These converters are used to convert from and to HTTP requests and responses.
+ */
+ public void setMessageConverters(HttpMessageConverter>[] messageConverters) {
+ this.messageConverters = messageConverters;
+ }
+
+ /**
+ * Return the message body converters that this adapter has been configured with.
+ */
+ public HttpMessageConverter>[] getMessageConverters() {
+ return messageConverters;
+ }
+
+ public void setActionInterceptors(ActionInterceptor[] actionInterceptors) {
+ this.actionInterceptors = actionInterceptors;
+ }
+
+ /**
+ * Specify the order value for this HandlerAdapter bean.
+ *
Default value is Integer.MAX_VALUE, meaning that it's non-ordered.
+ *
+ * @see org.springframework.core.Ordered#getOrder()
+ */
+ public void setOrder(int order) {
+ this.order = order;
+ }
+
+ public int getOrder() {
+ return this.order;
+ }
+
+ public void setBeanFactory(BeanFactory beanFactory) {
+ if (beanFactory instanceof ConfigurableBeanFactory) {
+ this.beanFactory = (ConfigurableBeanFactory) beanFactory;
+ this.expressionContext = new BeanExpressionContext(this.beanFactory, new RequestScope());
+ }
+ }
+
+
+ public boolean supports(Object handler) {
+ return getMethodResolver(handler).hasHandlerMethods();
+ }
+
+ public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
+ throws Exception {
+
+ if (AnnotationUtils.findAnnotation(handler.getClass(), SessionAttributes.class) != null) {
+ // Always prevent caching in case of session attribute management.
+ checkAndPrepare(request, response, this.cacheSecondsForSessionAttributeHandlers, true);
+ // Prepare cached set of session attributes names.
+ } else {
+ // Uses configured default cacheSeconds setting.
+ checkAndPrepare(request, response, true);
+ }
+
+ // Execute invokeHandlerMethod in synchronized block if required.
+ if (this.synchronizeOnSession) {
+ HttpSession session = request.getSession(false);
+ if (session != null) {
+ Object mutex = WebUtils.getSessionMutex(session);
+ synchronized (mutex) {
+ return invokeHandlerMethod(request, response, handler);
+ }
+ }
+ }
+
+ return invokeHandlerMethod(request, response, handler);
+ }
+
+ protected ModelAndView invokeHandlerMethod(HttpServletRequest request, HttpServletResponse response, Object handler)
+ throws Exception {
+
+ ServletHandlerMethodResolver methodResolver = getMethodResolver(handler);
+ Method handlerMethod = methodResolver.resolveHandlerMethod(request);
+ ServletHandlerMethodInvoker methodInvoker = new ServletHandlerMethodInvoker(methodResolver, actionInterceptors);
+ ServletWebRequest webRequest = new ServletWebRequest(request, response);
+ ExtendedModelMap implicitModel = new BindingAwareModelMap();
+
+
+ return methodInvoker.interceptingInvokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
+ //Object result = methodInvoker.invokeHandlerMethod(handlerMethod, handler, webRequest, implicitModel);
+ //ModelAndView mav =
+ // methodInvoker.getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, webRequest);
+ // methodInvoker.updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
+ // return mav;
+ }
+
+ public long getLastModified(HttpServletRequest request, Object handler) {
+ return -1;
+ }
+
+ /**
+ * Build a HandlerMethodResolver for the given handler type.
+ */
+ private ServletHandlerMethodResolver getMethodResolver(Object handler) {
+ Class handlerClass = ClassUtils.getUserClass(handler);
+ ServletHandlerMethodResolver resolver = this.methodResolverCache.get(handlerClass);
+ if (resolver == null) {
+ resolver = new ServletHandlerMethodResolver(handlerClass);
+ this.methodResolverCache.put(handlerClass, resolver);
+ }
+ return resolver;
+ }
+
+
+ /**
+ * Template method for creating a new ServletRequestDataBinder instance.
+ *
The default implementation creates a standard ServletRequestDataBinder.
+ * This can be overridden for custom ServletRequestDataBinder subclasses.
+ *
+ * @param request current HTTP request
+ * @param target the target object to bind onto (or null
+ * if the binder is just used to convert a plain parameter value)
+ * @param objectName the objectName of the target object
+ * @return the ServletRequestDataBinder instance to use
+ * @throws Exception in case of invalid state or arguments
+ * @see ServletRequestDataBinder#bind(javax.servlet.ServletRequest)
+ * @see ServletRequestDataBinder#convertIfNecessary(Object, Class, org.springframework.core.MethodParameter)
+ */
+ protected ServletRequestDataBinder createBinder(HttpServletRequest request, Object target, String objectName)
+ throws Exception {
+ return new ServletRequestDataBinder(target, objectName);
+ }
+
+ /**
+ * Template method for creating a new HttpInputMessage instance.
+ *
The default implementation creates a standard {@link ServletServerHttpRequest}.
+ * This can be overridden for custom {@code HttpInputMessage} implementations
+ *
+ * @param servletRequest current HTTP request
+ * @return the HttpInputMessage instance to use
+ * @throws Exception in case of errors
+ */
+ protected HttpInputMessage createHttpInputMessage(HttpServletRequest servletRequest) throws Exception {
+ return new ServletServerHttpRequest(servletRequest);
+ }
+
+ /**
+ * Template method for creating a new HttpOuputMessage instance.
+ *
The default implementation creates a standard {@link ServletServerHttpResponse}.
+ * This can be overridden for custom {@code HttpOutputMessage} implementations
+ *
+ * @param servletResponse current HTTP response
+ * @return the HttpInputMessage instance to use
+ * @throws Exception in case of errors
+ */
+ protected HttpOutputMessage createHttpOutputMessage(HttpServletResponse servletResponse) throws Exception {
+ return new ServletServerHttpResponse(servletResponse);
+ }
+
+
+ /**
+ * Servlet-specific subclass of {@link HandlerMethodResolver}.
+ */
+ private class ServletHandlerMethodResolver extends HandlerMethodResolver {
+
+ private final Map mappings = new HashMap();
+
+ private ServletHandlerMethodResolver(Class> handlerType) {
+ init(handlerType);
+ }
+
+ @Override
+ protected boolean isHandlerMethod(Method method) {
+ if (this.mappings.containsKey(method)) {
+ return true;
+ }
+ RequestMapping mapping = AnnotationUtils.findAnnotation(method, RequestMapping.class);
+ if (mapping != null) {
+ RequestMappingInfo mappingInfo = new RequestMappingInfo();
+ mappingInfo.patterns = mapping.value();
+ if (!hasTypeLevelMapping() || !Arrays.equals(mapping.method(), getTypeLevelMapping().method())) {
+ mappingInfo.methods = mapping.method();
+ }
+ if (!hasTypeLevelMapping() || !Arrays.equals(mapping.params(), getTypeLevelMapping().params())) {
+ mappingInfo.params = mapping.params();
+ }
+ if (!hasTypeLevelMapping() || !Arrays.equals(mapping.headers(), getTypeLevelMapping().headers())) {
+ mappingInfo.headers = mapping.headers();
+ }
+ this.mappings.put(method, mappingInfo);
+ return true;
+ }
+ return false;
+ }
+
+ public Method resolveHandlerMethod(HttpServletRequest request) throws ServletException {
+ String lookupPath = urlPathHelper.getLookupPathForRequest(request);
+ Comparator pathComparator = pathMatcher.getPatternComparator(lookupPath);
+ Map targetHandlerMethods = new LinkedHashMap();
+ Set allowedMethods = new LinkedHashSet(7);
+ String resolvedMethodName = null;
+ for (Method handlerMethod : getHandlerMethods()) {
+ RequestMappingInfo mappingInfo = this.mappings.get(handlerMethod);
+ boolean match = false;
+ if (mappingInfo.hasPatterns()) {
+ List matchingPatterns = new ArrayList(mappingInfo.patterns.length);
+ for (String pattern : mappingInfo.patterns) {
+ if (!hasTypeLevelMapping() && !pattern.startsWith("/")) {
+ pattern = "/" + pattern;
+ }
+ String combinedPattern = getCombinedPattern(pattern, lookupPath, request);
+ if (combinedPattern != null) {
+ if (mappingInfo.matches(request)) {
+ match = true;
+ matchingPatterns.add(combinedPattern);
+ } else {
+ if (!mappingInfo.matchesRequestMethod(request)) {
+ allowedMethods.addAll(mappingInfo.methodNames());
+ }
+ break;
+ }
+ }
+ }
+ Collections.sort(matchingPatterns, pathComparator);
+ mappingInfo.matchedPatterns = matchingPatterns;
+ } else {
+ // No paths specified: parameter match sufficient.
+ match = mappingInfo.matches(request);
+ if (match && mappingInfo.methods.length == 0 && mappingInfo.params.length == 0 &&
+ resolvedMethodName != null && !resolvedMethodName.equals(handlerMethod.getName())) {
+ match = false;
+ } else {
+ if (!mappingInfo.matchesRequestMethod(request)) {
+ allowedMethods.addAll(mappingInfo.methodNames());
+ }
+ }
+ }
+ if (match) {
+ Method oldMappedMethod = targetHandlerMethods.put(mappingInfo, handlerMethod);
+ if (oldMappedMethod != null && oldMappedMethod != handlerMethod) {
+ if (methodNameResolver != null && mappingInfo.patterns.length == 0) {
+ if (!oldMappedMethod.getName().equals(handlerMethod.getName())) {
+ if (resolvedMethodName == null) {
+ resolvedMethodName = methodNameResolver.getHandlerMethodName(request);
+ }
+ if (!resolvedMethodName.equals(oldMappedMethod.getName())) {
+ oldMappedMethod = null;
+ }
+ if (!resolvedMethodName.equals(handlerMethod.getName())) {
+ if (oldMappedMethod != null) {
+ targetHandlerMethods.put(mappingInfo, oldMappedMethod);
+ oldMappedMethod = null;
+ } else {
+ targetHandlerMethods.remove(mappingInfo);
+ }
+ }
+ }
+ }
+ if (oldMappedMethod != null) {
+ throw new IllegalStateException(
+ "Ambiguous handler methods mapped for HTTP path '" + lookupPath + "': {" +
+ oldMappedMethod + ", " + handlerMethod +
+ "}. If you intend to handle the same path in multiple methods, then factor " +
+ "them out into a dedicated handler class with that path mapped at the type level!");
+ }
+ }
+ }
+ }
+ if (!targetHandlerMethods.isEmpty()) {
+ List matches = new ArrayList(targetHandlerMethods.keySet());
+ RequestMappingInfoComparator requestMappingInfoComparator =
+ new RequestMappingInfoComparator(pathComparator, request);
+ Collections.sort(matches, requestMappingInfoComparator);
+ RequestMappingInfo bestMappingMatch = matches.get(0);
+ String bestMatchedPath = bestMappingMatch.bestMatchedPattern();
+ if (bestMatchedPath != null) {
+ extractHandlerMethodUriTemplates(bestMatchedPath, lookupPath, request);
+ }
+ return targetHandlerMethods.get(bestMappingMatch);
+ } else {
+ if (!allowedMethods.isEmpty()) {
+ throw new HttpRequestMethodNotSupportedException(request.getMethod(),
+ StringUtils.toStringArray(allowedMethods));
+ }
+ throw new NoSuchRequestHandlingMethodException(lookupPath, request.getMethod(),
+ request.getParameterMap());
+ }
+ }
+
+ /**
+ * Determines the combined pattern for the given methodLevelPattern and path.
+ * Uses the following algorithm:
+ * If there is a type-level mapping with path information, it is {@linkplain
+ * PathMatcher#combine(String, String) combined} with the method-level pattern.
+ * If there is a {@linkplain HandlerMapping#BEST_MATCHING_PATTERN_ATTRIBUTE best matching pattern} in the
+ * request, it is combined with the method-level pattern.
+ * Otherwise, the method-level pattern is returned.
+ *
+ */
+ private String getCombinedPattern(String methodLevelPattern, String lookupPath, HttpServletRequest request) {
+ if (hasTypeLevelMapping() && (!ObjectUtils.isEmpty(getTypeLevelMapping().value()))) {
+ String[] typeLevelPatterns = getTypeLevelMapping().value();
+ for (String typeLevelPattern : typeLevelPatterns) {
+ if (!typeLevelPattern.startsWith("/")) {
+ typeLevelPattern = "/" + typeLevelPattern;
+ }
+ String combinedPattern = pathMatcher.combine(typeLevelPattern, methodLevelPattern);
+ if (isPathMatchInternal(combinedPattern, lookupPath)) {
+ return combinedPattern;
+ }
+ }
+ return null;
+ }
+ String bestMatchingPattern = (String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE);
+ if (StringUtils.hasText(bestMatchingPattern) && bestMatchingPattern.endsWith("*")) {
+ String combinedPattern = pathMatcher.combine(bestMatchingPattern, methodLevelPattern);
+ if (!combinedPattern.equals(bestMatchingPattern) &&
+ (isPathMatchInternal(combinedPattern, lookupPath))) {
+ return combinedPattern;
+ }
+ }
+ if (isPathMatchInternal(methodLevelPattern, lookupPath)) {
+ return methodLevelPattern;
+ }
+ return null;
+ }
+
+ private boolean isPathMatchInternal(String pattern, String lookupPath) {
+ if (pattern.equals(lookupPath) || pathMatcher.match(pattern, lookupPath)) {
+ return true;
+ }
+ boolean hasSuffix = pattern.indexOf('.') != -1;
+ if (!hasSuffix && pathMatcher.match(pattern + ".*", lookupPath)) {
+ return true;
+ }
+ boolean endsWithSlash = pattern.endsWith("/");
+ if (!endsWithSlash && pathMatcher.match(pattern + "/", lookupPath)) {
+ return true;
+ }
+ return false;
+ }
+
+ @SuppressWarnings("unchecked")
+ private void extractHandlerMethodUriTemplates(String mappedPattern,
+ String lookupPath,
+ HttpServletRequest request) {
+
+ Map variables =
+ (Map) request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
+
+ int patternVariableCount = StringUtils.countOccurrencesOf(mappedPattern, "{");
+
+ if ((variables == null || patternVariableCount != variables.size())
+ && pathMatcher.match(mappedPattern, lookupPath)) {
+ variables = pathMatcher.extractUriTemplateVariables(mappedPattern, lookupPath);
+ request.setAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, variables);
+ }
+ }
+ }
+
+
+ /**
+ * Servlet-specific subclass of {@link HandlerMethodInvoker}.
+ */
+ private class ServletHandlerMethodInvoker extends InterceptingHandlerMethodInvoker {
+
+ private boolean responseArgumentUsed = false;
+
+ private ServletHandlerMethodInvoker(HandlerMethodResolver resolver, ActionInterceptor[] actionInterceptors) {
+ super(resolver, webBindingInitializer, amhaSessionAttributeStore, parameterNameDiscoverer,
+ customArgumentResolvers, messageConverters, actionInterceptors);
+ }
+
+ public ModelAndView interceptingInvokeHandlerMethod(Method handlerMethod, Object handler,
+ NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
+
+ int interceptorIndex = -1;
+ ModelAndView mav = null;
+ ActionExecutingContext executingContext = null;
+ Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
+ try {
+ boolean debug = logger.isDebugEnabled();
+ for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
+ Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
+ if (attrValue != null) {
+ implicitModel.addAttribute(attrName, attrValue);
+ }
+ }
+ for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
+ Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
+ Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
+ if (debug) {
+ logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
+ }
+ String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value();
+ if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
+ continue;
+ }
+ ReflectionUtils.makeAccessible(attributeMethodToInvoke);
+ Object attrValue = attributeMethodToInvoke.invoke(handler, args);
+ if ("".equals(attrName)) {
+ Class resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
+ attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
+ }
+ if (!implicitModel.containsAttribute(attrName)) {
+ implicitModel.addAttribute(attrName, attrValue);
+ }
+ }
+ Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
+ if (debug) {
+ logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
+ }
+ ReflectionUtils.makeAccessible(handlerMethodToInvoke);
+
+ executingContext = new ActionExecutingContext((ServletWebRequest) webRequest, handler, handlerMethodToInvoke, args, implicitModel);
+
+ // Apply preHandle methods of registered interceptors.
+ ActionInterceptor[] interceptors = getActionInterceptors();
+ if (interceptors != null) {
+ for (int i = 0; i < interceptors.length; i++) {
+ ActionInterceptor interceptor = interceptors[i];
+ if (!interceptor.preHandle(executingContext)) {
+ triggerAfterCompletion(executingContext, interceptorIndex, null, null);
+ return null; //TODO verify null is ok
+ }
+ interceptorIndex = i;
+ }
+ }
+
+ // Actually invoke the handler
+ Object result = handlerMethodToInvoke.invoke(handler, args);
+
+
+ mav = getModelAndView(handlerMethod, handler.getClass(), result, implicitModel, (ServletWebRequest) webRequest);
+ updateModelAttributes(handler, (mav != null ? mav.getModel() : null), implicitModel, webRequest);
+
+ // Trigger after-completion for successful outcome
+ triggerAfterCompletion(executingContext, interceptorIndex, mav, null);
+
+ return mav;
+ } catch (IllegalStateException ex) {
+ // Internal assertion failed (e.g. invalid signature):
+ // throw exception with full handler method context...
+ triggerAfterCompletion(executingContext, interceptorIndex, mav, ex);
+ throw new HandlerMethodInvocationException(handlerMethodToInvoke, ex);
+ } catch (InvocationTargetException ex) {
+ // User-defined @ModelAttribute/@InitBinder/@RequestMapping method threw an exception...
+ triggerAfterCompletion(executingContext, interceptorIndex, mav, ex);
+ ReflectionUtils.rethrowException(ex.getTargetException());
+ return null;
+ }
+ }
+
+ private void triggerAfterCompletion(ActionExecutingContext executingContext,
+ int interceptorIndex,
+ ModelAndView modelAndView,
+ Exception ex) throws Exception {
+
+ // Apply afterCompletion methods of registered interceptors.
+ if (executingContext.getHandler() != null) {
+ //TODO should be passed in;
+ ActionInterceptor[] interceptors = getActionInterceptors();
+ if (interceptors != null) {
+ for (int i = interceptorIndex; i >= 0; i--) {
+ ActionInterceptor interceptor = interceptors[i];
+ ActionExecutedContext actionExecutedContext = new ActionExecutedContext(executingContext, modelAndView, ex);
+ try {
+ interceptor.afterCompletion(actionExecutedContext);
+ } catch (Throwable ex2) {
+ logger.error("ActionInterceptor threw exception", ex2);
+ }
+ }
+ }
+ }
+ }
+
+
+ @Override
+ protected void raiseMissingParameterException(String paramName, Class paramType) throws Exception {
+ throw new MissingServletRequestParameterException(paramName, paramType.getSimpleName());
+ }
+
+ @Override
+ protected void raiseSessionRequiredException(String message) throws Exception {
+ throw new HttpSessionRequiredException(message);
+ }
+
+ @Override
+ protected WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName)
+ throws Exception {
+
+ return AnnotationMethodHandlerAdapter.this.createBinder(
+ webRequest.getNativeRequest(HttpServletRequest.class), target, objectName);
+ }
+
+ @Override
+ protected void doBind(WebDataBinder binder, NativeWebRequest webRequest) throws Exception {
+ ServletRequestDataBinder servletBinder = (ServletRequestDataBinder) binder;
+ servletBinder.bind(webRequest.getNativeRequest(ServletRequest.class));
+ }
+
+ @Override
+ protected HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest) throws Exception {
+ HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
+ return AnnotationMethodHandlerAdapter.this.createHttpInputMessage(servletRequest);
+ }
+
+ @Override
+ protected HttpOutputMessage createHttpOutputMessage(NativeWebRequest webRequest) throws Exception {
+ HttpServletResponse servletResponse = (HttpServletResponse) webRequest.getNativeResponse();
+ return AnnotationMethodHandlerAdapter.this.createHttpOutputMessage(servletResponse);
+ }
+
+ @Override
+ protected Object resolveDefaultValue(String value) {
+ if (beanFactory == null) {
+ return value;
+ }
+ String placeholdersResolved = beanFactory.resolveEmbeddedValue(value);
+ BeanExpressionResolver exprResolver = beanFactory.getBeanExpressionResolver();
+ if (exprResolver == null) {
+ return value;
+ }
+ return exprResolver.evaluate(placeholdersResolved, expressionContext);
+ }
+
+ @Override
+ protected Object resolveCookieValue(String cookieName, Class paramType, NativeWebRequest webRequest)
+ throws Exception {
+
+ HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
+ Cookie cookieValue = WebUtils.getCookie(servletRequest, cookieName);
+ if (Cookie.class.isAssignableFrom(paramType)) {
+ return cookieValue;
+ } else if (cookieValue != null) {
+ return urlPathHelper.decodeRequestString(servletRequest, cookieValue.getValue());
+ } else {
+ return null;
+ }
+ }
+
+ @Override
+ @SuppressWarnings({"unchecked"})
+ protected String resolvePathVariable(String pathVarName, Class paramType, NativeWebRequest webRequest)
+ throws Exception {
+
+ HttpServletRequest servletRequest = webRequest.getNativeRequest(HttpServletRequest.class);
+ Map uriTemplateVariables =
+ (Map) servletRequest.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE);
+ if (uriTemplateVariables == null || !uriTemplateVariables.containsKey(pathVarName)) {
+ throw new IllegalStateException(
+ "Could not find @PathVariable [" + pathVarName + "] in @RequestMapping");
+ }
+ return uriTemplateVariables.get(pathVarName);
+ }
+
+ @Override
+ protected Object resolveStandardArgument(Class> parameterType, NativeWebRequest webRequest) throws Exception {
+ HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
+ HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
+
+ if (ServletRequest.class.isAssignableFrom(parameterType) ||
+ MultipartRequest.class.isAssignableFrom(parameterType)) {
+ Object nativeRequest = webRequest.getNativeRequest(parameterType);
+ if (nativeRequest == null) {
+ throw new IllegalStateException(
+ "Current request is not of type [" + parameterType.getName() + "]: " + request);
+ }
+ return nativeRequest;
+ } else if (ServletResponse.class.isAssignableFrom(parameterType)) {
+ this.responseArgumentUsed = true;
+ Object nativeResponse = webRequest.getNativeResponse(parameterType);
+ if (nativeResponse == null) {
+ throw new IllegalStateException(
+ "Current response is not of type [" + parameterType.getName() + "]: " + response);
+ }
+ return nativeResponse;
+ } else if (HttpSession.class.isAssignableFrom(parameterType)) {
+ return request.getSession();
+ } else if (Principal.class.isAssignableFrom(parameterType)) {
+ return request.getUserPrincipal();
+ } else if (Locale.class.equals(parameterType)) {
+ return RequestContextUtils.getLocale(request);
+ } else if (InputStream.class.isAssignableFrom(parameterType)) {
+ return request.getInputStream();
+ } else if (Reader.class.isAssignableFrom(parameterType)) {
+ return request.getReader();
+ } else if (OutputStream.class.isAssignableFrom(parameterType)) {
+ this.responseArgumentUsed = true;
+ return response.getOutputStream();
+ } else if (Writer.class.isAssignableFrom(parameterType)) {
+ this.responseArgumentUsed = true;
+ return response.getWriter();
+ }
+ return super.resolveStandardArgument(parameterType, webRequest);
+ }
+
+ @SuppressWarnings("unchecked")
+ public ModelAndView getModelAndView(Method handlerMethod, Class handlerType, Object returnValue,
+ ExtendedModelMap implicitModel, ServletWebRequest webRequest) throws Exception {
+
+ ResponseStatus responseStatusAnn = AnnotationUtils.findAnnotation(handlerMethod, ResponseStatus.class);
+ if (responseStatusAnn != null) {
+ HttpStatus responseStatus = responseStatusAnn.value();
+ String reason = responseStatusAnn.reason();
+ if (!StringUtils.hasText(reason)) {
+ webRequest.getResponse().setStatus(responseStatus.value());
+ } else {
+ webRequest.getResponse().sendError(responseStatus.value(), reason);
+ }
+
+ // to be picked up by the RedirectView
+ webRequest.getRequest().setAttribute(View.RESPONSE_STATUS_ATTRIBUTE, responseStatus);
+
+ responseArgumentUsed = true;
+ }
+
+ // Invoke custom resolvers if present...
+ if (customModelAndViewResolvers != null) {
+ for (ModelAndViewResolver mavResolver : customModelAndViewResolvers) {
+ ModelAndView mav = mavResolver.resolveModelAndView(
+ handlerMethod, handlerType, returnValue, implicitModel, webRequest);
+ if (mav != ModelAndViewResolver.UNRESOLVED) {
+ return mav;
+ }
+ }
+ }
+
+ if (returnValue instanceof HttpEntity) {
+ handleHttpEntityResponse((HttpEntity>) returnValue, webRequest);
+ return null;
+ } else if (AnnotationUtils.findAnnotation(handlerMethod, ResponseBody.class) != null) {
+ handleResponseBody(returnValue, webRequest);
+ return null;
+ } else if (returnValue instanceof ModelAndView) {
+ ModelAndView mav = (ModelAndView) returnValue;
+ mav.getModelMap().mergeAttributes(implicitModel);
+ return mav;
+ } else if (returnValue instanceof Model) {
+ return new ModelAndView().addAllObjects(implicitModel).addAllObjects(((Model) returnValue).asMap());
+ } else if (returnValue instanceof View) {
+ return new ModelAndView((View) returnValue).addAllObjects(implicitModel);
+ } else if (AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class) != null) {
+ addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
+ return new ModelAndView().addAllObjects(implicitModel);
+ } else if (returnValue instanceof Map) {
+ return new ModelAndView().addAllObjects(implicitModel).addAllObjects((Map) returnValue);
+ } else if (returnValue instanceof String) {
+ return new ModelAndView((String) returnValue).addAllObjects(implicitModel);
+ } else if (returnValue == null) {
+ // Either returned null or was 'void' return.
+ if (this.responseArgumentUsed || webRequest.isNotModified()) {
+ return null;
+ } else {
+ // Assuming view name translation...
+ return new ModelAndView().addAllObjects(implicitModel);
+ }
+ } else if (!BeanUtils.isSimpleProperty(returnValue.getClass())) {
+ // Assume a single model attribute...
+ addReturnValueAsModelAttribute(handlerMethod, handlerType, returnValue, implicitModel);
+ return new ModelAndView().addAllObjects(implicitModel);
+ } else {
+ throw new IllegalArgumentException("Invalid handler method return value: " + returnValue);
+ }
+ }
+
+ private void handleResponseBody(Object returnValue, ServletWebRequest webRequest)
+ throws Exception {
+ if (returnValue == null) {
+ return;
+ }
+ HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
+ HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest);
+ writeWithMessageConverters(returnValue, inputMessage, outputMessage);
+ }
+
+ private void handleHttpEntityResponse(HttpEntity> responseEntity, ServletWebRequest webRequest)
+ throws Exception {
+ if (responseEntity == null) {
+ return;
+ }
+ HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
+ HttpOutputMessage outputMessage = createHttpOutputMessage(webRequest);
+ if (responseEntity instanceof ResponseEntity && outputMessage instanceof ServerHttpResponse) {
+ ((ServerHttpResponse) outputMessage).setStatusCode(((ResponseEntity) responseEntity).getStatusCode());
+ }
+ HttpHeaders entityHeaders = responseEntity.getHeaders();
+ if (!entityHeaders.isEmpty()) {
+ outputMessage.getHeaders().putAll(entityHeaders);
+ }
+ Object body = responseEntity.getBody();
+ if (body != null) {
+ writeWithMessageConverters(body, inputMessage, outputMessage);
+ } else {
+ // flush headers
+ outputMessage.getBody();
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private void writeWithMessageConverters(Object returnValue,
+ HttpInputMessage inputMessage, HttpOutputMessage outputMessage)
+ throws IOException, HttpMediaTypeNotAcceptableException {
+ List acceptedMediaTypes = inputMessage.getHeaders().getAccept();
+ if (acceptedMediaTypes.isEmpty()) {
+ acceptedMediaTypes = Collections.singletonList(MediaType.ALL);
+ }
+ MediaType.sortByQualityValue(acceptedMediaTypes);
+ Class> returnValueType = returnValue.getClass();
+ List allSupportedMediaTypes = new ArrayList();
+ if (getMessageConverters() != null) {
+ for (MediaType acceptedMediaType : acceptedMediaTypes) {
+ for (HttpMessageConverter messageConverter : getMessageConverters()) {
+ if (messageConverter.canWrite(returnValueType, acceptedMediaType)) {
+ messageConverter.write(returnValue, acceptedMediaType, outputMessage);
+ if (logger.isDebugEnabled()) {
+ MediaType contentType = outputMessage.getHeaders().getContentType();
+ if (contentType == null) {
+ contentType = acceptedMediaType;
+ }
+ logger.debug("Written [" + returnValue + "] as \"" + contentType +
+ "\" using [" + messageConverter + "]");
+ }
+ this.responseArgumentUsed = true;
+ return;
+ }
+ }
+ }
+ for (HttpMessageConverter messageConverter : messageConverters) {
+ allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
+ }
+ }
+ throw new HttpMediaTypeNotAcceptableException(allSupportedMediaTypes);
+ }
+
+ }
+
+
+ /**
+ * Holder for request mapping metadata. Allows for finding a best matching candidate.
+ */
+ static class RequestMappingInfo {
+
+ String[] patterns = new String[0];
+
+ List matchedPatterns = Collections.emptyList();
+
+ RequestMethod[] methods = new RequestMethod[0];
+
+ String[] params = new String[0];
+
+ String[] headers = new String[0];
+
+ public boolean hasPatterns() {
+ return patterns.length > 0;
+ }
+
+ public String bestMatchedPattern() {
+ return (!this.matchedPatterns.isEmpty() ? this.matchedPatterns.get(0) : null);
+ }
+
+ public boolean matches(HttpServletRequest request) {
+ return matchesRequestMethod(request) && matchesParameters(request) && matchesHeaders(request);
+ }
+
+ public boolean matchesHeaders(HttpServletRequest request) {
+ return ServletAnnotationMappingUtils.checkHeaders(this.headers, request);
+ }
+
+ public boolean matchesParameters(HttpServletRequest request) {
+ return ServletAnnotationMappingUtils.checkParameters(this.params, request);
+ }
+
+ public boolean matchesRequestMethod(HttpServletRequest request) {
+ return ServletAnnotationMappingUtils.checkRequestMethod(this.methods, request);
+ }
+
+ public Set methodNames() {
+ Set methodNames = new LinkedHashSet(methods.length);
+ for (RequestMethod method : methods) {
+ methodNames.add(method.name());
+ }
+ return methodNames;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ RequestMappingInfo other = (RequestMappingInfo) obj;
+ return (Arrays.equals(this.patterns, other.patterns) && Arrays.equals(this.methods, other.methods) &&
+ Arrays.equals(this.params, other.params) && Arrays.equals(this.headers, other.headers));
+ }
+
+ @Override
+ public int hashCode() {
+ return (Arrays.hashCode(this.patterns) * 23 + Arrays.hashCode(this.methods) * 29 +
+ Arrays.hashCode(this.params) * 31 + Arrays.hashCode(this.headers));
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(Arrays.asList(patterns));
+ if (methods.length > 0) {
+ builder.append(',');
+ builder.append(Arrays.asList(methods));
+ }
+ if (headers.length > 0) {
+ builder.append(',');
+ builder.append(Arrays.asList(headers));
+ }
+ if (params.length > 0) {
+ builder.append(',');
+ builder.append(Arrays.asList(params));
+ }
+ return builder.toString();
+ }
+ }
+
+
+ /**
+ * Comparator capable of sorting {@link RequestMappingInfo}s (RHIs) so that sorting a list with this comparator will
+ * result in:
+ *
+ * RHIs with {@linkplain RequestMappingInfo#matchedPatterns better matched paths} take prescedence
+ * over those with a weaker match (as expressed by the {@linkplain PathMatcher#getPatternComparator(String) path
+ * pattern comparator}.) Typically, this means that patterns without wild cards and uri templates will be ordered
+ * before those without.
+ * RHIs with one single {@linkplain RequestMappingInfo#methods request method} will be
+ * ordered before those without a method, or with more than one method.
+ * RHIs with more {@linkplain RequestMappingInfo#params request parameters} will be ordered before those with
+ * less parameters
+ *
+ */
+ static class RequestMappingInfoComparator implements Comparator {
+
+ private final Comparator pathComparator;
+
+ private final ServerHttpRequest request;
+
+ RequestMappingInfoComparator(Comparator pathComparator, HttpServletRequest request) {
+ this.pathComparator = pathComparator;
+ this.request = new ServletServerHttpRequest(request);
+ }
+
+ public int compare(RequestMappingInfo info1, RequestMappingInfo info2) {
+ int pathComparison = pathComparator.compare(info1.bestMatchedPattern(), info2.bestMatchedPattern());
+ if (pathComparison != 0) {
+ return pathComparison;
+ }
+ int info1ParamCount = info1.params.length;
+ int info2ParamCount = info2.params.length;
+ if (info1ParamCount != info2ParamCount) {
+ return info2ParamCount - info1ParamCount;
+ }
+ int info1HeaderCount = info1.headers.length;
+ int info2HeaderCount = info2.headers.length;
+ if (info1HeaderCount != info2HeaderCount) {
+ return info2HeaderCount - info1HeaderCount;
+ }
+ int acceptComparison = compareAcceptHeaders(info1, info2);
+ if (acceptComparison != 0) {
+ return acceptComparison;
+ }
+ int info1MethodCount = info1.methods.length;
+ int info2MethodCount = info2.methods.length;
+ if (info1MethodCount == 0 && info2MethodCount > 0) {
+ return 1;
+ } else if (info2MethodCount == 0 && info1MethodCount > 0) {
+ return -1;
+ } else if (info1MethodCount == 1 & info2MethodCount > 1) {
+ return -1;
+ } else if (info2MethodCount == 1 & info1MethodCount > 1) {
+ return 1;
+ }
+ return 0;
+ }
+
+ private int compareAcceptHeaders(RequestMappingInfo info1, RequestMappingInfo info2) {
+ List requestAccepts = request.getHeaders().getAccept();
+ MediaType.sortByQualityValue(requestAccepts);
+
+ List info1Accepts = getAcceptHeaderValue(info1);
+ List info2Accepts = getAcceptHeaderValue(info2);
+
+ for (MediaType requestAccept : requestAccepts) {
+ int pos1 = indexOfIncluded(info1Accepts, requestAccept);
+ int pos2 = indexOfIncluded(info2Accepts, requestAccept);
+ if (pos1 != pos2) {
+ return pos2 - pos1;
+ }
+ }
+ return 0;
+ }
+
+ private int indexOfIncluded(List infoAccepts, MediaType requestAccept) {
+ for (int i = 0; i < infoAccepts.size(); i++) {
+ MediaType info1Accept = infoAccepts.get(i);
+ if (requestAccept.includes(info1Accept)) {
+ return i;
+ }
+ }
+ return -1;
+ }
+
+ private List getAcceptHeaderValue(RequestMappingInfo info) {
+ for (String header : info.headers) {
+ int separator = header.indexOf('=');
+ if (separator != -1) {
+ String key = header.substring(0, separator);
+ String value = header.substring(separator + 1);
+ if ("Accept".equalsIgnoreCase(key)) {
+ return MediaType.parseMediaTypes(value);
+ }
+ }
+ }
+ return Collections.emptyList();
+ }
+ }
}
diff --git a/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/mvc/annotation/ServletAnnotationMappingUtils.java b/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/mvc/annotation/ServletAnnotationMappingUtils.java
index 279c8904b..5d0bd7386 100644
--- a/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/mvc/annotation/ServletAnnotationMappingUtils.java
+++ b/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/mvc/annotation/ServletAnnotationMappingUtils.java
@@ -34,110 +34,108 @@ import org.springframework.web.util.WebUtils;
*/
abstract class ServletAnnotationMappingUtils {
- /**
- * Check whether the given request matches the specified request methods.
- * @param methods the HTTP request methods to check against
- * @param request the current HTTP request to check
- */
- public static boolean checkRequestMethod(RequestMethod[] methods, HttpServletRequest request) {
- if (ObjectUtils.isEmpty(methods)) {
- return true;
- }
- for (RequestMethod method : methods) {
- if (method.name().equals(request.getMethod())) {
- return true;
- }
- }
- return false;
- }
+ /**
+ * Check whether the given request matches the specified request methods.
+ *
+ * @param methods the HTTP request methods to check against
+ * @param request the current HTTP request to check
+ */
+ public static boolean checkRequestMethod(RequestMethod[] methods, HttpServletRequest request) {
+ if (ObjectUtils.isEmpty(methods)) {
+ return true;
+ }
+ for (RequestMethod method : methods) {
+ if (method.name().equals(request.getMethod())) {
+ return true;
+ }
+ }
+ return false;
+ }
- /**
- * Check whether the given request matches the specified parameter conditions.
- * @param params the parameter conditions, following
- * {@link org.springframework.web.bind.annotation.RequestMapping#params() RequestMapping.#params()}
- * @param request the current HTTP request to check
- */
- public static boolean checkParameters(String[] params, HttpServletRequest request) {
- if (!ObjectUtils.isEmpty(params)) {
- for (String param : params) {
- int separator = param.indexOf('=');
- if (separator == -1) {
- if (param.startsWith("!")) {
- if (WebUtils.hasSubmitParameter(request, param.substring(1))) {
- return false;
- }
- }
- else if (!WebUtils.hasSubmitParameter(request, param)) {
- return false;
- }
- }
- else {
- boolean negated = separator > 0 && param.charAt(separator - 1) == '!';
- String key = !negated ? param.substring(0, separator) : param.substring(0, separator - 1);
- String value = param.substring(separator + 1);
- if (!value.equals(request.getParameter(key))) {
- return negated;
- }
- }
- }
- }
- return true;
- }
+ /**
+ * Check whether the given request matches the specified parameter conditions.
+ *
+ * @param params the parameter conditions, following
+ * {@link org.springframework.web.bind.annotation.RequestMapping#params() RequestMapping.#params()}
+ * @param request the current HTTP request to check
+ */
+ public static boolean checkParameters(String[] params, HttpServletRequest request) {
+ if (!ObjectUtils.isEmpty(params)) {
+ for (String param : params) {
+ int separator = param.indexOf('=');
+ if (separator == -1) {
+ if (param.startsWith("!")) {
+ if (WebUtils.hasSubmitParameter(request, param.substring(1))) {
+ return false;
+ }
+ } else if (!WebUtils.hasSubmitParameter(request, param)) {
+ return false;
+ }
+ } else {
+ boolean negated = separator > 0 && param.charAt(separator - 1) == '!';
+ String key = !negated ? param.substring(0, separator) : param.substring(0, separator - 1);
+ String value = param.substring(separator + 1);
+ if (!value.equals(request.getParameter(key))) {
+ return negated;
+ }
+ }
+ }
+ }
+ return true;
+ }
- /**
- * Check whether the given request matches the specified header conditions.
- * @param headers the header conditions, following
- * {@link org.springframework.web.bind.annotation.RequestMapping#headers() RequestMapping.headers()}
- * @param request the current HTTP request to check
- */
- public static boolean checkHeaders(String[] headers, HttpServletRequest request) {
- if (!ObjectUtils.isEmpty(headers)) {
- for (String header : headers) {
- int separator = header.indexOf('=');
- if (separator == -1) {
- if (header.startsWith("!")) {
- if (request.getHeader(header.substring(1)) != null) {
- return false;
- }
- }
- else if (request.getHeader(header) == null) {
- return false;
- }
- }
- else {
- boolean negated = separator > 0 && header.charAt(separator - 1) == '!';
- String key = !negated ? header.substring(0, separator) : header.substring(0, separator - 1);
- String value = header.substring(separator + 1);
- if (isMediaTypeHeader(key)) {
- List requestMediaTypes = MediaType.parseMediaTypes(request.getHeader(key));
- List valueMediaTypes = MediaType.parseMediaTypes(value);
- boolean found = false;
- for (Iterator valIter = valueMediaTypes.iterator(); valIter.hasNext() && !found;) {
- MediaType valueMediaType = valIter.next();
- for (Iterator reqIter = requestMediaTypes.iterator();
- reqIter.hasNext() && !found;) {
- MediaType requestMediaType = reqIter.next();
- if (valueMediaType.includes(requestMediaType)) {
- found = true;
- }
- }
+ /**
+ * Check whether the given request matches the specified header conditions.
+ *
+ * @param headers the header conditions, following
+ * {@link org.springframework.web.bind.annotation.RequestMapping#headers() RequestMapping.headers()}
+ * @param request the current HTTP request to check
+ */
+ public static boolean checkHeaders(String[] headers, HttpServletRequest request) {
+ if (!ObjectUtils.isEmpty(headers)) {
+ for (String header : headers) {
+ int separator = header.indexOf('=');
+ if (separator == -1) {
+ if (header.startsWith("!")) {
+ if (request.getHeader(header.substring(1)) != null) {
+ return false;
+ }
+ } else if (request.getHeader(header) == null) {
+ return false;
+ }
+ } else {
+ boolean negated = separator > 0 && header.charAt(separator - 1) == '!';
+ String key = !negated ? header.substring(0, separator) : header.substring(0, separator - 1);
+ String value = header.substring(separator + 1);
+ if (isMediaTypeHeader(key)) {
+ List requestMediaTypes = MediaType.parseMediaTypes(request.getHeader(key));
+ List valueMediaTypes = MediaType.parseMediaTypes(value);
+ boolean found = false;
+ for (Iterator valIter = valueMediaTypes.iterator(); valIter.hasNext() && !found;) {
+ MediaType valueMediaType = valIter.next();
+ for (Iterator reqIter = requestMediaTypes.iterator();
+ reqIter.hasNext() && !found;) {
+ MediaType requestMediaType = reqIter.next();
+ if (valueMediaType.includes(requestMediaType)) {
+ found = true;
+ }
+ }
- }
- if (!found) {
- return negated;
- }
- }
- else if (!value.equals(request.getHeader(key))) {
- return negated;
- }
- }
- }
- }
- return true;
- }
+ }
+ if (!found) {
+ return negated;
+ }
+ } else if (!value.equals(request.getHeader(key))) {
+ return negated;
+ }
+ }
+ }
+ }
+ return true;
+ }
- private static boolean isMediaTypeHeader(String headerName) {
- return "Accept".equalsIgnoreCase(headerName) || "Content-Type".equalsIgnoreCase(headerName);
- }
+ private static boolean isMediaTypeHeader(String headerName) {
+ return "Accept".equalsIgnoreCase(headerName) || "Content-Type".equalsIgnoreCase(headerName);
+ }
}
diff --git a/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/mvc/annotation/support/InterceptingHandlerMethodInvoker.java b/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/mvc/annotation/support/InterceptingHandlerMethodInvoker.java
index 8918ace3b..76abcbc31 100644
--- a/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/mvc/annotation/support/InterceptingHandlerMethodInvoker.java
+++ b/spring-data-document-core/src/main/java/org/springframework/data/document/web/servlet/mvc/annotation/support/InterceptingHandlerMethodInvoker.java
@@ -17,68 +17,30 @@
package org.springframework.data.document.web.servlet.mvc.annotation.support;
import java.lang.annotation.Annotation;
-import java.lang.reflect.Array;
-import java.lang.reflect.GenericArrayType;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.ParameterizedType;
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Iterator;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
+import java.lang.reflect.*;
+import java.util.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
-
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
-import org.springframework.core.BridgeMethodResolver;
-import org.springframework.core.Conventions;
-import org.springframework.core.GenericTypeResolver;
-import org.springframework.core.MethodParameter;
-import org.springframework.core.ParameterNameDiscoverer;
+import org.springframework.core.*;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.document.web.servlet.ActionInterceptor;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpInputMessage;
-import org.springframework.http.HttpOutputMessage;
-import org.springframework.http.MediaType;
+import org.springframework.http.*;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.ui.ExtendedModelMap;
import org.springframework.ui.Model;
-import org.springframework.util.Assert;
-import org.springframework.util.ClassUtils;
-import org.springframework.util.LinkedMultiValueMap;
-import org.springframework.util.MultiValueMap;
-import org.springframework.util.ReflectionUtils;
+import org.springframework.util.*;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.bind.WebDataBinder;
-import org.springframework.web.bind.annotation.CookieValue;
-import org.springframework.web.bind.annotation.InitBinder;
-import org.springframework.web.bind.annotation.ModelAttribute;
-import org.springframework.web.bind.annotation.PathVariable;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestHeader;
-import org.springframework.web.bind.annotation.RequestParam;
-import org.springframework.web.bind.annotation.ValueConstants;
+import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.support.HandlerMethodInvocationException;
import org.springframework.web.bind.annotation.support.HandlerMethodResolver;
-import org.springframework.web.bind.support.DefaultSessionAttributeStore;
-import org.springframework.web.bind.support.SessionAttributeStore;
-import org.springframework.web.bind.support.SessionStatus;
-import org.springframework.web.bind.support.SimpleSessionStatus;
-import org.springframework.web.bind.support.WebArgumentResolver;
-import org.springframework.web.bind.support.WebBindingInitializer;
-import org.springframework.web.bind.support.WebRequestDataBinder;
+import org.springframework.web.bind.support.*;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.multipart.MultipartFile;
@@ -87,819 +49,780 @@ import org.springframework.web.multipart.MultipartRequest;
/**
* Support class for invoking an annotated handler method. Operates on the introspection results of a {@link
* HandlerMethodResolver} for a specific handler type.
- *
+ *
* Used by {@link org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter} and {@link
* org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter}.
*
* @author Juergen Hoeller
* @author Arjen Poutsma
- * @since 2.5.2
* @see #invokeHandlerMethod
+ * @since 2.5.2
*/
public class InterceptingHandlerMethodInvoker {
- private static final String MODEL_KEY_PREFIX_STALE = SessionAttributeStore.class.getName() + ".STALE.";
-
- /** We'll create a lot of these objects, so we don't want a new logger every time. */
- private static final Log logger = LogFactory.getLog(InterceptingHandlerMethodInvoker.class);
-
- protected final HandlerMethodResolver methodResolver;
-
- private final WebBindingInitializer bindingInitializer;
-
- protected final SessionAttributeStore sessionAttributeStore;
-
- private final ParameterNameDiscoverer parameterNameDiscoverer;
-
- private final WebArgumentResolver[] customArgumentResolvers;
-
- private final HttpMessageConverter[] messageConverters;
-
- private final ActionInterceptor[] actionInterceptors;
-
- private final SimpleSessionStatus sessionStatus = new SimpleSessionStatus();
-
-
- public InterceptingHandlerMethodInvoker(HandlerMethodResolver methodResolver) {
- this(methodResolver, null);
- }
-
- public InterceptingHandlerMethodInvoker(HandlerMethodResolver methodResolver, WebBindingInitializer bindingInitializer) {
- this(methodResolver, bindingInitializer, new DefaultSessionAttributeStore(), null, null, null, null);
- }
-
- public InterceptingHandlerMethodInvoker(HandlerMethodResolver methodResolver, WebBindingInitializer bindingInitializer,
- SessionAttributeStore sessionAttributeStore, ParameterNameDiscoverer parameterNameDiscoverer,
- WebArgumentResolver[] customArgumentResolvers, HttpMessageConverter[] messageConverters, ActionInterceptor[] actionInterceptors) {
-
- this.methodResolver = methodResolver;
- this.bindingInitializer = bindingInitializer;
- this.sessionAttributeStore = sessionAttributeStore;
- this.parameterNameDiscoverer = parameterNameDiscoverer;
- this.customArgumentResolvers = customArgumentResolvers;
- this.messageConverters = messageConverters;
- this.actionInterceptors = actionInterceptors;
- }
-
- protected ActionInterceptor[] getActionInterceptors() {
- return actionInterceptors;
- }
-
-
- public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
- NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
-
- Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
- try {
- boolean debug = logger.isDebugEnabled();
- for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
- Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
- if (attrValue != null) {
- implicitModel.addAttribute(attrName, attrValue);
- }
- }
- for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
- Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
- Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
- if (debug) {
- logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
- }
- String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value();
- if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
- continue;
- }
- ReflectionUtils.makeAccessible(attributeMethodToInvoke);
- Object attrValue = attributeMethodToInvoke.invoke(handler, args);
- if ("".equals(attrName)) {
- Class resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
- attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
- }
- if (!implicitModel.containsAttribute(attrName)) {
- implicitModel.addAttribute(attrName, attrValue);
- }
- }
- Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
- if (debug) {
- logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
- }
- ReflectionUtils.makeAccessible(handlerMethodToInvoke);
- return handlerMethodToInvoke.invoke(handler, args);
- }
- catch (IllegalStateException ex) {
- // Internal assertion failed (e.g. invalid signature):
- // throw exception with full handler method context...
- throw new HandlerMethodInvocationException(handlerMethodToInvoke, ex);
- }
- catch (InvocationTargetException ex) {
- // User-defined @ModelAttribute/@InitBinder/@RequestMapping method threw an exception...
- ReflectionUtils.rethrowException(ex.getTargetException());
- return null;
- }
- }
-
- public final void updateModelAttributes(Object handler, Map mavModel,
- ExtendedModelMap implicitModel, NativeWebRequest webRequest) throws Exception {
-
- if (this.methodResolver.hasSessionAttributes() && this.sessionStatus.isComplete()) {
- for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
- this.sessionAttributeStore.cleanupAttribute(webRequest, attrName);
- }
- }
-
- // Expose model attributes as session attributes, if required.
- // Expose BindingResults for all attributes, making custom editors available.
- Map model = (mavModel != null ? mavModel : implicitModel);
- if (model != null) {
- try {
- String[] originalAttrNames = model.keySet().toArray(new String[model.size()]);
- for (String attrName : originalAttrNames) {
- Object attrValue = model.get(attrName);
- boolean isSessionAttr = this.methodResolver.isSessionAttribute(
- attrName, (attrValue != null ? attrValue.getClass() : null));
- if (isSessionAttr) {
- if (this.sessionStatus.isComplete()) {
- implicitModel.put(MODEL_KEY_PREFIX_STALE + attrName, Boolean.TRUE);
- }
- else if (!implicitModel.containsKey(MODEL_KEY_PREFIX_STALE + attrName)) {
- this.sessionAttributeStore.storeAttribute(webRequest, attrName, attrValue);
- }
- }
- if (!attrName.startsWith(BindingResult.MODEL_KEY_PREFIX) &&
- (isSessionAttr || isBindingCandidate(attrValue))) {
- String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attrName;
- if (mavModel != null && !model.containsKey(bindingResultKey)) {
- WebDataBinder binder = createBinder(webRequest, attrValue, attrName);
- initBinder(handler, attrName, binder, webRequest);
- mavModel.put(bindingResultKey, binder.getBindingResult());
- }
- }
- }
- }
- catch (InvocationTargetException ex) {
- // User-defined @InitBinder method threw an exception...
- ReflectionUtils.rethrowException(ex.getTargetException());
- }
- }
- }
-
-
- protected Object[] resolveHandlerArguments(Method handlerMethod, Object handler,
- NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
-
- Class[] paramTypes = handlerMethod.getParameterTypes();
- Object[] args = new Object[paramTypes.length];
-
- for (int i = 0; i < args.length; i++) {
- MethodParameter methodParam = new MethodParameter(handlerMethod, i);
- methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
- GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
- String paramName = null;
- String headerName = null;
- boolean requestBodyFound = false;
- String cookieName = null;
- String pathVarName = null;
- String attrName = null;
- boolean required = false;
- String defaultValue = null;
- boolean validate = false;
- int annotationsFound = 0;
- Annotation[] paramAnns = methodParam.getParameterAnnotations();
-
- for (Annotation paramAnn : paramAnns) {
- if (RequestParam.class.isInstance(paramAnn)) {
- RequestParam requestParam = (RequestParam) paramAnn;
- paramName = requestParam.value();
- required = requestParam.required();
- defaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
- annotationsFound++;
- }
- else if (RequestHeader.class.isInstance(paramAnn)) {
- RequestHeader requestHeader = (RequestHeader) paramAnn;
- headerName = requestHeader.value();
- required = requestHeader.required();
- defaultValue = parseDefaultValueAttribute(requestHeader.defaultValue());
- annotationsFound++;
- }
- else if (RequestBody.class.isInstance(paramAnn)) {
- requestBodyFound = true;
- annotationsFound++;
- }
- else if (CookieValue.class.isInstance(paramAnn)) {
- CookieValue cookieValue = (CookieValue) paramAnn;
- cookieName = cookieValue.value();
- required = cookieValue.required();
- defaultValue = parseDefaultValueAttribute(cookieValue.defaultValue());
- annotationsFound++;
- }
- else if (PathVariable.class.isInstance(paramAnn)) {
- PathVariable pathVar = (PathVariable) paramAnn;
- pathVarName = pathVar.value();
- annotationsFound++;
- }
- else if (ModelAttribute.class.isInstance(paramAnn)) {
- ModelAttribute attr = (ModelAttribute) paramAnn;
- attrName = attr.value();
- annotationsFound++;
- }
- else if (Value.class.isInstance(paramAnn)) {
- defaultValue = ((Value) paramAnn).value();
- }
- else if ("Valid".equals(paramAnn.annotationType().getSimpleName())) {
- validate = true;
- }
- }
-
- if (annotationsFound > 1) {
- throw new IllegalStateException("Handler parameter annotations are exclusive choices - " +
- "do not specify more than one such annotation on the same parameter: " + handlerMethod);
- }
-
- if (annotationsFound == 0) {
- Object argValue = resolveCommonArgument(methodParam, webRequest);
- if (argValue != WebArgumentResolver.UNRESOLVED) {
- args[i] = argValue;
- }
- else if (defaultValue != null) {
- args[i] = resolveDefaultValue(defaultValue);
- }
- else {
- Class paramType = methodParam.getParameterType();
- if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
- args[i] = implicitModel;
- }
- else if (SessionStatus.class.isAssignableFrom(paramType)) {
- args[i] = this.sessionStatus;
- }
- else if (HttpEntity.class.isAssignableFrom(paramType)) {
- args[i] = resolveHttpEntityRequest(methodParam, webRequest);
- }
- else if (Errors.class.isAssignableFrom(paramType)) {
- throw new IllegalStateException("Errors/BindingResult argument declared " +
- "without preceding model attribute. Check your handler method signature!");
- }
- else if (BeanUtils.isSimpleProperty(paramType)) {
- paramName = "";
- }
- else {
- attrName = "";
- }
- }
- }
-
- if (paramName != null) {
- args[i] = resolveRequestParam(paramName, required, defaultValue, methodParam, webRequest, handler);
- }
- else if (headerName != null) {
- args[i] = resolveRequestHeader(headerName, required, defaultValue, methodParam, webRequest, handler);
- }
- else if (requestBodyFound) {
- args[i] = resolveRequestBody(methodParam, webRequest, handler);
- }
- else if (cookieName != null) {
- args[i] = resolveCookieValue(cookieName, required, defaultValue, methodParam, webRequest, handler);
- }
- else if (pathVarName != null) {
- args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler);
- }
- else if (attrName != null) {
- WebDataBinder binder =
- resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
- boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
- if (binder.getTarget() != null) {
- doBind(binder, webRequest, validate, !assignBindingResult);
- }
- args[i] = binder.getTarget();
- if (assignBindingResult) {
- args[i + 1] = binder.getBindingResult();
- i++;
- }
- implicitModel.putAll(binder.getBindingResult().getModel());
- }
- }
-
- return args;
- }
-
- protected void initBinder(Object handler, String attrName, WebDataBinder binder, NativeWebRequest webRequest)
- throws Exception {
-
- if (this.bindingInitializer != null) {
- this.bindingInitializer.initBinder(binder, webRequest);
- }
- if (handler != null) {
- Set initBinderMethods = this.methodResolver.getInitBinderMethods();
- if (!initBinderMethods.isEmpty()) {
- boolean debug = logger.isDebugEnabled();
- for (Method initBinderMethod : initBinderMethods) {
- Method methodToInvoke = BridgeMethodResolver.findBridgedMethod(initBinderMethod);
- String[] targetNames = AnnotationUtils.findAnnotation(initBinderMethod, InitBinder.class).value();
- if (targetNames.length == 0 || Arrays.asList(targetNames).contains(attrName)) {
- Object[] initBinderArgs =
- resolveInitBinderArguments(handler, methodToInvoke, binder, webRequest);
- if (debug) {
- logger.debug("Invoking init-binder method: " + methodToInvoke);
- }
- ReflectionUtils.makeAccessible(methodToInvoke);
- Object returnValue = methodToInvoke.invoke(handler, initBinderArgs);
- if (returnValue != null) {
- throw new IllegalStateException(
- "InitBinder methods must not have a return value: " + methodToInvoke);
- }
- }
- }
- }
- }
- }
-
- private Object[] resolveInitBinderArguments(Object handler, Method initBinderMethod,
- WebDataBinder binder, NativeWebRequest webRequest) throws Exception {
-
- Class[] initBinderParams = initBinderMethod.getParameterTypes();
- Object[] initBinderArgs = new Object[initBinderParams.length];
-
- for (int i = 0; i < initBinderArgs.length; i++) {
- MethodParameter methodParam = new MethodParameter(initBinderMethod, i);
- methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
- GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
- String paramName = null;
- boolean paramRequired = false;
- String paramDefaultValue = null;
- String pathVarName = null;
- Annotation[] paramAnns = methodParam.getParameterAnnotations();
-
- for (Annotation paramAnn : paramAnns) {
- if (RequestParam.class.isInstance(paramAnn)) {
- RequestParam requestParam = (RequestParam) paramAnn;
- paramName = requestParam.value();
- paramRequired = requestParam.required();
- paramDefaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
- break;
- }
- else if (ModelAttribute.class.isInstance(paramAnn)) {
- throw new IllegalStateException(
- "@ModelAttribute is not supported on @InitBinder methods: " + initBinderMethod);
- }
- else if (PathVariable.class.isInstance(paramAnn)) {
- PathVariable pathVar = (PathVariable) paramAnn;
- pathVarName = pathVar.value();
- }
- }
-
- if (paramName == null && pathVarName == null) {
- Object argValue = resolveCommonArgument(methodParam, webRequest);
- if (argValue != WebArgumentResolver.UNRESOLVED) {
- initBinderArgs[i] = argValue;
- }
- else {
- Class paramType = initBinderParams[i];
- if (paramType.isInstance(binder)) {
- initBinderArgs[i] = binder;
- }
- else if (BeanUtils.isSimpleProperty(paramType)) {
- paramName = "";
- }
- else {
- throw new IllegalStateException("Unsupported argument [" + paramType.getName() +
- "] for @InitBinder method: " + initBinderMethod);
- }
- }
- }
-
- if (paramName != null) {
- initBinderArgs[i] =
- resolveRequestParam(paramName, paramRequired, paramDefaultValue, methodParam, webRequest, null);
- }
- else if (pathVarName != null) {
- initBinderArgs[i] = resolvePathVariable(pathVarName, methodParam, webRequest, null);
- }
- }
-
- return initBinderArgs;
- }
-
- @SuppressWarnings("unchecked")
- private Object resolveRequestParam(String paramName, boolean required, String defaultValue,
- MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
- throws Exception {
-
- Class> paramType = methodParam.getParameterType();
- if (Map.class.isAssignableFrom(paramType) && paramName.length() == 0) {
- return resolveRequestParamMap((Class extends Map>) paramType, webRequest);
- }
- if (paramName.length() == 0) {
- paramName = getRequiredParameterName(methodParam);
- }
- Object paramValue = null;
- MultipartRequest multipartRequest = webRequest.getNativeRequest(MultipartRequest.class);
- if (multipartRequest != null) {
- List files = multipartRequest.getFiles(paramName);
- if (!files.isEmpty()) {
- if (files.size() == 1 && !paramType.isArray() && !Collection.class.isAssignableFrom(paramType)) {
- paramValue = files.get(0);
- }
- else {
- paramValue = files;
- }
- }
- }
- if (paramValue == null) {
- String[] paramValues = webRequest.getParameterValues(paramName);
- if (paramValues != null) {
- if (paramValues.length == 1 && !paramType.isArray() && !Collection.class.isAssignableFrom(paramType)) {
- paramValue = paramValues[0];
- }
- else {
- paramValue = paramValues;
- }
- }
- }
- if (paramValue == null) {
- if (defaultValue != null) {
- paramValue = resolveDefaultValue(defaultValue);
- }
- else if (required) {
- raiseMissingParameterException(paramName, paramType);
- }
- paramValue = checkValue(paramName, paramValue, paramType);
- }
- WebDataBinder binder = createBinder(webRequest, null, paramName);
- initBinder(handlerForInitBinderCall, paramName, binder, webRequest);
- return binder.convertIfNecessary(paramValue, paramType, methodParam);
- }
-
- private Map resolveRequestParamMap(Class extends Map> mapType, NativeWebRequest webRequest) {
- Map parameterMap = webRequest.getParameterMap();
- if (MultiValueMap.class.isAssignableFrom(mapType)) {
- MultiValueMap result = new LinkedMultiValueMap(parameterMap.size());
- for (Map.Entry entry : parameterMap.entrySet()) {
- for (String value : entry.getValue()) {
- result.add(entry.getKey(), value);
- }
- }
- return result;
- }
- else {
- Map result = new LinkedHashMap(parameterMap.size());
- for (Map.Entry entry : parameterMap.entrySet()) {
- if (entry.getValue().length > 0) {
- result.put(entry.getKey(), entry.getValue()[0]);
- }
- }
- return result;
- }
- }
-
- @SuppressWarnings("unchecked")
- private Object resolveRequestHeader(String headerName, boolean required, String defaultValue,
- MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
- throws Exception {
-
- Class> paramType = methodParam.getParameterType();
- if (Map.class.isAssignableFrom(paramType)) {
- return resolveRequestHeaderMap((Class extends Map>) paramType, webRequest);
- }
- if (headerName.length() == 0) {
- headerName = getRequiredParameterName(methodParam);
- }
- Object headerValue = null;
- String[] headerValues = webRequest.getHeaderValues(headerName);
- if (headerValues != null) {
- headerValue = (headerValues.length == 1 ? headerValues[0] : headerValues);
- }
- if (headerValue == null) {
- if (defaultValue != null) {
- headerValue = resolveDefaultValue(defaultValue);
- }
- else if (required) {
- raiseMissingHeaderException(headerName, paramType);
- }
- headerValue = checkValue(headerName, headerValue, paramType);
- }
- WebDataBinder binder = createBinder(webRequest, null, headerName);
- initBinder(handlerForInitBinderCall, headerName, binder, webRequest);
- return binder.convertIfNecessary(headerValue, paramType, methodParam);
- }
-
- private Map resolveRequestHeaderMap(Class extends Map> mapType, NativeWebRequest webRequest) {
- if (MultiValueMap.class.isAssignableFrom(mapType)) {
- MultiValueMap result;
- if (HttpHeaders.class.isAssignableFrom(mapType)) {
- result = new HttpHeaders();
- }
- else {
- result = new LinkedMultiValueMap();
- }
- for (Iterator iterator = webRequest.getHeaderNames(); iterator.hasNext();) {
- String headerName = iterator.next();
- for (String headerValue : webRequest.getHeaderValues(headerName)) {
- result.add(headerName, headerValue);
- }
- }
- return result;
- }
- else {
- Map result = new LinkedHashMap();
- for (Iterator iterator = webRequest.getHeaderNames(); iterator.hasNext();) {
- String headerName = iterator.next();
- String headerValue = webRequest.getHeader(headerName);
- result.put(headerName, headerValue);
- }
- return result;
- }
- }
-
- /**
- * Resolves the given {@link RequestBody @RequestBody} annotation.
- */
- protected Object resolveRequestBody(MethodParameter methodParam, NativeWebRequest webRequest, Object handler)
- throws Exception {
-
- return readWithMessageConverters(methodParam, createHttpInputMessage(webRequest), methodParam.getParameterType());
- }
-
- private HttpEntity resolveHttpEntityRequest(MethodParameter methodParam, NativeWebRequest webRequest)
- throws Exception {
-
- HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
- Class> paramType = getHttpEntityType(methodParam);
- Object body = readWithMessageConverters(methodParam, inputMessage, paramType);
- return new HttpEntity(body, inputMessage.getHeaders());
- }
-
- private Object readWithMessageConverters(MethodParameter methodParam, HttpInputMessage inputMessage, Class paramType)
- throws Exception {
-
- MediaType contentType = inputMessage.getHeaders().getContentType();
- if (contentType == null) {
- StringBuilder builder = new StringBuilder(ClassUtils.getShortName(methodParam.getParameterType()));
- String paramName = methodParam.getParameterName();
- if (paramName != null) {
- builder.append(' ');
- builder.append(paramName);
- }
- throw new HttpMediaTypeNotSupportedException(
- "Cannot extract parameter (" + builder.toString() + "): no Content-Type found");
- }
-
- List allSupportedMediaTypes = new ArrayList();
- if (this.messageConverters != null) {
- for (HttpMessageConverter> messageConverter : this.messageConverters) {
- allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
- if (messageConverter.canRead(paramType, contentType)) {
- if (logger.isDebugEnabled()) {
- logger.debug("Reading [" + paramType.getName() + "] as \"" + contentType
- +"\" using [" + messageConverter + "]");
- }
- return messageConverter.read(paramType, inputMessage);
- }
- }
- }
- throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);
- }
-
- private Class> getHttpEntityType(MethodParameter methodParam) {
- Assert.isAssignable(HttpEntity.class, methodParam.getParameterType());
- ParameterizedType type = (ParameterizedType) methodParam.getGenericParameterType();
- if (type.getActualTypeArguments().length == 1) {
- Type typeArgument = type.getActualTypeArguments()[0];
- if (typeArgument instanceof Class) {
- return (Class>) typeArgument;
- }
- else if (typeArgument instanceof GenericArrayType) {
- Type componentType = ((GenericArrayType) typeArgument).getGenericComponentType();
- if (componentType instanceof Class) {
- // Surely, there should be a nicer way to do this
- Object array = Array.newInstance((Class>) componentType, 0);
- return array.getClass();
- }
- }
- }
- throw new IllegalArgumentException(
- "HttpEntity parameter (" + methodParam.getParameterName() + ") is not parameterized");
-
- }
-
- private Object resolveCookieValue(String cookieName, boolean required, String defaultValue,
- MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
- throws Exception {
-
- Class> paramType = methodParam.getParameterType();
- if (cookieName.length() == 0) {
- cookieName = getRequiredParameterName(methodParam);
- }
- Object cookieValue = resolveCookieValue(cookieName, paramType, webRequest);
- if (cookieValue == null) {
- if (defaultValue != null) {
- cookieValue = resolveDefaultValue(defaultValue);
- }
- else if (required) {
- raiseMissingCookieException(cookieName, paramType);
- }
- cookieValue = checkValue(cookieName, cookieValue, paramType);
- }
- WebDataBinder binder = createBinder(webRequest, null, cookieName);
- initBinder(handlerForInitBinderCall, cookieName, binder, webRequest);
- return binder.convertIfNecessary(cookieValue, paramType, methodParam);
- }
-
- /**
- * Resolves the given {@link CookieValue @CookieValue} annotation.
- * Throws an UnsupportedOperationException by default.
- */
- protected Object resolveCookieValue(String cookieName, Class paramType, NativeWebRequest webRequest)
- throws Exception {
-
- throw new UnsupportedOperationException("@CookieValue not supported");
- }
-
- private Object resolvePathVariable(String pathVarName, MethodParameter methodParam,
- NativeWebRequest webRequest, Object handlerForInitBinderCall) throws Exception {
-
- Class> paramType = methodParam.getParameterType();
- if (pathVarName.length() == 0) {
- pathVarName = getRequiredParameterName(methodParam);
- }
- String pathVarValue = resolvePathVariable(pathVarName, paramType, webRequest);
- WebDataBinder binder = createBinder(webRequest, null, pathVarName);
- initBinder(handlerForInitBinderCall, pathVarName, binder, webRequest);
- return binder.convertIfNecessary(pathVarValue, paramType, methodParam);
- }
-
- /**
- * Resolves the given {@link PathVariable @PathVariable} annotation.
- *
Throws an UnsupportedOperationException by default.
- */
- protected String resolvePathVariable(String pathVarName, Class paramType, NativeWebRequest webRequest)
- throws Exception {
-
- throw new UnsupportedOperationException("@PathVariable not supported");
- }
-
- private String getRequiredParameterName(MethodParameter methodParam) {
- String name = methodParam.getParameterName();
- if (name == null) {
- throw new IllegalStateException(
- "No parameter name specified for argument of type [" + methodParam.getParameterType().getName() +
- "], and no parameter name information found in class file either.");
- }
- return name;
- }
-
- private Object checkValue(String name, Object value, Class paramType) {
- if (value == null) {
- if (boolean.class.equals(paramType)) {
- return Boolean.FALSE;
- }
- else if (paramType.isPrimitive()) {
- throw new IllegalStateException("Optional " + paramType + " parameter '" + name +
- "' is not present but cannot be translated into a null value due to being declared as a " +
- "primitive type. Consider declaring it as object wrapper for the corresponding primitive type.");
- }
- }
- return value;
- }
-
- private WebDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam,
- ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler) throws Exception {
-
- // Bind request parameter onto object...
- String name = attrName;
- if ("".equals(name)) {
- name = Conventions.getVariableNameForParameter(methodParam);
- }
- Class> paramType = methodParam.getParameterType();
- Object bindObject;
- if (implicitModel.containsKey(name)) {
- bindObject = implicitModel.get(name);
- }
- else if (this.methodResolver.isSessionAttribute(name, paramType)) {
- bindObject = this.sessionAttributeStore.retrieveAttribute(webRequest, name);
- if (bindObject == null) {
- raiseSessionRequiredException("Session attribute '" + name + "' required - not found in session");
- }
- }
- else {
- bindObject = BeanUtils.instantiateClass(paramType);
- }
- WebDataBinder binder = createBinder(webRequest, bindObject, name);
- initBinder(handler, name, binder, webRequest);
- return binder;
- }
-
-
- /**
- * Determine whether the given value qualifies as a "binding candidate", i.e. might potentially be subject to
- * bean-style data binding later on.
- */
- protected boolean isBindingCandidate(Object value) {
- return (value != null && !value.getClass().isArray() && !(value instanceof Collection) &&
- !(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass()));
- }
-
- protected void raiseMissingParameterException(String paramName, Class paramType) throws Exception {
- throw new IllegalStateException("Missing parameter '" + paramName + "' of type [" + paramType.getName() + "]");
- }
-
- protected void raiseMissingHeaderException(String headerName, Class paramType) throws Exception {
- throw new IllegalStateException("Missing header '" + headerName + "' of type [" + paramType.getName() + "]");
- }
-
- protected void raiseMissingCookieException(String cookieName, Class paramType) throws Exception {
- throw new IllegalStateException(
- "Missing cookie value '" + cookieName + "' of type [" + paramType.getName() + "]");
- }
-
- protected void raiseSessionRequiredException(String message) throws Exception {
- throw new IllegalStateException(message);
- }
-
- protected WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName)
- throws Exception {
-
- return new WebRequestDataBinder(target, objectName);
- }
-
- private void doBind(WebDataBinder binder, NativeWebRequest webRequest, boolean validate, boolean failOnErrors)
- throws Exception {
-
- doBind(binder, webRequest);
- if (validate) {
- binder.validate();
- }
- if (failOnErrors && binder.getBindingResult().hasErrors()) {
- throw new BindException(binder.getBindingResult());
- }
- }
-
- protected void doBind(WebDataBinder binder, NativeWebRequest webRequest) throws Exception {
- ((WebRequestDataBinder) binder).bind(webRequest);
- }
-
- /**
- * Return a {@link HttpInputMessage} for the given {@link NativeWebRequest}.
- *
Throws an UnsupportedOperation1Exception by default.
- */
- protected HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest) throws Exception {
- throw new UnsupportedOperationException("@RequestBody not supported");
- }
-
- /**
- * Return a {@link HttpOutputMessage} for the given {@link NativeWebRequest}.
- *
Throws an UnsupportedOperationException by default.
- */
- protected HttpOutputMessage createHttpOutputMessage(NativeWebRequest webRequest) throws Exception {
- throw new UnsupportedOperationException("@ResponseBody not supported");
- }
-
- protected String parseDefaultValueAttribute(String value) {
- return (ValueConstants.DEFAULT_NONE.equals(value) ? null : value);
- }
-
- protected Object resolveDefaultValue(String value) {
- return value;
- }
-
- protected Object resolveCommonArgument(MethodParameter methodParameter, NativeWebRequest webRequest)
- throws Exception {
-
- // Invoke custom argument resolvers if present...
- if (this.customArgumentResolvers != null) {
- for (WebArgumentResolver argumentResolver : this.customArgumentResolvers) {
- Object value = argumentResolver.resolveArgument(methodParameter, webRequest);
- if (value != WebArgumentResolver.UNRESOLVED) {
- return value;
- }
- }
- }
-
- // Resolution of standard parameter types...
- Class paramType = methodParameter.getParameterType();
- Object value = resolveStandardArgument(paramType, webRequest);
- if (value != WebArgumentResolver.UNRESOLVED && !ClassUtils.isAssignableValue(paramType, value)) {
- throw new IllegalStateException("Standard argument type [" + paramType.getName() +
- "] resolved to incompatible value of type [" + (value != null ? value.getClass() : null) +
- "]. Consider declaring the argument type in a less specific fashion.");
- }
- return value;
- }
-
- protected Object resolveStandardArgument(Class> parameterType, NativeWebRequest webRequest) throws Exception {
- if (WebRequest.class.isAssignableFrom(parameterType)) {
- return webRequest;
- }
- return WebArgumentResolver.UNRESOLVED;
- }
-
- protected final void addReturnValueAsModelAttribute(Method handlerMethod, Class handlerType,
- Object returnValue, ExtendedModelMap implicitModel) {
-
- ModelAttribute attr = AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class);
- String attrName = (attr != null ? attr.value() : "");
- if ("".equals(attrName)) {
- Class resolvedType = GenericTypeResolver.resolveReturnType(handlerMethod, handlerType);
- attrName = Conventions.getVariableNameForReturnType(handlerMethod, resolvedType, returnValue);
- }
- implicitModel.addAttribute(attrName, returnValue);
- }
+ private static final String MODEL_KEY_PREFIX_STALE = SessionAttributeStore.class.getName() + ".STALE.";
+
+ /**
+ * We'll create a lot of these objects, so we don't want a new logger every time.
+ */
+ private static final Log logger = LogFactory.getLog(InterceptingHandlerMethodInvoker.class);
+
+ protected final HandlerMethodResolver methodResolver;
+
+ private final WebBindingInitializer bindingInitializer;
+
+ protected final SessionAttributeStore sessionAttributeStore;
+
+ private final ParameterNameDiscoverer parameterNameDiscoverer;
+
+ private final WebArgumentResolver[] customArgumentResolvers;
+
+ private final HttpMessageConverter[] messageConverters;
+
+ private final ActionInterceptor[] actionInterceptors;
+
+ private final SimpleSessionStatus sessionStatus = new SimpleSessionStatus();
+
+
+ public InterceptingHandlerMethodInvoker(HandlerMethodResolver methodResolver) {
+ this(methodResolver, null);
+ }
+
+ public InterceptingHandlerMethodInvoker(HandlerMethodResolver methodResolver, WebBindingInitializer bindingInitializer) {
+ this(methodResolver, bindingInitializer, new DefaultSessionAttributeStore(), null, null, null, null);
+ }
+
+ public InterceptingHandlerMethodInvoker(HandlerMethodResolver methodResolver, WebBindingInitializer bindingInitializer,
+ SessionAttributeStore sessionAttributeStore, ParameterNameDiscoverer parameterNameDiscoverer,
+ WebArgumentResolver[] customArgumentResolvers, HttpMessageConverter[] messageConverters, ActionInterceptor[] actionInterceptors) {
+
+ this.methodResolver = methodResolver;
+ this.bindingInitializer = bindingInitializer;
+ this.sessionAttributeStore = sessionAttributeStore;
+ this.parameterNameDiscoverer = parameterNameDiscoverer;
+ this.customArgumentResolvers = customArgumentResolvers;
+ this.messageConverters = messageConverters;
+ this.actionInterceptors = actionInterceptors;
+ }
+
+ protected ActionInterceptor[] getActionInterceptors() {
+ return actionInterceptors;
+ }
+
+
+ public final Object invokeHandlerMethod(Method handlerMethod, Object handler,
+ NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
+
+ Method handlerMethodToInvoke = BridgeMethodResolver.findBridgedMethod(handlerMethod);
+ try {
+ boolean debug = logger.isDebugEnabled();
+ for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
+ Object attrValue = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);
+ if (attrValue != null) {
+ implicitModel.addAttribute(attrName, attrValue);
+ }
+ }
+ for (Method attributeMethod : this.methodResolver.getModelAttributeMethods()) {
+ Method attributeMethodToInvoke = BridgeMethodResolver.findBridgedMethod(attributeMethod);
+ Object[] args = resolveHandlerArguments(attributeMethodToInvoke, handler, webRequest, implicitModel);
+ if (debug) {
+ logger.debug("Invoking model attribute method: " + attributeMethodToInvoke);
+ }
+ String attrName = AnnotationUtils.findAnnotation(attributeMethod, ModelAttribute.class).value();
+ if (!"".equals(attrName) && implicitModel.containsAttribute(attrName)) {
+ continue;
+ }
+ ReflectionUtils.makeAccessible(attributeMethodToInvoke);
+ Object attrValue = attributeMethodToInvoke.invoke(handler, args);
+ if ("".equals(attrName)) {
+ Class resolvedType = GenericTypeResolver.resolveReturnType(attributeMethodToInvoke, handler.getClass());
+ attrName = Conventions.getVariableNameForReturnType(attributeMethodToInvoke, resolvedType, attrValue);
+ }
+ if (!implicitModel.containsAttribute(attrName)) {
+ implicitModel.addAttribute(attrName, attrValue);
+ }
+ }
+ Object[] args = resolveHandlerArguments(handlerMethodToInvoke, handler, webRequest, implicitModel);
+ if (debug) {
+ logger.debug("Invoking request handler method: " + handlerMethodToInvoke);
+ }
+ ReflectionUtils.makeAccessible(handlerMethodToInvoke);
+ return handlerMethodToInvoke.invoke(handler, args);
+ } catch (IllegalStateException ex) {
+ // Internal assertion failed (e.g. invalid signature):
+ // throw exception with full handler method context...
+ throw new HandlerMethodInvocationException(handlerMethodToInvoke, ex);
+ } catch (InvocationTargetException ex) {
+ // User-defined @ModelAttribute/@InitBinder/@RequestMapping method threw an exception...
+ ReflectionUtils.rethrowException(ex.getTargetException());
+ return null;
+ }
+ }
+
+ public final void updateModelAttributes(Object handler, Map mavModel,
+ ExtendedModelMap implicitModel, NativeWebRequest webRequest) throws Exception {
+
+ if (this.methodResolver.hasSessionAttributes() && this.sessionStatus.isComplete()) {
+ for (String attrName : this.methodResolver.getActualSessionAttributeNames()) {
+ this.sessionAttributeStore.cleanupAttribute(webRequest, attrName);
+ }
+ }
+
+ // Expose model attributes as session attributes, if required.
+ // Expose BindingResults for all attributes, making custom editors available.
+ Map model = (mavModel != null ? mavModel : implicitModel);
+ if (model != null) {
+ try {
+ String[] originalAttrNames = model.keySet().toArray(new String[model.size()]);
+ for (String attrName : originalAttrNames) {
+ Object attrValue = model.get(attrName);
+ boolean isSessionAttr = this.methodResolver.isSessionAttribute(
+ attrName, (attrValue != null ? attrValue.getClass() : null));
+ if (isSessionAttr) {
+ if (this.sessionStatus.isComplete()) {
+ implicitModel.put(MODEL_KEY_PREFIX_STALE + attrName, Boolean.TRUE);
+ } else if (!implicitModel.containsKey(MODEL_KEY_PREFIX_STALE + attrName)) {
+ this.sessionAttributeStore.storeAttribute(webRequest, attrName, attrValue);
+ }
+ }
+ if (!attrName.startsWith(BindingResult.MODEL_KEY_PREFIX) &&
+ (isSessionAttr || isBindingCandidate(attrValue))) {
+ String bindingResultKey = BindingResult.MODEL_KEY_PREFIX + attrName;
+ if (mavModel != null && !model.containsKey(bindingResultKey)) {
+ WebDataBinder binder = createBinder(webRequest, attrValue, attrName);
+ initBinder(handler, attrName, binder, webRequest);
+ mavModel.put(bindingResultKey, binder.getBindingResult());
+ }
+ }
+ }
+ } catch (InvocationTargetException ex) {
+ // User-defined @InitBinder method threw an exception...
+ ReflectionUtils.rethrowException(ex.getTargetException());
+ }
+ }
+ }
+
+
+ protected Object[] resolveHandlerArguments(Method handlerMethod, Object handler,
+ NativeWebRequest webRequest, ExtendedModelMap implicitModel) throws Exception {
+
+ Class[] paramTypes = handlerMethod.getParameterTypes();
+ Object[] args = new Object[paramTypes.length];
+
+ for (int i = 0; i < args.length; i++) {
+ MethodParameter methodParam = new MethodParameter(handlerMethod, i);
+ methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
+ GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
+ String paramName = null;
+ String headerName = null;
+ boolean requestBodyFound = false;
+ String cookieName = null;
+ String pathVarName = null;
+ String attrName = null;
+ boolean required = false;
+ String defaultValue = null;
+ boolean validate = false;
+ int annotationsFound = 0;
+ Annotation[] paramAnns = methodParam.getParameterAnnotations();
+
+ for (Annotation paramAnn : paramAnns) {
+ if (RequestParam.class.isInstance(paramAnn)) {
+ RequestParam requestParam = (RequestParam) paramAnn;
+ paramName = requestParam.value();
+ required = requestParam.required();
+ defaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
+ annotationsFound++;
+ } else if (RequestHeader.class.isInstance(paramAnn)) {
+ RequestHeader requestHeader = (RequestHeader) paramAnn;
+ headerName = requestHeader.value();
+ required = requestHeader.required();
+ defaultValue = parseDefaultValueAttribute(requestHeader.defaultValue());
+ annotationsFound++;
+ } else if (RequestBody.class.isInstance(paramAnn)) {
+ requestBodyFound = true;
+ annotationsFound++;
+ } else if (CookieValue.class.isInstance(paramAnn)) {
+ CookieValue cookieValue = (CookieValue) paramAnn;
+ cookieName = cookieValue.value();
+ required = cookieValue.required();
+ defaultValue = parseDefaultValueAttribute(cookieValue.defaultValue());
+ annotationsFound++;
+ } else if (PathVariable.class.isInstance(paramAnn)) {
+ PathVariable pathVar = (PathVariable) paramAnn;
+ pathVarName = pathVar.value();
+ annotationsFound++;
+ } else if (ModelAttribute.class.isInstance(paramAnn)) {
+ ModelAttribute attr = (ModelAttribute) paramAnn;
+ attrName = attr.value();
+ annotationsFound++;
+ } else if (Value.class.isInstance(paramAnn)) {
+ defaultValue = ((Value) paramAnn).value();
+ } else if ("Valid".equals(paramAnn.annotationType().getSimpleName())) {
+ validate = true;
+ }
+ }
+
+ if (annotationsFound > 1) {
+ throw new IllegalStateException("Handler parameter annotations are exclusive choices - " +
+ "do not specify more than one such annotation on the same parameter: " + handlerMethod);
+ }
+
+ if (annotationsFound == 0) {
+ Object argValue = resolveCommonArgument(methodParam, webRequest);
+ if (argValue != WebArgumentResolver.UNRESOLVED) {
+ args[i] = argValue;
+ } else if (defaultValue != null) {
+ args[i] = resolveDefaultValue(defaultValue);
+ } else {
+ Class paramType = methodParam.getParameterType();
+ if (Model.class.isAssignableFrom(paramType) || Map.class.isAssignableFrom(paramType)) {
+ args[i] = implicitModel;
+ } else if (SessionStatus.class.isAssignableFrom(paramType)) {
+ args[i] = this.sessionStatus;
+ } else if (HttpEntity.class.isAssignableFrom(paramType)) {
+ args[i] = resolveHttpEntityRequest(methodParam, webRequest);
+ } else if (Errors.class.isAssignableFrom(paramType)) {
+ throw new IllegalStateException("Errors/BindingResult argument declared " +
+ "without preceding model attribute. Check your handler method signature!");
+ } else if (BeanUtils.isSimpleProperty(paramType)) {
+ paramName = "";
+ } else {
+ attrName = "";
+ }
+ }
+ }
+
+ if (paramName != null) {
+ args[i] = resolveRequestParam(paramName, required, defaultValue, methodParam, webRequest, handler);
+ } else if (headerName != null) {
+ args[i] = resolveRequestHeader(headerName, required, defaultValue, methodParam, webRequest, handler);
+ } else if (requestBodyFound) {
+ args[i] = resolveRequestBody(methodParam, webRequest, handler);
+ } else if (cookieName != null) {
+ args[i] = resolveCookieValue(cookieName, required, defaultValue, methodParam, webRequest, handler);
+ } else if (pathVarName != null) {
+ args[i] = resolvePathVariable(pathVarName, methodParam, webRequest, handler);
+ } else if (attrName != null) {
+ WebDataBinder binder =
+ resolveModelAttribute(attrName, methodParam, implicitModel, webRequest, handler);
+ boolean assignBindingResult = (args.length > i + 1 && Errors.class.isAssignableFrom(paramTypes[i + 1]));
+ if (binder.getTarget() != null) {
+ doBind(binder, webRequest, validate, !assignBindingResult);
+ }
+ args[i] = binder.getTarget();
+ if (assignBindingResult) {
+ args[i + 1] = binder.getBindingResult();
+ i++;
+ }
+ implicitModel.putAll(binder.getBindingResult().getModel());
+ }
+ }
+
+ return args;
+ }
+
+ protected void initBinder(Object handler, String attrName, WebDataBinder binder, NativeWebRequest webRequest)
+ throws Exception {
+
+ if (this.bindingInitializer != null) {
+ this.bindingInitializer.initBinder(binder, webRequest);
+ }
+ if (handler != null) {
+ Set initBinderMethods = this.methodResolver.getInitBinderMethods();
+ if (!initBinderMethods.isEmpty()) {
+ boolean debug = logger.isDebugEnabled();
+ for (Method initBinderMethod : initBinderMethods) {
+ Method methodToInvoke = BridgeMethodResolver.findBridgedMethod(initBinderMethod);
+ String[] targetNames = AnnotationUtils.findAnnotation(initBinderMethod, InitBinder.class).value();
+ if (targetNames.length == 0 || Arrays.asList(targetNames).contains(attrName)) {
+ Object[] initBinderArgs =
+ resolveInitBinderArguments(handler, methodToInvoke, binder, webRequest);
+ if (debug) {
+ logger.debug("Invoking init-binder method: " + methodToInvoke);
+ }
+ ReflectionUtils.makeAccessible(methodToInvoke);
+ Object returnValue = methodToInvoke.invoke(handler, initBinderArgs);
+ if (returnValue != null) {
+ throw new IllegalStateException(
+ "InitBinder methods must not have a return value: " + methodToInvoke);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private Object[] resolveInitBinderArguments(Object handler, Method initBinderMethod,
+ WebDataBinder binder, NativeWebRequest webRequest) throws Exception {
+
+ Class[] initBinderParams = initBinderMethod.getParameterTypes();
+ Object[] initBinderArgs = new Object[initBinderParams.length];
+
+ for (int i = 0; i < initBinderArgs.length; i++) {
+ MethodParameter methodParam = new MethodParameter(initBinderMethod, i);
+ methodParam.initParameterNameDiscovery(this.parameterNameDiscoverer);
+ GenericTypeResolver.resolveParameterType(methodParam, handler.getClass());
+ String paramName = null;
+ boolean paramRequired = false;
+ String paramDefaultValue = null;
+ String pathVarName = null;
+ Annotation[] paramAnns = methodParam.getParameterAnnotations();
+
+ for (Annotation paramAnn : paramAnns) {
+ if (RequestParam.class.isInstance(paramAnn)) {
+ RequestParam requestParam = (RequestParam) paramAnn;
+ paramName = requestParam.value();
+ paramRequired = requestParam.required();
+ paramDefaultValue = parseDefaultValueAttribute(requestParam.defaultValue());
+ break;
+ } else if (ModelAttribute.class.isInstance(paramAnn)) {
+ throw new IllegalStateException(
+ "@ModelAttribute is not supported on @InitBinder methods: " + initBinderMethod);
+ } else if (PathVariable.class.isInstance(paramAnn)) {
+ PathVariable pathVar = (PathVariable) paramAnn;
+ pathVarName = pathVar.value();
+ }
+ }
+
+ if (paramName == null && pathVarName == null) {
+ Object argValue = resolveCommonArgument(methodParam, webRequest);
+ if (argValue != WebArgumentResolver.UNRESOLVED) {
+ initBinderArgs[i] = argValue;
+ } else {
+ Class paramType = initBinderParams[i];
+ if (paramType.isInstance(binder)) {
+ initBinderArgs[i] = binder;
+ } else if (BeanUtils.isSimpleProperty(paramType)) {
+ paramName = "";
+ } else {
+ throw new IllegalStateException("Unsupported argument [" + paramType.getName() +
+ "] for @InitBinder method: " + initBinderMethod);
+ }
+ }
+ }
+
+ if (paramName != null) {
+ initBinderArgs[i] =
+ resolveRequestParam(paramName, paramRequired, paramDefaultValue, methodParam, webRequest, null);
+ } else if (pathVarName != null) {
+ initBinderArgs[i] = resolvePathVariable(pathVarName, methodParam, webRequest, null);
+ }
+ }
+
+ return initBinderArgs;
+ }
+
+ @SuppressWarnings("unchecked")
+ private Object resolveRequestParam(String paramName, boolean required, String defaultValue,
+ MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
+ throws Exception {
+
+ Class> paramType = methodParam.getParameterType();
+ if (Map.class.isAssignableFrom(paramType) && paramName.length() == 0) {
+ return resolveRequestParamMap((Class extends Map>) paramType, webRequest);
+ }
+ if (paramName.length() == 0) {
+ paramName = getRequiredParameterName(methodParam);
+ }
+ Object paramValue = null;
+ MultipartRequest multipartRequest = webRequest.getNativeRequest(MultipartRequest.class);
+ if (multipartRequest != null) {
+ List files = multipartRequest.getFiles(paramName);
+ if (!files.isEmpty()) {
+ if (files.size() == 1 && !paramType.isArray() && !Collection.class.isAssignableFrom(paramType)) {
+ paramValue = files.get(0);
+ } else {
+ paramValue = files;
+ }
+ }
+ }
+ if (paramValue == null) {
+ String[] paramValues = webRequest.getParameterValues(paramName);
+ if (paramValues != null) {
+ if (paramValues.length == 1 && !paramType.isArray() && !Collection.class.isAssignableFrom(paramType)) {
+ paramValue = paramValues[0];
+ } else {
+ paramValue = paramValues;
+ }
+ }
+ }
+ if (paramValue == null) {
+ if (defaultValue != null) {
+ paramValue = resolveDefaultValue(defaultValue);
+ } else if (required) {
+ raiseMissingParameterException(paramName, paramType);
+ }
+ paramValue = checkValue(paramName, paramValue, paramType);
+ }
+ WebDataBinder binder = createBinder(webRequest, null, paramName);
+ initBinder(handlerForInitBinderCall, paramName, binder, webRequest);
+ return binder.convertIfNecessary(paramValue, paramType, methodParam);
+ }
+
+ private Map resolveRequestParamMap(Class extends Map> mapType, NativeWebRequest webRequest) {
+ Map parameterMap = webRequest.getParameterMap();
+ if (MultiValueMap.class.isAssignableFrom(mapType)) {
+ MultiValueMap result = new LinkedMultiValueMap(parameterMap.size());
+ for (Map.Entry entry : parameterMap.entrySet()) {
+ for (String value : entry.getValue()) {
+ result.add(entry.getKey(), value);
+ }
+ }
+ return result;
+ } else {
+ Map result = new LinkedHashMap(parameterMap.size());
+ for (Map.Entry entry : parameterMap.entrySet()) {
+ if (entry.getValue().length > 0) {
+ result.put(entry.getKey(), entry.getValue()[0]);
+ }
+ }
+ return result;
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ private Object resolveRequestHeader(String headerName, boolean required, String defaultValue,
+ MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
+ throws Exception {
+
+ Class> paramType = methodParam.getParameterType();
+ if (Map.class.isAssignableFrom(paramType)) {
+ return resolveRequestHeaderMap((Class extends Map>) paramType, webRequest);
+ }
+ if (headerName.length() == 0) {
+ headerName = getRequiredParameterName(methodParam);
+ }
+ Object headerValue = null;
+ String[] headerValues = webRequest.getHeaderValues(headerName);
+ if (headerValues != null) {
+ headerValue = (headerValues.length == 1 ? headerValues[0] : headerValues);
+ }
+ if (headerValue == null) {
+ if (defaultValue != null) {
+ headerValue = resolveDefaultValue(defaultValue);
+ } else if (required) {
+ raiseMissingHeaderException(headerName, paramType);
+ }
+ headerValue = checkValue(headerName, headerValue, paramType);
+ }
+ WebDataBinder binder = createBinder(webRequest, null, headerName);
+ initBinder(handlerForInitBinderCall, headerName, binder, webRequest);
+ return binder.convertIfNecessary(headerValue, paramType, methodParam);
+ }
+
+ private Map resolveRequestHeaderMap(Class extends Map> mapType, NativeWebRequest webRequest) {
+ if (MultiValueMap.class.isAssignableFrom(mapType)) {
+ MultiValueMap result;
+ if (HttpHeaders.class.isAssignableFrom(mapType)) {
+ result = new HttpHeaders();
+ } else {
+ result = new LinkedMultiValueMap();
+ }
+ for (Iterator iterator = webRequest.getHeaderNames(); iterator.hasNext();) {
+ String headerName = iterator.next();
+ for (String headerValue : webRequest.getHeaderValues(headerName)) {
+ result.add(headerName, headerValue);
+ }
+ }
+ return result;
+ } else {
+ Map result = new LinkedHashMap();
+ for (Iterator iterator = webRequest.getHeaderNames(); iterator.hasNext();) {
+ String headerName = iterator.next();
+ String headerValue = webRequest.getHeader(headerName);
+ result.put(headerName, headerValue);
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Resolves the given {@link RequestBody @RequestBody} annotation.
+ */
+ protected Object resolveRequestBody(MethodParameter methodParam, NativeWebRequest webRequest, Object handler)
+ throws Exception {
+
+ return readWithMessageConverters(methodParam, createHttpInputMessage(webRequest), methodParam.getParameterType());
+ }
+
+ private HttpEntity resolveHttpEntityRequest(MethodParameter methodParam, NativeWebRequest webRequest)
+ throws Exception {
+
+ HttpInputMessage inputMessage = createHttpInputMessage(webRequest);
+ Class> paramType = getHttpEntityType(methodParam);
+ Object body = readWithMessageConverters(methodParam, inputMessage, paramType);
+ return new HttpEntity(body, inputMessage.getHeaders());
+ }
+
+ private Object readWithMessageConverters(MethodParameter methodParam, HttpInputMessage inputMessage, Class paramType)
+ throws Exception {
+
+ MediaType contentType = inputMessage.getHeaders().getContentType();
+ if (contentType == null) {
+ StringBuilder builder = new StringBuilder(ClassUtils.getShortName(methodParam.getParameterType()));
+ String paramName = methodParam.getParameterName();
+ if (paramName != null) {
+ builder.append(' ');
+ builder.append(paramName);
+ }
+ throw new HttpMediaTypeNotSupportedException(
+ "Cannot extract parameter (" + builder.toString() + "): no Content-Type found");
+ }
+
+ List allSupportedMediaTypes = new ArrayList();
+ if (this.messageConverters != null) {
+ for (HttpMessageConverter> messageConverter : this.messageConverters) {
+ allSupportedMediaTypes.addAll(messageConverter.getSupportedMediaTypes());
+ if (messageConverter.canRead(paramType, contentType)) {
+ if (logger.isDebugEnabled()) {
+ logger.debug("Reading [" + paramType.getName() + "] as \"" + contentType
+ + "\" using [" + messageConverter + "]");
+ }
+ return messageConverter.read(paramType, inputMessage);
+ }
+ }
+ }
+ throw new HttpMediaTypeNotSupportedException(contentType, allSupportedMediaTypes);
+ }
+
+ private Class> getHttpEntityType(MethodParameter methodParam) {
+ Assert.isAssignable(HttpEntity.class, methodParam.getParameterType());
+ ParameterizedType type = (ParameterizedType) methodParam.getGenericParameterType();
+ if (type.getActualTypeArguments().length == 1) {
+ Type typeArgument = type.getActualTypeArguments()[0];
+ if (typeArgument instanceof Class) {
+ return (Class>) typeArgument;
+ } else if (typeArgument instanceof GenericArrayType) {
+ Type componentType = ((GenericArrayType) typeArgument).getGenericComponentType();
+ if (componentType instanceof Class) {
+ // Surely, there should be a nicer way to do this
+ Object array = Array.newInstance((Class>) componentType, 0);
+ return array.getClass();
+ }
+ }
+ }
+ throw new IllegalArgumentException(
+ "HttpEntity parameter (" + methodParam.getParameterName() + ") is not parameterized");
+
+ }
+
+ private Object resolveCookieValue(String cookieName, boolean required, String defaultValue,
+ MethodParameter methodParam, NativeWebRequest webRequest, Object handlerForInitBinderCall)
+ throws Exception {
+
+ Class> paramType = methodParam.getParameterType();
+ if (cookieName.length() == 0) {
+ cookieName = getRequiredParameterName(methodParam);
+ }
+ Object cookieValue = resolveCookieValue(cookieName, paramType, webRequest);
+ if (cookieValue == null) {
+ if (defaultValue != null) {
+ cookieValue = resolveDefaultValue(defaultValue);
+ } else if (required) {
+ raiseMissingCookieException(cookieName, paramType);
+ }
+ cookieValue = checkValue(cookieName, cookieValue, paramType);
+ }
+ WebDataBinder binder = createBinder(webRequest, null, cookieName);
+ initBinder(handlerForInitBinderCall, cookieName, binder, webRequest);
+ return binder.convertIfNecessary(cookieValue, paramType, methodParam);
+ }
+
+ /**
+ * Resolves the given {@link CookieValue @CookieValue} annotation.
+ * Throws an UnsupportedOperationException by default.
+ */
+ protected Object resolveCookieValue(String cookieName, Class paramType, NativeWebRequest webRequest)
+ throws Exception {
+
+ throw new UnsupportedOperationException("@CookieValue not supported");
+ }
+
+ private Object resolvePathVariable(String pathVarName, MethodParameter methodParam,
+ NativeWebRequest webRequest, Object handlerForInitBinderCall) throws Exception {
+
+ Class> paramType = methodParam.getParameterType();
+ if (pathVarName.length() == 0) {
+ pathVarName = getRequiredParameterName(methodParam);
+ }
+ String pathVarValue = resolvePathVariable(pathVarName, paramType, webRequest);
+ WebDataBinder binder = createBinder(webRequest, null, pathVarName);
+ initBinder(handlerForInitBinderCall, pathVarName, binder, webRequest);
+ return binder.convertIfNecessary(pathVarValue, paramType, methodParam);
+ }
+
+ /**
+ * Resolves the given {@link PathVariable @PathVariable} annotation.
+ *
Throws an UnsupportedOperationException by default.
+ */
+ protected String resolvePathVariable(String pathVarName, Class paramType, NativeWebRequest webRequest)
+ throws Exception {
+
+ throw new UnsupportedOperationException("@PathVariable not supported");
+ }
+
+ private String getRequiredParameterName(MethodParameter methodParam) {
+ String name = methodParam.getParameterName();
+ if (name == null) {
+ throw new IllegalStateException(
+ "No parameter name specified for argument of type [" + methodParam.getParameterType().getName() +
+ "], and no parameter name information found in class file either.");
+ }
+ return name;
+ }
+
+ private Object checkValue(String name, Object value, Class paramType) {
+ if (value == null) {
+ if (boolean.class.equals(paramType)) {
+ return Boolean.FALSE;
+ } else if (paramType.isPrimitive()) {
+ throw new IllegalStateException("Optional " + paramType + " parameter '" + name +
+ "' is not present but cannot be translated into a null value due to being declared as a " +
+ "primitive type. Consider declaring it as object wrapper for the corresponding primitive type.");
+ }
+ }
+ return value;
+ }
+
+ private WebDataBinder resolveModelAttribute(String attrName, MethodParameter methodParam,
+ ExtendedModelMap implicitModel, NativeWebRequest webRequest, Object handler) throws Exception {
+
+ // Bind request parameter onto object...
+ String name = attrName;
+ if ("".equals(name)) {
+ name = Conventions.getVariableNameForParameter(methodParam);
+ }
+ Class> paramType = methodParam.getParameterType();
+ Object bindObject;
+ if (implicitModel.containsKey(name)) {
+ bindObject = implicitModel.get(name);
+ } else if (this.methodResolver.isSessionAttribute(name, paramType)) {
+ bindObject = this.sessionAttributeStore.retrieveAttribute(webRequest, name);
+ if (bindObject == null) {
+ raiseSessionRequiredException("Session attribute '" + name + "' required - not found in session");
+ }
+ } else {
+ bindObject = BeanUtils.instantiateClass(paramType);
+ }
+ WebDataBinder binder = createBinder(webRequest, bindObject, name);
+ initBinder(handler, name, binder, webRequest);
+ return binder;
+ }
+
+
+ /**
+ * Determine whether the given value qualifies as a "binding candidate", i.e. might potentially be subject to
+ * bean-style data binding later on.
+ */
+ protected boolean isBindingCandidate(Object value) {
+ return (value != null && !value.getClass().isArray() && !(value instanceof Collection) &&
+ !(value instanceof Map) && !BeanUtils.isSimpleValueType(value.getClass()));
+ }
+
+ protected void raiseMissingParameterException(String paramName, Class paramType) throws Exception {
+ throw new IllegalStateException("Missing parameter '" + paramName + "' of type [" + paramType.getName() + "]");
+ }
+
+ protected void raiseMissingHeaderException(String headerName, Class paramType) throws Exception {
+ throw new IllegalStateException("Missing header '" + headerName + "' of type [" + paramType.getName() + "]");
+ }
+
+ protected void raiseMissingCookieException(String cookieName, Class paramType) throws Exception {
+ throw new IllegalStateException(
+ "Missing cookie value '" + cookieName + "' of type [" + paramType.getName() + "]");
+ }
+
+ protected void raiseSessionRequiredException(String message) throws Exception {
+ throw new IllegalStateException(message);
+ }
+
+ protected WebDataBinder createBinder(NativeWebRequest webRequest, Object target, String objectName)
+ throws Exception {
+
+ return new WebRequestDataBinder(target, objectName);
+ }
+
+ private void doBind(WebDataBinder binder, NativeWebRequest webRequest, boolean validate, boolean failOnErrors)
+ throws Exception {
+
+ doBind(binder, webRequest);
+ if (validate) {
+ binder.validate();
+ }
+ if (failOnErrors && binder.getBindingResult().hasErrors()) {
+ throw new BindException(binder.getBindingResult());
+ }
+ }
+
+ protected void doBind(WebDataBinder binder, NativeWebRequest webRequest) throws Exception {
+ ((WebRequestDataBinder) binder).bind(webRequest);
+ }
+
+ /**
+ * Return a {@link HttpInputMessage} for the given {@link NativeWebRequest}.
+ *
Throws an UnsupportedOperation1Exception by default.
+ */
+ protected HttpInputMessage createHttpInputMessage(NativeWebRequest webRequest) throws Exception {
+ throw new UnsupportedOperationException("@RequestBody not supported");
+ }
+
+ /**
+ * Return a {@link HttpOutputMessage} for the given {@link NativeWebRequest}.
+ *
Throws an UnsupportedOperationException by default.
+ */
+ protected HttpOutputMessage createHttpOutputMessage(NativeWebRequest webRequest) throws Exception {
+ throw new UnsupportedOperationException("@ResponseBody not supported");
+ }
+
+ protected String parseDefaultValueAttribute(String value) {
+ return (ValueConstants.DEFAULT_NONE.equals(value) ? null : value);
+ }
+
+ protected Object resolveDefaultValue(String value) {
+ return value;
+ }
+
+ protected Object resolveCommonArgument(MethodParameter methodParameter, NativeWebRequest webRequest)
+ throws Exception {
+
+ // Invoke custom argument resolvers if present...
+ if (this.customArgumentResolvers != null) {
+ for (WebArgumentResolver argumentResolver : this.customArgumentResolvers) {
+ Object value = argumentResolver.resolveArgument(methodParameter, webRequest);
+ if (value != WebArgumentResolver.UNRESOLVED) {
+ return value;
+ }
+ }
+ }
+
+ // Resolution of standard parameter types...
+ Class paramType = methodParameter.getParameterType();
+ Object value = resolveStandardArgument(paramType, webRequest);
+ if (value != WebArgumentResolver.UNRESOLVED && !ClassUtils.isAssignableValue(paramType, value)) {
+ throw new IllegalStateException("Standard argument type [" + paramType.getName() +
+ "] resolved to incompatible value of type [" + (value != null ? value.getClass() : null) +
+ "]. Consider declaring the argument type in a less specific fashion.");
+ }
+ return value;
+ }
+
+ protected Object resolveStandardArgument(Class> parameterType, NativeWebRequest webRequest) throws Exception {
+ if (WebRequest.class.isAssignableFrom(parameterType)) {
+ return webRequest;
+ }
+ return WebArgumentResolver.UNRESOLVED;
+ }
+
+ protected final void addReturnValueAsModelAttribute(Method handlerMethod, Class handlerType,
+ Object returnValue, ExtendedModelMap implicitModel) {
+
+ ModelAttribute attr = AnnotationUtils.findAnnotation(handlerMethod, ModelAttribute.class);
+ String attrName = (attr != null ? attr.value() : "");
+ if ("".equals(attrName)) {
+ Class resolvedType = GenericTypeResolver.resolveReturnType(handlerMethod, handlerType);
+ attrName = Conventions.getVariableNameForReturnType(handlerMethod, resolvedType, returnValue);
+ }
+ implicitModel.addAttribute(attrName, returnValue);
+ }
}
diff --git a/spring-data-document-parent/.project b/spring-data-document-parent/.project
deleted file mode 100644
index a33fec0be..000000000
--- a/spring-data-document-parent/.project
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
- spring-data-document-parent
-
-
-
-
-
- org.maven.ide.eclipse.maven2Builder
-
-
-
-
-
- org.maven.ide.eclipse.maven2Nature
-
-
diff --git a/spring-data-document-parent/.settings/org.maven.ide.eclipse.prefs b/spring-data-document-parent/.settings/org.maven.ide.eclipse.prefs
deleted file mode 100644
index 7ccc99204..000000000
--- a/spring-data-document-parent/.settings/org.maven.ide.eclipse.prefs
+++ /dev/null
@@ -1,9 +0,0 @@
-#Tue Jun 29 21:59:00 EDT 2010
-activeProfiles=
-eclipse.preferences.version=1
-fullBuildGoals=process-test-resources
-includeModules=false
-resolveWorkspaceProjects=true
-resourceFilterGoals=process-resources resources\:testResources
-skipCompilerPlugin=true
-version=1
diff --git a/spring-data-document-parent/pom.xml b/spring-data-document-parent/pom.xml
index bbdab5f08..fb6f3de57 100644
--- a/spring-data-document-parent/pom.xml
+++ b/spring-data-document-parent/pom.xml
@@ -1,91 +1,92 @@
- 4.0.0
- org.springframework.data
- spring-data-document-parent
- Spring Data Document Parent
- http://www.springsource.org/spring-data/data-document
- 1.0.0.BUILD-SNAPSHOT
- pom
-
- UTF-8
-
- 4.8.1
- 1.2.15
- 1.8.4
- 1.5.10
- 1.6.1
- 3.0.5.RELEASE
- 1.0.0.BUILD-SNAPSHOT
- 1.6.11.M2
-
-
-
- strict
-
- false
-
-
-
- fast
-
- true
- true
-
-
-
- staging
-
-
- spring-site-staging
- file:///${java.io.tmpdir}/spring-data/data-document/docs
-
-
- spring-milestone-staging
- file:///${java.io.tmpdir}/spring-data/data-document/milestone
-
-
- spring-snapshot-staging
- file:///${java.io.tmpdir}/spring-data/data-document/snapshot
-
-
-
-
- bootstrap
-
-
-
-
-
- http://www.springsource.com/download/community
-
-
- static.springframework.org
- scp://static.springframework.org/var/www/domains/springframework.org/static/htdocs/spring-data/data-document/docs/${project.version}
-
-
-
- spring-milestone
- Spring Milestone Repository
- s3://maven.springframework.org/milestone
-
-
- spring-snapshot
- Spring Snapshot Repository
- s3://maven.springframework.org/snapshot
-
-
-
-
-
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ 4.0.0
+ org.springframework.data
+ spring-data-document-parent
+ Spring Data Document Parent
+ http://www.springsource.org/spring-data/data-document
+ 1.0.0.BUILD-SNAPSHOT
+ pom
+
+ UTF-8
+
+ 4.8.1
+ 1.2.15
+ 1.8.4
+ 1.5.10
+ 1.6.1
+ 3.0.5.RELEASE
+ 1.0.0.BUILD-SNAPSHOT
+ 1.6.11.M2
+
+
+
+ strict
+
+ false
+
+
+
+ fast
+
+ true
+ true
+
+
+
+ staging
+
+
+ spring-site-staging
+ file:///${java.io.tmpdir}/spring-data/data-document/docs
+
+
+ spring-milestone-staging
+ file:///${java.io.tmpdir}/spring-data/data-document/milestone
+
+
+ spring-snapshot-staging
+ file:///${java.io.tmpdir}/spring-data/data-document/snapshot
+
+
+
+
+ bootstrap
+
+
+
+
+
+ http://www.springsource.com/download/community
+
+
+ static.springframework.org
+
+ scp://static.springframework.org/var/www/domains/springframework.org/static/htdocs/spring-data/data-document/docs/${project.version}
+
+
+
+ spring-milestone
+ Spring Milestone Repository
+ s3://maven.springframework.org/milestone
+
+
+ spring-snapshot
+ Spring Snapshot Repository
+ s3://maven.springframework.org/snapshot
+
+
+
+
+
diff --git a/spring-data-mongodb-cross-store/.classpath b/spring-data-mongodb-cross-store/.classpath
deleted file mode 100644
index 0df52bfe5..000000000
--- a/spring-data-mongodb-cross-store/.classpath
+++ /dev/null
@@ -1,9 +0,0 @@
-
-
-
-
-
-
-
-
-
diff --git a/spring-data-mongodb-cross-store/.project b/spring-data-mongodb-cross-store/.project
deleted file mode 100644
index 20315de9a..000000000
--- a/spring-data-mongodb-cross-store/.project
+++ /dev/null
@@ -1,24 +0,0 @@
-
-
- spring-data-mongodb-cross-store
-
-
-
-
-
- org.eclipse.ajdt.core.ajbuilder
-
-
-
-
- org.maven.ide.eclipse.maven2Builder
-
-
-
-
-
- org.eclipse.ajdt.ui.ajnature
- org.eclipse.jdt.core.javanature
- org.maven.ide.eclipse.maven2Nature
-
-
diff --git a/spring-data-mongodb-cross-store/.settings/org.eclipse.jdt.core.prefs b/spring-data-mongodb-cross-store/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644
index 644302d5c..000000000
--- a/spring-data-mongodb-cross-store/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,9 +0,0 @@
-#Tue Mar 01 09:48:37 EST 2011
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
-org.eclipse.jdt.core.compiler.compliance=1.6
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
-org.eclipse.jdt.core.compiler.source=1.6
diff --git a/spring-data-mongodb-cross-store/.settings/org.maven.ide.eclipse.prefs b/spring-data-mongodb-cross-store/.settings/org.maven.ide.eclipse.prefs
deleted file mode 100644
index e53ed8823..000000000
--- a/spring-data-mongodb-cross-store/.settings/org.maven.ide.eclipse.prefs
+++ /dev/null
@@ -1,8 +0,0 @@
-#Mon Feb 28 16:25:59 EST 2011
-activeProfiles=
-eclipse.preferences.version=1
-fullBuildGoals=process-test-resources
-resolveWorkspaceProjects=true
-resourceFilterGoals=process-resources resources\:testResources
-skipCompilerPlugin=true
-version=1
diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml
index e9151dc1f..437bcad25 100644
--- a/spring-data-mongodb-cross-store/pom.xml
+++ b/spring-data-mongodb-cross-store/pom.xml
@@ -115,72 +115,72 @@
1.0.0.Final
-
-
-
- jboss-repository
- JBoss Public Repository
- http://repository.jboss.org/nexus/content/groups/public-jboss
-
-
- spring-maven-snapshot
-
- true
-
- Springframework Maven SNAPSHOT Repository
- http://maven.springframework.org/snapshot
-
-
-
-
- spring-maven-milestones
- Springframework Maven Milestone Repository
- http://maven.springframework.org/milestone
-
-
-
-
-
- org.codehaus.mojo
- aspectj-maven-plugin
- 1.2
-
-
- org.aspectj
- aspectjrt
- ${aspectj.version}
-
-
- org.aspectj
- aspectjtools
- ${aspectj.version}
-
-
-
-
-
- compile
- test-compile
-
-
-
-
- true
-
-
- org.springframework
- spring-aspects
-
-
- org.springframework.data
- spring-data-commons-aspects
-
-
- 1.6
- 1.6
-
-
-
-
+
+
+
+ jboss-repository
+ JBoss Public Repository
+ http://repository.jboss.org/nexus/content/groups/public-jboss
+
+
+ spring-maven-snapshot
+
+ true
+
+ Springframework Maven SNAPSHOT Repository
+ http://maven.springframework.org/snapshot
+
+
+
+
+ spring-maven-milestones
+ Springframework Maven Milestone Repository
+ http://maven.springframework.org/milestone
+
+
+
+
+
+ org.codehaus.mojo
+ aspectj-maven-plugin
+ 1.2
+
+
+ org.aspectj
+ aspectjrt
+ ${aspectj.version}
+
+
+ org.aspectj
+ aspectjtools
+ ${aspectj.version}
+
+
+
+
+
+ compile
+ test-compile
+
+
+
+
+ true
+
+
+ org.springframework
+ spring-aspects
+
+
+ org.springframework.data
+ spring-data-commons-aspects
+
+
+ 1.6
+ 1.6
+
+
+
+
diff --git a/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/DocumentEntity.java b/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/DocumentEntity.java
index b3130164e..2b690df97 100644
--- a/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/DocumentEntity.java
+++ b/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/DocumentEntity.java
@@ -8,7 +8,7 @@ import java.lang.annotation.Target;
/**
* Annotation to denote an object that should be transparently persisted
* using MongoDB
- *
+ *
* @author Thomas Risberg
*/
diff --git a/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoChangeSetPersister.java b/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoChangeSetPersister.java
index a46d456c9..6801b8b83 100644
--- a/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoChangeSetPersister.java
+++ b/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoChangeSetPersister.java
@@ -1,7 +1,9 @@
package org.springframework.persistence.document;
-import java.util.Map;
-
+import com.mongodb.BasicDBObject;
+import com.mongodb.DBCollection;
+import com.mongodb.DBObject;
+import com.mongodb.MongoException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -14,111 +16,104 @@ import org.springframework.persistence.support.ChangeSetBacked;
import org.springframework.persistence.support.ChangeSetPersister;
import org.springframework.util.ClassUtils;
-import com.mongodb.BasicDBObject;
-import com.mongodb.DB;
-import com.mongodb.DBCollection;
-import com.mongodb.DBObject;
-import com.mongodb.Mongo;
-import com.mongodb.MongoException;
-
//import edu.emory.mathcs.backport.java.util.Arrays;
public class MongoChangeSetPersister implements ChangeSetPersister {
- protected final Log log = LogFactory.getLog(getClass());
+ protected final Log log = LogFactory.getLog(getClass());
- @Autowired
- private MongoTemplate mongoTemplate;
-
- @Autowired
- private ConversionService conversionService;
-
- @Override
- public void getPersistentState(Class extends ChangeSetBacked> entityClass, Object id, ChangeSet changeSet)
- throws DataAccessException, NotFoundException {
- String collection = ClassUtils.getShortName(entityClass).toLowerCase();
- DBObject q = new BasicDBObject();
- q.put("_id", id);
- try {
- DBObject dbo = mongoTemplate.getCollection(collection).findOne(q);
- log.debug("Found DBObject: " + dbo);
- if (dbo == null) {
- throw new NotFoundException();
- }
- String classShortName = ClassUtils.getShortName(entityClass);
- for (Object property : dbo.toMap().keySet()) {
- String propertyKey = (String) property;
- String propertyName = propertyKey.startsWith(classShortName) ? propertyKey.substring(propertyKey.indexOf(classShortName)
- + classShortName.length() + 1) : propertyKey;
- // System.err.println("Mongo persisted property [" + propertyName + "] :: " + propertyKey + " = " + dbo.get(propertyKey));
- if (propertyKey.startsWith("_")) {
- // Id or class
- changeSet.set(propertyKey, dbo.get(propertyKey));
- } else {
- //throw new IllegalStateException("Unknown property [" + propertyName + "] found in MongoDB store");
- changeSet.set(propertyKey, dbo.get(propertyKey));
- }
- }
- } catch (MongoException ex) {
- throw new DataAccessResourceFailureException("Can't read from Mongo", ex);
- }
- }
-
- @Override
- public Object getPersistentId(Class extends ChangeSetBacked> entityClass,
- ChangeSet cs) throws DataAccessException {
- log.debug("getPersistentId called on " + entityClass);
- if (cs == null) {
- return null;
- }
- if (cs.getValues().get(ChangeSetPersister.ID_KEY) == null) {
- // Not yet persistent
- return null;
- }
- Object o = cs.getValues().get(ChangeSetPersister.ID_KEY);
- return o;
- }
-
- @Override
- public Object persistState(Class extends ChangeSetBacked> entityClass, ChangeSet cs) throws DataAccessException {
- log.info("PERSIST::"+cs);
- cs.set(CLASS_KEY, entityClass.getName());
- String idstr = cs.get(ID_KEY, String.class, this.conversionService);
- Object id = null;
- if (idstr != null) {
- id = idstr;
- }
- if (id == null) {
- log.info("Flush: entity make persistent; data store will assign id");
- cs.set("_class", entityClass.getName());
- String collection = entityClass.getSimpleName().toLowerCase();
- DBCollection dbc = mongoTemplate.getCollection(collection);
- DBObject dbo = mapChangeSetToDbObject(cs);
- if (dbc == null) {
- dbc = mongoTemplate.createCollection(collection);
- }
- dbc.save(dbo);
- id = dbo.get(ID_KEY);
- log.info("Data store assigned id: " + id);
- } else {
- log.info("Flush: entity already persistent with id=" + id);
- String collection = entityClass.getName();
- DBCollection dbc = mongoTemplate.getCollection(collection);
- DBObject dbo = mapChangeSetToDbObject(cs);
- if (dbc == null) {
- throw new DataAccessResourceFailureException("Expected to find a collection named '" + collection +"'. It was not found, so ChangeSet can't be persisted.");
- }
- dbc.save(dbo);
- }
+ @Autowired
+ private MongoTemplate mongoTemplate;
- return 0L;
- }
+ @Autowired
+ private ConversionService conversionService;
- private DBObject mapChangeSetToDbObject(ChangeSet cs) {
- BasicDBObject dbo = new BasicDBObject();
- for (String property : cs.getValues().keySet()) {
- dbo.put(property, cs.getValues().get(property));
- }
- return dbo;
- }
+ @Override
+ public void getPersistentState(Class extends ChangeSetBacked> entityClass, Object id, ChangeSet changeSet)
+ throws DataAccessException, NotFoundException {
+ String collection = ClassUtils.getShortName(entityClass).toLowerCase();
+ DBObject q = new BasicDBObject();
+ q.put("_id", id);
+ try {
+ DBObject dbo = mongoTemplate.getCollection(collection).findOne(q);
+ log.debug("Found DBObject: " + dbo);
+ if (dbo == null) {
+ throw new NotFoundException();
+ }
+ String classShortName = ClassUtils.getShortName(entityClass);
+ for (Object property : dbo.toMap().keySet()) {
+ String propertyKey = (String) property;
+ String propertyName = propertyKey.startsWith(classShortName) ? propertyKey.substring(propertyKey.indexOf(classShortName)
+ + classShortName.length() + 1) : propertyKey;
+ // System.err.println("Mongo persisted property [" + propertyName + "] :: " + propertyKey + " = " + dbo.get(propertyKey));
+ if (propertyKey.startsWith("_")) {
+ // Id or class
+ changeSet.set(propertyKey, dbo.get(propertyKey));
+ } else {
+ //throw new IllegalStateException("Unknown property [" + propertyName + "] found in MongoDB store");
+ changeSet.set(propertyKey, dbo.get(propertyKey));
+ }
+ }
+ } catch (MongoException ex) {
+ throw new DataAccessResourceFailureException("Can't read from Mongo", ex);
+ }
+ }
+
+ @Override
+ public Object getPersistentId(Class extends ChangeSetBacked> entityClass,
+ ChangeSet cs) throws DataAccessException {
+ log.debug("getPersistentId called on " + entityClass);
+ if (cs == null) {
+ return null;
+ }
+ if (cs.getValues().get(ChangeSetPersister.ID_KEY) == null) {
+ // Not yet persistent
+ return null;
+ }
+ Object o = cs.getValues().get(ChangeSetPersister.ID_KEY);
+ return o;
+ }
+
+ @Override
+ public Object persistState(Class extends ChangeSetBacked> entityClass, ChangeSet cs) throws DataAccessException {
+ log.info("PERSIST::" + cs);
+ cs.set(CLASS_KEY, entityClass.getName());
+ String idstr = cs.get(ID_KEY, String.class, this.conversionService);
+ Object id = null;
+ if (idstr != null) {
+ id = idstr;
+ }
+ if (id == null) {
+ log.info("Flush: entity make persistent; data store will assign id");
+ cs.set("_class", entityClass.getName());
+ String collection = entityClass.getSimpleName().toLowerCase();
+ DBCollection dbc = mongoTemplate.getCollection(collection);
+ DBObject dbo = mapChangeSetToDbObject(cs);
+ if (dbc == null) {
+ dbc = mongoTemplate.createCollection(collection);
+ }
+ dbc.save(dbo);
+ id = dbo.get(ID_KEY);
+ log.info("Data store assigned id: " + id);
+ } else {
+ log.info("Flush: entity already persistent with id=" + id);
+ String collection = entityClass.getName();
+ DBCollection dbc = mongoTemplate.getCollection(collection);
+ DBObject dbo = mapChangeSetToDbObject(cs);
+ if (dbc == null) {
+ throw new DataAccessResourceFailureException("Expected to find a collection named '" + collection + "'. It was not found, so ChangeSet can't be persisted.");
+ }
+ dbc.save(dbo);
+ }
+
+ return 0L;
+ }
+
+ private DBObject mapChangeSetToDbObject(ChangeSet cs) {
+ BasicDBObject dbo = new BasicDBObject();
+ for (String property : cs.getValues().keySet()) {
+ dbo.put(property, cs.getValues().get(property));
+ }
+ return dbo;
+ }
}
diff --git a/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoDocumentBacking.aj b/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoDocumentBacking.aj
index 914c1efcf..133e482b7 100644
--- a/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoDocumentBacking.aj
+++ b/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoDocumentBacking.aj
@@ -4,7 +4,7 @@ import org.springframework.persistence.support.AbstractDeferredUpdateMixinFields
/**
* Aspect to turn an object annotated with DocumentEntity into a document entity using Mongo.
- *
+ *
* @author Thomas Risberg
*/
public aspect MongoDocumentBacking extends AbstractDeferredUpdateMixinFields {
diff --git a/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoEntityOperations.java b/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoEntityOperations.java
index 7e09692d7..b735456b7 100644
--- a/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoEntityOperations.java
+++ b/spring-data-mongodb-cross-store/src/main/java/org/springframework/persistence/document/MongoEntityOperations.java
@@ -13,69 +13,66 @@ import org.springframework.persistence.support.ChangeSetPersister.NotFoundExcept
import org.springframework.persistence.support.EntityInstantiator;
import org.springframework.persistence.support.HashMapChangeSet;
-import com.mongodb.DB;
-
public class MongoEntityOperations extends OrderedEntityOperations {
-
- @Autowired
- private MongoTemplate mongoTemplate;
-
- private EntityInstantiator entityInstantiator;
- private MongoChangeSetPersister changeSetPersister;
+ @Autowired
+ private MongoTemplate mongoTemplate;
- public void setEntityInstantiator(EntityInstantiator entityInstantiator) {
- this.entityInstantiator = entityInstantiator;
- }
+ private EntityInstantiator entityInstantiator;
- @Autowired
- public void setChangeSetPersister(MongoChangeSetPersister changeSetPersister) {
- this.changeSetPersister = changeSetPersister;
- }
+ private MongoChangeSetPersister changeSetPersister;
+
+ public void setEntityInstantiator(EntityInstantiator entityInstantiator) {
+ this.entityInstantiator = entityInstantiator;
+ }
+
+ @Autowired
+ public void setChangeSetPersister(MongoChangeSetPersister changeSetPersister) {
+ this.changeSetPersister = changeSetPersister;
+ }
- @Override
- public boolean cacheInEntity() {
- return true;
- }
+ @Override
+ public boolean cacheInEntity() {
+ return true;
+ }
- @Override
- public ChangeSetBacked findEntity(Class entityClass, Object key) throws DataAccessException {
- try {
- ChangeSet cs = new HashMapChangeSet();
- changeSetPersister.getPersistentState(entityClass, key, cs);
- return entityInstantiator.createEntityFromState(cs, entityClass);
- }
- catch (NotFoundException ex) {
- return null;
- }
- }
+ @Override
+ public ChangeSetBacked findEntity(Class entityClass, Object key) throws DataAccessException {
+ try {
+ ChangeSet cs = new HashMapChangeSet();
+ changeSetPersister.getPersistentState(entityClass, key, cs);
+ return entityInstantiator.createEntityFromState(cs, entityClass);
+ } catch (NotFoundException ex) {
+ return null;
+ }
+ }
- @Override
- public Object findUniqueKey(ChangeSetBacked entity) throws DataAccessException {
- return entity.getId();
- }
+ @Override
+ public Object findUniqueKey(ChangeSetBacked entity) throws DataAccessException {
+ return entity.getId();
+ }
- @Override
- public boolean isTransactional() {
- // TODO
- return false;
- }
+ @Override
+ public boolean isTransactional() {
+ // TODO
+ return false;
+ }
- @Override
- public boolean isTransient(ChangeSetBacked entity) throws DataAccessException {
- return entity.getId() == null;
- }
+ @Override
+ public boolean isTransient(ChangeSetBacked entity) throws DataAccessException {
+ return entity.getId() == null;
+ }
- @Override
- public Object makePersistent(Object owner, ChangeSetBacked entity, Field f, RelatedEntity fs) throws DataAccessException {
- changeSetPersister.persistState(entity.getClass(), entity.getChangeSet());
- return entity.getId();
- }
+ @Override
+ public Object makePersistent(Object owner, ChangeSetBacked entity, Field f, RelatedEntity fs) throws DataAccessException {
+ changeSetPersister.persistState(entity.getClass(), entity.getChangeSet());
+ return entity.getId();
+ }
- @Override
- public boolean supports(Class> entityClass, RelatedEntity fs) {
- return entityClass.isAnnotationPresent(DocumentEntity.class);
- }
+ @Override
+ public boolean supports(Class> entityClass, RelatedEntity fs) {
+ return entityClass.isAnnotationPresent(DocumentEntity.class);
+ }
}
diff --git a/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/CrossStoreMongoTests.java b/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/CrossStoreMongoTests.java
index 137ea7eaf..215639511 100644
--- a/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/CrossStoreMongoTests.java
+++ b/spring-data-mongodb-cross-store/src/test/java/org/springframework/data/document/persistence/CrossStoreMongoTests.java
@@ -3,6 +3,7 @@ package org.springframework.data.document.persistence;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
+import com.mongodb.*;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -17,122 +18,116 @@ import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
-import com.mongodb.DBCollection;
-import com.mongodb.DBCursor;
-import com.mongodb.DBObject;
-import com.mongodb.Mongo;
-import com.mongodb.MongoException;
-
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:/META-INF/spring/applicationContext.xml")
public class CrossStoreMongoTests {
- @Autowired
- private Mongo mongo;
+ @Autowired
+ private Mongo mongo;
- @Autowired
- private MongoTemplate mongoTemplate;
-
- private EntityManager entityManager;
-
- private String colName = MongoPerson.class.getSimpleName().toLowerCase();
+ @Autowired
+ private MongoTemplate mongoTemplate;
-
- @PersistenceContext
- public void setEntityManager(EntityManager entityManager) {
- this.entityManager = entityManager;
+ private EntityManager entityManager;
+
+ private String colName = MongoPerson.class.getSimpleName().toLowerCase();
+
+
+ @PersistenceContext
+ public void setEntityManager(EntityManager entityManager) {
+ this.entityManager = entityManager;
+ }
+
+ private void clearData(String collectionName) {
+ DBCollection col = this.mongoTemplate.getCollection(collectionName);
+ if (col != null) {
+ this.mongoTemplate.dropCollection(collectionName);
}
+ }
- private void clearData(String collectionName) {
- DBCollection col = this.mongoTemplate.getCollection(collectionName);
- if (col != null) {
- this.mongoTemplate.dropCollection(collectionName);
- }
- }
-
- @Test
- @Transactional
- @Rollback(false)
- public void testUserConstructor() {
- clearData(colName);
- int age = 33;
- MongoPerson p = new MongoPerson("Thomas", age);
- Assert.assertEquals(age, p.getAge());
- p.birthday();
- Assert.assertEquals(1 + age, p.getAge());
- }
+ @Test
+ @Transactional
+ @Rollback(false)
+ public void testUserConstructor() {
+ clearData(colName);
+ int age = 33;
+ MongoPerson p = new MongoPerson("Thomas", age);
+ Assert.assertEquals(age, p.getAge());
+ p.birthday();
+ Assert.assertEquals(1 + age, p.getAge());
+ }
- @Test
- @Transactional
- public void testInstantiatedFinder() throws MongoException {
- DBCollection col = this.mongoTemplate.getCollection(colName);
- DBObject dbo = col.findOne();
- Object _id = dbo.get("_id");
- MongoPerson found = MongoPerson.findPerson(_id);
- Assert.assertNotNull(found);
- Assert.assertEquals(_id, found.getId());
- System.out.println("Loaded MongoPerson data: " + found);
- }
+ @Test
+ @Transactional
+ public void testInstantiatedFinder() throws MongoException {
+ DBCollection col = this.mongoTemplate.getCollection(colName);
+ DBObject dbo = col.findOne();
+ Object _id = dbo.get("_id");
+ MongoPerson found = MongoPerson.findPerson(_id);
+ Assert.assertNotNull(found);
+ Assert.assertEquals(_id, found.getId());
+ System.out.println("Loaded MongoPerson data: " + found);
+ }
- @Test
- @Transactional
- @Rollback(false)
- public void testCreateMongoToJpaEntityRelationship() {
- clearData(colName);
- Account a = new Account();
- a.setName("My Account");
- a.setFriendlyName("My Test Acct.");
- a.setBalance(123.45F);
- a.setId(2L);
- MongoPerson p = new MongoPerson("Jack", 22);
- entityManager.persist(a);
- p.setAccount(a);
- }
+ @Test
+ @Transactional
+ @Rollback(false)
+ public void testCreateMongoToJpaEntityRelationship() {
+ clearData(colName);
+ Account a = new Account();
+ a.setName("My Account");
+ a.setFriendlyName("My Test Acct.");
+ a.setBalance(123.45F);
+ a.setId(2L);
+ MongoPerson p = new MongoPerson("Jack", 22);
+ entityManager.persist(a);
+ p.setAccount(a);
+ }
- @Test
- @Transactional
- public void testReadMongoToJpaEntityRelationship() {
- DBCollection col = this.mongoTemplate.getCollection(colName);
- DBCursor dbc = col.find();
- Object _id = null;
- for (DBObject dbo : dbc) {
- System.out.println(dbo);
- if ("Jack".equals(dbo.get("name"))) {
- _id = dbo.get("_id");
- break;
- }
- }
- System.out.println(_id);
- MongoPerson found = MongoPerson.findPerson(_id);
- System.out.println(found);
- if (found != null)
- System.out.println(found.getAccount());
- }
+ @Test
+ @Transactional
+ public void testReadMongoToJpaEntityRelationship() {
+ DBCollection col = this.mongoTemplate.getCollection(colName);
+ DBCursor dbc = col.find();
+ Object _id = null;
+ for (DBObject dbo : dbc) {
+ System.out.println(dbo);
+ if ("Jack".equals(dbo.get("name"))) {
+ _id = dbo.get("_id");
+ break;
+ }
+ }
+ System.out.println(_id);
+ MongoPerson found = MongoPerson.findPerson(_id);
+ System.out.println(found);
+ if (found != null)
+ System.out.println(found.getAccount());
+ }
- @Test
- @Transactional
- @Rollback(false)
- public void testCreateJpaToMongoEntityRelationship() {
- clearData("resume");
- Person p = new Person("Thomas", 20);
- Resume r = new Resume();
- r.addEducation("Skanstulls High School, 1975");
- r.addEducation("Univ. of Stockholm, 1980");
- r.addJob("DiMark, DBA, 1990-2000");
- r.addJob("VMware, Developer, 2007-");
- p.setResume(r);
- p.setId(1L);
- entityManager.persist(p);
- }
+ @Test
+ @Transactional
+ @Rollback(false)
+ public void testCreateJpaToMongoEntityRelationship() {
+ clearData("resume");
+ Person p = new Person("Thomas", 20);
+ Resume r = new Resume();
+ r.addEducation("Skanstulls High School, 1975");
+ r.addEducation("Univ. of Stockholm, 1980");
+ r.addJob("DiMark, DBA, 1990-2000");
+ r.addJob("VMware, Developer, 2007-");
+ p.setResume(r);
+ p.setId(1L);
+ entityManager.persist(p);
+ }
- @Test
- @Transactional
- public void testReadJpaToMongoEntityRelationship() {
- Person found = entityManager.find(Person.class, 1L);
- System.out.println(found);
+ @Test
+ @Transactional
+ public void testReadJpaToMongoEntityRelationship() {
+ Person found = entityManager.find(Person.class, 1L);
+ System.out.println(found);
// TODO: This part isn't working yet - there is no reference to the Momgo _id stored in the db
// if (found != null)
// System.out.println(found.getResume());
- }
+ }
}
diff --git a/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Account.java b/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Account.java
index 56b340bcf..2b41a7ad3 100644
--- a/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Account.java
+++ b/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Account.java
@@ -5,61 +5,62 @@ import javax.persistence.Id;
@Entity
public class Account {
-
- @Id private Long id;
- private String name;
+ @Id
+ private Long id;
- private float balance;
+ private String name;
- private String friendlyName;
+ private float balance;
- private String whatever;
+ private String friendlyName;
- public Long getId() {
- return id;
- }
+ private String whatever;
- public void setId(Long id) {
- this.id = id;
- }
+ public Long getId() {
+ return id;
+ }
- public String getName() {
- return name;
- }
+ public void setId(Long id) {
+ this.id = id;
+ }
- public void setName(String name) {
- this.name = name;
- }
+ public String getName() {
+ return name;
+ }
- public float getBalance() {
- return balance;
- }
+ public void setName(String name) {
+ this.name = name;
+ }
- public void setBalance(float balance) {
- this.balance = balance;
- }
+ public float getBalance() {
+ return balance;
+ }
- public String getFriendlyName() {
- return friendlyName;
- }
+ public void setBalance(float balance) {
+ this.balance = balance;
+ }
- public void setFriendlyName(String friendlyName) {
- this.friendlyName = friendlyName;
- }
+ public String getFriendlyName() {
+ return friendlyName;
+ }
- public String getWhatever() {
- return whatever;
- }
+ public void setFriendlyName(String friendlyName) {
+ this.friendlyName = friendlyName;
+ }
- public void setWhatever(String whatever) {
- this.whatever = whatever;
- }
+ public String getWhatever() {
+ return whatever;
+ }
- @Override
- public String toString() {
- return "Account [id=" + id + ", name=" + name + ", balance=" + balance
- + ", friendlyName=" + friendlyName + "]";
- }
+ public void setWhatever(String whatever) {
+ this.whatever = whatever;
+ }
+
+ @Override
+ public String toString() {
+ return "Account [id=" + id + ", name=" + name + ", balance=" + balance
+ + ", friendlyName=" + friendlyName + "]";
+ }
}
diff --git a/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/MongoPerson.java b/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/MongoPerson.java
index 0603beb86..7b123a977 100644
--- a/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/MongoPerson.java
+++ b/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/MongoPerson.java
@@ -2,62 +2,62 @@ package org.springframework.persistence.document.test;
import org.springframework.persistence.RelatedEntity;
import org.springframework.persistence.document.DocumentEntity;
-
+
@DocumentEntity
public class MongoPerson {
-
- // TODO only public because of AspectJ bug
- public String name;
-
- public int age;
-
- public java.util.Date birthDate;
- // TODO only public to check weaving bug--
- // seems to work whereas private didn't
- @RelatedEntity
- public Account account;
+ // TODO only public because of AspectJ bug
+ public String name;
- public MongoPerson(String name, int age) {
- this.name = name;
- this.age = age;
- this.birthDate = new java.util.Date();
- }
+ public int age;
- public void birthday() {
- ++age;
- }
+ public java.util.Date birthDate;
- public String getName() {
- return name;
- }
+ // TODO only public to check weaving bug--
+ // seems to work whereas private didn't
+ @RelatedEntity
+ public Account account;
- public void setName(String name) {
- this.name = name;
- }
+ public MongoPerson(String name, int age) {
+ this.name = name;
+ this.age = age;
+ this.birthDate = new java.util.Date();
+ }
- public int getAge() {
- return age;
- }
+ public void birthday() {
+ ++age;
+ }
- public void setAge(int age) {
- this.age = age;
- }
+ public String getName() {
+ return name;
+ }
- public java.util.Date getBirthDate() {
- return birthDate;
- }
+ public void setName(String name) {
+ this.name = name;
+ }
- public void setBirthDate(java.util.Date birthDate) {
- this.birthDate = birthDate;
- }
+ public int getAge() {
+ return age;
+ }
- public Account getAccount() {
- return account;
- }
+ public void setAge(int age) {
+ this.age = age;
+ }
- public void setAccount(Account account) {
- this.account = account;
- }
+ public java.util.Date getBirthDate() {
+ return birthDate;
+ }
+
+ public void setBirthDate(java.util.Date birthDate) {
+ this.birthDate = birthDate;
+ }
+
+ public Account getAccount() {
+ return account;
+ }
+
+ public void setAccount(Account account) {
+ this.account = account;
+ }
}
diff --git a/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/MongoPerson_Roo_Mongo_Entity.aj b/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/MongoPerson_Roo_Mongo_Entity.aj
index 3cd14ee18..cb7751aa7 100644
--- a/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/MongoPerson_Roo_Mongo_Entity.aj
+++ b/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/MongoPerson_Roo_Mongo_Entity.aj
@@ -3,58 +3,52 @@ package org.springframework.persistence.document.test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.persistence.support.ChangeSet;
-import org.springframework.persistence.support.ChangeSetBacked;
-import org.springframework.persistence.support.ChangeSetConfiguration;
-import org.springframework.persistence.support.ChangeSetPersister;
-import org.springframework.persistence.support.ChangeSetSynchronizer;
-import org.springframework.persistence.support.HashMapChangeSet;
+import org.springframework.persistence.support.*;
import org.springframework.persistence.support.ChangeSetPersister.NotFoundException;
/**
* EXAMPLE OF CODE THAT SHOULD BE GENERATED BY ROO BESIDES EACH MONGOENTITY CLASS
- *
+ *
* Note: Combines X_Roo_Entity with X_Roo_Finder, as
* we need only a single aspect for entities.
- *
+ *
* @author Thomas Risberg
*
*/
privileged aspect MongoPerson_Roo_Mongo_Entity {
-
- private static ChangeSetPersister changeSetPersister() {
- return new MongoConfigurationHolder().changeSetConfig.getChangeSetPersister();
- }
-
- private static ChangeSetSynchronizer changeSetManager() {
- return new MongoConfigurationHolder().changeSetConfig.getChangeSetManager();
- }
- @Configurable
- public static class MongoConfigurationHolder {
- @Autowired
- @Qualifier("mongoChangeSetConfiguration")
- public ChangeSetConfiguration changeSetConfig;
- }
+ private static ChangeSetPersister changeSetPersister() {
+ return new MongoConfigurationHolder().changeSetConfig.getChangeSetPersister();
+ }
- /**
- * Add constructor that takes ChangeSet.
- * @param ChangeSet
- */
- public MongoPerson.new(ChangeSet cs) {
- super();
- setChangeSet(cs);
- }
+ private static ChangeSetSynchronizer changeSetManager() {
+ return new MongoConfigurationHolder().changeSetConfig.getChangeSetManager();
+ }
- public static MongoPerson MongoPerson.findPerson(Object id) {
- ChangeSet rv = new HashMapChangeSet();
- try {
- changeSetPersister().getPersistentState(MongoPerson.class, id, rv);
- return new MongoPerson(rv);
- }
- catch (NotFoundException ex) {
- return null;
- }
- }
+ @Configurable
+ public static class MongoConfigurationHolder {
+ @Autowired
+ @Qualifier("mongoChangeSetConfiguration")
+ public ChangeSetConfiguration changeSetConfig;
+ }
+
+ /**
+ * Add constructor that takes ChangeSet.
+ * @param ChangeSet
+ */
+ public MongoPerson.new(ChangeSet cs) {
+ super();
+ setChangeSet(cs);
+ }
+
+ public static MongoPerson MongoPerson.findPerson(Object id) {
+ ChangeSet rv = new HashMapChangeSet();
+ try {
+ changeSetPersister().getPersistentState(MongoPerson.class, id, rv);
+ return new MongoPerson(rv);
+ } catch (NotFoundException ex) {
+ return null;
+ }
+ }
}
diff --git a/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Person.java b/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Person.java
index 070fa11e3..71d323b91 100644
--- a/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Person.java
+++ b/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Person.java
@@ -4,73 +4,74 @@ import javax.persistence.Entity;
import javax.persistence.Id;
import org.springframework.persistence.RelatedEntity;
-
+
@Entity
public class Person {
-
- @Id Long id;
-
- private String name;
-
- private int age;
-
- private java.util.Date birthDate;
-// @Document // need to decide what the annotation here should be
- @RelatedEntity
- public Resume resume;
+ @Id
+ Long id;
- public Person() {
- }
+ private String name;
- public Person(String name, int age) {
- this.name = name;
- this.age = age;
- this.birthDate = new java.util.Date();
- }
+ private int age;
- public void birthday() {
- ++age;
- }
+ private java.util.Date birthDate;
- public Long getId() {
- return id;
- }
+ // @Document // need to decide what the annotation here should be
+ @RelatedEntity
+ public Resume resume;
- public void setId(Long id) {
- this.id = id;
- }
+ public Person() {
+ }
- public String getName() {
- return name;
- }
+ public Person(String name, int age) {
+ this.name = name;
+ this.age = age;
+ this.birthDate = new java.util.Date();
+ }
- public void setName(String name) {
- this.name = name;
- }
+ public void birthday() {
+ ++age;
+ }
- public int getAge() {
- return age;
- }
+ public Long getId() {
+ return id;
+ }
- public void setAge(int age) {
- this.age = age;
- }
+ public void setId(Long id) {
+ this.id = id;
+ }
- public java.util.Date getBirthDate() {
- return birthDate;
- }
+ public String getName() {
+ return name;
+ }
- public void setBirthDate(java.util.Date birthDate) {
- this.birthDate = birthDate;
- }
+ public void setName(String name) {
+ this.name = name;
+ }
- public Resume getResume() {
- return resume;
- }
+ public int getAge() {
+ return age;
+ }
- public void setResume(Resume resume) {
- this.resume = resume;
- }
+ public void setAge(int age) {
+ this.age = age;
+ }
+
+ public java.util.Date getBirthDate() {
+ return birthDate;
+ }
+
+ public void setBirthDate(java.util.Date birthDate) {
+ this.birthDate = birthDate;
+ }
+
+ public Resume getResume() {
+ return resume;
+ }
+
+ public void setResume(Resume resume) {
+ this.resume = resume;
+ }
}
diff --git a/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Resume.java b/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Resume.java
index dc751afb4..f7eaa3fd5 100644
--- a/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Resume.java
+++ b/spring-data-mongodb-cross-store/src/test/java/org/springframework/persistence/document/test/Resume.java
@@ -5,29 +5,29 @@ import org.springframework.persistence.document.DocumentEntity;
@DocumentEntity
public class Resume {
- private String education = "";
-
- private String jobs = "";
+ private String education = "";
- public String getEducation() {
- return education;
- }
+ private String jobs = "";
- public void addEducation(String education) {
- this.education = this.education + (this.education.length() > 0 ? "; " : "") + education;
- }
+ public String getEducation() {
+ return education;
+ }
- public String getJobs() {
- return jobs;
- }
+ public void addEducation(String education) {
+ this.education = this.education + (this.education.length() > 0 ? "; " : "") + education;
+ }
- public void addJob(String job) {
- this.jobs = this.jobs + (this.jobs.length() > 0 ? "; " : "") + job;
- }
+ public String getJobs() {
+ return jobs;
+ }
+
+ public void addJob(String job) {
+ this.jobs = this.jobs + (this.jobs.length() > 0 ? "; " : "") + job;
+ }
+
+ @Override
+ public String toString() {
+ return "Resume [education=" + education + ", jobs=" + jobs + "]";
+ }
- @Override
- public String toString() {
- return "Resume [education=" + education + ", jobs=" + jobs + "]";
- }
-
}
diff --git a/spring-data-mongodb-cross-store/src/test/resources/META-INF/persistence.xml b/spring-data-mongodb-cross-store/src/test/resources/META-INF/persistence.xml
index 232536c0d..294794afa 100644
--- a/spring-data-mongodb-cross-store/src/test/resources/META-INF/persistence.xml
+++ b/spring-data-mongodb-cross-store/src/test/resources/META-INF/persistence.xml
@@ -1,13 +1,15 @@
-
-
- org.hibernate.ejb.HibernatePersistence
- org.springframework.persistence.document.test.Account
-
-
-
-
-
-
-
+
+
+ org.hibernate.ejb.HibernatePersistence
+ org.springframework.persistence.document.test.Account
+
+
+
+
+
+
+
diff --git a/spring-data-mongodb-cross-store/src/test/resources/META-INF/spring/applicationContext.xml b/spring-data-mongodb-cross-store/src/test/resources/META-INF/spring/applicationContext.xml
index 8b359c8b8..807f56a55 100644
--- a/spring-data-mongodb-cross-store/src/test/resources/META-INF/spring/applicationContext.xml
+++ b/spring-data-mongodb-cross-store/src/test/resources/META-INF/spring/applicationContext.xml
@@ -1,98 +1,99 @@
-
+
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
+
+
+
+
+
-
+
-
-
-
+
-
-
-
-
-
+
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-data-mongodb/.classpath b/spring-data-mongodb/.classpath
deleted file mode 100644
index 40f8440e7..000000000
--- a/spring-data-mongodb/.classpath
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/spring-data-mongodb/.project b/spring-data-mongodb/.project
deleted file mode 100644
index 942129127..000000000
--- a/spring-data-mongodb/.project
+++ /dev/null
@@ -1,36 +0,0 @@
-
-
- spring-data-mongodb
-
-
-
-
-
- org.eclipse.wst.common.project.facet.core.builder
-
-
-
-
- org.eclipse.jdt.core.javabuilder
-
-
-
-
- org.maven.ide.eclipse.maven2Builder
-
-
-
-
- org.eclipse.wst.validation.validationbuilder
-
-
-
-
-
- org.eclipse.jem.workbench.JavaEMFNature
- org.eclipse.wst.common.modulecore.ModuleCoreNature
- org.eclipse.jdt.core.javanature
- org.maven.ide.eclipse.maven2Nature
- org.eclipse.wst.common.project.facet.core.nature
-
-
diff --git a/spring-data-mongodb/.settings/org.eclipse.jdt.core.prefs b/spring-data-mongodb/.settings/org.eclipse.jdt.core.prefs
deleted file mode 100644
index b3ef56f5a..000000000
--- a/spring-data-mongodb/.settings/org.eclipse.jdt.core.prefs
+++ /dev/null
@@ -1,9 +0,0 @@
-#Wed Mar 09 13:51:37 EST 2011
-eclipse.preferences.version=1
-org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
-org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
-org.eclipse.jdt.core.compiler.compliance=1.6
-org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
-org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
-org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
-org.eclipse.jdt.core.compiler.source=1.6
diff --git a/spring-data-mongodb/.settings/org.eclipse.wst.common.component b/spring-data-mongodb/.settings/org.eclipse.wst.common.component
deleted file mode 100644
index d56de02bd..000000000
--- a/spring-data-mongodb/.settings/org.eclipse.wst.common.component
+++ /dev/null
@@ -1,7 +0,0 @@
-
-
-
-
-
-
-
diff --git a/spring-data-mongodb/.settings/org.eclipse.wst.common.project.facet.core.xml b/spring-data-mongodb/.settings/org.eclipse.wst.common.project.facet.core.xml
deleted file mode 100644
index 5c9bd7532..000000000
--- a/spring-data-mongodb/.settings/org.eclipse.wst.common.project.facet.core.xml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
diff --git a/spring-data-mongodb/.settings/org.maven.ide.eclipse.prefs b/spring-data-mongodb/.settings/org.maven.ide.eclipse.prefs
deleted file mode 100644
index 5545b44b1..000000000
--- a/spring-data-mongodb/.settings/org.maven.ide.eclipse.prefs
+++ /dev/null
@@ -1,9 +0,0 @@
-#Wed Oct 06 14:49:46 EDT 2010
-activeProfiles=
-eclipse.preferences.version=1
-fullBuildGoals=process-test-resources
-includeModules=false
-resolveWorkspaceProjects=true
-resourceFilterGoals=process-resources resources\:testResources
-skipCompilerPlugin=true
-version=1
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/CannotGetMongoDbConnectionException.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/CannotGetMongoDbConnectionException.java
index 6595b89c8..d9e2505ca 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/CannotGetMongoDbConnectionException.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/CannotGetMongoDbConnectionException.java
@@ -19,13 +19,13 @@ import org.springframework.dao.DataAccessResourceFailureException;
public class CannotGetMongoDbConnectionException extends DataAccessResourceFailureException {
- private static final long serialVersionUID = 1172099106475265589L;
+ private static final long serialVersionUID = 1172099106475265589L;
- public CannotGetMongoDbConnectionException(String msg, Throwable cause) {
- super(msg, cause);
- }
+ public CannotGetMongoDbConnectionException(String msg, Throwable cause) {
+ super(msg, cause);
+ }
- public CannotGetMongoDbConnectionException(String msg) {
- super(msg);
- }
+ public CannotGetMongoDbConnectionException(String msg) {
+ super(msg);
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/CollectionCallback.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/CollectionCallback.java
index b100e6638..8c29a6178 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/CollectionCallback.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/CollectionCallback.java
@@ -15,13 +15,12 @@
*/
package org.springframework.data.document.mongodb;
-import org.springframework.dao.DataAccessException;
-
import com.mongodb.DBCollection;
import com.mongodb.MongoException;
+import org.springframework.dao.DataAccessException;
public interface CollectionCallback {
- T doInCollection(DBCollection collection) throws MongoException, DataAccessException;
-
+ T doInCollection(DBCollection collection) throws MongoException, DataAccessException;
+
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/CollectionOptions.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/CollectionOptions.java
index 15795b8e6..52f4769f0 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/CollectionOptions.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/CollectionOptions.java
@@ -16,56 +16,56 @@
package org.springframework.data.document.mongodb;
/**
- * Provides a simple wrapper to encapsulate the variety of settings you can use when creating a collection.
- *
- * @author Thomas Risberg
+ * Provides a simple wrapper to encapsulate the variety of settings you can use when creating a collection.
*
+ * @author Thomas Risberg
*/
public class CollectionOptions {
-
- private Integer maxDocuments;
-
- private Integer size;
-
- private Boolean capped;
-
- /**
- * Constructs a new CollectionOptions instance.
- * @param size the collection size in bytes, this data space is preallocated
- * @param maxDocuments the maximum number of documents in the collection.
- * @param capped true to created a "capped" collection (fixed size with auto-FIFO behavior
- * based on insertion order), false otherwise.
- */
- public CollectionOptions(Integer size, Integer maxDocuments, Boolean capped) {
- super();
- this.maxDocuments = maxDocuments;
- this.size = size;
- this.capped = capped;
- }
- public Integer getMaxDocuments() {
- return maxDocuments;
- }
+ private Integer maxDocuments;
- public void setMaxDocuments(Integer maxDocuments) {
- this.maxDocuments = maxDocuments;
- }
+ private Integer size;
- public Integer getSize() {
- return size;
- }
+ private Boolean capped;
- public void setSize(Integer size) {
- this.size = size;
- }
+ /**
+ * Constructs a new CollectionOptions instance.
+ *
+ * @param size the collection size in bytes, this data space is preallocated
+ * @param maxDocuments the maximum number of documents in the collection.
+ * @param capped true to created a "capped" collection (fixed size with auto-FIFO behavior
+ * based on insertion order), false otherwise.
+ */
+ public CollectionOptions(Integer size, Integer maxDocuments, Boolean capped) {
+ super();
+ this.maxDocuments = maxDocuments;
+ this.size = size;
+ this.capped = capped;
+ }
+
+ public Integer getMaxDocuments() {
+ return maxDocuments;
+ }
+
+ public void setMaxDocuments(Integer maxDocuments) {
+ this.maxDocuments = maxDocuments;
+ }
+
+ public Integer getSize() {
+ return size;
+ }
+
+ public void setSize(Integer size) {
+ this.size = size;
+ }
+
+ public Boolean getCapped() {
+ return capped;
+ }
+
+ public void setCapped(Boolean capped) {
+ this.capped = capped;
+ }
- public Boolean getCapped() {
- return capped;
- }
- public void setCapped(Boolean capped) {
- this.capped = capped;
- }
-
-
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/CursorPreparer.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/CursorPreparer.java
index b12ac7349..9d07c0684 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/CursorPreparer.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/CursorPreparer.java
@@ -20,15 +20,15 @@ import com.mongodb.DBCursor;
/**
* Simple callback interface to allow customization of a {@link DBCursor}.
- *
+ *
* @author Oliver Gierke
*/
public interface CursorPreparer {
- /**
- * Prepare the given cursor (apply limits, skips and so on). Returns th eprepared cursor.
- *
- * @param cursor
- */
- DBCursor prepare(DBCursor cursor);
+ /**
+ * Prepare the given cursor (apply limits, skips and so on). Returns th eprepared cursor.
+ *
+ * @param cursor
+ */
+ DBCursor prepare(DBCursor cursor);
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/DbCallback.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/DbCallback.java
index a54efdd0f..4ea233359 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/DbCallback.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/DbCallback.java
@@ -15,12 +15,11 @@
*/
package org.springframework.data.document.mongodb;
-import org.springframework.dao.DataAccessException;
-
import com.mongodb.DB;
import com.mongodb.MongoException;
+import org.springframework.dao.DataAccessException;
public interface DbCallback {
- T doInDB(DB db) throws MongoException, DataAccessException;
+ T doInDB(DB db) throws MongoException, DataAccessException;
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/DbHolder.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/DbHolder.java
index 596cd31dc..d6f713aa1 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/DbHolder.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/DbHolder.java
@@ -3,69 +3,68 @@ package org.springframework.data.document.mongodb;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
+import com.mongodb.DB;
import org.springframework.transaction.support.ResourceHolderSupport;
import org.springframework.util.Assert;
-import com.mongodb.DB;
-
class DbHolder extends ResourceHolderSupport {
- private static final Object DEFAULT_KEY = new Object();
+ private static final Object DEFAULT_KEY = new Object();
- private final Map dbMap = new ConcurrentHashMap();
+ private final Map dbMap = new ConcurrentHashMap();
- public DbHolder(DB db) {
- addDB(db);
- }
+ public DbHolder(DB db) {
+ addDB(db);
+ }
- public DbHolder(Object key, DB db) {
- addDB(key, db);
- }
+ public DbHolder(Object key, DB db) {
+ addDB(key, db);
+ }
- public DB getDB() {
- return getDB(DEFAULT_KEY);
- }
+ public DB getDB() {
+ return getDB(DEFAULT_KEY);
+ }
- public DB getDB(Object key) {
- return this.dbMap.get(key);
- }
+ public DB getDB(Object key) {
+ return this.dbMap.get(key);
+ }
- public DB getAnyDB() {
- if (!this.dbMap.isEmpty()) {
- return this.dbMap.values().iterator().next();
- }
- return null;
- }
+ public DB getAnyDB() {
+ if (!this.dbMap.isEmpty()) {
+ return this.dbMap.values().iterator().next();
+ }
+ return null;
+ }
- public void addDB(DB session) {
- addDB(DEFAULT_KEY, session);
- }
+ public void addDB(DB session) {
+ addDB(DEFAULT_KEY, session);
+ }
- public void addDB(Object key, DB session) {
- Assert.notNull(key, "Key must not be null");
- Assert.notNull(session, "DB must not be null");
- this.dbMap.put(key, session);
- }
+ public void addDB(Object key, DB session) {
+ Assert.notNull(key, "Key must not be null");
+ Assert.notNull(session, "DB must not be null");
+ this.dbMap.put(key, session);
+ }
- public DB removeDB(Object key) {
- return this.dbMap.remove(key);
- }
+ public DB removeDB(Object key) {
+ return this.dbMap.remove(key);
+ }
- public boolean containsDB(DB session) {
- return this.dbMap.containsValue(session);
- }
+ public boolean containsDB(DB session) {
+ return this.dbMap.containsValue(session);
+ }
- public boolean isEmpty() {
- return this.dbMap.isEmpty();
- }
+ public boolean isEmpty() {
+ return this.dbMap.isEmpty();
+ }
- public boolean doesNotHoldNonDefaultDB() {
- synchronized (this.dbMap) {
- return this.dbMap.isEmpty() ||
- (this.dbMap.size() == 1 && this.dbMap.containsKey(DEFAULT_KEY));
- }
- }
+ public boolean doesNotHoldNonDefaultDB() {
+ synchronized (this.dbMap) {
+ return this.dbMap.isEmpty() ||
+ (this.dbMap.size() == 1 && this.dbMap.containsKey(DEFAULT_KEY));
+ }
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoAdmin.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoAdmin.java
index eac359332..4b20e5c76 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoAdmin.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoAdmin.java
@@ -15,80 +15,80 @@
*/
package org.springframework.data.document.mongodb;
+import com.mongodb.DB;
+import com.mongodb.Mongo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jmx.export.annotation.ManagedOperation;
import org.springframework.jmx.export.annotation.ManagedResource;
-import com.mongodb.DB;
-import com.mongodb.Mongo;
-
/**
* Mongo server administration exposed via JMX annotations
- *
- * @author Mark Pollack
*
+ * @author Mark Pollack
*/
-@ManagedResource(description="Mongo Admin Operations")
+@ManagedResource(description = "Mongo Admin Operations")
public class MongoAdmin implements MongoAdminOperations {
- /** Logger available to subclasses */
- protected final Log logger = LogFactory.getLog(getClass());
-
- private Mongo mongo;
- private String username;
- private String password;
-
- public MongoAdmin(Mongo mongo) {
- this.mongo = mongo;
- }
-
- /* (non-Javadoc)
- * @see org.springframework.data.document.mongodb.MongoAdminOperations#dropDatabase(java.lang.String)
- */
- @ManagedOperation
- public void dropDatabase(String databaseName) {
- mongo.getDB(databaseName).dropDatabase();
- }
-
- /* (non-Javadoc)
- * @see org.springframework.data.document.mongodb.MongoAdminOperations#createDatabase(java.lang.String)
- */
- @ManagedOperation
- public void createDatabase(String databaseName) {
- mongo.getDB(databaseName);
- }
-
- /* (non-Javadoc)
- * @see org.springframework.data.document.mongodb.MongoAdminOperations#getDatabaseStats(java.lang.String)
- */
- @ManagedOperation
- public String getDatabaseStats(String databaseName) {
- return mongo.getDB("testAdminDb").getStats().toString();
- }
-
- /**
- * Sets the username to use to connect to the Mongo database
- *
- * @param username The username to use
- */
- public void setUsername(String username) {
- this.username = username;
- }
+ /**
+ * Logger available to subclasses
+ */
+ protected final Log logger = LogFactory.getLog(getClass());
- /**
- * Sets the password to use to authenticate with the Mongo database.
- *
- * @param password The password to use
- */
- public void setPassword(String password) {
-
- this.password = password;
- }
-
-
- public DB getDb(String databaseName) {
- return MongoDbUtils.getDB(mongo, databaseName, username, password == null ? null : password.toCharArray());
- }
+ private Mongo mongo;
+ private String username;
+ private String password;
+
+ public MongoAdmin(Mongo mongo) {
+ this.mongo = mongo;
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.data.document.mongodb.MongoAdminOperations#dropDatabase(java.lang.String)
+ */
+ @ManagedOperation
+ public void dropDatabase(String databaseName) {
+ mongo.getDB(databaseName).dropDatabase();
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.data.document.mongodb.MongoAdminOperations#createDatabase(java.lang.String)
+ */
+ @ManagedOperation
+ public void createDatabase(String databaseName) {
+ mongo.getDB(databaseName);
+ }
+
+ /* (non-Javadoc)
+ * @see org.springframework.data.document.mongodb.MongoAdminOperations#getDatabaseStats(java.lang.String)
+ */
+ @ManagedOperation
+ public String getDatabaseStats(String databaseName) {
+ return mongo.getDB("testAdminDb").getStats().toString();
+ }
+
+ /**
+ * Sets the username to use to connect to the Mongo database
+ *
+ * @param username The username to use
+ */
+ public void setUsername(String username) {
+ this.username = username;
+ }
+
+ /**
+ * Sets the password to use to authenticate with the Mongo database.
+ *
+ * @param password The password to use
+ */
+ public void setPassword(String password) {
+
+ this.password = password;
+ }
+
+
+ public DB getDb(String databaseName) {
+ return MongoDbUtils.getDB(mongo, databaseName, username, password == null ? null : password.toCharArray());
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoAdminOperations.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoAdminOperations.java
index 40113cbd6..4d1b799c0 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoAdminOperations.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoAdminOperations.java
@@ -4,13 +4,13 @@ import org.springframework.jmx.export.annotation.ManagedOperation;
public interface MongoAdminOperations {
- @ManagedOperation
- public abstract void dropDatabase(String databaseName);
+ @ManagedOperation
+ public abstract void dropDatabase(String databaseName);
- @ManagedOperation
- public abstract void createDatabase(String databaseName);
+ @ManagedOperation
+ public abstract void createDatabase(String databaseName);
- @ManagedOperation
- public abstract String getDatabaseStats(String databaseName);
+ @ManagedOperation
+ public abstract String getDatabaseStats(String databaseName);
}
\ No newline at end of file
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoDbUtils.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoDbUtils.java
index 57c392281..d690ecc4e 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoDbUtils.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoDbUtils.java
@@ -16,151 +16,148 @@
package org.springframework.data.document.mongodb;
+import com.mongodb.DB;
+import com.mongodb.Mongo;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
-import com.mongodb.DB;
-import com.mongodb.Mongo;
-
/**
* Helper class featuring helper methods for internal MongoDb classes.
- *
+ *
* Mainly intended for internal use within the framework.
*
* @author Thomas Risberg
* @author Graeme Rocher
* @author Oliver Gierke
- *
* @since 1.0
*/
public abstract class MongoDbUtils {
- private static final Log LOGGER = LogFactory.getLog(MongoDbUtils.class);
-
- /**
- * Private constructor to prevent instantiation.
- */
- private MongoDbUtils() {
-
- }
+ private static final Log LOGGER = LogFactory.getLog(MongoDbUtils.class);
- /**
- * Obtains a {@link DB} connection for the given {@link Mongo} instance and database name
- *
- * @param mongo The {@link Mongo} instance
- * @param databaseName The database name
- * @return The {@link DB} connection
- */
- public static DB getDB(Mongo mongo, String databaseName) {
- return doGetDB(mongo, databaseName,null,null, true);
- }
+ /**
+ * Private constructor to prevent instantiation.
+ */
+ private MongoDbUtils() {
- /**
- *
- * Obtains a {@link DB} connection for the given {@link Mongo} instance and database name
- *
- * @param mongo The {@link Mongo} instance
- * @param databaseName The database name
- * @param username The username to authenticate with
- * @param password The password to authenticate with
- * @return The {@link DB} connection
- */
- public static DB getDB(Mongo mongo, String databaseName, String username, char[] password) {
- return doGetDB(mongo, databaseName,username,password, true);
- }
+ }
- public static DB doGetDB(Mongo mongo, String databaseName, String username, char[] password, boolean allowCreate) {
- Assert.notNull(mongo, "No Mongo instance specified");
+ /**
+ * Obtains a {@link DB} connection for the given {@link Mongo} instance and database name
+ *
+ * @param mongo The {@link Mongo} instance
+ * @param databaseName The database name
+ * @return The {@link DB} connection
+ */
+ public static DB getDB(Mongo mongo, String databaseName) {
+ return doGetDB(mongo, databaseName, null, null, true);
+ }
- DbHolder dbHolder = (DbHolder) TransactionSynchronizationManager.getResource(mongo);
- if (dbHolder != null && !dbHolder.isEmpty()) {
- // pre-bound Mongo DB
- DB db = null;
- if (TransactionSynchronizationManager.isSynchronizationActive() &&
- dbHolder.doesNotHoldNonDefaultDB()) {
- // Spring transaction management is active ->
- db = dbHolder.getDB();
- if (db != null && !dbHolder.isSynchronizedWithTransaction()) {
- LOGGER.debug("Registering Spring transaction synchronization for existing Mongo DB");
- TransactionSynchronizationManager.registerSynchronization(new MongoSynchronization(dbHolder, mongo));
- dbHolder.setSynchronizedWithTransaction(true);
- }
- }
- if (db != null) {
- return db;
- }
- }
+ /**
+ * Obtains a {@link DB} connection for the given {@link Mongo} instance and database name
+ *
+ * @param mongo The {@link Mongo} instance
+ * @param databaseName The database name
+ * @param username The username to authenticate with
+ * @param password The password to authenticate with
+ * @return The {@link DB} connection
+ */
+ public static DB getDB(Mongo mongo, String databaseName, String username, char[] password) {
+ return doGetDB(mongo, databaseName, username, password, true);
+ }
- LOGGER.debug("Opening Mongo DB");
- DB db = mongo.getDB(databaseName);
- boolean creadentialsGiven = username != null && password != null;
-
- if(creadentialsGiven && !db.authenticate(username, password)) {
- throw new CannotGetMongoDbConnectionException("Failed to authenticate with Mongo using the given credentials");
- }
-
- // Use same Session for further Mongo actions within the transaction.
- // Thread object will get removed by synchronization at transaction completion.
- if (TransactionSynchronizationManager.isSynchronizationActive()) {
- // We're within a Spring-managed transaction, possibly from JtaTransactionManager.
- LOGGER.debug("Registering Spring transaction synchronization for new Hibernate Session");
- DbHolder holderToUse = dbHolder;
- if (holderToUse == null) {
- holderToUse = new DbHolder(db);
- }
- else {
- holderToUse.addDB(db);
- }
- TransactionSynchronizationManager.registerSynchronization(new MongoSynchronization(holderToUse, mongo));
- holderToUse.setSynchronizedWithTransaction(true);
- if (holderToUse != dbHolder) {
- TransactionSynchronizationManager.bindResource(mongo, holderToUse);
- }
- }
+ public static DB doGetDB(Mongo mongo, String databaseName, String username, char[] password, boolean allowCreate) {
+ Assert.notNull(mongo, "No Mongo instance specified");
- // Check whether we are allowed to return the DB.
- if (!allowCreate && !isDBTransactional(db, mongo)) {
- throw new IllegalStateException("No Mongo DB bound to thread, " +
- "and configuration does not allow creation of non-transactional one here");
- }
+ DbHolder dbHolder = (DbHolder) TransactionSynchronizationManager.getResource(mongo);
+ if (dbHolder != null && !dbHolder.isEmpty()) {
+ // pre-bound Mongo DB
+ DB db = null;
+ if (TransactionSynchronizationManager.isSynchronizationActive() &&
+ dbHolder.doesNotHoldNonDefaultDB()) {
+ // Spring transaction management is active ->
+ db = dbHolder.getDB();
+ if (db != null && !dbHolder.isSynchronizedWithTransaction()) {
+ LOGGER.debug("Registering Spring transaction synchronization for existing Mongo DB");
+ TransactionSynchronizationManager.registerSynchronization(new MongoSynchronization(dbHolder, mongo));
+ dbHolder.setSynchronizedWithTransaction(true);
+ }
+ }
+ if (db != null) {
+ return db;
+ }
+ }
- return db;
- }
-
+ LOGGER.debug("Opening Mongo DB");
+ DB db = mongo.getDB(databaseName);
+ boolean creadentialsGiven = username != null && password != null;
- /**
- * Return whether the given DB instance is transactional, that is,
- * bound to the current thread by Spring's transaction facilities.
- * @param db the DB to check
- * @param mongo the Mongo instance that the DB was created with
- * (may be null)
- * @return whether the DB is transactional
- */
- public static boolean isDBTransactional(DB db, Mongo mongo) {
- if (mongo == null) {
- return false;
- }
- DbHolder dbHolder =
- (DbHolder) TransactionSynchronizationManager.getResource(mongo);
- return (dbHolder != null && dbHolder.containsDB(db));
- }
-
- /**
- * Perform actual closing of the Mongo DB object,
- * catching and logging any cleanup exceptions thrown.
- * @param db the DB to close (may be null)
- */
- public static void closeDB(DB db) {
- if (db != null) {
- LOGGER.debug("Closing Mongo DB object");
- try {
- db.requestDone();
- }
- catch (Throwable ex) {
- LOGGER.debug("Unexpected exception on closing Mongo DB object", ex);
- }
- }
- }
+ if (creadentialsGiven && !db.authenticate(username, password)) {
+ throw new CannotGetMongoDbConnectionException("Failed to authenticate with Mongo using the given credentials");
+ }
+
+ // Use same Session for further Mongo actions within the transaction.
+ // Thread object will get removed by synchronization at transaction completion.
+ if (TransactionSynchronizationManager.isSynchronizationActive()) {
+ // We're within a Spring-managed transaction, possibly from JtaTransactionManager.
+ LOGGER.debug("Registering Spring transaction synchronization for new Hibernate Session");
+ DbHolder holderToUse = dbHolder;
+ if (holderToUse == null) {
+ holderToUse = new DbHolder(db);
+ } else {
+ holderToUse.addDB(db);
+ }
+ TransactionSynchronizationManager.registerSynchronization(new MongoSynchronization(holderToUse, mongo));
+ holderToUse.setSynchronizedWithTransaction(true);
+ if (holderToUse != dbHolder) {
+ TransactionSynchronizationManager.bindResource(mongo, holderToUse);
+ }
+ }
+
+ // Check whether we are allowed to return the DB.
+ if (!allowCreate && !isDBTransactional(db, mongo)) {
+ throw new IllegalStateException("No Mongo DB bound to thread, " +
+ "and configuration does not allow creation of non-transactional one here");
+ }
+
+ return db;
+ }
+
+
+ /**
+ * Return whether the given DB instance is transactional, that is,
+ * bound to the current thread by Spring's transaction facilities.
+ *
+ * @param db the DB to check
+ * @param mongo the Mongo instance that the DB was created with
+ * (may be null)
+ * @return whether the DB is transactional
+ */
+ public static boolean isDBTransactional(DB db, Mongo mongo) {
+ if (mongo == null) {
+ return false;
+ }
+ DbHolder dbHolder =
+ (DbHolder) TransactionSynchronizationManager.getResource(mongo);
+ return (dbHolder != null && dbHolder.containsDB(db));
+ }
+
+ /**
+ * Perform actual closing of the Mongo DB object,
+ * catching and logging any cleanup exceptions thrown.
+ *
+ * @param db the DB to close (may be null)
+ */
+ public static void closeDB(DB db) {
+ if (db != null) {
+ LOGGER.debug("Closing Mongo DB object");
+ try {
+ db.requestDone();
+ } catch (Throwable ex) {
+ LOGGER.debug("Unexpected exception on closing Mongo DB object", ex);
+ }
+ }
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoExceptionTranslator.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoExceptionTranslator.java
index 381708793..ecf9caad4 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoExceptionTranslator.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoExceptionTranslator.java
@@ -15,52 +15,50 @@
*/
package org.springframework.data.document.mongodb;
+import com.mongodb.MongoException;
+import com.mongodb.MongoException.DuplicateKey;
+import com.mongodb.MongoException.Network;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.data.document.UncategorizedDocumentStoreException;
-import com.mongodb.MongoException;
-import com.mongodb.MongoException.DuplicateKey;
-import com.mongodb.MongoException.Network;
-
/**
* Simple {@link PersistenceExceptionTranslator} for Mongo. Convert the given runtime exception to an appropriate
* exception from the {@code org.springframework.dao} hierarchy. Return {@literal null} if no translation is
* appropriate: any other exception may have resulted from user code, and should not be translated.
+ *
* @param ex runtime exception that occurred
- * @return the corresponding DataAccessException instance, or {@literal null} if the exception should not be translated
- *
- *
* @author Oliver Gierke
+ * @return the corresponding DataAccessException instance, or {@literal null} if the exception should not be translated
*/
public class MongoExceptionTranslator implements PersistenceExceptionTranslator {
- /*
- * (non-Javadoc)
- *
- * @see org.springframework.dao.support.PersistenceExceptionTranslator#
- * translateExceptionIfPossible(java.lang.RuntimeException)
- */
- public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.springframework.dao.support.PersistenceExceptionTranslator#
+ * translateExceptionIfPossible(java.lang.RuntimeException)
+ */
+ public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
- // Check for well-known MongoException subclasses.
+ // Check for well-known MongoException subclasses.
- // All other MongoExceptions
- if (ex instanceof DuplicateKey) {
- return new DataIntegrityViolationException(ex.getMessage(), ex);
- }
- if (ex instanceof Network) {
- return new DataAccessResourceFailureException(ex.getMessage(), ex);
- }
- if (ex instanceof MongoException) {
- return new UncategorizedDocumentStoreException(ex.getMessage(), ex);
- }
+ // All other MongoExceptions
+ if (ex instanceof DuplicateKey) {
+ return new DataIntegrityViolationException(ex.getMessage(), ex);
+ }
+ if (ex instanceof Network) {
+ return new DataAccessResourceFailureException(ex.getMessage(), ex);
+ }
+ if (ex instanceof MongoException) {
+ return new UncategorizedDocumentStoreException(ex.getMessage(), ex);
+ }
- // If we get here, we have an exception that resulted from user code,
- // rather than the persistence provider, so we return null to indicate
- // that translation should not occur.
- return null;
- }
+ // If we get here, we have an exception that resulted from user code,
+ // rather than the persistence provider, so we return null to indicate
+ // that translation should not occur.
+ return null;
+ }
}
diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoFactoryBean.java b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoFactoryBean.java
index 4e2bec879..36ccfe9fc 100644
--- a/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoFactoryBean.java
+++ b/spring-data-mongodb/src/main/java/org/springframework/data/document/mongodb/MongoFactoryBean.java
@@ -18,6 +18,9 @@ package org.springframework.data.document.mongodb;
import java.util.List;
+import com.mongodb.Mongo;
+import com.mongodb.MongoOptions;
+import com.mongodb.ServerAddress;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.FactoryBean;
@@ -26,112 +29,103 @@ import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.util.Assert;
-import com.mongodb.Mongo;
-import com.mongodb.MongoOptions;
-import com.mongodb.ServerAddress;
-
/**
* Convenient factory for configuring MongoDB.
*
* @author Thomas Risberg
* @author Graeme Rocher
- *
* @since 1.0
*/
public class MongoFactoryBean implements FactoryBean, InitializingBean, PersistenceExceptionTranslator {
-
- /**
- * Logger, available to subclasses.
- */
- protected final Log logger = LogFactory.getLog(getClass());
- private Mongo mongo;
- private MongoOptions mongoOptions;
- private String host;
- private Integer port;
- private List replicaSetSeeds;
- private List replicaPair;
+ /**
+ * Logger, available to subclasses.
+ */
+ protected final Log logger = LogFactory.getLog(getClass());
- private PersistenceExceptionTranslator exceptionTranslator = new MongoExceptionTranslator();
-
- public void setMongoOptions(MongoOptions mongoOptions) {
- this.mongoOptions = mongoOptions;
- }
+ private Mongo mongo;
+ private MongoOptions mongoOptions;
+ private String host;
+ private Integer port;
+ private List replicaSetSeeds;
+ private List replicaPair;
- public void setReplicaSetSeeds(List replicaSetSeeds) {
- this.replicaSetSeeds = replicaSetSeeds;
- }
-
- public void setReplicaPair(List replicaPair) {
- this.replicaPair = replicaPair;
- }
+ private PersistenceExceptionTranslator exceptionTranslator = new MongoExceptionTranslator();
- public void setHost(String host) {
- this.host = host;
- }
+ public void setMongoOptions(MongoOptions mongoOptions) {
+ this.mongoOptions = mongoOptions;
+ }
- public void setPort(int port) {
- this.port = port;
- }
+ public void setReplicaSetSeeds(List