diff --git a/pom.xml b/pom.xml
index f36edda3f..649bfcd78 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,6 +32,7 @@
1.7.0.BUILD-SNAPSHOT
3.3.0.BUILD-SNAPSHOT
1.6.0.BUILD-SNAPSHOT
+ 1.4.0.BUILD-SNAPSHOT
4.3.5.Final
@@ -109,6 +110,13 @@
test
+
+ org.springframework.data
+ spring-data-solr
+ ${springdata.solr}
+ test
+
+
org.hsqldb
hsqldb
diff --git a/spring-data-rest-webmvc/pom.xml b/spring-data-rest-webmvc/pom.xml
index 26fd1ced8..f833f12e9 100644
--- a/spring-data-rest-webmvc/pom.xml
+++ b/spring-data-rest-webmvc/pom.xml
@@ -110,6 +110,23 @@
test
+
+ org.apache.solr
+ solr-core
+ 4.7.2
+ test
+
+
+ org.slf4j
+ slf4j-api
+
+
+ jdk.tools
+ jdk.tools
+
+
+
+
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractWebIntegrationTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractWebIntegrationTests.java
index 9b276b00b..dd216274f 100644
--- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractWebIntegrationTests.java
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/AbstractWebIntegrationTests.java
@@ -57,6 +57,7 @@ import com.jayway.jsonpath.JsonPath;
*
* @author Oliver Gierke
* @author Greg Turnquist
+ * @author Christoph Strobl
*/
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
@@ -205,10 +206,18 @@ public abstract class AbstractWebIntegrationTests {
protected String assertJsonPathEquals(String path, String expected, MockHttpServletResponse response)
throws Exception {
- String jsonQueryResults = assertHasJsonPathValue(path, response);
- assertThat(jsonQueryResults, is(expected));
+ Object jsonQueryResults = assertHasJsonPathValue(path, response);
- return jsonQueryResults;
+ String jsonString = "";
+
+ if (jsonQueryResults instanceof JSONArray) {
+ jsonString = ((JSONArray) jsonQueryResults).toJSONString();
+ } else {
+ jsonString = jsonQueryResults != null ? jsonQueryResults.toString() : null;
+ }
+
+ assertThat(jsonString, is(expected));
+ return jsonString;
}
protected ResultMatcher doesNotHaveLinkWithRel(final String rel) {
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/solr/Product.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/solr/Product.java
new file mode 100644
index 000000000..a955682b1
--- /dev/null
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/solr/Product.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.rest.webmvc.solr;
+
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.springframework.data.annotation.Id;
+import org.springframework.data.solr.core.mapping.Indexed;
+import org.springframework.data.solr.core.mapping.SolrDocument;
+import org.springframework.util.ObjectUtils;
+
+/**
+ * @author Christoph Strobl
+ */
+@SolrDocument(solrCoreName = "collection1")
+public class Product {
+
+ private @Id String id;
+ private @Indexed String name;
+ private @Indexed(name = "cat") List categories;
+
+ public Product() {}
+
+ public Product(String id, String name, String... categories) {
+
+ this.id = id;
+ this.name = name;
+ this.categories = ObjectUtils.isEmpty(categories) ? Collections. emptyList() : Arrays.asList(categories);
+ }
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public List getCategories() {
+ return categories;
+ }
+
+ public void setCategories(List categories) {
+ this.categories = categories;
+ }
+}
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/solr/ProductRepository.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/solr/ProductRepository.java
new file mode 100644
index 000000000..097d8c62c
--- /dev/null
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/solr/ProductRepository.java
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.rest.webmvc.solr;
+
+import org.springframework.data.repository.PagingAndSortingRepository;
+
+/**
+ * @author Christoph Strobl
+ */
+public interface ProductRepository extends PagingAndSortingRepository {}
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/solr/SolrInfrastructureConfig.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/solr/SolrInfrastructureConfig.java
new file mode 100644
index 000000000..3ba66d249
--- /dev/null
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/solr/SolrInfrastructureConfig.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.rest.webmvc.solr;
+
+import static org.springframework.data.rest.webmvc.util.TestUtils.*;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
+import org.springframework.data.solr.core.SolrTemplate;
+import org.springframework.data.solr.server.SolrServerFactory;
+import org.springframework.data.solr.server.support.EmbeddedSolrServerFactory;
+import org.springframework.util.FileCopyUtils;
+import org.xml.sax.SAXException;
+
+/**
+ * @author Christoph Strobl
+ */
+@Configuration
+public class SolrInfrastructureConfig {
+
+ private static final String CORE_PROPERTIES = "name=collection1";
+ private static final Resource SOLR_CONFIG = new ClassPathResource("solrconfig.xml", SolrInfrastructureConfig.class);
+ private static final Resource SOLR_SCHEMA = new ClassPathResource("schema.xml", SolrInfrastructureConfig.class);
+
+ @Bean
+ public SolrServerFactory solrServerFactory(final String solrHomeDir) throws ParserConfigurationException,
+ IOException, SAXException {
+
+ prepareConfiguration(solrHomeDir);
+ return new EmbeddedSolrServerFactory(solrHomeDir);
+ }
+
+ @Bean
+ public SolrTemplate solrTemplate(SolrServerFactory factory) {
+ return new SolrTemplate(factory);
+ }
+
+ private static void prepareConfiguration(final String solrHomeDir) throws IOException {
+
+ Map configParams = new HashMap();
+ configParams.put("${data.dir}", solrHomeDir);
+ configParams.put("${lucene.version}", "4.7");
+
+ Resource solrConfig = filterResource(SOLR_CONFIG, configParams);
+ Resource solrSchema = SOLR_SCHEMA;
+
+ File confDir = new File(new File(solrHomeDir, "collection1"), "conf");
+ confDir.mkdirs();
+
+ FileCopyUtils.copy(solrSchema.getInputStream(), new FileOutputStream(createFile(confDir, "schema.xml")));
+ FileCopyUtils.copy(solrConfig.getInputStream(), new FileOutputStream(createFile(confDir, "solrconfig.xml")));
+ FileCopyUtils.copy(CORE_PROPERTIES.getBytes(),
+ new FileOutputStream(createFile(new File(solrHomeDir), "config.properties")));
+ }
+
+ private static File createFile(File parent, String child) throws IOException {
+
+ File file = new File(parent, child);
+ if (!file.exists()) {
+ file.createNewFile();
+ }
+ return file;
+ }
+}
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/solr/SolrTestBase.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/solr/SolrTestBase.java
new file mode 100644
index 000000000..641432150
--- /dev/null
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/solr/SolrTestBase.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.rest.webmvc.solr;
+
+import org.junit.ClassRule;
+import org.junit.rules.TemporaryFolder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.data.solr.repository.config.EnableSolrRepositories;
+import org.springframework.test.context.ContextConfiguration;
+
+/**
+ * @author Christoph Strobl
+ */
+@ContextConfiguration
+public class SolrTestBase {
+
+ public static @ClassRule TemporaryFolder TEMP_FOLDER = new TemporaryFolder();
+
+ @Configuration
+ @EnableSolrRepositories
+ @Import(SolrInfrastructureConfig.class)
+ static class MyConf {
+
+ @Bean
+ String solrHomeDir() {
+ return TEMP_FOLDER.getRoot().getAbsolutePath();
+ }
+ }
+}
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/solr/SolrWebTests.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/solr/SolrWebTests.java
new file mode 100644
index 000000000..ad5c6a154
--- /dev/null
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/solr/SolrWebTests.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2014 the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springframework.data.rest.webmvc.solr;
+
+import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
+import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
+
+import java.util.Arrays;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.ClassRule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.data.rest.webmvc.AbstractWebIntegrationTests;
+import org.springframework.data.solr.repository.config.EnableSolrRepositories;
+import org.springframework.hateoas.Link;
+import org.springframework.http.MediaType;
+import org.springframework.mock.web.MockHttpServletResponse;
+import org.springframework.test.context.ContextConfiguration;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+/**
+ * @author Christoph Strobl
+ */
+@ContextConfiguration(classes = { SolrWebTests.MyConf.class })
+public class SolrWebTests extends AbstractWebIntegrationTests {
+
+ public static @ClassRule TemporaryFolder TEMP_FOLDER = new TemporaryFolder();
+
+ private static final Product PLAYSTATION = new Product("1", "playstation", "electronic", "game", "media");
+ private static final Product GAMEBOY = new Product("2", "gameboy", "electronic");
+ private static final Product AMIGA500 = new Product("3", "amiga500", "ancient");
+
+ private static final ObjectMapper MAPPER = new ObjectMapper();
+
+ @Configuration
+ @EnableSolrRepositories
+ @Import(value = { SolrInfrastructureConfig.class })
+ static class MyConf {
+
+ @Bean
+ String solrHomeDir() {
+ return TEMP_FOLDER.getRoot().getAbsolutePath();
+ }
+
+ }
+
+ @Autowired ProductRepository repo;
+
+ @Before
+ public void setUp() {
+
+ super.setUp();
+ repo.save(Arrays.asList(PLAYSTATION, GAMEBOY, AMIGA500));
+ }
+
+ @After
+ public void tearDown() {
+ repo.deleteAll();
+ }
+
+ /**
+ * @see DATAREST-387
+ */
+ @Test
+ public void allowsPaginationThroughData() throws Exception {
+
+ MockHttpServletResponse response = client.request("/products?page=0&size=1");
+
+ Link nextLink = client.assertHasLinkWithRel(Link.REL_NEXT, response);
+ assertDoesNotHaveLinkWithRel(Link.REL_PREVIOUS, response);
+
+ response = client.request(nextLink);
+ client.assertHasLinkWithRel(Link.REL_PREVIOUS, response);
+ nextLink = client.assertHasLinkWithRel(Link.REL_NEXT, response);
+
+ response = client.request(nextLink);
+ client.assertHasLinkWithRel(Link.REL_PREVIOUS, response);
+ assertDoesNotHaveLinkWithRel(Link.REL_NEXT, response);
+ }
+
+ /**
+ * @see DATAREST-387
+ */
+ @Test
+ public void allowsRetrievingDataById() throws Exception {
+ requestAndCompare(PLAYSTATION);
+ }
+
+ /**
+ * @see DATAREST-387
+ */
+ @Test
+ public void createsEntitesCorrectly() throws Exception {
+
+ Product product = new Product("4", "iWatch", "trends", "scary");
+
+ mvc.perform(
+ put("/products/{id}", 4).content(MAPPER.writeValueAsString(product)).contentType(MediaType.APPLICATION_JSON))
+ .andExpect(status().isCreated()).andReturn().getResponse();
+
+ assertJsonDocumentMatches(product);
+ }
+
+ /**
+ * @see DATAREST-387
+ */
+ @Test
+ public void deletesEntitiesCorrectly() throws Exception {
+ deleteAndVerify(new Link("/products/1"));
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.springframework.data.rest.webmvc.AbstractWebIntegrationTests#expectedRootLinkRels()
+ */
+ @Override
+ protected Iterable expectedRootLinkRels() {
+ return Arrays.asList("products");
+ }
+
+ private void assertJsonDocumentMatches(Product reference) throws Exception {
+ requestAndCompare(reference);
+ }
+
+ private MockHttpServletResponse requestAndCompare(Product reference) throws Exception {
+
+ MockHttpServletResponse response = client.request("/products/" + reference.getId());
+
+ assertJsonPathEquals("name", reference.getName(), response);
+ assertJsonPathEquals("categories", MAPPER.writeValueAsString(reference.getCategories()), response);
+
+ return response;
+ }
+}
diff --git a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/util/TestUtils.java b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/util/TestUtils.java
index 4e75d962c..e8c53a046 100644
--- a/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/util/TestUtils.java
+++ b/spring-data-rest-webmvc/src/test/java/org/springframework/data/rest/webmvc/util/TestUtils.java
@@ -16,27 +16,37 @@
package org.springframework.data.rest.webmvc.util;
import java.io.ByteArrayInputStream;
+import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
+import java.util.Map;
import java.util.Scanner;
+import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.ClassPathResource;
+import org.springframework.core.io.Resource;
import org.springframework.data.rest.webmvc.jpa.JpaWebTests;
import org.springframework.util.Assert;
+import org.springframework.util.CollectionUtils;
+import org.springframework.util.StreamUtils;
+import org.springframework.util.StringUtils;
/**
* Test helper methods.
*
* @author Oliver Gierke
+ * @author Christoph Strobl
*/
public class TestUtils {
+ private static final Charset UTF8 = Charset.forName("UTF-8");
+
public static String readFileFromClasspath(String name) throws Exception {
ClassPathResource file = new ClassPathResource(name, JpaWebTests.class);
StringBuilder builder = new StringBuilder();
- Scanner scanner = new Scanner(file.getFile(), "UTF-8");
+ Scanner scanner = new Scanner(file.getFile(), UTF8.name());
try {
@@ -59,6 +69,31 @@ public class TestUtils {
*/
public static InputStream asStream(String source) {
Assert.notNull(source, "Source string must not be null!");
- return new ByteArrayInputStream(source.getBytes(Charset.forName("UTF-8")));
+ return new ByteArrayInputStream(source.getBytes(UTF8));
}
+
+ /**
+ * Filters the given {@link Resource} by replacing values within.
+ *
+ * @param source must not be {@literal null}.
+ * @param replacements
+ * @return {@link Resource} with replaced values.
+ * @throws IOException
+ */
+ public static Resource filterResource(Resource source, Map replacements) throws IOException {
+
+ Assert.notNull(source, "Cannot filter 'null' resource");
+ if (CollectionUtils.isEmpty(replacements)) {
+ return source;
+ }
+
+ String temp = StreamUtils.copyToString(source.getInputStream(), UTF8);
+
+ for (Map.Entry entry : replacements.entrySet()) {
+ temp = StringUtils.replace(temp, entry.getKey(), entry.getValue() != null ? entry.getValue().toString() : "");
+ }
+
+ return new ByteArrayResource(temp.getBytes(UTF8));
+ }
+
}
diff --git a/spring-data-rest-webmvc/src/test/resources/org/springframework/data/rest/webmvc/solr/schema.xml b/spring-data-rest-webmvc/src/test/resources/org/springframework/data/rest/webmvc/solr/schema.xml
new file mode 100644
index 000000000..d4422dae5
--- /dev/null
+++ b/spring-data-rest-webmvc/src/test/resources/org/springframework/data/rest/webmvc/solr/schema.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+ id
+
diff --git a/spring-data-rest-webmvc/src/test/resources/org/springframework/data/rest/webmvc/solr/solrconfig.xml b/spring-data-rest-webmvc/src/test/resources/org/springframework/data/rest/webmvc/solr/solrconfig.xml
new file mode 100644
index 000000000..bc38797bd
--- /dev/null
+++ b/spring-data-rest-webmvc/src/test/resources/org/springframework/data/rest/webmvc/solr/solrconfig.xml
@@ -0,0 +1,22 @@
+
+
+ ${data.dir}
+
+ ${lucene.version}
+
+
+ ${solr.commitwithin.softcommit:true}
+
+
+
+
+ explicit
+ true
+ text
+
+
+
+
+