/*
* 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.core.yard;
import java.util.Dictionary;
import java.util.Hashtable;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.stanbol.entityhub.core.query.DefaultQueryFactory;
import org.apache.stanbol.entityhub.servicesapi.model.Representation;
import org.apache.stanbol.entityhub.servicesapi.model.ValueFactory;
import org.apache.stanbol.entityhub.servicesapi.query.FieldQueryFactory;
import org.apache.stanbol.entityhub.servicesapi.util.ModelUtils;
import org.apache.stanbol.entityhub.servicesapi.yard.Yard;
import org.apache.stanbol.entityhub.servicesapi.yard.YardException;
import org.osgi.service.cm.ConfigurationException;
@Component(componentAbstract=true)
@Properties(value={
@Property(name=Yard.ID,value="entityhubYard"),
@Property(name=Yard.NAME,value="Entityhub Yard"),
@Property(name=Yard.DESCRIPTION,value="Default values for configuring the Yard used by the Entityhub without editing"),
@Property(name=AbstractYard.DEFAULT_QUERY_RESULT_NUMBER,intValue=-1),
@Property(name=AbstractYard.MAX_QUERY_RESULT_NUMBER,intValue=-1)
})
public abstract class AbstractYard implements Yard {
/**
* Key used to configure maximum number of query results supported by
* this yard.<br>
* The default (if not set) is set to {@link #SolrQueryFactoy#MAX_QUERY_RESULT_NUMBER}
* ({@value #SolrQueryFactoy#MAX_QUERY_RESULT_NUMBER})
*/
public static final String MAX_QUERY_RESULT_NUMBER = "org.apache.stanbol.entityhub.yard.maxQueryResultNumber";
/**
* Key used to configure the default number of query results supported by
* this yard. <br>
* The default (if not set) is set to {@link #SolrQueryFactoy#DEFAULT_QUERY_RESULT_NUMBER}
* ({@value #SolrQueryFactoy#DEFAULT_QUERY_RESULT_NUMBER})
*/
public static final String DEFAULT_QUERY_RESULT_NUMBER = "org.apache.stanbol.entityhub.yard.defaultQueryResultNumber";
/**
* This Yard uses the default in-memory implementation of the Entityhub model.
*/
private ValueFactory valueFactory;
/**
* The QueryFactory as required by {@link Yard#getQueryFactory()}. This
* Yard uses the default implementation as provided by the
* {@link DefaultQueryFactory}.
*/
private FieldQueryFactory queryFactory;
/**
* Holds the configuration of the Yard.
*/
private YardConfig config;
/**
* The default prefix used for created URIs.
* @see #getUriPrefix()
*/
private String defaultPrefix;
/**
* Default constructor to create an uninitialised Yard. Typically used
* within an OSGI environment
*/
protected AbstractYard(){}
// /**
// * Constructor to create an initialised Yard.
// * @param valueFactory The value factory for the yard
// * @param queryFactory The query factory for the yard
// * @param config The configuration of the yard
// * @throws IllegalArgumentException if any of the three parameter is <code>null</code>
// */
// protected AbstractYard(ValueFactory valueFactory,FieldQueryFactory queryFactory, YardConfig config) throws IllegalArgumentException{
// activate(valueFactory, queryFactory, config);
// }
/**
* Activates the Yard based on the parsed parameter. Typically called within
* an OSGI environment by the activate method. Internally called by the
* {@link #AbstractYard(ValueFactory, FieldQueryFactory, YardConfig)}
* constructor.
* @param valueFactory The value factory for the yard
* @param queryFactory The query factory for the yard
* @param config The configuration of the yard
*/
protected final void activate(ValueFactory valueFactory,FieldQueryFactory queryFactory, YardConfig config) {
if(valueFactory == null){
throw new IllegalArgumentException("Unable to activate: The ValueFactory MUST NOT be NULL!");
}
if(queryFactory == null){
throw new IllegalArgumentException("Unable to activate: The QueryFactory MUST NOT be NULL!");
}
if(config == null){
throw new IllegalArgumentException("Unable to activate: The YardConfig MUST NOT be NULL!");
}
this.queryFactory = queryFactory;
this.valueFactory = valueFactory;
this.config = config;
this.defaultPrefix = String.format("urn:org.apache.stanbol:entityhub.yard.%s:%s.",
getClass().getSimpleName(),
config.getId());
}
/**
* Deactivates this yard instance. Typically called within an OSGI environment
* by the deacivate method.
*
*/
protected final void deactivate(){
this.queryFactory = null;
this.valueFactory = null;
this.config = null;
this.defaultPrefix = null;
}
/**
* Creates a new representation with a random uuid by using the pattern:
* <code><pre>
* urn:org.apache.stanbol:entityhub.yard.<getClass().getSimpleName()>:<getId()>.<uuid>
* </pre></code>
* @see Yard#create()
*/
@Override
public final Representation create() throws IllegalArgumentException, YardException{
return create(null);
}
/**
* Creates a representation with the parsed ID. If <code>null</code> is
* parsed a random UUID is generated as describe in {@link #create()}.<p>
* Note that {@link #store(Representation)} is called for the newly created
* representation and the Representation returned by this Method is returned.
* @param id The id or <code>null</code> to create a random uuid.
* @return The newly created, empty and stored representation
* @see Yard#create(String)
* @see Yard#store(Representation)
*/
@Override
public final Representation create(String id) throws IllegalArgumentException,YardException {
if(config == null){
throw new IllegalStateException("This Yard is not activated");
}
if(id == null){ //create a new ID
do {
id = createRandomEntityUri();
} while(isRepresentation(id));
} else if(isRepresentation(id)){
throw new IllegalArgumentException(
String.format("An representation with the parsed ID %s is already present in this Yard",id));
}
return store(valueFactory.createRepresentation(id));
}
@Override
public final String getDescription() {
if(config == null){
throw new IllegalStateException("This Yard is not activated");
}
return config.getDescription();
}
@Override
public final String getId() {
if(config == null){
throw new IllegalStateException("This Yard is not activated");
}
return config.getId();
}
@Override
public final String getName() {
if(config == null){
throw new IllegalStateException("This Yard is not activated");
}
return config.getName();
}
@Override
public final FieldQueryFactory getQueryFactory() {
if(queryFactory == null){
throw new IllegalStateException("This Yard is not activated");
}
return queryFactory;
}
@Override
public final ValueFactory getValueFactory() {
if(valueFactory == null){
throw new IllegalStateException("This Yard is not activated");
}
return valueFactory;
}
/**
* This provides the prefix for URIs created by this Yard. This is used for
* creating new unique URIs for Representation if {@link #create()} is
* called. <p>
* By default this implementation uses:<br>
* <code>"urn:org.apache.stanbol:entityhub.yard."+this.getClass.getSimpleName()+":"+getId()+"."</code>
* <p>
* Subclasses can override this Method to use a different namespace for entities.
* @return The UriPrefix used by this Yard instance for creating URIs
*/
protected String getUriPrefix(){
return defaultPrefix;
}
protected final YardConfig getConfig() {
return config;
}
protected final void setConfig(YardConfig config) {
this.config = config;
}
/**
* Creates an unique ID by using the {@link #getUriPrefix()} the parsed
* separator (non if <code>null</code>) and an uuid created by using
* {@link ModelUtils#randomUUID()}.
* <p>
* This Method is used for the {@link #create()} and the {@link #create(String)}
* - if <code>null</code> is parsed - to generate an unique URI for the
* created Representation.
* <p>
* Subclasses can override this Method to use other algorithms for generating
* URIs for entities.
* @return the created URI as string.
*/
protected final String createRandomEntityUri(){
return getUriPrefix()+ModelUtils.randomUUID().toString();
}
/** ------------------------------------------------------------------------
* Methods that need to be implemented by Sub-Classes
* ------------------------------------------------------------------------
*/
// @Override
// public abstract QueryResultList<Representation> find(FieldQuery query);
// @Override
// public abstract QueryResultList<String> findReferences(FieldQuery query);
// @Override
// public abstract QueryResultList<Representation> findRepresentation(FieldQuery query);
// @Override
// public abstract Representation getRepresentation(String id);
// @Override
// public abstract boolean isRepresentation(String id);
// @Override
// public abstract void remove(String id) throws IllegalArgumentException;
// @Override
// public abstract void store(Representation representation) throws IllegalArgumentException;
// @Override
// public abstract void update(Representation represnetation) throws IllegalArgumentException;
public abstract static class YardConfig {
protected final Dictionary<String, Object> config;
/**
* Creates a new config with the minimal set of required properties
* @param id the ID of the Yard
* @throws IllegalArgumentException if the parsed valued do not fulfil the
* requirements.
*/
protected YardConfig(String id) throws IllegalArgumentException{
this.config = new Hashtable<String, Object>();
setId(id);
}
/**
* Initialise the Yard configuration based on a parsed configuration. Usually
* used on the context of an OSGI environment in the activate method.
* @param config the configuration usually parsed within an OSGI activate
* method
* @throws ConfigurationException if the configuration is incomplete of
* some values are not valid
* @throws IllegalArgumentException if <code>null</code> is parsed as
* configuration
*/
protected YardConfig(Dictionary<String, Object> config) throws ConfigurationException,IllegalArgumentException {
if(config == null){
throw new IllegalArgumentException("The parsed configuration MUST NOT be NULL");
}
this.config = config;
isValid();
}
/**
* Setter for the ID of the yard. The id is usually a sort name such as
* "dbpedia", "freebase", "geonames.org", "my.projects" ...<p>
* If {@link #isMultiYardIndexLayout()} than this ID is used to identify
* Representations of this Yard within the SolrIndex.
* @param the id of the yard. Required, not null, not empty!
*/
public final void setId(String id) {
if(id != null){
config.put(Yard.ID, id);
} else {
config.remove(Yard.ID);
}
}
/**
* Getter for the ID of the yard
* @return the id of the yard
*/
public final String getId() {
Object value = config.get(Yard.ID);
return value==null?null:value.toString();
}
/**
* Setter for the name of this yard. If not set the {@link #getId(String)}
* is used as default
* @param name The name or <code>null</code> to use {@link #getId()}.
*/
public final void setName(String name) {
if(name != null){
config.put(Yard.NAME, name);
} else {
config.remove(Yard.NAME);
}
}
/**
* Getter for the human readable name of the Yard
* @return the name
*/
public final String getName() {
Object value = config.get(Yard.NAME);
return value==null?getId():value.toString();
}
/**
* Setter for the description of this Yard
* @param description the description. Optional parameter
*/
public final void setDescription(String description) {
if(description != null){
config.put(Yard.DESCRIPTION, description);
} else {
config.remove(Yard.DESCRIPTION);
}
}
/**
* Getter for the description
* @return description The description or <code>null</code> if not defined
*/
public final String getDescription() {
Object value = config.get(Yard.DESCRIPTION);
return value==null?null:value.toString();
}
/**
* Setter for the default number of query results. This is used if parsed
* queries do not define a limit for the maximum number of results.
* @param defaultQueryResults the default number of query results.
* <code>null</code> or a negative number to use the default value defined
* by the Yard.
*/
public final void setDefaultQueryResultNumber(Integer defaultQueryResults) {
if(defaultQueryResults != null){
config.put(DEFAULT_QUERY_RESULT_NUMBER, defaultQueryResults);
} else {
config.remove(DEFAULT_QUERY_RESULT_NUMBER);
}
}
/**
* Getter for the default number of query results. This is used if parsed
* queries do not define a limit for the maximum number of results.<p>
* If {@link #getMaxQueryResultNumber()} is defines (>0), than this
* method returns the minimum of the two configured values.
* @return the default number used as the maximum number of results per
* query if not otherwise set by the parsed query. Returns <code>0</code>
* if the value was set to a number lower or equals 0 and -1 if the
* value is not configured at all.
*/
public final int getDefaultQueryResultNumber() throws NumberFormatException {
Object value = config.get(DEFAULT_QUERY_RESULT_NUMBER);
Integer number;
if(value != null){
if(value instanceof Integer){
number = (Integer) value;
} else {
try {
number = Integer.valueOf(value.toString());
} catch (NumberFormatException e){
return -1;
}
}
} else {
return -1;
}
if(number.intValue() <= 0){
return 0;
} else {
if(getMaxQueryResultNumber() > 0){
return Math.min(getMaxQueryResultNumber(), number);
} else {
return number;
}
}
}
/**
* Setter for the maximum number of query results. This is used to limit the
* maximum number of results when parsed queries define limits that are
* greater this value.
* @param maxQueryResults The maximum number of results for queries.
* <code>null</code> or a negative number to use the default as defined by
* the Yard implementation.
*/
public final void setMaxQueryResultNumber(Integer maxQueryResults) {
if(maxQueryResults != null){
config.put(MAX_QUERY_RESULT_NUMBER, maxQueryResults);
} else {
config.remove(MAX_QUERY_RESULT_NUMBER);
}
}
/**
* Getter for the maximum number of query results.This is used to limit the
* maximum number of results when parsed queries define limits that are
* greater this value.
* @return the maximum number of query results. Returns <code>0</code>
* if the value was set to a number lower or equals 0 and -1 if the
* value is not configured at all.
*/
public final int getMaxQueryResultNumber() {
Object value = config.get(MAX_QUERY_RESULT_NUMBER);
Integer number;
if(value != null){
if(value instanceof Integer){
number = (Integer) value;
} else {
try {
number = Integer.valueOf(value.toString());
}catch (NumberFormatException e) {
return -1;
}
}
} else {
return -1;
}
if(number.intValue()<=0){
return 0;
} else {
return number;
}
}
/**
* Getter for the {@link Dictionary} used to store the configuration
* wrapped by this API
* @return the configuration
*/
public final Dictionary<String,Object> getDictionary(){
return this.config;
}
/**
* Checks if the configuration is valid and throws a {@link ConfigurationException}
* if not.<p>
* This method checks the {@link Yard#ID} property and than calls
* {@link #validateConfig()} to check additional constraints of specific
* Yard configurations (subclasses of this class)
* @return returns true if valid
* @throws ConfigurationException
*/
protected final boolean isValid() throws ConfigurationException{
String id = getId();
if(id == null || id.isEmpty()){
throw new ConfigurationException(Yard.ID, "The ID of the Yard MUST NOT be NULL nor empty!");
}
validateConfig();
return true;
}
/**
* Needs to be implemented by Subclasses to check required configurations of
* specific Yard Implementations.
* @throws ConfigurationException In case of a missing or invalid configuration
* for one of the properties.
*/
protected abstract void validateConfig() throws ConfigurationException;
}
}