/*
* 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.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
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.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.apache.felix.scr.annotations.Service;
import org.apache.stanbol.commons.namespaceprefix.NamespacePrefixService;
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.query.DefaultQueryFactory;
import org.apache.stanbol.entityhub.core.query.QueryResultListImpl;
import org.apache.stanbol.entityhub.servicesapi.Entityhub;
import org.apache.stanbol.entityhub.servicesapi.EntityhubConfiguration;
import org.apache.stanbol.entityhub.servicesapi.EntityhubException;
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.ManagedEntityState;
import org.apache.stanbol.entityhub.servicesapi.model.MappingState;
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.query.ReferenceConstraint;
import org.apache.stanbol.entityhub.servicesapi.site.ManagedSiteConfiguration;
import org.apache.stanbol.entityhub.servicesapi.site.Site;
import org.apache.stanbol.entityhub.servicesapi.site.ReferencedSiteConfiguration;
import org.apache.stanbol.entityhub.servicesapi.site.SiteManager;
import org.apache.stanbol.entityhub.servicesapi.site.SiteConfiguration;
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.framework.Constants;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.component.ComponentContext;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default implementation of the Entityhub.
* @author Rupert Westenthaler
*
*/
@Component(immediate=true)
@Service
public final class EntityhubImpl implements Entityhub {//, ServiceListener {
private final Logger log = LoggerFactory.getLogger(EntityhubImpl.class);
/**
* The OSGI component context of the Entityhub
*/
@SuppressWarnings("unused")
private ComponentContext context;
/**
* The field mapper holding global mappings that are used for mapping
* representations of entities for any referenced sites
*/
private FieldMapper fieldMapper;
/**
* Tracks the availability of the Yard used by the Entityhub.
*/
private ServiceTracker entityhubYardTracker; //reference initialised in the activate method
/**
* The Configuration of the Entityhub
*/
@Reference // 1..1, static
private EntityhubConfiguration config;
/**
* The site manager is used to search for entities within the Entityhub framework
*/
@Reference // 1..1, static
private SiteManager siteManager;
@Reference(cardinality=ReferenceCardinality.OPTIONAL_UNARY)
private NamespacePrefixService nsPrefixService;
private static final String DEFAULT_MANAGED_ENTITY_PREFIX = "entity";
private static final String DEFAULT_MAPPING_PREFIX = "mapping";
/**
* Activates the Entityhub (OSGI Lifecycle method)
* @param context the OSGI component context (stored in {@link #context})
* @throws ConfigurationException On any error during the activation of
* the Entityhub
*/
@Activate
protected void activate(final ComponentContext context) throws ConfigurationException {
if(context == null){
throw new IllegalStateException("Unable to activate if parsed ComponentContext is NULL");
}
log.info("activating Entityhub with configuration "+context.getProperties());
this.context = context;
//First check the entityhub ID and
log.info(" ... init Basic Properties");
if(config.getID() == null || config.getID().isEmpty()){
throw new ConfigurationException(EntityhubConfiguration.ID, "The Entityhub Configuration does not define a ID for the Entityhub");
} else {
log.info(" + id: "+config.getID());
}
if(config.getName() == null || config.getName().isEmpty()){
throw new ConfigurationException(EntityhubConfiguration.NAME, "The Entityhub Configuration does not define a name for the Entityhub");
} else {
log.info(" + id: "+config.getName());
}
if(config.getDescription() != null){
log.info(" + id: "+config.getDescription());
}
if(config.getEntityhubPrefix() == null){
throw new ConfigurationException(EntityhubConfiguration.PREFIX, "The Entityhub Configuration does not define a Prefix for the Entityhub");
}
try {
new URI(config.getEntityhubPrefix());
log.info(" + prefix: "+config.getEntityhubPrefix());
} catch (URISyntaxException e1) {
throw new ConfigurationException(EntityhubConfiguration.PREFIX, "The Prefix configured for the Entityhub is not an valied URI (prefix="+config.getEntityhubPrefix()+")");
}
//next get the reference to the configured EntityhubYard
if(config.getEntityhubPrefix() == null){
throw new ConfigurationException(EntityhubConfiguration.ENTITYHUB_YARD_ID, "The ID of the Yard used by the Entityhub MUST NOT be NULL");
}
if(config.getEntityhubYardId().isEmpty()){
throw new ConfigurationException(EntityhubConfiguration.ENTITYHUB_YARD_ID, "The ID of the Yard used by the Entityhub MUST NOT be empty");
}
String entityhubYardFilterString = String.format("(&(%s=%s)(%s=%s))",
Constants.OBJECTCLASS,Yard.class.getName(),
Yard.ID,config.getEntityhubYardId());
log.info(" ... tracking EntityhubYard by Filter:"+entityhubYardFilterString);
try {
entityhubYardTracker = new ServiceTracker(context.getBundleContext(),
context.getBundleContext().createFilter(entityhubYardFilterString), null);
} catch (InvalidSyntaxException e) {
throw new IllegalStateException("Got Invalid Syntax Exception for Entityhub filter ",e);
}
entityhubYardTracker.open(); //start the tracking
//at last get the FieldMappingConfig and create the FieldMappings instance
// -> we need to do that after the init of the Entityhub-yard, because than we
// can use the valueFactory of the configured Yard to create instances
// of converted values!
log.info(" ... init FieldMappings");
fieldMapper = new DefaultFieldMapperImpl(ValueConverterFactory.getDefaultInstance());
for(String mappingString : config.getFieldMappingConfig()){
FieldMapping mapping = FieldMappingUtils.parseFieldMapping(mappingString,nsPrefixService);
if(mapping != null){
log.info(" + mapping: "+mapping);
fieldMapper.addMapping(mapping);
}
}
}
@Deactivate
protected void deactivate(ComponentContext context) {
log.info("deactivate "+EntityhubImpl.class);
this.fieldMapper = null;
if(entityhubYardTracker != null){
this.entityhubYardTracker.close();
this.entityhubYardTracker = null;
}
this.context = null;
}
// @Override
// public EntityhubConfiguration getEntityhubConfiguration() {
// return config;
// }
@Override
public Yard getYard() {
return (Yard)entityhubYardTracker.getService();
}
/**
* Internally used to lookup the yard. This throws an {@link YardException}
* in case the yard is currently not available
* @return the yard
* @throws YardException in case the yard is not active
*/
private Yard lookupYard() throws YardException {
Yard yard = getYard();
if(yard == null){
throw new YardException("The Entityhub Yard (ID="+config.getEntityhubYardId()+") is not active! Please check the configuration!!");
}
return yard;
}
@Override
public final EntityhubConfiguration getConfig() {
return config;
}
@Override
public Entity lookupLocalEntity(String entity) throws YardException{
return lookupLocalEntity(entity,false);
}
@Override
public Entity lookupLocalEntity(String entityId, boolean create) throws YardException {
Entity entity = getEntity(entityId);
if(entity != null){
return entity;
} else {
//parsed reference was not a locally managed entity ->
//search for an mapped Entity
Entity entityMapping = getMappingBySource(entityId);
if(entityMapping != null){
entity = getEntity(EntityMapping.getTargetId(entityMapping));
if(entity != null){
return entity;
} else {
log.warn("Unable to find Entity for Entity Mapping "+entityMapping+"!");
return recoverEntity(EntityMapping.getTargetId(entityMapping));
}
} else if(create){
//search if the parsed reference is known by any referenced site
// and if YES, create a new Symbol
Entity remoteEntity = siteManager.getEntity(entityId);
if(remoteEntity == null){ //id not found by any referred site
return null;
} else {
return importEntity(remoteEntity);
}
} else {
return null;
}
}
}
@Override
public final Entity importEntity(String reference) throws IllegalStateException, IllegalArgumentException, YardException {
Entity entity = getEntity(reference);
if(entity == null){
Entity entityMapping = getMappingBySource(reference);
if(entityMapping == null){
Entity remoteEntity = siteManager.getEntity(reference);
if(remoteEntity == null){
return null;
} else {
return importEntity(remoteEntity);
}
} else {
throw new IllegalStateException(String.format(
"The reference %s is already imported to the Entityhub " +
"(local Entity %s)",reference,
EntityMapping.getTargetId(entityMapping)));
}
} else {
throw new IllegalStateException("The parsed id "+reference+" refers " +
"to an Entity managed by the Entityhub (entity="+entity+")!");
}
}
/**
* Recovers a missing locally managed entity based on the available
* {@link EntityMapping}s and the data available through referenced sites.
* @param entityId the id of the missing locally managed entity
* @return the recovered Entity or <code>null</code> if the recovering is
* not possible
*/
private Entity recoverEntity(String entityId) {
/*
* TODO: recover the Symbol by recreating it based on the available
* mapped Entities
* 1) search for all EntityMappings with this SymbolId
* 2) get all mapped Entities
* 3) apply the FieldMappings
* 4) store the Symbol
* 5) return the recovered Symbol
*/
return null;
}
/*
* @throws IllegalArgumentException if a {@link Representation} for the parsed ID is present in the
* {@link #entityhubYard}, but the representation can not be wrapped by an {@link Symbol}.
*/
@Override
public final Entity getEntity(String entityId) throws IllegalArgumentException,IllegalStateException, YardException {
if(entityId == null || entityId.isEmpty()){
throw new IllegalArgumentException("The parsed id MUST NOT be NULL nor empty!");
}
Yard entityhubYard = lookupYard();
Entity entity = loadEntity(entityhubYard, entityId);
if(entity == null){
return null;
} else if (ManagedEntity.canWrap(entity)){
return entity;
} else {
log.info("The parsed id does not represent a locally managed Entity {}", entity);
return null;
}
}
@Override
public final boolean isRepresentation(String entityId) throws EntityhubException, IllegalArgumentException {
if(entityId == null || entityId.isEmpty()){
throw new IllegalArgumentException("The parsed id MUST NOT be NULL nor empty!");
}
return lookupYard().isRepresentation(entityId);
}
@Override
public final Entity store(Representation representation) throws EntityhubException, IllegalArgumentException {
if(representation == null){
throw new IllegalArgumentException("The parsed Representation MUST NOT be NULL!");
}
Yard yard = lookupYard();
//parse only the id of the representation, because we need the current
//stored version of the entity!
Entity entity = loadEntity(yard, representation.getId());
//now we need to check if the parsed representation is the data or the
//metadata of the Entity
ManagedEntity updated;
if(entity == null || representation.getId().equals(entity.getRepresentation().getId())){
//update the data or create a new entity
updated = ManagedEntity.init(new EntityImpl(config.getID(), representation,
entity != null ? entity.getMetadata() : null),
config.getDefaultManagedEntityState());
if(entity == null){ //add creation date
updated.setCreated(new Date());
}
} else {
//update the metadata
entity = new EntityImpl(config.getID(), entity.getRepresentation(),
representation);
//we need to validate the metadata
updated = ManagedEntity.init(
entity, config.getDefaultManagedEntityState());
}
storeEntity(yard,updated.getWrappedEntity());
return updated.getWrappedEntity();
}
@Override
public final Entity delete(String id) throws EntityhubException, IllegalArgumentException {
if(id == null || id.isEmpty()){
throw new IllegalArgumentException("The parsed id MUST NOT be NULL nor emtpty!");
}
Yard yard = lookupYard();
Entity entity = loadEntity(yard, id);
if(entity != null){
log.debug("delete Entity {} as requested by the parsed id {}",entity.getId(),id);
//we need to remove all mappings for this Entity
deleteMappingsbyTarget(yard,entity.getId());
deleteEntity(yard,entity);
} else {
log.debug("Unable to delete Entity for id {}, because no Entity for this id is" +
"managed by the Entityhub",id);
}
return entity;
}
@Override
public final void deleteAll() throws EntityhubException {
Yard yard = lookupYard();
yard.removeAll();
}
@Override
public final Entity setState(String id, ManagedEntityState state) throws EntityhubException,
IllegalArgumentException {
if(id == null || id.isEmpty()){
throw new IllegalStateException("The parsed id MUST NOT be NULL nor empty!");
}
if(state == null){
throw new IllegalStateException("The parsed state for the Entity MUST NOT ne NULL");
}
Yard yard = lookupYard();
Entity entity = loadEntity(yard, id);
if(entity != null){
ManagedEntity managed = new ManagedEntity(entity);
if(managed.getState() != state){
managed.setState(state);
storeEntity(yard, entity);
}
}
return entity;
}
/**
* Deleted both the representation and the metadata of an Entity
* @param yard the yard to delete the entity from
* @param entity the entity to delete
* @throws YardException an any Exception while deleting the Entity
*/
private void deleteEntity(Yard yard,Entity entity) throws YardException {
if(entity != null){
yard.remove(Arrays.asList(
entity.getRepresentation().getId(),
entity.getMetadata().getId()));
}
}
private void deleteEntities(Yard yard, Collection<String> ids) throws YardException {
FieldQuery fieldQuery = getQueryFactory().createFieldQuery();
Collection<String> toDelete = new HashSet<String>(ids);
for(String id : ids){
if(id != null && !id.isEmpty()){
fieldQuery.setConstraint(RdfResourceEnum.aboutRepresentation.getUri(), new ReferenceConstraint(id));
for(Iterator<String> it = yard.findReferences(fieldQuery).iterator();it.hasNext();){
toDelete.add(it.next());
}
}
}
if(!toDelete.isEmpty()){
yard.remove(toDelete);
}
}
private void deleteMappingsbyTarget(Yard yard,String id) throws YardException {
if(id != null && !id.isEmpty()){
FieldQuery fieldQuery = getQueryFactory().createFieldQuery();
fieldQuery.setConstraint(RdfResourceEnum.mappingTarget.getUri(), new ReferenceConstraint(id));
deleteEntities(yard, ModelUtils.asCollection(
yard.findReferences(fieldQuery).iterator()));
}
}
/**
* Imports the Entity
* @param remoteEntity the Entity to import
* @return the Entity created and stored within the Entityhub
* @throws YardException
*/
protected Entity importEntity(Entity remoteEntity) throws YardException{
if(remoteEntity == null){
return null;
}
Site site = siteManager.getSite(remoteEntity.getSite());
if(site == null){
log.warn("Unable to import Entity {} because the ReferencedSite {} is currently not active -> return null",
remoteEntity.getId(),remoteEntity.getSite());
return null;
}
Yard entityhubYard = lookupYard();
ValueFactory valueFactory = entityhubYard.getValueFactory();
//Create the locally managed Entity
Representation localRep = entityhubYard.create(constructResourceId(DEFAULT_MANAGED_ENTITY_PREFIX));
Entity localEntity = loadEntity(entityhubYard, localRep);
importEntity(remoteEntity, site, localEntity, valueFactory);
//Second create and init the Mapping
Representation entityMappingRepresentation = entityhubYard.create(
constructResourceId(DEFAULT_MAPPING_PREFIX));
Entity entityMappingEntity = loadEntity(entityhubYard, entityMappingRepresentation);
establishMapping(localEntity, remoteEntity, site, entityMappingEntity);
//Store the entity and the mappedEntity in the entityhubYard
storeEntity(entityhubYard, localEntity);
storeEntity(entityhubYard,entityMappingEntity);
return localEntity;
}
/**
* Imports a {@link Entity} from a {@link Site}. This Method imports
* the {@link Representation} by applying all configured mappings. It also
* sets the {@link ManagedEntityState} to the configured default value by the
* referenced site of the imported entity or the default for the Entityhub
* if the site does not define this configuration.<p>
* @param remoteEntity The entity to import
* @param site the referenced site of the entity to import
* @param localEntity the target entity for the import
* @param valueFactory the valusFactory used to create instance while importing
*/
private void importEntity(Entity remoteEntity,
Site site,
Entity localEntity,
ValueFactory valueFactory) {
SiteConfiguration siteConfig = site.getConfiguration();
ManagedEntityState state;
state = siteConfig.getDefaultManagedEntityState();
if(state == null){
state = config.getDefaultManagedEntityState();
}
//this wrapper allows to use an API to write metadata
ManagedEntity managedEntity = ManagedEntity.init(localEntity, state);
FieldMapper siteMapper = site.getFieldMapper();
FieldMapper mapper = this.fieldMapper.clone();
for(FieldMapping siteMapping : siteMapper.getMappings()){
mapper.addMapping(siteMapping);
}
//TODO: As soon as MappingActivities are implemented we need to add such
// information to the EntityMapping instance!
mapper.applyMappings(remoteEntity.getRepresentation(),
localEntity.getRepresentation(),valueFactory);
//set general metadata
managedEntity.setCreated(new Date());
//set the metadata required by the referenced site
managedEntity.addAttributionLink(site.getConfiguration().getAttributionUrl());
managedEntity.addAttributionText(site.getConfiguration().getAttribution(), null);
//TODO: maybe replace with the URL of the site
managedEntity.addContributorName(site.getConfiguration().getName());
}
/**
* Creates a new {@link EntityMapping} for the parsed source and target
* {@link Entity}. This also sets the State and the expire date based on the
* configurations of the ReferencesSite of the source entity and the defaults of the
* Entityhub
* @param localEntity the locally managed entity (target for the mapping)
* @param remoteEntity the remote Entity (source for the mapping)
* @param site the referenced site managing the source Entity
* @param entityMappingRepresentation the Entity for the mapping
* @return the EntityMapping
*/
private EntityMapping establishMapping(Entity localEntity,
Entity remoteEntity,
Site site,
Entity entityMappingEntity) {
EntityMapping entityMapping = EntityMapping.init(entityMappingEntity);
//now init the mappingState and the expireDate based on the config of the
//ReferencedSite of the source entity (considering also the defaults of the entityhub)
SiteConfiguration siteConfig = site.getConfiguration();
MappingState mappingState = siteConfig.getDefaultMappedEntityState();
if(mappingState == null){
mappingState = config.getDefaultMappingState();
}
entityMapping.setState(mappingState);
long expireDuration = siteConfig.getDefaultExpireDuration();
if(expireDuration > 0){
Date expireDate = new Date(System.currentTimeMillis()+expireDuration);
entityMapping.setExpires(expireDate);
}
entityMapping.setSourceId(remoteEntity.getId());
entityMapping.setTargetId(localEntity.getId());
//initialise additional metadata
entityMapping.setCreated(new Date());
return entityMapping;
}
/**
* Uses the Prefix as configured by the {@link #config} and the parsed
* prefix for the type to create an unique ID for a resource.
* @param typePrefix the prefix of the type
* @return An id in the form <code> {@link EntityhubConfiguration#getEntityhubPrefix()}
* + typePrefix + '.' + {@link ModelUtils#randomUUID()}</code>. Note that between
* the entity hub prefix and the type prefix a separation chars are added
* if it is not already defined by the {@link EntityhubConfiguration#getEntityhubPrefix()}
* value.
*/
private String constructResourceId(String typePrefix) {
StringBuilder id = new StringBuilder();
String prefix = config.getEntityhubPrefix();
if(prefix == null || prefix.isEmpty()){
prefix = Entityhub.DEFAUTL_ENTITYHUB_PREFIX;
}
id.append(prefix);
switch(prefix.charAt(prefix.length()-1)){
case '#':
case ':':
break;
default: //add a separator
if(prefix.startsWith("urn:")){
id.append(':'); //use a point for now
} else {
id.append('/'); //use '/' instead of '#' because one needs not to escape it in GET requests
}
}
if(typePrefix != null && !typePrefix.isEmpty()){
id.append(typePrefix);
id.append('.');
}
id.append(ModelUtils.randomUUID());
return id.toString();
}
@Override
public Entity getMappingBySource(String reference) throws YardException{
if(reference == null){
log.warn("NULL parsed as Reference -> call to getMappingByEntity ignored (return null)");
return null;
}
FieldQuery fieldQuery = getQueryFactory().createFieldQuery();
fieldQuery.setConstraint(RdfResourceEnum.mappingSource.getUri(), new ReferenceConstraint(reference));
Yard entityhubYard = lookupYard();
QueryResultList<Representation> resultList = entityhubYard.findRepresentation(fieldQuery);
if(!resultList.isEmpty()){
Iterator<Representation> resultIterator = resultList.iterator();
Entity mapping = loadEntity(entityhubYard, resultIterator.next());
//print warnings in case of multiple mappings
if(resultIterator.hasNext()){
log.warn("Multiple Mappings found for Entity {}!",reference);
log.warn(" > {} -> returned instance",mapping.getId());
while(resultIterator.hasNext()){
log.warn(" > {} -> ignored",resultIterator.next());
}
}
if(!EntityMapping.isValid(mapping)){
log.warn("Entity {} is not a valid EntityMapping. -> return null",mapping);
mapping = null;
}
return mapping;
} else {
log.debug("No Mapping found for Entity {}",reference);
return null;
}
}
@Override
public Collection<Entity> getMappingsByTarget(String targetId) throws YardException{
if(targetId == null){
log.warn("NULL parsed as Reference -> call to getMappingsBySymbol ignored (return null)");
return null;
}
FieldQuery fieldQuery = getQueryFactory().createFieldQuery();
fieldQuery.setConstraint(RdfResourceEnum.mappingTarget.getUri(), new ReferenceConstraint(targetId));
Yard enttiyhubYard = lookupYard();
QueryResultList<Representation> resultList = enttiyhubYard.findRepresentation(fieldQuery);
Collection<Entity> mappings = new HashSet<Entity>();
for(Representation rep : resultList){
mappings.add(loadEntity(enttiyhubYard, rep));
}
return mappings;
}
/**
* Loads an Entity (Representation and Metadata) for the parsed id. In case
* the parsed id represents metadata, than the id of the returned Entity will
* be different from the parsed id.
* @param entityhubYard the yard used by the Entityhub
* @param id the id of the data or the metadata of the Entity to load
* @return the Entity or <code>null</code> if not found
* @throws YardException On any error with the parsed Yard.
*/
private Entity loadEntity(Yard entityhubYard,String id) throws YardException {
return id == null || id.isEmpty() ? null :
loadEntity(entityhubYard,entityhubYard.getRepresentation(id));
}
/**
* Loads the Entity based on the parsed representation. The parsed
* {@link Representation} can be both the data and the metadata. In case the
* parsed representation are metadat the id of the returned Entity will be
* not the same as the id of the parsed {@link Representation}.
* @param entityhubYard the yard used by the Entityhub
* @param rep the representation or metadata of an entity
* @return the created Entity including both data and metadata or
* <code>null</code> if the parsed Representation does not represent a
* Representation managed by the Entityhub (this may be the case if an other
* thread has deleted that Entity in the meantime)
* @throws YardException On any error with the parsed Yard.
*/
private Entity loadEntity(Yard entityhubYard, Representation rep) throws YardException {
if(rep != null){
Representation data;
Representation metadata = null;
String entityId = ModelUtils.getAboutRepresentation(rep);
if(entityId != null){
data = entityhubYard.getRepresentation(entityId);
metadata = rep;
} else {
data = rep;
entityId = rep.getId(); //needed for logs
}
if(data != null){
metadata = lookupMetadata(entityhubYard, rep.getId(),true);
return new EntityImpl(config.getID(), data,metadata);
} else {
log.warn("Unable find representation for Entity {} (metadata: {}",
entityId,metadata);
return null;
}
} else {
return null;
}
}
/**
* Lookups (or initialises) the metadata for the entity with the parsed id
* @param entityhubYard the yard to search the metadata
* @param entityId The id of the entity
* @param init if the metadata should be initialised of not existing
* @return the metadata for that Entity or <code>null</code> if not existing
* and <code>init == false</code>
* @throws YardException
*/
private Representation lookupMetadata(Yard entityhubYard, String entityId, boolean init) throws YardException {
Representation metadata;
//TODO: check the asumption that the Metadata always use the
// extension ".meta"
String metaID = entityId+".meta";
metadata = entityhubYard.getRepresentation(metaID);
if(metadata == null){
metadata = entityhubYard.create(metaID);
}
return metadata;
}
/**
* Stores both the Representation and the Metadata of the parsed Entity to the
* parsed yard.<p>
* This Method also updated the modification date of the Metadata.
* @param entityhubYard the Yard used to store the Entity
* @param entity the stored entity
* @throws YardException
*/
private void storeEntity(Yard entityhubYard, Entity entity) throws YardException{
if(entity != null){
entityhubYard.store(entity.getRepresentation());
entity.getMetadata().set(NamespaceEnum.dcTerms+"modified", new Date());
entityhubYard.store(entity.getMetadata());
}
}
@Override
public final Entity getMappingById(String id) throws IllegalArgumentException, EntityhubException{
if(id == null || id.isEmpty()){
throw new IllegalArgumentException("The parsed id MUST NOT be NULL nor empty");
}
Yard entityhubYard = lookupYard();
Entity mapping = loadEntity(entityhubYard, id);
if(mapping == null){
return null;
} else if(mapping != null && EntityMapping.isValid(mapping)){
return mapping;
} else {
log.info("The Entity {} for the parsed id does not represent an EntityMapping -> return null!",
mapping);
return null;
}
}
@Override
public final FieldQueryFactory getQueryFactory() {
Yard entityhubYard = getYard();
return entityhubYard==null? //if no yard available
DefaultQueryFactory.getInstance(): //use the default
entityhubYard.getQueryFactory(); //else return the query factory used by the yard
}
@Override
public final FieldMapper getFieldMappings() {
return fieldMapper;
}
@Override
public final QueryResultList<Representation> find(FieldQuery query) throws YardException{
return lookupYard().find(query);
}
@Override
public final QueryResultList<String> findEntityReferences(FieldQuery query) throws YardException{
return lookupYard().findReferences(query);
}
@Override
public final QueryResultList<Entity> findEntities(FieldQuery query) throws YardException{
QueryResultList<String> references = lookupYard().findReferences(query);
List<Entity> entities = new ArrayList<Entity>(references.size());
for(String reference : references){
Entity entity = lookupLocalEntity(reference);
if(entity != null){
entities.add(entity);
} else {
log.warn("Unable to create Entity for Reference {} in the Yard " +
"usd by the entity hub [id={}] -> ignore reference",
reference,config.getEntityhubYardId());
}
}
return new QueryResultListImpl<Entity>(references.getQuery(), entities, Entity.class);
}
}