/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with this
* work for additional information regarding copyright ownership. The ASF
* licenses this file to You 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.apache.stanbol.entityhub.it;
import static org.apache.clerezza.rdf.core.serializedform.SupportedFormat.RDF_XML;
import static org.apache.stanbol.entityhub.test.it.AssertEntityhubJson.assertEntity;
import static org.apache.stanbol.entityhub.test.it.AssertEntityhubJson.assertQueryResults;
import static org.apache.stanbol.entityhub.test.it.AssertEntityhubJson.assertRepresentation;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.InputStreamEntity;
import org.apache.stanbol.commons.testing.http.Request;
import org.apache.stanbol.commons.testing.http.RequestExecutor;
import org.apache.stanbol.entityhub.servicesapi.defaults.NamespaceEnum;
import org.apache.stanbol.entityhub.test.query.FieldQueryTestCase;
import org.apache.stanbol.entityhub.test.query.FindQueryTestCase;
import org.apache.stanbol.entityhub.test.query.QueryTestBase;
import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.junit.Assert;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Tests the RESTful service provided by the Entityhub. Note that this
* extends the QueryTestBase. By that generic tests for the query interface
* (e.g. illegal requests, usage of default values ...) are already covered.
* @author Rupert Westenthaler
*
*/
public final class EntityhubTest extends QueryTestBase {
public EntityhubTest() {
super("/entityhub", null);
}
private final Logger log = LoggerFactory.getLogger(getClass());
private static final Collection<String> EXPECTED_DOAP_FIELDS;
static {
Collection<String> fields = new ArrayList<String>();
fields.add("http://usefulinc.com/ns/doap#created");
fields.add("http://usefulinc.com/ns/doap#license");
fields.add("http://usefulinc.com/ns/doap#name");
fields.add("http://usefulinc.com/ns/doap#homepage");
fields.add("http://usefulinc.com/ns/doap#shortdesc");
fields.add("http://usefulinc.com/ns/doap#description");
fields.add("http://usefulinc.com/ns/doap#bug-database");
fields.add("http://usefulinc.com/ns/doap#mailing-list");
fields.add("http://usefulinc.com/ns/doap#download-page");
fields.add("http://usefulinc.com/ns/doap#programming-language");
fields.add("http://usefulinc.com/ns/doap#category");
fields.add("http://projects.apache.org/ns/asfext#pmc");
EXPECTED_DOAP_FIELDS = Collections.unmodifiableCollection(fields);
}
@Override
protected String getDefaultFindQueryField() {
return NamespaceEnum.entityhub+"label";
}
/*
* Tests the CRUD interface
*
*/
@Test
public void testEntityCrud() throws IOException, JSONException {
//execution order is important
testEntityCreation();
testEntityCreated();
testEntityUpdate();
testEntityUpdated();
testEntityDelete();
testEntityDeleted();
testEntityDeleteAll();
testAllEntitiesDeleted();
}
private void testEntityCreation() throws IOException {
InputStream in = EntityhubTest.class.getClassLoader().getResourceAsStream("doap_Stanbol.rdf");
Assert.assertNotNull("Unable to load test resource 'doap_Stanbol.rdf'", in);
String stanbolProjectUri = "http://stanbol.apache.org";
//create a POST request with a test RDF file
RequestExecutor test = executor.execute(
buildImportRdfData(in ,RDF_XML, true, stanbolProjectUri));
//assert that the entity was created
test.assertStatus(201);
}
private void testEntityCreated() throws IOException, JSONException {
String id = "http://stanbol.apache.org";
RequestExecutor re = executor.execute(
builder.buildGetRequest("/entityhub/entity","id",id)
.withHeader("Accept", "application/json"));
re.assertStatus(200);
JSONObject jEntity = assertEntity(re.getContent(), id, "entityhub");
Map<String,Set<List<String>>> data = assertRepresentation(jEntity.getJSONObject("representation"),
EXPECTED_DOAP_FIELDS, null);
//test values of two properties we will update in a following test
Set<List<String>> pmcValues = data.get("http://projects.apache.org/ns/asfext#pmc");
Assert.assertNotNull(pmcValues);
Assert.assertEquals(1, pmcValues.size());
Assert.assertEquals("http://incubator.apache.org", pmcValues.iterator().next().get(0));
Set<List<String>> downloadValues = data.get("http://usefulinc.com/ns/doap#download-page");
Assert.assertNotNull(downloadValues);
Assert.assertEquals(1, downloadValues.size());
Assert.assertEquals("http://stanbol.apache.org", downloadValues.iterator().next().get(0));
}
private void testEntityUpdate() throws IOException, JSONException {
InputStream in = EntityhubTest.class.getClassLoader().getResourceAsStream("mod_doap_Stanbol.rdf");
Assert.assertNotNull("Unable to load test resource 'mod_doap_Stanbol.rdf'", in);
String stanbolProjectUri = "http://stanbol.apache.org";
//create a POST request with a test RDF file
RequestExecutor test = executor.execute(
buildImportRdfData(in ,RDF_XML, false, stanbolProjectUri));
//assert that the entity was created
test.assertStatus(200);
//check that the updated entity was returned
assertEntity(test.getContent(), stanbolProjectUri, "entityhub");
}
private void testEntityUpdated() throws IOException, JSONException {
String id = "http://stanbol.apache.org";
RequestExecutor re = executor.execute(
builder.buildGetRequest("/entityhub/entity","id",id)
.withHeader("Accept", "application/json"));
re.assertStatus(200);
JSONObject jEntity = assertEntity(re.getContent(), id, "entityhub");
Map<String,Set<List<String>>> data = assertRepresentation(jEntity.getJSONObject("representation"),
EXPECTED_DOAP_FIELDS, null);
Set<List<String>> pmcValues = data.get("http://projects.apache.org/ns/asfext#pmc");
Assert.assertNotNull(pmcValues);
Assert.assertEquals(1, pmcValues.size());
Assert.assertEquals("http://stanbol.apache.org", pmcValues.iterator().next().get(0));
Set<List<String>> downloadValues = data.get("http://usefulinc.com/ns/doap#download-page");
Assert.assertNotNull(downloadValues);
Assert.assertEquals(1, downloadValues.size());
Assert.assertEquals("http://stanbol.apache.org/downloads/", downloadValues.iterator().next().get(0));
}
private void testEntityDelete() throws IOException {
String stanbolProjectUri = "http://stanbol.apache.org";
Request request = builder.buildOtherRequest(new HttpDelete(
builder.buildUrl("/entityhub/entity", "id", stanbolProjectUri)));
RequestExecutor re = executor.execute(request);
re.assertStatus(200);
}
private void testEntityDeleted() throws IOException {
String id = "http://stanbol.apache.org";
RequestExecutor re = executor.execute(
builder.buildGetRequest("/entityhub/entity","id",id)
.withHeader("Accept", "application/json"));
re.assertStatus(404);
}
private void testEntityDeleteAll() throws IOException {
Request request = builder.buildOtherRequest(new HttpDelete(
builder.buildUrl("/entityhub/entity", "id", "*")));
RequestExecutor re = executor.execute(request);
re.assertStatus(200);
}
private void testAllEntitiesDeleted() throws IOException {
String id = "http://xml.apache.org/xerces-c/";
RequestExecutor re = executor.execute(
builder.buildGetRequest("/entityhub/entity","id",id)
.withHeader("Accept", "application/json"));
re.assertStatus(404);
}
@Test
public void testEntityLookup() throws IOException, JSONException {
String uri = "http://dbpedia.org/resource/Paris";
//first check that lookup without create returns 404
RequestExecutor re = executor.execute(builder.buildGetRequest(
"/entityhub/lookup", "id",uri));
re.assertStatus(404);
//Now check that lookup with create does work
re = executor.execute(builder.buildGetRequest(
"/entityhub/lookup", "id",uri,"create","true"));
re.assertStatus(200);
JSONObject entity = assertEntity(re.getContent(), null, "entityhub");
String ehUri = entity.optString("id", null);
//try to retrieve the entity with the generated id
re = executor.execute(builder.buildGetRequest(
"/entityhub/entity", "id",ehUri));
re.assertStatus(200);
assertEntity(re.getContent(), ehUri, "entityhub");
//no try again to lookup the entity without create
re = executor.execute(builder.buildGetRequest(
"/entityhub/lookup", "id",uri));
re.assertStatus(200);
assertEntity(re.getContent(), ehUri, "entityhub");
//finally delete the entity
re = executor.execute(builder.buildOtherRequest(new HttpDelete(
builder.buildUrl("/entityhub/entity", "id", ehUri))));
re.assertStatus(200);
}
@Test
public void testQueries() throws IOException, JSONException {
//first load the data for the rquery test
URL url = EntityhubTest.class.getClassLoader().getResource("apache-project-doap-files.zip");
Assert.assertNotNull(url);
File f;
try {
f = new File(url.toURI());
} catch(URISyntaxException e) {
f = new File(url.getPath());
}
Assert.assertNotNull(f.isFile());
ZipFile archive = new ZipFile(f);
try {
for(Enumeration<? extends ZipEntry> e = archive.entries();e.hasMoreElements();){
ZipEntry entry = e.nextElement();
log.debug(" - uploading {} to entityhub",entry);
RequestExecutor re = executor.execute(
buildImportRdfData(archive.getInputStream(entry) ,RDF_XML, false, null));
//assert that the entity was created (or already existed)
//some projects seams to have more than a single doap file
int status = re.getResponse().getStatusLine().getStatusCode();
Assert.assertTrue("Unable to add '"+entry.getName()+"'! Status:"
+ re.getResponse().getStatusLine(), status == 200 || status == 304);
}
} finally {
archive.close();
}
testFindNameQuery();
testFindWildcards();
testFindLimitAndOffsetQuery();
testFieldQueryTextConstraints();
//finally delete all added entity
RequestExecutor re = executor.execute(builder.buildOtherRequest(new HttpDelete(
builder.buildUrl("/entityhub/entity", "id", "*"))));
re.assertStatus(200);
}
private void testFindNameQuery() throws IOException, JSONException {
FindQueryTestCase test = new FindQueryTestCase("Apache Stanbol",
Arrays.asList(
"http://stanbol.apache.org"));//,
//"http://dbpedia.org/resource/Paris_Hilton"));
test.setField("http://usefulinc.com/ns/doap#name");
test.setLanguage(null);
executeQuery(test);
}
private void testFindWildcards() throws IOException, JSONException {
FindQueryTestCase test = new FindQueryTestCase("Hiv*",
Arrays.asList(
"http://hive.apache.org",
"http://jakarta.apache.org/hivemind/"),
Arrays.asList(
"http://beehive.apache.org"));
test.setField("http://usefulinc.com/ns/doap#name");
test.setLanguage(null);
executeQuery(test);
}
private void testFindLimitAndOffsetQuery() throws IOException, JSONException {
//With Solr4 we need a test that produces different scores for results,
//to ensure consistant odering
FindQueryTestCase test = new FindQueryTestCase("XML XSL*",
Arrays.asList(
"http://velocity.apache.org/anakia/",
"http://xalan.apache.org/xalan-c/",
"http://xalan.apache.org/xalan-j/",
"http://velocity.apache.org/dvsl/devel/",
"http://xmlgraphics.apache.org/commons/",
"http://xmlgraphics.apache.org/fop"),
null);
test.setField("http://usefulinc.com/ns/doap#description");
test.setLimit(10);
test.setLanguage(null);
RequestExecutor re = executeQuery(test);
//get the list of results (will assert the response twice)
//to check the expected limit and offset results
List<String> resultList = assertQueryResults(re,test);
List<String> expected = resultList.subList(2, 4); //3rd and 4th element
List<String> excluded = new ArrayList<String>(); //all other
excluded.addAll(resultList.subList(0, 2));
excluded.addAll(resultList.subList(4, resultList.size()));
//repeat the test with offset 2 and limit 2 to only retrieve the 3-4 result
test = new FindQueryTestCase("XML XSL*", expected, excluded);
test.setField("http://usefulinc.com/ns/doap#description");
test.setOffset(2);
test.setLimit(2);
test.setLanguage(null);
executeQuery(test);
}
private void testFieldQueryTextConstraints() throws IOException, JSONException {
FieldQueryTestCase test = new FieldQueryTestCase(
"{ "+
"'selected': ["+
"'http:\\/\\/usefulinc.com\\/ns\\/doap#name'],"+
"'offset': '0',"+
"'limit': '3',"+
"'constraints': [{ "+
"'type': 'text', "+
"'patternType': 'wildcard', "+
"'text': 'Stanbol', "+
"'field': 'http:\\/\\/usefulinc.com\\/ns\\/doap#name' "+
"},{ "+
"'type': 'text', "+
"'patternType': 'wildcard', "+
"'text': 'Java', "+
"'field': 'http:\\/\\/usefulinc.com\\/ns\\/doap#programming-language' "+
"}]"+
"}",
Arrays.asList( //list of expected results
"http://stanbol.apache.org"),
null);
//now execute the test
executeQuery(test);
}
/**
* Imports/updates RDF data of the file to the entityhub with the possibility
* to restrict imports/updates to the parsed uri
* @param file the file with the RDF data (needs to be in the classpath)
* @param create if <code>true</code> the data are created (POST) otherwise
* updated (PUT).
* @param uri if not <code>null</code> only data of this URI are imported by
* specifying the id parameter
*/
protected Request buildImportRdfData(InputStream in, String contentType, boolean create, String uri){
Assert.assertNotNull(in);
Assert.assertNotNull(contentType);
Request request;
String path;
if(uri != null){
path = builder.buildUrl("/entityhub/entity", "id",uri);
} else {
path = builder.buildUrl("/entityhub/entity");
}
if(create){
request = builder.buildOtherRequest(new HttpPost(path));
} else {
request = builder.buildOtherRequest(new HttpPut(path));
}
//set the HttpEntity (both PUT and POST are HttpEntityEnclosingRequests)
((HttpEntityEnclosingRequest)request.getRequest()).setEntity(
new InputStreamEntity(in, -1));
//finally set the correct content-type of the provided data
//currently fixed to "application/rdf+xml"
request.getRequest().setHeader("Content-Type", contentType);
return request;
}
}