/*
* 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.indexing.core.destination;
import java.io.File;
import java.io.IOException;
import java.util.Dictionary;
import java.util.Hashtable;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.felix.cm.file.ConfigurationHandler;
import org.apache.stanbol.entityhub.indexing.core.config.IndexingConfig;
import org.apache.stanbol.entityhub.servicesapi.site.ReferencedSiteConfiguration;
import org.apache.stanbol.entityhub.servicesapi.site.SiteConfiguration;
import org.apache.stanbol.entityhub.servicesapi.yard.Cache;
import org.apache.stanbol.entityhub.servicesapi.yard.CacheStrategy;
import org.apache.stanbol.entityhub.servicesapi.yard.Yard;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import aQute.lib.osgi.Builder;
import aQute.lib.osgi.Jar;
public final class OsgiConfigurationUtil {
private OsgiConfigurationUtil() {/* Do not create instances of Util classes*/}
private static final Logger log = LoggerFactory.getLogger(OsgiConfigurationUtil.class);
public static final String DEFAULT_MAPPING_STATE = "proposed";
public static final String DEFAULT_IMPORTED_ENTTIY_STATE = "proposed";
public static final Object DEFAULT_EXPIRE_DURATION = Integer.valueOf(0);
public static final String REFERENCED_SITE_COMPONENT_ID = "org.apache.stanbol.entityhub.site.referencedSite";
public static final String CACHE_COMPONENT_ID = "org.apache.stanbol.entityhub.core.site.CacheImpl";
private static final String CONFIG_ROOT = "config";
private static final String[] CONFIG_PATH_ELEMENTS = new String[]{"org","apache","stanbol","data","site"};
private static final String CONFIG_PATH;
private static final String CONFIG_PACKAGE;
static {
StringBuilder path = new StringBuilder();
StringBuilder java = new StringBuilder();
for(String part : CONFIG_PATH_ELEMENTS){
path.append(part);
java.append(part);
path.append(File.separatorChar);
java.append('.');
}
CONFIG_PATH = path.toString();
CONFIG_PACKAGE = java.toString();
}
public static void writeOsgiConfig(IndexingConfig indexingConfig,String name, Dictionary<String,Object> config) throws IOException{
if(indexingConfig == null){
throw new IllegalArgumentException("The parsed IndexingConfiguration MUST NOT be NULL");
}
if(name == null){
throw new IllegalArgumentException("The parsed file name MUST NOT be NULL");
}
if(config == null){
throw new IllegalArgumentException("The parsed configuration MUST NOT be NULL");
}
if(config.isEmpty()){
throw new IllegalArgumentException("The parsed configuration MUST NOT be empty");
}
File configFile = new File(getConfigDirectory(indexingConfig),name);
ConfigurationHandler.write(FileUtils.openOutputStream(configFile), config);
}
private static Dictionary<String,Object> createSiteConfig(IndexingConfig indexingConfig){
Dictionary<String,Object> config = new Hashtable<String,Object>();
//basic properties
//we use the name as ID
config.put(SiteConfiguration.ID, indexingConfig.getName());
//also set the id as name
config.put(SiteConfiguration.NAME, indexingConfig.getName());
//config.put(SiteConfiguration.NAME, indexingConfig.getName());
if(indexingConfig.getDescription() != null && !indexingConfig.getDescription().isEmpty()){
config.put(SiteConfiguration.DESCRIPTION, indexingConfig.getDescription());
}
//the cache
//name the Cache is the same as for the Yard.
config.put(ReferencedSiteConfiguration.CACHE_ID, getYardID(indexingConfig));
config.put(ReferencedSiteConfiguration.CACHE_STRATEGY, CacheStrategy.all);
//Entity Dereferencer (optional)
if(addProperty(ReferencedSiteConfiguration.ACCESS_URI, config, indexingConfig)){
addProperty(ReferencedSiteConfiguration.ENTITY_DEREFERENCER_TYPE, config, indexingConfig,
"Referenced Site for " + indexingConfig.getName() +
" (including a full local Cache)");
}
//Entity Searcher (optional)
if(addProperty(ReferencedSiteConfiguration.QUERY_URI, config, indexingConfig)){
addProperty(ReferencedSiteConfiguration.ENTITY_SEARCHER_TYPE, config, indexingConfig);
}
//General Properties
addProperty(SiteConfiguration.DEFAULT_EXPIRE_DURATION, config, indexingConfig,DEFAULT_EXPIRE_DURATION);
addProperty(SiteConfiguration.DEFAULT_MAPPING_STATE, config, indexingConfig,DEFAULT_MAPPING_STATE);
addProperty(SiteConfiguration.DEFAULT_SYMBOL_STATE, config, indexingConfig,DEFAULT_IMPORTED_ENTTIY_STATE);
//the entity prefix is optional and may be an array
addPropertyValues(SiteConfiguration.ENTITY_PREFIX, config, indexingConfig);
//add the Field Mappings when entities of this Site are imported to the
//entityhub. This may be the same mappings as used for the Cache however
//they may be also different.
Object value = indexingConfig.getProperty(SiteConfiguration.SITE_FIELD_MAPPINGS);
if(value != null){
File fieldMappingConfig = indexingConfig.getConfigFile(value.toString());
if(fieldMappingConfig != null){
try {
config.put(SiteConfiguration.SITE_FIELD_MAPPINGS,
FileUtils.readLines(fieldMappingConfig, "UTF-8"));
} catch (IOException e) {
log.warn(String.format("Unable to read Field Mappings for Referenced Site " +
"configuration"),e);
}
} else {
log.warn("Unable to load configured Field Mappings for Reference Site " +
"{}={}",SiteConfiguration.SITE_FIELD_MAPPINGS,value);
}
}
//set other optional properties
addProperty(SiteConfiguration.SITE_ATTRIBUTION, config, indexingConfig);
addProperty(SiteConfiguration.SITE_ATTRIBUTION_URL, config, indexingConfig);
addPropertyValues(SiteConfiguration.SITE_LICENCE_NAME, config, indexingConfig);
addPropertyValues(SiteConfiguration.SITE_LICENCE_TEXT, config, indexingConfig);
addPropertyValues(SiteConfiguration.SITE_LICENCE_URL, config, indexingConfig);
return config;
}
private static Dictionary<String,Object> createCacheConfig(IndexingConfig indexingConfig){
Dictionary<String,Object> config = new Hashtable<String,Object>();
//a cache needs to provide the ID of the Yard
String yardId = getYardID(indexingConfig);
config.put(Cache.ID, yardId);
config.put(Cache.NAME, indexingConfig.getName()+" Cache");
config.put(Cache.DESCRIPTION, "Cache for the "+indexingConfig.getName()+
" Referenced Site using the "+yardId+".");
config.put(Cache.CACHE_YARD, getYardID(indexingConfig));
//additinal Mappings:
// This can be used to define what information are store to the cache
// if an Entity is updated from a remote site.
// If not present the mappings used by the Yard are used. This default
// is sufficient for full indexes as created by the indexing utils
// therefore we need not to deal with additional mappings here
return config;
}
/**
* Adds the configurations as defined by the Yard Interface. Configurations
* of specific Yard implementations might need to add additional
* parameters. <p>
* This also ensures that the ID of the Yard is the same as referenced by the
* configurations for the referenced site and the cache.
* @param indexingConfig
* @return
*/
public static Dictionary<String,Object> createYardConfig(IndexingConfig indexingConfig){
Dictionary<String,Object> config = new Hashtable<String,Object>();
config.put(Yard.ID, getYardID(indexingConfig));
config.put(Yard.NAME, indexingConfig.getName()+" Index");
config.put(Yard.DESCRIPTION,"Full local index for the Referenced Site \""+indexingConfig.getName()+"\".");
return config;
}
public static void writeSiteConfiguration(IndexingConfig indexingConfig) throws IOException {
String siteConfigFileName = REFERENCED_SITE_COMPONENT_ID +
"-" + indexingConfig.getName()+".config";
writeOsgiConfig(indexingConfig,siteConfigFileName, createSiteConfig(indexingConfig));
}
public static void writeCacheConfiguration(IndexingConfig indexingConfig) throws IOException {
String cacheFileName = CACHE_COMPONENT_ID +
"-" + indexingConfig.getName()+".config";
writeOsgiConfig(indexingConfig,cacheFileName, createCacheConfig(indexingConfig));
}
/**
* Getter for default ID of the yard based on the value of
* {@link IndexingConfig#getName()}
* @param config the IndexingConfig
* @return the default ID of the yard based on the value of
* {@link IndexingConfig#getName()}
*/
public static String getYardID(IndexingConfig config){
return config.getName()+"Index";
}
private static boolean addPropertyValues(String key, Dictionary<String,Object> config, IndexingConfig indexingConfig){
Object value = indexingConfig.getProperty(key);
if(value != null && !value.toString().isEmpty()) {
config.put(key, value.toString().split(";"));
return true;
} else {
return false;
}
}
private static boolean addProperty(String key, Dictionary<String,Object> config, IndexingConfig indexingConfig){
return addProperty(key, config, indexingConfig, null);
}
private static boolean addProperty(String key, Dictionary<String,Object> config, IndexingConfig indexingConfig,Object defaultValue){
Object value = indexingConfig.getProperty(key);
if(value != null || defaultValue != null){
config.put(key, value != null ? value : defaultValue);
return true;
} else {
return false;
}
}
/**
* Getter for the Directory that need to contain all Files to be included
* in the OSGI Bundle.
* @param config the indexing configuration
* @return the directory (created if not already existing)
* @throws IOException If the directory could not be created
*/
public static File getConfigDirectory(IndexingConfig config) throws IOException{
File configRoot = new File(config.getDestinationFolder(),CONFIG_ROOT);
File siteConfigDir = new File(configRoot,CONFIG_PATH+config.getName().toLowerCase());
if(!siteConfigDir.isDirectory()){
if(!siteConfigDir.mkdirs()){
throw new IOException("Unable to create config Directory "+siteConfigDir);
}
}
return siteConfigDir;
}
public static void createBundle(IndexingConfig config){
Builder builder = new Builder();
builder.setProperty("Install-Path",
FilenameUtils.separatorsToUnix(CONFIG_PATH) //see STANBOL-768
+ config.getName().toLowerCase());
builder.setProperty(Builder.EXPORT_PACKAGE,CONFIG_PACKAGE+config.getName().toLowerCase());
builder.setProperty(Builder.BUNDLE_CATEGORY, "Stanbol Data");
builder.setProperty(Builder.BUNDLE_NAME, "Apache Stanbol Data: "+config.getName());
builder.setProperty(Builder.CREATED_BY, "Apache Stanbol Entityhub Indexing Utils");
builder.setProperty(Builder.BUNDLE_VENDOR, "Apache Stanbol (Incubating)");//TODO make configureable
builder.setProperty(Builder.BUNDLE_VERSION, "1.0.0");
builder.setProperty(Builder.BUNDLE_DESCRIPTION, "Bundle created for import of the referenced site "
+ config.getName() +" into the Apache Stanbol Entityhub");
builder.setProperty(Builder.BUNDLE_SYMBOLICNAME, CONFIG_PACKAGE+config.getName().toLowerCase());
try {
builder.addClasspath(new File(config.getDestinationFolder(),CONFIG_ROOT));
} catch (IOException e) {
log.warn("Unable to build OSGI Bundle for Indexed Referenced Site "+config.getName(),e);
return;
}
Jar jar;
try {
jar = builder.build();
} catch (Exception e) {
log.warn("Unable to build OSGI Bundle for Indexed Referenced Site "+config.getName(),e);
return;
}
try {
jar.write(new File(config.getDistributionFolder(),
CONFIG_PACKAGE+config.getName()+"-1.0.0.jar"));
} catch (Exception e) {
log.warn("Unable to write OSGI Bundle for Indexed Referenced Site "+config.getName(),e);
}
}
}