package org.infinispan.rest;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertNull;
import static org.testng.AssertJUnit.assertTrue;
import static org.testng.AssertJUnit.fail;
import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import javax.ws.rs.core.CacheControl;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpMethod;
import org.apache.commons.httpclient.HttpMethodBase;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.methods.ByteArrayRequestEntity;
import org.apache.commons.httpclient.methods.DeleteMethod;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.methods.HeadMethod;
import org.apache.commons.httpclient.methods.InputStreamRequestEntity;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.httpclient.methods.PutMethod;
import org.apache.commons.httpclient.methods.RequestEntity;
import org.apache.commons.httpclient.methods.StringRequestEntity;
import org.infinispan.Cache;
import org.infinispan.cache.impl.AbstractDelegatingAdvancedCache;
import org.infinispan.commons.CacheException;
import org.infinispan.commons.api.BasicCacheContainer;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.manager.impl.AbstractDelegatingEmbeddedCacheManager;
import org.infinispan.metadata.Metadata;
import org.infinispan.remoting.MIMECacheEntry;
import org.infinispan.test.TestingUtil;
import org.infinispan.test.fwk.TestCacheManagerFactory;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
/**
* This tests using the Apache HTTP commons client library - but you could use anything Decided to do this instead of
* testing the Server implementation itself, as testing the impl directly was kind of too easy. (Given that RESTEasy
* does most of the heavy lifting !).
*
* @author Michael Neale
* @author Galder ZamarreƱo
* @author Michal Linhard
* @since 4.0
*/
@Test(groups = "functional", testName = "rest.IntegrationTest")
public class IntegrationTest extends RestServerTestBase {
private static final String HOST = "http://localhost:8888";
private static final String cacheName = BasicCacheContainer.DEFAULT_CACHE_NAME;
private static final String fullPath = HOST + "/rest/" + cacheName;
private static final String DATE_PATTERN_RFC1123 = "EEE, dd MMM yyyy HH:mm:ss zzz";
private EmbeddedCacheManager cacheManager = null;
private String fullPathWithPort(Method m, int port) {
return "http://localhost:" + port + "/rest/" + cacheName + "/" + m.getName();
}
@BeforeClass(alwaysRun = true)
void setUp() throws Exception {
cacheManager = TestCacheManagerFactory.fromXml("test-config.xml");
addServer("single", 8888, cacheManager);
startServers();
createClient();
}
EmbeddedCacheManager createCacheManager() throws IOException {
return TestCacheManagerFactory.fromXml("test-config.xml");
}
@AfterClass(alwaysRun = true)
void tearDown() throws Exception {
destroyClient();
stopServers();
TestingUtil.killCacheManagers(cacheManager);
}
public void testBasicOperation(Method m) throws Exception {
String fullPathKey = fullPath + "/" + m.getName();
PutMethod insert = new PutMethod(fullPathKey);
String initialXML = "<hey>ho</hey>";
insert.setRequestEntity(new ByteArrayRequestEntity(initialXML
.getBytes(), "application/octet-stream"));
call(insert);
assertEquals("", insert.getResponseBodyAsString().trim());
assertEquals(HttpStatus.SC_OK, insert.getStatusCode());
GetMethod get = new GetMethod(fullPathKey);
call(get);
byte[] bytes = get.getResponseBody();
assertEquals(bytes.length, initialXML.getBytes().length);
assertEquals(initialXML, get.getResponseBodyAsString());
Header hdr = get.getResponseHeader("Content-Type");
assertEquals("application/octet-stream", hdr.getValue());
DeleteMethod remove = new DeleteMethod(fullPathKey);
call(remove);
call(get);
assertEquals(HttpStatus.SC_NOT_FOUND, get.getStatusCode());
call(insert);
call(get);
assertEquals(initialXML, get.getResponseBodyAsString());
DeleteMethod removeAll = new DeleteMethod(fullPath);
assertEquals(HttpStatus.SC_OK, call(removeAll).getStatusCode());
call(get);
assertEquals(HttpStatus.SC_NOT_FOUND, get.getStatusCode());
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bout);
oo.writeObject(new MIMECacheEntry("foo", "hey".getBytes()));
oo.flush();
byte[] byteData = bout.toByteArray();
PutMethod insertMore = new PutMethod(fullPathKey);
insertMore.setRequestEntity(new ByteArrayRequestEntity(byteData, "application/octet-stream"));
call(insertMore);
GetMethod getMore = new GetMethod(fullPathKey);
call(getMore);
byte[] bytesBack = getMore.getResponseBody();
assertEquals(byteData.length, bytesBack.length);
ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(bytesBack));
MIMECacheEntry ce = (MIMECacheEntry) oin.readObject();
assertEquals("foo", ce.contentType);
}
public void testEmptyGet() throws Exception {
assertEquals(
HttpStatus.SC_NOT_FOUND,
call(new GetMethod(HOST + "/rest/" + cacheName + "/nodata")).getStatusCode()
);
}
public void testDeleteNonExistent() throws Exception {
assertEquals(
HttpStatus.SC_NOT_FOUND,
call(new DeleteMethod(HOST + "/rest/" + cacheName + "/nodata")).getStatusCode()
);
}
public void testGetCollection() throws Exception {
PostMethod post_a = new PostMethod(fullPath + "/a");
post_a.setRequestEntity(new StringRequestEntity("data", "application/text", null));
call(post_a);
PostMethod post_b = new PostMethod(fullPath + "/b");
post_b.setRequestEntity(new StringRequestEntity("data", "application/text", null));
call(post_b);
String html = getCollection("text/html");
assertTrue(html.contains("<a href=\"" + cacheName + "/a\">a</a>"));
assertTrue(html.contains("<a href=\"" + cacheName + "/b\">b</a>"));
String xml = getCollection("application/xml");
assertTrue(xml.contains("<key>a</key>"));
assertTrue(xml.contains("<key>b</key>"));
String plain = getCollection("text/plain;charset=UTF-8");
assertTrue(plain.contains("a" + System.lineSeparator()));
assertTrue(plain.contains("b" + System.lineSeparator()));
String json = getCollection("application/json");
assertTrue(json.contains("\"a\""));
assertTrue(json.contains("\"b\""));
}
public void testGetCollectionEscape() throws Exception {
PostMethod post_a = new PostMethod(fullPath + "/%22a%22");
post_a.setRequestEntity(new StringRequestEntity("data", "application/text", null));
call(post_a);
PostMethod post_b = new PostMethod(fullPath + "/b%3E");
post_b.setRequestEntity(new StringRequestEntity("data", "application/text", null));
call(post_b);
String html = getCollection("text/html");
assertTrue(html.contains("<a href=\"" + cacheName + "/"a"\">"a"</a>"));
assertTrue(html.contains("<a href=\"" + cacheName + "/b>\">b></a>"));
String xml = getCollection("application/xml");
assertTrue(xml.contains("<key>"a"</key>"));
assertTrue(xml.contains("<key>b></key>"));
String plain = getCollection("text/plain;charset=UTF-8");
assertTrue(plain.contains("\"a\"" + System.lineSeparator()));
assertTrue(plain.contains("b>" + System.lineSeparator()));
String json = getCollection("application/json");
assertTrue(json.contains("\\\"a\\\""));
assertTrue(json.contains("\"b>\""));
}
private String getCollection(String variant) throws Exception {
GetMethod get = new GetMethod(fullPath);
get.addRequestHeader("Accept", variant);
HttpMethodBase coll = call(get);
assertEquals(HttpStatus.SC_OK, coll.getStatusCode());
assertEquals(variant, coll.getResponseHeader("Content-Type").getValue());
return coll.getResponseBodyAsString();
}
public void testGet(Method m) throws Exception {
String fullPathKey = fullPath + "/" + m.getName();
PostMethod post = new PostMethod(fullPathKey);
post.setRequestEntity(new StringRequestEntity("data", "application/text", null));
call(post);
HttpMethodBase get = call(new GetMethod(fullPathKey));
assertEquals(HttpStatus.SC_OK, get.getStatusCode());
assertNotNull(get.getResponseHeader("ETag").getValue());
assertNotNull(get.getResponseHeader("Last-Modified").getValue());
assertEquals("application/text", get.getResponseHeader("Content-Type").getValue());
assertEquals("data", get.getResponseBodyAsString());
}
public void testHead(Method m) throws Exception {
String fullPathKey = fullPath + "/" + m.getName();
PostMethod post = new PostMethod(fullPathKey);
post.setRequestEntity(new StringRequestEntity("data", "application/text", null));
call(post);
HttpMethodBase get = call(new HeadMethod(fullPathKey));
assertEquals(HttpStatus.SC_OK, get.getStatusCode());
assertNotNull(get.getResponseHeader("ETag").getValue());
assertNotNull(get.getResponseHeader("Last-Modified").getValue());
assertEquals("application/text", get.getResponseHeader("Content-Type").getValue());
assertNull(get.getResponseBodyAsString());
}
public void testGetIfUnmodified(Method m) throws Exception {
String fullPathKey = fullPath + "/" + m.getName();
PostMethod post = new PostMethod(fullPathKey);
post.setRequestEntity(new StringRequestEntity("data", "application/text", null));
call(post);
HttpMethodBase get = call(new GetMethod(fullPathKey));
assertEquals(HttpStatus.SC_OK, get.getStatusCode());
assertNotNull(get.getResponseHeader("ETag").getValue());
String lastMod = get.getResponseHeader("Last-Modified").getValue();
assertNotNull(lastMod);
assertEquals("application/text", get.getResponseHeader("Content-Type").getValue());
assertEquals("data", get.getResponseBodyAsString());
// now get again
GetMethod getAgain = new GetMethod(fullPathKey);
getAgain.addRequestHeader("If-Unmodified-Since", lastMod);
get = call(getAgain);
assertEquals(HttpStatus.SC_OK, get.getStatusCode());
assertNotNull(get.getResponseHeader("ETag").getValue());
assertNotNull(get.getResponseHeader("Last-Modified").getValue());
assertEquals("application/text", get.getResponseHeader("Content-Type").getValue());
assertEquals("data", get.getResponseBodyAsString());
}
public void testPostDuplicate(Method m) throws Exception {
String fullPathKey = fullPath + "/" + m.getName();
PostMethod post = new PostMethod(fullPathKey);
post.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
call(post);
//Should get a conflict as its a DUPE post
assertEquals(HttpStatus.SC_CONFLICT, call(post).getStatusCode());
PutMethod put = new PutMethod(fullPathKey);
put.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
//Should be all ok as its a put
assertEquals(HttpStatus.SC_OK, call(put).getStatusCode());
}
public void testPutDataWithTimeToLive(Method m) throws Exception {
putAndAssertEphemeralData(m, "2", "3");
}
public void testPutDataWithMaxIdleOnly(Method m) throws Exception {
putAndAssertEphemeralData(m, "", "3");
}
public void testPutDataWithTimeToLiveOnly(Method m) throws Exception {
putAndAssertEphemeralData(m, "3", "");
}
private void putAndAssertEphemeralData(Method m, String timeToLiveSeconds, String maxIdleTimeSeconds) throws Exception {
String fullPathKey = fullPath + "/" + m.getName();
PostMethod post = new PostMethod(fullPathKey);
int maxWaitTime = 0;
if (!timeToLiveSeconds.isEmpty()) {
maxWaitTime = Math.max(maxWaitTime, Integer.parseInt(timeToLiveSeconds));
post.setRequestHeader("timeToLiveSeconds", timeToLiveSeconds);
}
if (!maxIdleTimeSeconds.isEmpty()) {
maxWaitTime = Math.max(maxWaitTime, Integer.parseInt(maxIdleTimeSeconds));
post.setRequestHeader("maxIdleTimeSeconds", maxIdleTimeSeconds);
}
post.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
call(post);
HttpMethodBase get = call(new GetMethod(fullPathKey));
assertEquals("data", get.getResponseBodyAsString());
TestingUtil.sleepThread((maxWaitTime + 1) * 1000);
call(get);
assertEquals(HttpStatus.SC_NOT_FOUND, get.getStatusCode());
}
public void testPutDataWithIfMatch(Method m) throws Exception {
// Put the data first
String fullPathKey = fullPath + "/" + m.getName();
PutMethod put = new PutMethod(fullPathKey);
put.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
call(put);
// Now get it to retrieve some attributes
HttpMethodBase get = call(new GetMethod(fullPathKey));
assertEquals(HttpStatus.SC_OK, get.getStatusCode());
String etag = get.getResponseHeader("ETag").getValue();
// Put again using the If-Match with the ETag we got back from the get
PutMethod reput = new PutMethod(fullPathKey);
reput.setRequestHeader("If-Match", etag);
reput.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
assertEquals(HttpStatus.SC_OK, call(reput).getStatusCode());
// Try to put again, but with a different ETag
PutMethod reputAgain = new PutMethod(fullPathKey);
reputAgain.setRequestHeader("If-Match", "x");
reputAgain.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
assertEquals(HttpStatus.SC_PRECONDITION_FAILED, call(reputAgain).getStatusCode());
}
public void testPutDataWithIfNoneMatch(Method m) throws Exception {
// Put the data first
String fullPathKey = fullPath + "/" + m.getName();
PutMethod put = new PutMethod(fullPathKey);
put.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
call(put);
// Now get it to retrieve some attributes
HttpMethodBase get = call(new GetMethod(fullPathKey));
assertEquals(HttpStatus.SC_OK, get.getStatusCode());
String etag = get.getResponseHeader("ETag").getValue();
// Put again using the If-Match with the ETag we got back from the get
PutMethod reput = new PutMethod(fullPathKey);
reput.setRequestHeader("If-None-Match", "x");
reput.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
assertEquals(HttpStatus.SC_OK, call(reput).getStatusCode());
// Try to put again, but with a different ETag
PutMethod reputAgain = new PutMethod(fullPathKey);
reputAgain.setRequestHeader("If-None-Match", etag);
reputAgain.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
assertEquals(HttpStatus.SC_PRECONDITION_FAILED, call(reputAgain).getStatusCode());
}
public void testPutDataWithIfModifiedSince(Method m) throws Exception {
// Put the data first
String fullPathKey = fullPath + "/" + m.getName();
PutMethod put = new PutMethod(fullPathKey);
put.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
call(put);
// Now get it to retrieve some attributes
HttpMethodBase get = call(new GetMethod(fullPathKey));
assertEquals(HttpStatus.SC_OK, get.getStatusCode());
String lastMod = get.getResponseHeader("Last-Modified").getValue();
// Put again using the If-Modified-Since with the lastMod we got back from the get
PutMethod reput = new PutMethod(fullPathKey);
reput.setRequestHeader("If-Modified-Since", lastMod);
reput.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
assertEquals(HttpStatus.SC_NOT_MODIFIED, call(reput).getStatusCode());
// Try to put again, but with an older last modification date
PutMethod reputAgain = new PutMethod(fullPathKey);
String dateMinus = addDay(lastMod, -1);
reputAgain.setRequestHeader("If-Modified-Since", dateMinus);
reputAgain.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
assertEquals(HttpStatus.SC_OK, call(reputAgain).getStatusCode());
}
public void testPutDataWithIfUnModifiedSince(Method m) throws Exception {
// Put the data first
String fullPathKey = fullPath + "/" + m.getName();
PutMethod put = new PutMethod(fullPathKey);
put.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
call(put);
// Now get it to retrieve some attributes
HttpMethodBase get = call(new GetMethod(fullPathKey));
assertEquals(HttpStatus.SC_OK, get.getStatusCode());
String lastMod = get.getResponseHeader("Last-Modified").getValue();
// Put again using the If-Unmodified-Since with a date earlier than the one we got back from the GET
PutMethod reput = new PutMethod(fullPathKey);
String dateMinus = addDay(lastMod, -1);
reput.setRequestHeader("If-Unmodified-Since", dateMinus);
reput.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
assertEquals(HttpStatus.SC_PRECONDITION_FAILED, call(reput).getStatusCode());
// Try to put again, but using the date returned by the GET
PutMethod reputAgain = new PutMethod(fullPathKey);
reputAgain.setRequestHeader("If-Unmodified-Since", lastMod);
reputAgain.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
assertEquals(HttpStatus.SC_OK, call(reputAgain).getStatusCode());
}
public void testDeleteDataWithIfMatch(Method m) throws Exception {
// Put the data first
String fullPathKey = fullPath + "/" + m.getName();
PutMethod put = new PutMethod(fullPathKey);
put.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
call(put);
// Now get it to retrieve some attributes
HttpMethodBase get = call(new GetMethod(fullPathKey));
assertEquals(HttpStatus.SC_OK, get.getStatusCode());
String etag = get.getResponseHeader("ETag").getValue();
// Attempt to delete with a wrong ETag
DeleteMethod delete = new DeleteMethod(fullPathKey);
delete.setRequestHeader("If-Match", "x");
assertEquals(HttpStatus.SC_PRECONDITION_FAILED, call(delete).getStatusCode());
// Try to delete again, but with the proper ETag
DeleteMethod deleteAgain = new DeleteMethod(fullPathKey);
deleteAgain.setRequestHeader("If-Match", etag);
assertEquals(HttpStatus.SC_OK, call(deleteAgain).getStatusCode());
}
public void testDeleteDataWithIfNoneMatch(Method m) throws Exception {
// Put the data first
String fullPathKey = fullPath + "/" + m.getName();
PutMethod put = new PutMethod(fullPathKey);
put.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
call(put);
// Now get it to retrieve some attributes
HttpMethodBase get = call(new GetMethod(fullPathKey));
assertEquals(HttpStatus.SC_OK, get.getStatusCode());
String etag = get.getResponseHeader("ETag").getValue();
// Attempt to delete with the ETag
DeleteMethod delete = new DeleteMethod(fullPathKey);
delete.setRequestHeader("If-None-Match", etag);
assertEquals(HttpStatus.SC_PRECONDITION_FAILED, call(delete).getStatusCode());
// Try to delete again, but with a non-matching ETag
DeleteMethod deleteAgain = new DeleteMethod(fullPathKey);
deleteAgain.setRequestHeader("If-None-Match", "x");
assertEquals(HttpStatus.SC_OK, call(deleteAgain).getStatusCode());
}
public void testDeleteDataWithIfModifiedSince(Method m) throws Exception {
// Put the data first
String fullPathKey = fullPath + "/" + m.getName();
PutMethod put = new PutMethod(fullPathKey);
put.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
call(put);
// Now get it to retrieve some attributes
HttpMethodBase get = call(new GetMethod(fullPathKey));
assertEquals(HttpStatus.SC_OK, get.getStatusCode());
String lastMod = get.getResponseHeader("Last-Modified").getValue();
// Attempt to delete using the If-Modified-Since header with the lastMod we got back from the get
DeleteMethod delete = new DeleteMethod(fullPathKey);
delete.setRequestHeader("If-Modified-Since", lastMod);
assertEquals(HttpStatus.SC_NOT_MODIFIED, call(delete).getStatusCode());
// Try to delete again, but with an older last modification date
DeleteMethod deleteAgain = new DeleteMethod(fullPathKey);
String dateMinus = addDay(lastMod, -1);
deleteAgain.setRequestHeader("If-Modified-Since", dateMinus);
assertEquals(HttpStatus.SC_OK, call(deleteAgain).getStatusCode());
}
public void testDeleteDataWithIfUnmodifiedSince(Method m) throws Exception {
// Put the data first
String fullPathKey = fullPath + "/" + m.getName();
PutMethod put = new PutMethod(fullPathKey);
put.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
call(put);
// Now get it to retrieve some attributes
HttpMethodBase get = call(new GetMethod(fullPathKey));
assertEquals(HttpStatus.SC_OK, get.getStatusCode());
String lastMod = get.getResponseHeader("Last-Modified").getValue();
// Attempt to delete using the If-Unmodified-Since header with a date earlier than the one we got back from the GET
DeleteMethod delete = new DeleteMethod(fullPathKey);
String dateMinus = addDay(lastMod, -1);
delete.setRequestHeader("If-Unmodified-Since", dateMinus);
assertEquals(HttpStatus.SC_PRECONDITION_FAILED, call(delete).getStatusCode());
// Try to delete again, but with an older last modification date
DeleteMethod deleteAgain = new DeleteMethod(fullPathKey);
deleteAgain.setRequestHeader("If-Unmodified-Since", lastMod);
assertEquals(HttpStatus.SC_OK, call(deleteAgain).getStatusCode());
}
public void testDeleteCachePreconditionUnimplemented(Method m) throws Exception {
testDeletePreconditionalUnimplemented(fullPath);
}
private void testDeletePreconditionalUnimplemented(String fullPathKey) throws Exception {
testDeletePreconditionalUnimplemented(fullPathKey, "If-Match");
testDeletePreconditionalUnimplemented(fullPathKey, "If-None-Match");
testDeletePreconditionalUnimplemented(fullPathKey, "If-Modified-Since");
testDeletePreconditionalUnimplemented(fullPathKey, "If-Unmodified-Since");
}
private void testDeletePreconditionalUnimplemented(
String fullPathKey, String preconditionalHeaderName) throws Exception {
DeleteMethod delete = new DeleteMethod(fullPathKey);
delete.setRequestHeader(preconditionalHeaderName, "*");
call(delete);
assertNotImplemented(delete);
}
private void assertNotImplemented(HttpMethod method) throws IOException {
assertEquals(method.getStatusCode(), 501);
assertEquals(method.getStatusText(), "Not Implemented");
assert (method.getResponseBodyAsString().toLowerCase().contains("precondition"));
}
public void testRemoveEntry(Method m) throws Exception {
String fullPathKey = fullPath + "/" + m.getName();
PostMethod put = new PostMethod(fullPathKey);
put.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
call(put);
assertEquals(HttpStatus.SC_OK, call(new HeadMethod(fullPathKey)).getStatusCode());
call(new DeleteMethod(fullPathKey));
assertEquals(HttpStatus.SC_NOT_FOUND, call(new HeadMethod(fullPathKey)).getStatusCode());
}
public void testWipeCacheBucket(Method m) throws Exception {
String fullPathKey = fullPath + "/" + m.getName();
PostMethod put = new PostMethod(fullPathKey);
put.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
call(put);
PostMethod put_ = new PostMethod(fullPathKey + "2");
put_.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
call(put_);
assertEquals(HttpStatus.SC_OK, call(new HeadMethod(fullPathKey)).getStatusCode());
call(new DeleteMethod(fullPath));
assertEquals(HttpStatus.SC_NOT_FOUND, call(new HeadMethod(fullPathKey)).getStatusCode());
}
public void testAsyncAddRemove(Method m) throws Exception {
String fullPathKey = fullPath + "/" + m.getName();
PostMethod put = new PostMethod(fullPathKey);
put.setRequestHeader("performAsync", "true");
put.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
call(put);
Thread.sleep(50);
assertEquals(HttpStatus.SC_OK, call(new HeadMethod(fullPathKey)).getStatusCode());
DeleteMethod del = new DeleteMethod(fullPathKey);
del.setRequestHeader("performAsync", "true");
call(del);
Thread.sleep(50);
assertEquals(HttpStatus.SC_NOT_FOUND, call(new HeadMethod(fullPathKey)).getStatusCode());
}
public void testShouldCopeWithSerializable(Method m) throws Exception {
String fullPathKey = fullPath + "/" + m.getName();
call(new GetMethod(fullPathKey));
MySer obj = new MySer();
obj.name = "mic";
getCacheManager("single").getCache(BasicCacheContainer.DEFAULT_CACHE_NAME).put(m.getName(), obj);
getCacheManager("single").getCache(BasicCacheContainer.DEFAULT_CACHE_NAME).put(m.getName() + "2", "hola");
getCacheManager("single").getCache(BasicCacheContainer.DEFAULT_CACHE_NAME).put(m.getName() + "3", new MyNonSer());
//check we can get it back as an object...
GetMethod get = new GetMethod(fullPathKey);
get.setRequestHeader("Accept", "application/x-java-serialized-object");
call(get);
assertEquals(HttpStatus.SC_OK, get.getStatusCode());
ObjectInputStream in = new ObjectInputStream(get.getResponseBodyAsStream());
MySer res = (MySer) in.readObject();
assertNotNull(res);
assertEquals("mic", res.name);
assertEquals("application/x-java-serialized-object", get.getResponseHeader("Content-Type").getValue());
HttpMethodBase getStr = call(new GetMethod(fullPathKey + "2"));
assertEquals("hola", getStr.getResponseBodyAsString());
assertEquals("text/plain", getStr.getResponseHeader("Content-Type").getValue());
//now check we can get it back as JSON if we want...
get.setRequestHeader("Accept", "application/json");
call(get);
assertEquals("{\"name\":\"mic\"}", get.getResponseBodyAsString());
assertEquals("application/json", get.getResponseHeader("Content-Type").getValue());
//and why not XML
get.setRequestHeader("Accept", "application/xml");
call(get);
assertEquals("application/xml", get.getResponseHeader("Content-Type").getValue());
assertTrue(get.getResponseBodyAsString().indexOf("<org.infinispan.rest.MySer>") > -1);
//now check we can get it back as JSON if we want...
GetMethod get3 = new GetMethod(fullPathKey + "3");
get3.setRequestHeader("Accept", "application/json");
call(get3);
assertEquals("{\"name\":\"mic\"}", get3.getResponseBodyAsString());
assertEquals("application/json", get3.getResponseHeader("Content-Type").getValue());
}
public void testInsertSerializableObjects(Method m) throws Exception {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
new ObjectOutputStream(bout).writeObject(new MySer());
put(fullPathKey(m), bout.toByteArray(), "application/x-java-serialized-object");
getCacheManager("single").getCache(BasicCacheContainer.DEFAULT_CACHE_NAME)
.get(m.getName());
}
public void testNonexistentCache(Method m) throws Exception {
String fullPathKey = HOST + "/rest/nonexistent/" + m.getName();
GetMethod get = new GetMethod(fullPathKey);
call(get);
assertEquals(HttpStatus.SC_NOT_FOUND, get.getStatusCode());
HttpMethodBase head = call(new HeadMethod(fullPathKey));
assertEquals(HttpStatus.SC_NOT_FOUND, head.getStatusCode());
PostMethod put = new PostMethod(fullPathKey);
put.setRequestEntity(new StringRequestEntity("data", "application/text", "UTF-8"));
call(put);
assertEquals(HttpStatus.SC_NOT_FOUND, put.getStatusCode());
}
public void testByteArrayAsSerializedObjects(Method m) throws Exception {
sendByteArrayAs(m, "application/x-java-serialized-object");
}
public void testByteArrayAsOctecStreamObjects(Method m) throws Exception {
sendByteArrayAs(m, "application/octet-stream");
}
private void sendByteArrayAs(Method m, String contentType) throws Exception {
byte[] serializedOnClient = new byte[]{0x65, 0x66, 0x67};
put(fullPathKey(m), serializedOnClient, contentType);
BufferedInputStream dataRead = new BufferedInputStream(
get(m, Optional.empty(), Optional.of(contentType)).getResponseBodyAsStream());
byte[] bytesRead = new byte[3];
dataRead.read(bytesRead);
assertEquals(serializedOnClient, bytesRead);
}
public void testIfUnmodifiedSince(Method m) throws Exception {
put(m);
HttpMethodBase result = get(m);
String dateLast = result.getResponseHeader("Last-Modified").getValue();
String dateMinus = addDay(dateLast, -1);
String datePlus = addDay(dateLast, 1);
assertNotNull(get(m, Optional.of(dateLast)).getResponseBodyAsString());
assertNotNull(get(m, Optional.of(datePlus)).getResponseBodyAsString());
result = get(m, Optional.of(dateMinus), Optional.empty(), HttpStatus.SC_PRECONDITION_FAILED);
}
public void testETagChanges(Method m) throws Exception {
put(m, "data1");
String eTagFirst = get(m).getResponseHeader("ETag").getValue();
// second get should get the same ETag
assertEquals(eTagFirst, get(m).getResponseHeader("ETag").getValue());
// do second PUT
put(m, "data2");
// get ETag again
String eTagSecond = get(m).getResponseHeader("ETag").getValue();
assertFalse("etag1 %s; etag2 %s; equals? %b".format(
eTagFirst, eTagSecond, eTagFirst.equals(eTagSecond)),
eTagFirst.equals(eTagSecond));
}
public void testConcurrentETagChanges(Method m) throws Exception {
CountDownLatch v2PutLatch = new CountDownLatch(1);
CountDownLatch v3PutLatch = new CountDownLatch(1);
CountDownLatch v2FinishLatch = new CountDownLatch(1);
EmbeddedCacheManager cacheManager = createCacheManager();
ControlledCacheManager mockCacheManager = new ControlledCacheManager(cacheManager, v2PutLatch, v3PutLatch, v2FinishLatch);
EmbeddedRestServer server = RestTestingUtil.startRestServer(mockCacheManager);
put(fullPathWithPort(m, server.getPort()), "data1", "application/text");
try {
Future replaceFuture = fork(() -> {
// Put again, with a different client (separate thread);
HttpClient newClient = new HttpClient();
PutMethod put = new PutMethod(fullPathWithPort(m, server.getPort()));
put.setRequestHeader("Content-Type", "application/text");
put.setRequestEntity(new StringRequestEntity("data2", null, null));
newClient.executeMethod(put);
assertEquals(HttpStatus.SC_OK, put.getStatusCode());
// 5. v2 applied, let v3 finish
v2FinishLatch.countDown();
return null;
});
// 1. Wait for v3 to be allowed
boolean cont = v3PutLatch.await(10, TimeUnit.SECONDS);
assertTrue("Timed out waiting for concurrent put", cont);
// Ready to do concurrent put which should not be allowed
PutMethod put = new PutMethod(fullPathWithPort(m, server.getPort()));
put.setRequestHeader("Content-Type", "application/text");
put.setRequestEntity(new StringRequestEntity("data3", null, null));
call(put);
assertEquals(HttpStatus.SC_PRECONDITION_FAILED,
put.getStatusCode());
// Wait for replace to happen
replaceFuture.get(5, TimeUnit.SECONDS);
// Final data should be v2
assertEquals("data2", get(m).getResponseBodyAsString());
} finally {
server.stop();
TestingUtil.killCacheManagers(cacheManager);
}
}
public void testSerializedStringGetBytes(Method m) throws Exception {
byte[] data = ("v-" + m.getName()).getBytes("UTF-8");
ByteArrayOutputStream bout = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bout);
oo.writeObject(data);
oo.flush();
byte[] bytes = bout.toByteArray();
put(fullPathKey(m), bytes, "application/x-java-serialized-object");
byte[] bytesRead = get(m, Optional.empty(), Optional.of("application/x-java-serialized-object")).getResponseBody();
assertTrue(Arrays.equals(bytes, bytesRead));
ObjectInputStream oin = new ObjectInputStream(new ByteArrayInputStream(bytesRead));
byte[] dataBack = (byte[]) oin.readObject();
assertTrue(Arrays.equals(data, dataBack));
}
public void testDefaultConfiguredExpiryValues(Method m) throws Exception {
String cacheName = "evictExpiryCache";
String fullPathKey = String.format("%s/rest/%s/%s", HOST, cacheName, m.getName());
PostMethod post = new PostMethod(fullPathKey);
// Live forever...
post.setRequestHeader("timeToLiveSeconds", "-1");
post.setRequestEntity(new StringRequestEntity("data", "text/plain", "UTF-8"));
call(post);
// Sleep way beyond the default in the config
Thread.sleep(5000);
HttpMethodBase get = call(new GetMethod(fullPathKey));
assertEquals("data", get.getResponseBodyAsString());
assertNull(get.getResponseHeader("Expires"));
long startTime = System.currentTimeMillis();
int lifespan = 3000;
fullPathKey = fullPathKey + "-2";
post = new PostMethod(fullPathKey);
post.setRequestHeader("Content-Type", "application/text");
// It'll fallback on configured lifespan/maxIdle values.
post.setRequestHeader("timeToLiveSeconds", "0");
post.setRequestHeader("maxIdleTimeSeconds", "0");
post.setRequestEntity(new StringRequestEntity("data2", "text/plain", "UTF-8"));
call(post);
while (System.currentTimeMillis() < startTime + lifespan) {
get = call(new GetMethod(fullPathKey));
String response = get.getResponseBodyAsString();
// The entry could have expired before our request got to the server
// Scala doesn't support break, so we need to test the current time twice
if (System.currentTimeMillis() < startTime + lifespan) {
assertEquals("data2", response);
assertNotNull(get.getResponseHeader("Expires"));
Thread.sleep(100);
}
}
// Make sure that in the next 20 secs data is removed
waitNotFound(startTime, lifespan, fullPathKey);
assertEquals(HttpStatus.SC_NOT_FOUND, call(new GetMethod(fullPathKey)).getStatusCode());
startTime = System.currentTimeMillis();
lifespan = 0;
fullPathKey = "%s-3".format(fullPathKey);
post = new PostMethod(fullPathKey);
// Will use the configured lifespan
post.setRequestHeader("timeToLiveSeconds", "0");
post.setRequestEntity(new StringRequestEntity("data3", "text/plain", "UTF-8"));
call(post);
Thread.sleep(1000);
get = call(new GetMethod(fullPathKey));
String response = get.getResponseBodyAsString();
assertEquals("data3", response);
Thread.sleep(2000);
assertEquals(HttpStatus.SC_NOT_FOUND, call(new GetMethod(fullPathKey)).getStatusCode());
fullPathKey = "%s-4".format(fullPathKey);
post = new PostMethod(fullPathKey);
// It will use configured maxIdle
post.setRequestHeader("maxIdleTimeSeconds", "0");
post.setRequestEntity(new StringRequestEntity("data4", "text/plain", "UTF-8"));
call(post);
// Sleep way beyond the default in the config
Thread.sleep(2500);
assertEquals(HttpStatus.SC_NOT_FOUND, call(new GetMethod(fullPathKey)).getStatusCode());
}
public void testCacheControlResponseHeader(Method m) throws Exception {
String cacheName = "evictExpiryCache";
String fullPathKey = String.format("%s/rest/%s/%s", HOST, cacheName, m.getName());
PostMethod post = new PostMethod(fullPathKey);
post.setRequestHeader("timeToLiveSeconds", "10");
post.setRequestEntity(new StringRequestEntity("data", "text/plain", "UTF-8"));
call(post);
Thread.sleep(2000);
HttpMethodBase get = call(new GetMethod(fullPathKey));
assertEquals("data", get.getResponseBodyAsString());
assertNotNull(get.getResponseHeader("Cache-Control"));
int retrievedMaxAge = CacheControl.valueOf(get.getResponseHeader("Cache-Control").getValue()).getMaxAge();
assertTrue(retrievedMaxAge > 0);
}
public void testGetCacheControlMinFreshRequestHeader(Method m) throws Exception {
String cacheName = "evictExpiryCache";
String fullPathKey = String.format("%s/rest/%s/%s", HOST, cacheName, m.getName());
PostMethod post = new PostMethod(fullPathKey);
post.setRequestHeader("timeToLiveSeconds", "10");
post.setRequestEntity(new StringRequestEntity("data", "text/plain", "UTF-8"));
call(post);
Thread.sleep(2000);
GetMethod getLongMinFresh = new GetMethod(fullPathKey);
getLongMinFresh.addRequestHeader("Cache-Control", "no-transform, min-fresh=20");
HttpMethodBase getResp = call(getLongMinFresh);
assertEquals(HttpStatus.SC_NOT_FOUND, getResp.getStatusCode());
GetMethod getShortMinFresh = new GetMethod(fullPathKey);
getShortMinFresh.addRequestHeader("Cache-Control", "no-transform, min-fresh=2");
getResp = call(getShortMinFresh);
assertNotNull(getResp.getResponseHeader("Cache-Control"));
assertEquals("data", getResp.getResponseBodyAsString());
GetMethod getNoMinFresh = new GetMethod(fullPathKey);
getResp = call(getNoMinFresh);
assertNotNull(getResp.getResponseHeader("Cache-Control"));
assertEquals("data", getResp.getResponseBodyAsString());
}
public void testHeadCacheControlMinFreshRequestHeader(Method m) throws Exception {
String cacheName = "evictExpiryCache";
String fullPathKey = String.format("%s/rest/%s/%s", HOST, cacheName, m.getName());
PostMethod post = new PostMethod(fullPathKey);
post.setRequestHeader("timeToLiveSeconds", "10");
post.setRequestEntity(new StringRequestEntity("data", "text/plain", "UTF-8"));
call(post);
Thread.sleep(2000);
HeadMethod headLongMinFresh = new HeadMethod(fullPathKey);
headLongMinFresh.addRequestHeader("Cache-Control", "no-transform, min-fresh=20");
HttpMethodBase headResp = call(headLongMinFresh);
assertEquals(HttpStatus.SC_NOT_FOUND, headResp.getStatusCode());
HeadMethod headShortMinFresh = new HeadMethod(fullPathKey);
headShortMinFresh.addRequestHeader("Cache-Control", "no-transform, min-fresh=2");
headResp = call(headShortMinFresh);
int retrievedMaxAge = CacheControl.valueOf(headResp.getResponseHeader("Cache-Control").getValue()).getMaxAge();
assertTrue(retrievedMaxAge > 0);
HeadMethod headNoMinFresh = new HeadMethod(fullPathKey);
headResp = call(headNoMinFresh);
assertNotNull(headResp.getResponseHeader("Cache-Control"));
}
public void testPutByteArrayTwice(Method m) throws Exception {
String fullPathKey = fullPath + "/" + m.getName();
PutMethod put = new PutMethod(fullPathKey);
byte[] data = new byte[]{42, 42, 42};
put.setRequestEntity(new ByteArrayRequestEntity(data, "application/x-java-serialized-object"));
assertEquals(HttpStatus.SC_OK, call(put).getStatusCode());
HttpMethodBase get = call(new GetMethod(fullPathKey));
assertEquals(HttpStatus.SC_OK, get.getStatusCode());
PutMethod reput = new PutMethod(fullPathKey);
reput.setRequestEntity(new ByteArrayRequestEntity(data, "application/x-java-serialized-object"));
assertEquals(HttpStatus.SC_OK, call(reput).getStatusCode());
}
public void testDeleteSerializedObject(Method m) throws Exception {
String fullPathKey = fullPath + "/" + m.getName();
PutMethod put = new PutMethod(fullPathKey);
byte[] data = new byte[]{42, 42, 42};
put.setRequestEntity(new ByteArrayRequestEntity(data, "application/x-java-serialized-object"));
assertEquals(HttpStatus.SC_OK, call(put).getStatusCode());
HttpMethodBase get = call(new GetMethod(fullPathKey));
assertEquals(HttpStatus.SC_OK, get.getStatusCode());
HttpMethodBase delete = call(new DeleteMethod(fullPathKey));
assertEquals(HttpStatus.SC_OK, delete.getStatusCode());
}
public void testDisableCache(Method m) throws Exception {
Callable<HttpMethodBase> doGet = () -> call(new GetMethod(fullPathKey(m)));
put(m);
assertEquals(HttpStatus.SC_OK, doGet.call().getStatusCode());
ignoreCache(cacheName);
assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, doGet.call().getStatusCode());
enableCache(cacheName);
assertEquals(HttpStatus.SC_OK, doGet.call().getStatusCode());
}
private void waitNotFound(Long startTime, int lifespan, String fullPathKey) throws Exception {
if (System.currentTimeMillis() < startTime + lifespan + 20000) {
if (HttpStatus.SC_NOT_FOUND != (call(new GetMethod(fullPathKey)).getStatusCode())) {
Thread.sleep(100);
waitNotFound(startTime, lifespan, fullPathKey); // Good ol' tail recursion :);
}
}
}
private HttpMethodBase put(Method m) throws Exception {
return put(fullPathKey(m), "data", "application/text");
}
private HttpMethodBase put(Method m, Object data) throws Exception {
return put(fullPathKey(m), data, "application/text");
}
private HttpMethodBase put(String path, Object data, String contentType) throws Exception {
PutMethod put = new PutMethod(path);
put.setRequestHeader("Content-Type", contentType);
RequestEntity reqEntity;
if (data instanceof String) {
reqEntity = new StringRequestEntity((String) data, null, null);
} else if (data instanceof byte[]) {
reqEntity = new InputStreamRequestEntity(new ByteArrayInputStream((byte[]) data));
} else {
throw new IllegalArgumentException("Only String or byte[] allowed, received: " + data.getClass());
}
put.setRequestEntity(reqEntity);
call(put);
assertEquals(HttpStatus.SC_OK, put.getStatusCode());
return put;
}
private HttpMethodBase get(Method m) throws Exception {
return get(m, Optional.empty());
}
private HttpMethodBase get(Method m, Optional<String> unmodSince) throws Exception {
return get(m, unmodSince, Optional.empty(), HttpStatus.SC_OK);
}
private HttpMethodBase get(Method m, Optional<String> unmodSince, Optional<String> acceptType) throws Exception {
return get(m, unmodSince, acceptType, HttpStatus.SC_OK);
}
private HttpMethodBase get(Method m, Optional<String> unmodSince, Optional<String> acceptType, int expCode) throws Exception {
GetMethod get = new GetMethod(fullPathKey(m));
if (unmodSince.isPresent())
get.setRequestHeader("If-Unmodified-Since", unmodSince.get());
if (acceptType.isPresent())
get.setRequestHeader("Accept", acceptType.get());
call(get);
assertEquals(expCode, get.getStatusCode());
return get;
}
private String fullPathKey(Method m) {
return fullPath + "/" + m.getName();
}
private String fullPathKey(Method m, int port) {
return fullPath + "/" + m.getName();
}
String addDay(String aDate, int days) throws ParseException {
DateFormat format = new SimpleDateFormat(DATE_PATTERN_RFC1123, Locale.US);
Date date = format.parse(aDate);
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.DATE, days);
return format.format(cal.getTime());
}
class ControlledCacheManager extends AbstractDelegatingEmbeddedCacheManager {
private final CountDownLatch v2PutLatch;
private final CountDownLatch v3PutLatch;
private final CountDownLatch v2FinishLatch;
public ControlledCacheManager(EmbeddedCacheManager cm, CountDownLatch v2PutLatch, CountDownLatch v3PutLatch,
CountDownLatch v2FinishLatch) {
super(cm);
this.v2PutLatch = v2PutLatch;
this.v3PutLatch = v3PutLatch;
this.v2FinishLatch = v2FinishLatch;
}
@Override
public <K, V> Cache<K, V> getCache() {
return (Cache<K, V>) new ControlledCache(super.getCache(),
v2PutLatch, v3PutLatch, v2FinishLatch);
}
}
class ControlledCache extends AbstractDelegatingAdvancedCache<String, Object> {
private final CountDownLatch v2PutLatch;
private final CountDownLatch v3PutLatch;
private final CountDownLatch v2FinishLatch;
public ControlledCache(Cache<String, Object> cache, CountDownLatch v2PutLatch, CountDownLatch v3PutLatch,
CountDownLatch v2FinishLatch) {
super(cache.getAdvancedCache());
this.v2PutLatch = v2PutLatch;
this.v3PutLatch = v3PutLatch;
this.v2FinishLatch = v2FinishLatch;
}
@Override
public boolean replace(String key, Object oldValue, Object value, Metadata metadata) {
byte[] newByteArray = (byte[]) value;
byte[] oldByteArray = (byte[]) oldValue;
String oldAsString = new String(oldByteArray);
String newAsString = new String(newByteArray);
try {
if (Arrays.equals(newByteArray, "data2".getBytes())) {
log.debug("Let v3 apply...");
v3PutLatch.countDown(); // 2. Let the v3 put come in
log.debug("Wait until v2 can be stored");
// 3. Wait until v2 can apply
boolean cont = v2PutLatch.await(10, TimeUnit.SECONDS);
if (!cont)
fail("Timed out waiting for v2 to be allowed");
} else if (Arrays.equals(newByteArray, "data3".getBytes())) {
log.debugf("About to store v3, let v2 apply, oldValue(for v3)=%s",
oldAsString);
// 4. Let data2 apply
v2PutLatch.countDown();
v2FinishLatch.await(10, TimeUnit.SECONDS); // Wait for data2 apply
}
} catch (InterruptedException e) {
throw new CacheException(e);
}
log.debugf("Replace key=%s, oldValue=%s, value=%s",
key, oldAsString, newAsString);
return super.replace(key, oldValue, value, metadata);
}
}
}
class MyNonSer {
String name = "mic";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class MySer extends MyNonSer implements Serializable {
}