Package org.apache.stanbol.entityhub.core.impl

Source Code of org.apache.stanbol.entityhub.core.impl.ReferencedSiteImpl$ComponentFactoryListener

/*
* 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.impl;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.ConfigurationPolicy;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.PropertyOption;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.ReferencePolicy;
import org.apache.felix.scr.annotations.ReferenceStrategy;
import org.apache.felix.scr.annotations.Service;
import org.apache.stanbol.commons.stanboltools.offline.OfflineMode;
import org.apache.stanbol.entityhub.core.mapping.DefaultFieldMapperImpl;
import org.apache.stanbol.entityhub.core.mapping.FieldMappingUtils;
import org.apache.stanbol.entityhub.core.mapping.ValueConverterFactory;
import org.apache.stanbol.entityhub.core.model.EntityImpl;
import org.apache.stanbol.entityhub.core.model.InMemoryValueFactory;
import org.apache.stanbol.entityhub.core.query.DefaultQueryFactory;
import org.apache.stanbol.entityhub.core.query.QueryResultListImpl;
import org.apache.stanbol.entityhub.core.utils.OsgiUtils;
import org.apache.stanbol.entityhub.servicesapi.defaults.NamespaceEnum;
import org.apache.stanbol.entityhub.servicesapi.mapping.FieldMapper;
import org.apache.stanbol.entityhub.servicesapi.mapping.FieldMapping;
import org.apache.stanbol.entityhub.servicesapi.model.Representation;
import org.apache.stanbol.entityhub.servicesapi.model.Entity;
import org.apache.stanbol.entityhub.servicesapi.model.ValueFactory;
import org.apache.stanbol.entityhub.servicesapi.model.rdf.RdfResourceEnum;
import org.apache.stanbol.entityhub.servicesapi.query.FieldQuery;
import org.apache.stanbol.entityhub.servicesapi.query.FieldQueryFactory;
import org.apache.stanbol.entityhub.servicesapi.query.QueryResultList;
import org.apache.stanbol.entityhub.servicesapi.site.EntityDereferencer;
import org.apache.stanbol.entityhub.servicesapi.site.EntitySearcher;
import org.apache.stanbol.entityhub.servicesapi.site.License;
import org.apache.stanbol.entityhub.servicesapi.site.ReferencedSite;
import org.apache.stanbol.entityhub.servicesapi.site.ReferencedSiteException;
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.apache.stanbol.entityhub.servicesapi.yard.YardException;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.Filter;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceEvent;
import org.osgi.framework.ServiceListener;
import org.osgi.framework.ServiceReference;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.ComponentFactory;
import org.osgi.service.component.ComponentInstance;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* This in the Default implementation of the {@link ReferencedSite} interface.
* However this implementation forwards calls to methods defined within the
* {@link EntityDereferencer} and {@link EntitySearcher} to sub components
* (See the detailed description below).<p>
* Each {@link ReferencedSite} with an {@link CacheStrategy} other than
* {@link CacheStrategy#none} needs an associated {@link Cache}.
* <p>
* The Initialisation of the sub-components:
* <ul>
* <li> <b>{@link EntityDereferencer}:</b> Implementations of this interface are
*      specific to the used protocol/technology of the referenced site.
*      Because of that calls to methods defined in this interface are forwarded
*      to an site specific instance of the {@link EntityDereferencer} interface
*      as configured by the {@link SiteConfiguration#ENTITY_DEREFERENCER_TYPE} property.<br>
*      During activation the the {@link BundleContext} is used to
*      search for {@link ComponentFactory} with the configuration <code>
*      "component.name= {@link ComponentContext#getProperties()}.get(
*      {@link SiteConfiguration#ENTITY_DEREFERENCER_TYPE})</code>. This factory is used
*      to create an instance of {@link EntityDereferencer}. <br>
*      Note also, that the configuration of this instance that is covered
*      by the {@link SiteConfiguration} interface are parsed to the
*      {@link EntityDereferencer} instance.
* <li> <b> {@link EntitySearcher}:</b> Implementations of this interface are
*      also specific to the used protocol/technology of the referenced site.
*      Because of that calls to methods defined in this interface are forwarded
*      to an site specific instance of the {@link EntitySearcher} interface
*      as configured by the {@link SiteConfiguration#ENTITY_SEARCHER_TYPE} property.<br>
*      The initialisation of this instance works similar as described for the
*      {@link EntityDereferencer}. However if the value of the {@link SiteConfiguration#ENTITY_SEARCHER_TYPE}
*      is equals to {@link SiteConfiguration#ENTITY_DEREFERENCER_TYPE} or the
*      {@link SiteConfiguration#ENTITY_SEARCHER_TYPE} is not defined at all, than the
*      Dereferencer Instance is also used as {@link EntitySearcher}. If the
*      according cast does not succeed, an {@link ConfigurationException} for the
*      {@link SiteConfiguration#ENTITY_SEARCHER_TYPE} property is thrown.
* <li> <b>{@link Cache}: </b> An instance of a {@link Cache} is used to
*      cache {@link Representation}s loaded form the Site. A cache is a wrapper
*      over a {@link Yard} instance that allows to configure what data are
*      stored for each representation cached form this referenced site. A
*      {@link ServiceTracker} is used for managing the dependency with the cache.
*      So if a cache is no longer available a referenced site can still be used -
*      only the local cache can not be used to retrieve entity representations.
* </ul>
*
* @author Rupert Westenthaler
*
*/
@Component(
        name="org.apache.stanbol.entityhub.site.referencedSite",
        configurationFactory=true,
        policy=ConfigurationPolicy.REQUIRE, //the baseUri is required!
        specVersion="1.1",
        metatype = true,
        immediate = true
        )
@Service(value=ReferencedSite.class)
@Properties(value={
        @Property(name=SiteConfiguration.ID),
        @Property(name=SiteConfiguration.NAME),
        @Property(name=SiteConfiguration.DESCRIPTION),
        @Property(name=SiteConfiguration.ENTITY_PREFIX, cardinality=1000),
        @Property(name=SiteConfiguration.ACCESS_URI),
        @Property(name=SiteConfiguration.ENTITY_DEREFERENCER_TYPE,
            options={
                @PropertyOption(
                        value='%'+SiteConfiguration.ENTITY_DEREFERENCER_TYPE+".option.none",
                        name=""),
                @PropertyOption(
                    value='%'+SiteConfiguration.ENTITY_DEREFERENCER_TYPE+".option.sparql",
                    name="org.apache.stanbol.entityhub.dereferencer.SparqlDereferencer"),
                @PropertyOption(
                        value='%'+SiteConfiguration.ENTITY_DEREFERENCER_TYPE+".option.coolUri",
                        name="org.apache.stanbol.entityhub.dereferencer.CoolUriDereferencer")
            },value="org.apache.stanbol.entityhub.dereferencer.SparqlDereferencer"),
        @Property(name=SiteConfiguration.QUERY_URI), //the deri server has better performance
        @Property(name=SiteConfiguration.ENTITY_SEARCHER_TYPE,
            options={
                @PropertyOption(
                        value='%'+SiteConfiguration.ENTITY_SEARCHER_TYPE+".option.none",
                        name=""),
                @PropertyOption(
                    value='%'+SiteConfiguration.ENTITY_SEARCHER_TYPE+".option.sparql",
                    name="org.apache.stanbol.entityhub.searcher.SparqlSearcher"),
                @PropertyOption(
                        value='%'+SiteConfiguration.ENTITY_SEARCHER_TYPE+".option.sparql-virtuoso",
                        name="org.apache.stanbol.entityhub.searcher.VirtuosoSearcher"),
                @PropertyOption(
                        value='%'+SiteConfiguration.ENTITY_SEARCHER_TYPE+".option.sparql-larq",
                        name="org.apache.stanbol.entityhub.searcher.LarqSearcher")
            },value="org.apache.stanbol.entityhub.searcher.SparqlSearcher"),
        @Property(name=SiteConfiguration.DEFAULT_SYMBOL_STATE,
            options={
                @PropertyOption( //seems, that name and value are exchanged ...
                        value='%'+SiteConfiguration.DEFAULT_SYMBOL_STATE+".option.proposed",
                        name="proposed"),
                @PropertyOption(
                        value='%'+SiteConfiguration.DEFAULT_SYMBOL_STATE+".option.active",
                        name="active")
                //the other states make no sense for new symbols
            }, value="proposed"),
        @Property(name=SiteConfiguration.DEFAULT_MAPPING_STATE,
            options={
                @PropertyOption(
                        value='%'+SiteConfiguration.DEFAULT_MAPPING_STATE+".option.proposed",
                        name="proposed"),
                @PropertyOption(
                        value='%'+SiteConfiguration.DEFAULT_MAPPING_STATE+".option.confirmed",
                        name="confirmed")
                //the other states make no sense for new symbols
            }, value="proposed"),
        @Property(name=SiteConfiguration.DEFAULT_EXPIRE_DURATION,
            options={
                @PropertyOption(
                        value='%'+SiteConfiguration.DEFAULT_EXPIRE_DURATION+".option.oneMonth",
                        name=""+(1000L*60*60*24*30)),
                @PropertyOption(
                        value='%'+SiteConfiguration.DEFAULT_EXPIRE_DURATION+".option.halfYear",
                        name=""+(1000L*60*60*24*183)),
                @PropertyOption(
                        value='%'+SiteConfiguration.DEFAULT_EXPIRE_DURATION+".option.oneYear",
                        name=""+(1000L*60*60*24*365)),
                @PropertyOption(
                        value='%'+SiteConfiguration.DEFAULT_EXPIRE_DURATION+".option.none",
                        name="0")
            }, value="0"),
        @Property(name=SiteConfiguration.CACHE_STRATEGY,
            options={
                @PropertyOption(
                        value='%'+SiteConfiguration.CACHE_STRATEGY+".option.none",
                        name="none"),
                @PropertyOption(
                        value='%'+SiteConfiguration.CACHE_STRATEGY+".option.used",
                        name="used"),
                @PropertyOption(
                        value='%'+SiteConfiguration.CACHE_STRATEGY+".option.all",
                        name="all")
            }, value="none"),
        @Property(name=SiteConfiguration.CACHE_ID),
        @Property(name=SiteConfiguration.SITE_FIELD_MAPPINGS,cardinality=1000)
        })
public class ReferencedSiteImpl implements ReferencedSite {
    static final int maxInt = Integer.MAX_VALUE;
    private final Logger log;
    private ComponentContext context;
    private FieldMapper fieldMappings;

    private final Object searcherAndDereferencerLock = new Object();
    private Boolean dereferencerEqualsEntitySearcherComponent;
    private ComponentFactoryListener dereferencerComponentFactoryListener;
    private ComponentFactoryListener searcherComponentFactoryListener;

//    private String dereferencerComponentName;
    private ComponentInstance dereferencerComponentInstance;
    private EntityDereferencer dereferencer;

//    private String entitySearcherComponentName;
    private EntitySearcher entitySearcher;
    private ComponentInstance entitySearcherComponentInstance;

    private ServiceTracker cacheTracker;
   
    private SiteConfiguration siteConfiguration;
    /**
     * Stores keys -> values to be added to the metadata of {@link Entity Entities}
     * created by this site.
     */
    private Map<String,Object> siteMetadata;
   
    /**
     * The {@link OfflineMode} is used by Stanbol to indicate that no external
     * service should be referenced. For the ReferencedSiteImpl this means that
     * the {@link EntityDereferencer} and {@link EntitySearcher} interfaces
     * are no longer used.<p>
     * @see #enableOfflineMode(OfflineMode)
     * @see #disableOfflineMode(OfflineMode)
     * @see #isOfflineMode()
     * @see #ensureOnline(String, Class)
     */
    @Reference(
        cardinality=ReferenceCardinality.OPTIONAL_UNARY,
        policy=ReferencePolicy.DYNAMIC,
        bind="enableOfflineMode",
        unbind="disableOfflineMode",
        strategy=ReferenceStrategy.EVENT)
    private OfflineMode offlineMode;


   
    public ReferencedSiteImpl(){
        this(LoggerFactory.getLogger(ReferencedSiteImpl.class));
    }
    protected ReferencedSiteImpl(Logger log){
        this.log = log;
           log.info("create instance of {}",this.getClass().getName());
    }
    public String getId(){
        return siteConfiguration.getId();
    }
    @Override
    public QueryResultList<Entity> findEntities(FieldQuery query) throws ReferencedSiteException {
        List<Entity> results;
        if(siteConfiguration.getCacheStrategy() == CacheStrategy.all){
            //TODO: check if query can be executed based on the base configuration of the Cache
            Cache cache = getCache();
            if(cache != null){
                try {
                    //When using the Cache, directly get the representations!
                    QueryResultList<Representation> representations = cache.findRepresentation((query));
                    results = new ArrayList<Entity>(representations.size());
                    for(Representation result : representations){
                        Entity entity = new EntityImpl(getId(),result,null);
                        results.add(entity);
                        initEntityMetadata(entity,true);
                    }
                    return new QueryResultListImpl<Entity>(query, results, Entity.class);
                } catch (YardException e) {
                    if(siteConfiguration.getEntitySearcherType()==null || isOfflineMode()){
                        throw new ReferencedSiteException("Unable to execute query on Cache "+siteConfiguration.getCacheId(),e);
                    } else {
                        log.warn(String.format("Error while performing query on Cache %s! Try to use remote site %s as fallback!",
                            siteConfiguration.getCacheId(),siteConfiguration.getQueryUri()),e);
                    }
                }
            } else {
                if(siteConfiguration.getEntitySearcherType()==null || isOfflineMode()){
                    throw new ReferencedSiteException(String.format("Unable to execute query on Cache %s because it is currently not active",
                        siteConfiguration.getCacheId()));
                } else {
                    log.warn(String.format("Cache %s currently not active will query remote Site %s as fallback",
                        siteConfiguration.getCacheId(),siteConfiguration.getQueryUri()));
                }
            }
        }
        QueryResultList<String> entityIds;
        if(entitySearcher == null) {
            throw new ReferencedSiteException(
                String.format("EntitySearcher %s not available for remote site %s!",siteConfiguration.getEntitySearcherType(),
                    siteConfiguration.getQueryUri()));
        }
        ensureOnline(siteConfiguration.getQueryUri(),entitySearcher.getClass());
        try {
            entityIds = entitySearcher.findEntities(query);
        } catch (IOException e) {
            throw new ReferencedSiteException(String.format("Unable to execute query on remote site %s with entitySearcher %s!",
                    siteConfiguration.getQueryUri(),siteConfiguration.getEntitySearcherType()), e);
        }
        int numResults = entityIds.size();
        List<Entity> entities = new ArrayList<Entity>(numResults);
        int errors = 0;
        ReferencedSiteException lastError = null;
        for(String id : entityIds){
            Entity entity;
            try {
                entity = getEntity(id);
                if(entity == null){
                    log.warn("Unable to create Entity for ID that was selected by an FieldQuery (id="+id+")");
                }
                entities.add(entity);
                //use the position in the list as resultSocre
                entity.getRepresentation().set(RdfResourceEnum.resultScore.getUri(), Float.valueOf((float)numResults));
            } catch (ReferencedSiteException e) {
                lastError = e;
                errors++;
                log.warn(String.format("Unable to get Representation for Entity %s. -> %d Error%s for %d Entities in QueryResult (Reason:%s)",
                        id,errors,errors>1?"s":"",entityIds.size(),e.getMessage()));
            }
            //decrease numResults because it is used as resultScore for entities
            numResults--;
        }
        if(lastError != null){
            if(entities.isEmpty()){
                throw new ReferencedSiteException("Unable to get anly Representations for Entities selected by the parsed Query (Root-Cause is the last Exception trown)",lastError);
            } else {
                log.warn(String.format("Unable to get %d/%d Represetnations for selected Entities.",errors,entityIds.size()));
                log.warn("Stack trace of the last Exception:",lastError);
            }
        }
        return new QueryResultListImpl<Entity>(query, entities,Entity.class);
    }
    @Override
    public QueryResultList<Representation> find(FieldQuery query) throws ReferencedSiteException{
        if(siteConfiguration.getCacheStrategy() == CacheStrategy.all){
            //TODO: check if query can be executed based on the base configuration of the Cache
            Cache cache = getCache();
            if(cache != null){
                try {
                    return cache.find(query);
                } catch (YardException e) {
                    if(siteConfiguration.getEntitySearcherType()==null || isOfflineMode()){
                        throw new ReferencedSiteException("Unable to execute query on Cache "+siteConfiguration.getCacheId(),e);
                    } else {
                        log.warn(String.format("Error while performing query on Cache %s! Try to use remote site %s as fallback!",
                            siteConfiguration.getCacheId(),siteConfiguration.getQueryUri()),e);
                    }
                }
            } else {
                if(siteConfiguration.getEntitySearcherType()==null || isOfflineMode()){
                    throw new ReferencedSiteException(String.format("Unable to execute query because Cache %s is currently not active",
                        siteConfiguration.getCacheId()));
                } else {
                    log.warn(String.format("Cache %s currently not active will query remote Site %s as fallback",
                        siteConfiguration.getCacheId(),siteConfiguration.getQueryUri()));
                }
            }
        }
        if(entitySearcher == null){
            throw new ReferencedSiteException(
                String.format("EntitySearcher %s not available for remote site %s!",siteConfiguration.getEntitySearcherType(),
                    siteConfiguration.getQueryUri()));
        }
        ensureOnline(siteConfiguration.getQueryUri(), entitySearcher.getClass());
        try {
            return entitySearcher.find(query);
        } catch (IOException e) {
            throw new ReferencedSiteException("Unable execute Query on remote site "+
                siteConfiguration.getQueryUri(),e);
        }
    }
    @Override
    public QueryResultList<String> findReferences(FieldQuery query) throws ReferencedSiteException {
        if(siteConfiguration.getCacheStrategy() == CacheStrategy.all){
            //TODO: check if query can be executed based on the base configuration of the Cache
            Cache cache = getCache();
            if(cache != null){
                try {
                    return cache.findReferences(query);
                } catch (YardException e) {
                    if(siteConfiguration.getEntitySearcherType()==null || isOfflineMode()){
                        throw new ReferencedSiteException("Unable to execute query on Cache "+siteConfiguration.getCacheId(),e);
                    } else {
                        log.warn(String.format("Error while performing query on Cache %s! Try to use remote site %s as fallback!",
                            siteConfiguration.getCacheId(),siteConfiguration.getQueryUri()),e);
                    }
                }
            } else {
                if(siteConfiguration.getEntitySearcherType()==null  || isOfflineMode()){
                    throw new ReferencedSiteException(
                        String.format("Unable to execute query on Cache %s because it is currently not active",
                            siteConfiguration.getCacheId()));
                } else {
                    log.warn(String.format("Cache %s currently not active will query remote Site %s as fallback",
                        siteConfiguration.getCacheId(),siteConfiguration.getQueryUri()));
                }
            }
        }
        if(entitySearcher == null){
            throw new ReferencedSiteException(
                String.format("EntitySearcher %s not available for remote site %s!",siteConfiguration.getEntitySearcherType(),
                    siteConfiguration.getQueryUri()));
        }
        ensureOnline(siteConfiguration.getQueryUri(), entitySearcher.getClass());
        try {
            return entitySearcher.findEntities(query);
        } catch (IOException e) {
            throw new ReferencedSiteException("Unable execute Query on remote site "+
                siteConfiguration.getQueryUri(),e);
        }
    }
    @Override
    public InputStream getContent(String id, String contentType) throws ReferencedSiteException {
        if(siteConfiguration.getEntityDereferencerType() == null){
            throw new ReferencedSiteException(
                String.format("Unable to get Content for Entity %s because No dereferencer configured for ReferencedSite %s",
                    id,getId()));
        }
        if(dereferencer == null){
            throw new ReferencedSiteException(
                String.format("Dereferencer %s for remote site %s is not available",siteConfiguration.getEntityDereferencerType(),
                siteConfiguration.getAccessUri()));
        }
        ensureOnline(siteConfiguration.getAccessUri(), dereferencer.getClass());
        try {
            return dereferencer.dereference(id, contentType);
        } catch (IOException e) {
            throw new ReferencedSiteException(
                String.format("Unable to load content for Entity %s and mediaType %s from remote site %s by using dereferencer %s",
                    id,contentType,siteConfiguration.getAccessUri(),siteConfiguration.getEntityDereferencerType()),e);
        }
    }
    @Override
    public Entity getEntity(String id) throws ReferencedSiteException {
        Cache cache = getCache();
        Entity entity = null;
        long start = System.currentTimeMillis();
        if (cache != null) {
            try {
                Representation rep = cache.getRepresentation(id);
                if(rep != null){
                   entity = new EntityImpl(getId(), rep, null);
                   initEntityMetadata(entity, true);
                } else if(siteConfiguration.getCacheStrategy() == CacheStrategy.all){
                    return null; //do no remote lokkups on CacheStrategy.all!!
                }
            } catch (YardException e) {
                if (siteConfiguration.getEntityDereferencerType() == null || isOfflineMode()) {
                    throw new ReferencedSiteException(String.format("Unable to get Represetnation %s form Cache %s",
                        id, siteConfiguration.getCacheId()), e);
                } else {
                    log.warn(String.format("Unable to get Represetnation %s form Cache %s. Will dereference from remote site %s",
                            id, siteConfiguration.getCacheId(), siteConfiguration.getAccessUri()), e);
                }
            }
        } else {
            if (siteConfiguration.getEntityDereferencerType() == null || isOfflineMode()) {
                throw new ReferencedSiteException(String.format("Unable to get Represetnation %s because configured Cache %s is currently not available",
                        id, siteConfiguration.getCacheId()));
            } else {
                log.warn(String.format("Cache %s is currently not available. Will use remote site %s to load Representation %s",
                        siteConfiguration.getCacheId(), siteConfiguration.getEntityDereferencerType(), id));
            }
        }
        if (entity == null) { // no cache or not found in cache
            if(dereferencer == null){
                throw new ReferencedSiteException(String.format("Entity Dereferencer %s for accessing remote site %s is not available",
                    siteConfiguration.getEntityDereferencerType(),siteConfiguration.getAccessUri()));
            }
            ensureOnline(siteConfiguration.getAccessUri(), dereferencer.getClass());
            Representation rep = null;
            try {
                rep = dereferencer.dereference(id);
            } catch (IOException e) {
                throw new ReferencedSiteException(
                    String.format("Unable to load Representation for entity %s form remote site %s with dereferencer %s",
                        id, siteConfiguration.getAccessUri(), siteConfiguration.getEntityDereferencerType()), e);
            }
            //representation loaded from remote site and cache is available
            if (rep != null){
                Boolean cachedVersion = Boolean.FALSE;
                if(cache != null) {// -> cache the representation
                    try {
                        start = System.currentTimeMillis();
                        //return the the cached version
                        rep = cache.store(rep);
                        cachedVersion = Boolean.TRUE;
                        log.debug("  - cached Representation {} in {} ms",    id, (System.currentTimeMillis() - start));
                    } catch (YardException e) {
                        log.warn(String.format("Unable to cache Represetnation %s in Cache %s! Representation not cached!",
                            id, siteConfiguration.getCacheId()), e);
                    }
                }
                entity = new EntityImpl(getId(), rep, null);
                initEntityMetadata(entity, cachedVersion);
            }
        } else {
            log.debug("  - loaded Representation {} from Cache in {} ms",
                    id, (System.currentTimeMillis() - start));
        }
        return entity;
    }
    /**
     * Initialises the {@link Entity#getMetadata()} with the properties
     * configured for this site (attribution and license information)
     * @param entity the entity
     * @param of this Entity is locally cached or not. If <code>null</code>
     * this information is not set in the metadata.
     */
    private void initEntityMetadata(Entity entity, Boolean isChached) {
        Representation metadata = entity.getMetadata();
        if(isChached != null){
            metadata.set(RdfResourceEnum.isChached.getUri(), isChached);
        }
        for(Entry<String,Object> entry : siteMetadata.entrySet()){
            metadata.add(entry.getKey(), entry.getValue());
        }
    }
    @Override
    public SiteConfiguration getConfiguration() {
        return siteConfiguration;
    }

    @Override
    public String toString() {
        return siteConfiguration!= null?siteConfiguration.getName():null;
    }
    @Override
    public int hashCode() {
        return siteConfiguration!=null?getId().hashCode():-1;
    }
    @Override
    public boolean equals(Object obj) {
        if(obj instanceof ReferencedSite) {
            SiteConfiguration osc = ((ReferencedSite)obj).getConfiguration();
            //this will return false if one of the two sites is not activated
            // -> this should be OK
            return siteConfiguration != null && osc != null &&
                getId().equals(osc.getId());
        } else {
            return false;
        }
    }
    @Override
    public FieldMapper getFieldMapper() {
        return fieldMappings;
    }

    /**
     * In case {@link CacheStrategy#all} this Method returns the
     * query factory of the Cache.
     * Otherwise it returns {@link DefaultQueryFactory#getInstance()}.
     */
    @Override
    public FieldQueryFactory getQueryFactory() {
        FieldQueryFactory factory = null;
        if(siteConfiguration.getCacheStrategy() == CacheStrategy.all){
            Cache cache = getCache();
            if(cache != null){
                factory = cache.getQueryFactory();
            }
        }
        if(factory == null){
            factory = DefaultQueryFactory.getInstance();
        }
        return factory;
    }
    public boolean supportsLocalMode(){
        return siteConfiguration.getCacheStrategy() == CacheStrategy.all &&
            getCache() != null;
    }
    public boolean supportsSearch(){
        return supportsLocalMode() ||
            entitySearcher != null;
    }
    /**
     * Internally used to get the Cache for this site. If
     * {@link CacheStrategy#none}, this methods always returns <code>null</code>,
     * otherwise it returns the Cache for the configured Yard or <code>null</code>
     * if no such Cache is available.
     * @return the cache or <code>null</code> if {@link CacheStrategy#none} or
     * the configured cache instance is not available.
     */
    protected Cache getCache(){
        if(siteConfiguration.getCacheStrategy() == CacheStrategy.none){
            return null;
        } else {
            Cache cache = (Cache)cacheTracker.getService();
            if(cache != null && cache.isAvailable()){
                return cache;
            } else {
                return null;
            }
        }
    }

    /*--------------------------------------------------------------------------
     *  OSGI LIFECYCLE and LISTENER METHODS
     *--------------------------------------------------------------------------
     */

    @SuppressWarnings("unchecked")
    @Activate
    protected void activate(final ComponentContext context) throws ConfigurationException, YardException, InvalidSyntaxException {
        log.debug("in {} activate with properties {}",
            ReferencedSiteImpl.class.getSimpleName(),context.getProperties());
        if(context == null || context.getProperties() == null){
            throw new IllegalStateException("No Component Context and/or Dictionary properties object parsed to the acticate methode");
        }
        this.context = context;
        //create the SiteConfiguration based on the parsed properties
        Map<String,Object> config = new HashMap<String,Object>();
        Dictionary<String,Object> properties = (Dictionary<String,Object>)context.getProperties();
        //copy the properties to a map
        for(Enumeration<String> e = properties.keys();e.hasMoreElements();){
            String key = e.nextElement();
            config.put(key, properties.get(key));
        }
        //NOTE that the constructor also validation of the parsed configuration
        siteConfiguration = new DefaultSiteConfiguration(config);
        if(PROHIBITED_SITE_IDS.contains(siteConfiguration.getId().toLowerCase())){
            throw new ConfigurationException(SiteConfiguration.ID, String.format(
                "The ID '%s' of this Referenced Site is one of the following " +
                "prohibited IDs: {} (case insensitive)",siteConfiguration.getId(),
                PROHIBITED_SITE_IDS));
        }
        log.info(" > initialise Referenced Site {}",siteConfiguration.getName());
        siteMetadata = new HashMap<String,Object>();
        ValueFactory vf = InMemoryValueFactory.getInstance();
        if(siteConfiguration.getAttribution() != null){
            siteMetadata.put(NamespaceEnum.cc.getNamespace()+"attributionName",
                vf.createText(siteConfiguration.getAttribution()));
        }
        if(siteConfiguration.getAttributionUrl() != null){
            siteMetadata.put(NamespaceEnum.cc.getNamespace()+"attributionURL",
                vf.createReference(siteConfiguration.getAttributionUrl()));
        }
        //add the licenses
        if(siteConfiguration.getLicenses() != null){
            for(License license : siteConfiguration.getLicenses()){
                if(license.getUrl() != null){
                    siteMetadata.put(NamespaceEnum.cc.getNamespace()+"license",
                        vf.createReference(license.getUrl()));
                } else if(license.getText() != null){
                    siteMetadata.put(NamespaceEnum.cc.getNamespace()+"license",
                        vf.createText(license.getText()));
                }
                //if defined add the name to dc:license
                if(license.getName() != null){
                    siteMetadata.put(NamespaceEnum.dcTerms.getNamespace()+"license",
                        vf.createText(license.getName()));
                }
                //link to the license via cc:license
            }
        }
       
        //if the accessUri is the same as the queryUri and both the dereferencer and
        //the entitySearcher uses the same component, than we need only one component
        //for both dependencies.
        this.dereferencerEqualsEntitySearcherComponent =
            //(1) accessURI == queryURI
            siteConfiguration.getAccessUri() != null &&
            siteConfiguration.getAccessUri().equals(siteConfiguration.getQueryUri()) &&
            //(2) entity dereferencer == entity searcher
            siteConfiguration.getEntityDereferencerType()!= null &&
            siteConfiguration.getEntityDereferencerType().equals(siteConfiguration.getEntitySearcherType());

        //init the fieldMapper based on the configuration
        fieldMappings = new DefaultFieldMapperImpl(ValueConverterFactory.getDefaultInstance());
        if(siteConfiguration.getFieldMappings() != null){
            log.debug(" > Initialise configured field mappings");
            for(String configuredMapping : siteConfiguration.getFieldMappings()){
                FieldMapping mapping = FieldMappingUtils.parseFieldMapping(configuredMapping);
                if(mapping != null){
                    log.debug("   - add FieldMapping {}",mapping);
                    fieldMappings.addMapping(mapping);
                }
            }
        }
        //now init the referenced Services
        initDereferencerAndEntitySearcher();

        // If a cache is configured init the ServiceTracker used to manage the
        // Reference to the cache!
        if(siteConfiguration.getCacheId() != null){
            String cacheFilter = String.format("(&(%s=%s)(%s=%s))",
                    Constants.OBJECTCLASS,Cache.class.getName(),
                    Cache.CACHE_YARD,siteConfiguration.getCacheId());
            cacheTracker = new ServiceTracker(context.getBundleContext(),
                    context.getBundleContext().createFilter(cacheFilter), null);
            cacheTracker.open();
        }
    }

    /**
     * Initialise the dereferencer and searcher component as soon as the according
     * {@link ComponentFactory} gets registered.<p>
     * First this Methods tries to find the according {@link ServiceReference}s
     * directly. If they are not available (e.g. because the component factories
     * are not yet started) than it adds a {@link ServiceListener} for the missing
     * {@link ComponentFactory} that calls the {@link #createDereferencerComponent(ComponentFactory)}
     * and {@link #createEntitySearcherComponent(ComponentFactory)} as soon as
     * the factory gets registered.
     * @throws InvalidSyntaxException if the #entitySearcherComponentName or the
     * {@link #dereferencerComponentName} somehow cause an invalid formated string
     * that can not be used to parse a {@link Filter}.
     */
    private void initDereferencerAndEntitySearcher() throws InvalidSyntaxException {
        if(siteConfiguration.getAccessUri() != null && //initialise only if a accessUri
                !siteConfiguration.getAccessUri().isEmpty() && // is configured
                siteConfiguration.getEntitySearcherType() != null) {
            String componentNameFilterString = String.format("(%s=%s)",
                    "component.name",siteConfiguration.getEntitySearcherType());
            String filterString = String.format("(&(%s=%s)%s)",
                    Constants.OBJECTCLASS,ComponentFactory.class.getName(),
                    componentNameFilterString);
            ServiceReference[] refs = context.getBundleContext().getServiceReferences(ComponentFactory.class.getName(),componentNameFilterString);
            if(refs != null && refs.length>0){
                createEntitySearcherComponent((ComponentFactory)context.getBundleContext().getService(refs[0]));
            } else { //service factory not yet available -> add servicelistener
                this.searcherComponentFactoryListener = new ComponentFactoryListener(context.getBundleContext());
                context.getBundleContext().addServiceListener(this.searcherComponentFactoryListener,filterString); //NOTE: here the filter MUST include also the objectClass!
            }
            //context.getComponentInstance().dispose();
            //throw an exception to avoid an successful activation
        }
        if(siteConfiguration.getQueryUri() != null && //initialise only if a query URI
                !siteConfiguration.getQueryUri().isEmpty() && // is configured
                siteConfiguration.getEntityDereferencerType() != null &&
                !this.dereferencerEqualsEntitySearcherComponent){
            String componentNameFilterString = String.format("(%s=%s)",
                    "component.name",siteConfiguration.getEntityDereferencerType());
            String filterString = String.format("(&(%s=%s)%s)",
                    Constants.OBJECTCLASS,ComponentFactory.class.getName(),
                    componentNameFilterString);
            ServiceReference[] refs = context.getBundleContext().getServiceReferences(ComponentFactory.class.getName(),componentNameFilterString);
            if(refs != null && refs.length>0){
                createDereferencerComponent((ComponentFactory)context.getBundleContext().getService(refs[0]));
            } else { //service factory not yet available -> add servicelistener
                this.dereferencerComponentFactoryListener = new ComponentFactoryListener(context.getBundleContext());
                this.context.getBundleContext().addServiceListener(this.dereferencerComponentFactoryListener,filterString); //NOTE: here the filter MUST include also the objectClass!
            }
        }
    }
    /**
     * Creates the entity searcher component used by this {@link ReferencedSite}
     * (and configured via the {@link SiteConfiguration#ENTITY_SEARCHER_TYPE} property).<p>
     * If the {@link SiteConfiguration#ENTITY_DEREFERENCER_TYPE} is set to the same vale
     * and the {@link #accessUri} also equals the {@link #queryUri}, than the
     * component created for the entity searcher is also used as dereferencer.
     * @param factory The component factory used to create the
     * {@link #entitySearcherComponentInstance}
     */
    @SuppressWarnings("unchecked")
    protected void createEntitySearcherComponent(ComponentFactory factory){
        //both create*** methods sync on the searcherAndDereferencerLock to avoid
        //multiple component instances because of concurrent calls
        synchronized (this.searcherAndDereferencerLock ) {
            if(entitySearcherComponentInstance == null){
                this.entitySearcherComponentInstance = factory.newInstance(OsgiUtils.copyConfig(context.getProperties()));
                this.entitySearcher = (EntitySearcher)entitySearcherComponentInstance.getInstance();
            }
            if(dereferencerEqualsEntitySearcherComponent){
                this.dereferencer = (EntityDereferencer) entitySearcher;
            }
        }
    }
    /**
     * Creates the entity dereferencer component used by this {@link ReferencedSite}.
     * The implementation used as the dereferencer is configured by the
     * {@link SiteConfiguration#ENTITY_DEREFERENCER_TYPE} property.
     * @param factory the component factory used to create the {@link #dereferencer}
     */
    @SuppressWarnings("unchecked")
    protected void createDereferencerComponent(ComponentFactory factory){
        //both create*** methods sync on searcherAndDereferencerLock to avoid
        //multiple component instances because of concurrent calls
        synchronized (this.searcherAndDereferencerLock) {
            if(dereferencerComponentInstance == null){
                dereferencerComponentInstance=factory.newInstance(OsgiUtils.copyConfig(context.getProperties()));
                this.dereferencer = (EntityDereferencer)dereferencerComponentInstance.getInstance();
            }
        }
    }

    /**
     * Simple {@link ServiceListener} implementation that is used to get notified
     * if one of the {@link ComponentFactory component factories} for the
     * configured implementation of the {@link EntityDereferencer} or
     * {@link EntitySearcher} interfaces get registered.
     * @author Rupert Westenthaler
     *
     */
    private class ComponentFactoryListener implements ServiceListener {
        private BundleContext bundleContext;
        protected ComponentFactoryListener(BundleContext bundleContext){
            if(bundleContext == null){
                throw new IllegalArgumentException("The BundleContext MUST NOT be NULL!");
            }
            this.bundleContext = bundleContext;
        }
        @Override
        public void serviceChanged(ServiceEvent event) {
            Object eventComponentName = event.getServiceReference().getProperty("component.name");
            if(event.getType() == ServiceEvent.REGISTERED){
                log.info("Process ServiceEvent for ComponentFactory {} and State REGISTERED",
                        eventComponentName);
                ComponentFactory factory = (ComponentFactory)bundleContext.getService(event.getServiceReference());
                if(siteConfiguration.getEntityDereferencerType() != null &&
                        siteConfiguration.getEntityDereferencerType().equals(eventComponentName)){
                    createDereferencerComponent(factory);
                }
                if(siteConfiguration.getEntitySearcherType()!= null &&
                        siteConfiguration.getEntitySearcherType().equals(eventComponentName)){
                    createEntitySearcherComponent(factory);
                }
            } else {
                log.info("Ignore ServiceEvent for ComponentFactory {} and state {}",
                        eventComponentName,
                        event.getType()==ServiceEvent.MODIFIED?"MODIFIED":event.getType()==ServiceEvent.UNREGISTERING?"UNREGISTERING":"MODIFIED_ENDMATCH");
            }
        }

    }

    @Deactivate
    protected void deactivate(ComponentContext context) {
        log.info("deactivate Referenced Site {}",siteConfiguration.getName());
        this.dereferencer = null;
        if(this.dereferencerComponentInstance != null){
            this.dereferencerComponentInstance.dispose();
            this.dereferencerComponentInstance = null;
        }
        this.entitySearcher = null;
        if(this.entitySearcherComponentInstance != null){
            this.entitySearcherComponentInstance.dispose();
            this.entitySearcherComponentInstance = null;
        }
        if(searcherComponentFactoryListener != null){
            context.getBundleContext().removeServiceListener(searcherComponentFactoryListener);
            searcherComponentFactoryListener = null;
        }
        if(dereferencerComponentFactoryListener != null){
            context.getBundleContext().removeServiceListener(dereferencerComponentFactoryListener);
            dereferencerComponentFactoryListener = null;
        }
        if(cacheTracker != null){
            cacheTracker.close();
            cacheTracker = null;
        }
        this.fieldMappings = null;
        this.context = null;
        this.siteConfiguration = null;
    }
   
    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     * Method for handling the OfflineMode
     * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
     */
   
    /**
     * Called by the ConfigurationAdmin to bind the {@link #offlineMode} if the
     * service becomes available
     * @param mode
     */
    protected final void enableOfflineMode(OfflineMode mode){
        this.offlineMode = mode;
    }
    /**
     * Called by the ConfigurationAdmin to unbind the {@link #offlineMode} if the
     * service becomes unavailable
     * @param mode
     */
    protected final void disableOfflineMode(OfflineMode mode){
        this.offlineMode = null;
    }
    /**
     * Returns <code>true</code> only if Stanbol operates in {@link OfflineMode}.
     * @return the offline state
     */
    protected final boolean isOfflineMode(){
        return offlineMode != null;
    }
    /**
     * Basically this Method throws an {@link ReferencedSiteException} in case
     * Stanbol operates in offline mode
     * @param uri the URI of the remote service
     * @param clazz the clazz of the service that would like to refer the remote
     * service
     * @throws ReferencedSiteException in case {@link #isOfflineMode()} returns
     * <code>true</code>
     */
    private void ensureOnline(String uri, Class<?> clazz) throws ReferencedSiteException {
        if(isOfflineMode()){
            throw new ReferencedSiteException(String.format(
                "Unable to access remote Service %s by using %s because Stanbol runs in OfflineMode",
                uri,clazz.getSimpleName()));
        }
    }
}
TOP

Related Classes of org.apache.stanbol.entityhub.core.impl.ReferencedSiteImpl$ComponentFactoryListener

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.