blob: 4d07fc097309d0f713167e5df18805ce81826682 [file] [log] [blame]
/*
* 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.chain.list.impl;
import static org.apache.stanbol.enhancer.servicesapi.helper.ConfigUtils.getState;
import static org.apache.stanbol.enhancer.servicesapi.helper.ExecutionPlanHelper.createExecutionPlan;
import static org.apache.stanbol.enhancer.servicesapi.helper.ExecutionPlanHelper.writeExecutionNode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.clerezza.rdf.core.Graph;
import org.apache.clerezza.rdf.core.MGraph;
import org.apache.clerezza.rdf.core.NonLiteral;
import org.apache.clerezza.rdf.core.impl.SimpleMGraph;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.ConfigurationPolicy;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Service;
import org.apache.stanbol.enhancer.servicesapi.Chain;
import org.apache.stanbol.enhancer.servicesapi.ChainException;
import org.apache.stanbol.enhancer.servicesapi.helper.ConfigUtils;
import org.apache.stanbol.enhancer.servicesapi.impl.AbstractChain;
import org.osgi.framework.Constants;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* This Chain implementation takes a list of engines names as input
* and uses the "org.apache.stanbol.enhancer.engine.order " metadata provided by
* such engines to calculate the ExecutionGraph.<p>
*
* Similar the current WeightedJobManager implementation Engines would be
* dependent to each other based on decreasing order values. Engines with the
* same order value would could be executed in parallel.<p>
*
* This implementation is targeted for easy configuration - just a list of the
* engine names contained within a chain - but has limited possibilities to
* control the execution order within an chain. However it is expected
* that it provides enough flexibility for most of the usage scenarios.<p>
*
* This engine also supports the definition of additional parameters for
* Enhancement Engines. The syntax is the same as used by BND tools:
* <pre><code>
* &lt;engineName&gt;;&ltparam-name&gt=&ltparam-value&gt
* &lt;engineName&gt;;&ltparam-name&gt
* </code></pre>
* Parameter without value are interpreted as enabled boolean switch<p>
*
* Currently this Chain implementation supports the following Parameter: <ul>
* <li> optional: Boolean switch that allows to define that the execution of this
* engine is not required.
* </ul>
*
* @author Rupert Westenthaler
*
*/
@Component(inherit=true,configurationFactory=true,metatype=true,
policy=ConfigurationPolicy.REQUIRE)
@Properties(value={
@Property(name=Chain.PROPERTY_NAME),
@Property(name=ListChain.PROPERTY_ENGINE_LIST, cardinality=1000),
@Property(name=Constants.SERVICE_RANKING, intValue=0)
})
@Service(value=Chain.class)
public class ListChain extends AbstractChain implements Chain {
private final Logger log = LoggerFactory.getLogger(ListChain.class);
/**
* The list of Enhancement Engine names used to build the Execution Plan
*/
public static final String PROPERTY_ENGINE_LIST = "stanbol.enhancer.chain.list.enginelist";
private Set<String> engineNames;
private Graph executionPlan;
@Override
protected void activate(ComponentContext ctx) throws ConfigurationException {
super.activate(ctx);
Object value = ctx.getProperties().get(PROPERTY_ENGINE_LIST);
List<String> configuredChain = new ArrayList<String>();
if(value instanceof String[]){
configuredChain.addAll(Arrays.asList((String[])value));
} else if(value instanceof List<?>){
for(Object o : (List<?>)value){
if(o != null){
configuredChain.add(o.toString());
}
}
} else {
throw new ConfigurationException(PROPERTY_ENGINE_LIST,
"The engines of a List Chain MUST BE configured as Array/List of " +
"Strings (parsed: "+
(value != null?value.getClass():"null")+")");
}
Set<String> engineNames = new HashSet<String>(configuredChain.size());
NonLiteral last = null;
MGraph ep = new SimpleMGraph();
NonLiteral epNode = createExecutionPlan(ep, getName());
log.debug("Parse ListChain config:");
for(String line : configuredChain){
try {
Entry<String,Map<String,List<String>>> parsed = ConfigUtils.parseConfigEntry(line);
if(!engineNames.add(parsed.getKey())){
throw new ConfigurationException(PROPERTY_ENGINE_LIST,
"The EnhancementEngine '"+parsed.getKey()+"' is mentioned"
+ "twice in the configured list!");
}
boolean optional = getState(parsed.getValue(), "optional");
log.debug(" > Engine: {} ({})",parsed.getKey(),optional? "optional" : "required");
last = writeExecutionNode(ep, epNode, parsed.getKey(), optional,
last == null ? null : Collections.singleton(last));
} catch (IllegalArgumentException e) {
throw new ConfigurationException(PROPERTY_ENGINE_LIST, "Unable to parse Chain Configuraiton (message: '"+
e.getMessage()+"')!",e);
}
}
if(engineNames.isEmpty()){
throw new ConfigurationException(PROPERTY_ENGINE_LIST,
"The configured chain MUST at least contain a single valid entry!");
}
this.engineNames = Collections.unmodifiableSet(engineNames);
this.executionPlan = ep.getGraph();
}
@Override
protected void deactivate(ComponentContext ctx) {
this.engineNames = null;
this.executionPlan = null;
super.deactivate(ctx);
}
@Override
public Graph getExecutionPlan() throws ChainException {
return executionPlan;
}
@Override
public Set<String> getEngines() {
return engineNames;
}
}