/*
* 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.enhancer.it;
import static org.junit.Assert.fail;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.apache.stanbol.commons.testing.http.RetryLoop;
import org.apache.stanbol.commons.testing.stanbol.StanbolTestBase;
import org.junit.Before;
/** Inherit from this to wait for all default enhancement
* engines to be up before running tests.
*/
public class EnhancerTestBase extends StanbolTestBase {
private final Logger log = LoggerFactory.getLogger(getClass());
// TODO configurable via system properties??
public static final int ENGINES_TIMEOUT_SECONDS = 60;
public static final int WAIT_BETWEEN_TRIES_MSEC = 1000;
boolean enginesReady;
boolean timedOut;
protected String endpoint;
/* List of expected engines could be made configurable via system
* properties, but we don't expect it to change often.
*/
protected final String[] assertEngines;
/**
* The "/enhancer" endpoint"
*/
public static final String ENHANCER_ENDPOINT = "/enhancer";
/**
* The default endpoint
* @see #ENHANCER_ENDPOINT
*/
public static final String DEFAULT_ENDPOINT = ENHANCER_ENDPOINT;
/**
* The "/engines" endpoint the only endpoint supported to enhance
* content items before STANBOL-431. This endpoint is still supported
*/
public static final String ENGINES_ENDPOINT = "/engines";
/**
* The root for the endpoints of specific enhancement chains
* @see #getChainEndpoint(String)
*/
private static final String CHAINS_ROOT = "/enhancer/chain/";
private static final String[] DEFAULT_ASSERT_ENGINES =
new String[]{
//"metaxa","MetaxaEngine", deactivated see STANBOL-510
"langdetect","LanguageDetectionEnhancementEngine",
"ner","NamedEntityExtractionEnhancementEngine",
"entityhubExtraction","NamedEntityExtractionEnhancementEngine",
"dbpediaLinking","NamedEntityTaggingEngine",
"tika","TikaEngine"
};
/**
* Getter for the Endpoint for a specific enhancement chain
* @param chainName the name of the chain
* @return the endpoint
* @throws IllegalArgumentException if the parsed chain is <code>null</code>
* or invalid
*/
public static final String getChainEndpoint(String chainName){
if(chainName == null || chainName.isEmpty()){
throw new IllegalArgumentException("The parsed Chain name MUST NOT BE NULL nor empty!");
}
if(chainName.charAt(0) == '/'){
if(chainName.length()<2){
throw new IllegalArgumentException("The parsed Chain name '/' is invalid!");
}
return CHAINS_ROOT+chainName.substring(1);
} else {
return CHAINS_ROOT+chainName;
}
}
public EnhancerTestBase(){
this(null, (String[])null);
}
public EnhancerTestBase(String endpoint){
this(endpoint,(String[])null);
}
public EnhancerTestBase(String endpoint,String...assertEngines){
super();
setEndpoint(endpoint);
if(assertEngines == null){
this.assertEngines = DEFAULT_ASSERT_ENGINES;
} else {
this.assertEngines = assertEngines;
}
}
/**
* Setter for the endpoint. Keeps care of leading '/' and supports optional query parameter
* @param endpoint the endpoint or <code>null</code> to use the default
* @param params optional query parameter(s) [key,value,key,value,...]
*/
protected void setEndpoint(String endpoint,String...params) {
StringBuilder sb = new StringBuilder();
if(endpoint == null){
sb.append(DEFAULT_ENDPOINT);;
} else if(endpoint.charAt(0) != '/')
sb.append("/").append(endpoint);
else{
sb.append(endpoint);
}
if(params != null && params.length > 1){
for(int i=0;i<params.length-1;i++){
sb.append(i==0?'?':'&');
sb.append(params[i]).append('=');
i++;
try {
sb.append(URLEncoder.encode(params[i], "UTF-8"));
} catch (UnsupportedEncodingException e) {
throw new IllegalStateException(e.getMessage(),e);
}
}
}
this.endpoint = sb.toString();
}
public String getEndpoint(){
return endpoint;
}
@Before
public void checkEnginesReady() throws Exception {
log.debug("before {}#checkEngineReady", getClass().getSimpleName());
// Check only once per test run
if(enginesReady) {
log.debug(" ... engines already marked as ready");
return;
}
// If we timed out previously, don't waste time checking again
if(timedOut) {
fail("Timeout in previous check of enhancement engines, cannot run tests");
}
// We'll retry the check for all engines to be ready
// for up to ENGINES_TIMEOUT_SECONDS
final RetryLoop.Condition c = new RetryLoop.Condition() {
@Override
public boolean isTrue() throws Exception {
log.debug("> checking for Enhancer services: ");
executor.execute(
builder.buildGetRequest(endpoint)
.withHeader("Accept", "text/html")
)
.assertStatus(200)
.assertContentType("text/html")
.assertContentRegexp(assertEngines);
log.info(" ... enpoint '{}' is ready", endpoint);
/* List of expected referencedSites could also be made
* configurable via system properties, but we don't expect it
* to change often.
*/
executor.execute(
builder.buildGetRequest("/entityhub/sites/referenced")
.withHeader("Accept", "application/json")
)
.assertStatus(200)
.assertContentType("application/json")
.assertContentRegexp(
"http:\\\\/\\\\/.*\\\\/entityhub\\\\/site\\\\/dbpedia\\\\/"
);
log.debug(" ... entityhub DBpedia referencedSite is ready", endpoint);
//also assert that the SolrYard for the dbpedia site is fully
//initialized
//No longer needed with STANBOL-996
// executor.execute(
// builder.buildGetRequest("/entityhub/site/dbpedia" +
// "/entity?id=urn:does:not:exist:f82js95xsig39s.23987")
// .withHeader("Accept", "application/json"))
// .assertStatus(404);
log.info("Enhancement engines checked for '{}', all present", endpoint);
return true;
}
@Override
public String getDescription() {
return String.format("Checking that all enhancement engines for " +
"endpoint '%s' are ready",endpoint);
}
};
new RetryLoop(c, ENGINES_TIMEOUT_SECONDS, WAIT_BETWEEN_TRIES_MSEC) {
@Override
protected void reportException(Throwable t) {
log.info("Exception in RetryLoop, will retry for up to "
+ getRemainingTimeSeconds() + " seconds: ", t);
}
protected void onTimeout() {
timedOut = true;
}
};
enginesReady = true;
}
}