CollectionRepositoryDefault.java
package org.linkedopenactors.rdfpub.adapter.driven;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Model;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.model.util.Values;
import org.eclipse.rdf4j.query.Binding;
import org.eclipse.rdf4j.query.BindingSet;
import org.eclipse.rdf4j.query.QueryResults;
import org.eclipse.rdf4j.query.TupleQuery;
import org.eclipse.rdf4j.query.TupleQueryResult;
import org.eclipse.rdf4j.repository.Repository;
import org.eclipse.rdf4j.repository.RepositoryConnection;
import org.linkedopenactors.rdfpub.domain.AS;
import org.linkedopenactors.rdfpub.domain.Activity;
import org.linkedopenactors.rdfpub.domain.ActivityBuilder;
import org.linkedopenactors.rdfpub.domain.Actor;
import org.linkedopenactors.rdfpub.domain.CollectionRepository;
import org.linkedopenactors.rdfpub.domain.DomainObjectBuilders;
import org.linkedopenactors.rdfpub.domain.OrderedCollectionPage;
import org.linkedopenactors.rdfpub.domain.OrderedCollectionPageWithActivities;
import org.linkedopenactors.rdfpub.domain.ResourceFactory;
import de.naturzukunft.rdf4j.utils.ModelLogger;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class CollectionRepositoryDefault implements CollectionRepository {
private final RdfTypeRepository rdfTypeRepository;
private final Repository repository;
private final ResourceFactory resourceFactory;
private Actor actor;
private final DomainObjectBuilders domainObjectBuilders;
public CollectionRepositoryDefault(Actor actor, Repository repository,
ResourceFactory resourceFactory, RdfTypeRepository rdfTypeRepository, DomainObjectBuilders domainObjectBuilders) {
this.actor = actor;
this.repository = repository;
this.resourceFactory = resourceFactory;
this.rdfTypeRepository = rdfTypeRepository;
this.domainObjectBuilders = domainObjectBuilders;
}
@Override
public OrderedCollectionPageWithActivities getInbox(Integer startIndex, Integer pageSize) {
return getCollection(Values.iri(actor.getInboxEndpoint().toString()), pageSize, startIndex);
}
@Override
public OrderedCollectionPageWithActivities getPublic(Integer startIndex, Integer pageSize) {
return getCollection(Values.iri(AS.Public().toString()), pageSize, startIndex);
}
@Override
public OrderedCollectionPageWithActivities getOutbox(Integer startIndex, Integer pageSize) {
return getCollection(Values.iri(actor.getOutboxEndpoint().toString()), pageSize, startIndex);
}
private OrderedCollectionPageWithActivities getCollection(IRI namedGraphOfCollectionToUse, Integer pageSize, Integer startIndex) {
List<Activity> activities = getActivities(namedGraphOfCollectionToUse, pageSize, startIndex);
String subjectOfOrderedCollection = String.format("%s?pageSize=%s&startIndex=%s", namedGraphOfCollectionToUse.stringValue(), pageSize, startIndex);
OrderedCollectionPage page = domainObjectBuilders.orderedCollectionPageBuilder()
.subject(resourceFactory.iri(subjectOfOrderedCollection))
.summary("collection: " +namedGraphOfCollectionToUse.stringValue() + "; pageSize: " + pageSize + "; startIndex: " + startIndex)
.partOf(resourceFactory.iri(namedGraphOfCollectionToUse.stringValue()))
.totalItems(activities.size())
.items( activities.stream().map(Activity::getSubject).collect(Collectors.toSet()) )
.build();
OrderedCollectionPageWithActivities pageWithActivities = new OrderedCollectionPageWithActivities(activities, page);
return pageWithActivities;
}
private List<Activity> getActivities(IRI namedGraphToUse, int pageSize, int startIndex) {
String select = ("PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n"
+ "SELECT DISTINCT ?entity ?published ?type ?actor ?name ?summary ?object\n");
if(namedGraphToUse!=null) {
select = select + String.format("FROM <%s> \n", namedGraphToUse.stringValue());
}
String typeDefFroms = "FROM " + rdfTypeRepository.getContextsOfAddedTypeDefinitions().stream()
.map(IRI::stringValue)
.map(iri->"<"+iri+">")
.collect(Collectors.joining("\nFROM "));
String queryString = String.format(select
+ typeDefFroms
+ "\nWHERE\n"
+ "{\n"
+ " ?entity rdf:type ?type.\n"
+ " OPTIONAL {?entity <https://www.w3.org/ns/activitystreams#published> ?published .}\n"
+ " OPTIONAL {?entity <https://www.w3.org/ns/activitystreams#actor> ?actor .}\n"
+ " OPTIONAL {?entity <https://www.w3.org/ns/activitystreams#name> ?name .}\n"
+ " OPTIONAL {?entity <https://www.w3.org/ns/activitystreams#summary> ?summary .}\n"
+ " OPTIONAL {?entity <https://www.w3.org/ns/activitystreams#object> ?object .}\n"
+ " ?type rdfs:subClassOf* <https://www.w3.org/ns/activitystreams#Activity> .\n"
+ "}\n"
+ "ORDER BY desc(?published)\n"
+ "LIMIT %s\n"
+ "OFFSET %s", pageSize, startIndex)
;
log.trace("queryString: \n" + queryString);
try (RepositoryConnection conn = repository.getConnection()) {
TupleQuery tupleQuery = conn.prepareTupleQuery(queryString);
TupleQueryResult result = tupleQuery.evaluate();
log.trace(namedGraphToUse + " -> result.hasNext(): " + result.hasNext());
Model pureStatements = QueryResults.asModel(conn.getStatements(null, null, null, namedGraphToUse));
ModelLogger.trace(log, pureStatements, "pureStatements of '"+namedGraphToUse+"'");
List<Activity> activities = new ArrayList<>();
while (result.hasNext()) {
BindingSet bindingSet = result.next();
log.trace("bindingSet: " + bindingSet);
String subject = bindingSet.getBinding("entity").getValue().stringValue();
org.linkedopenactors.rdfpub.domain.IRI type = toIRI(bindingSet, "type").orElseThrow(()->new IllegalStateException("type is mandatory!"));
ActivityBuilder activityBuilder = domainObjectBuilders.activityBuilder();
activityBuilder = activityBuilder.subject(getActivityType(type), resourceFactory.iri(subject));
toString(bindingSet, "published").ifPresent(activityBuilder::published);
toString(bindingSet, "name").ifPresent(activityBuilder::name);
toString(bindingSet, "summary").ifPresent(activityBuilder::summary);
toIRI(bindingSet, "actor").ifPresent(activityBuilder::actor);
toIRI(bindingSet, "object").ifPresent(activityBuilder::object);
activities.add(activityBuilder.build());
}
return activities;
}
}
private org.linkedopenactors.rdfpub.domain.ActivityType getActivityType(org.linkedopenactors.rdfpub.domain.IRI type) {
if(org.linkedopenactors.rdfpub.domain.AS.Create().equals(type)) {
return org.linkedopenactors.rdfpub.domain.ActivityType.CREATE;
} else if(org.linkedopenactors.rdfpub.domain.AS.Create().equals(type)) {
return org.linkedopenactors.rdfpub.domain.ActivityType.UPDATE;
} else {
throw new IllegalStateException(" unsupported activity type: " + type);
}
}
private Optional<org.linkedopenactors.rdfpub.domain.IRI> toIRI(BindingSet bindingSet, String bindingName) {
return toString(bindingSet, bindingName)
.map(resourceFactory::iri);
}
private Optional<String> toString(BindingSet bindingSet, String bindingName) {
return Optional.ofNullable(
bindingSet.getBinding(bindingName))
.map(Binding::getValue)
.map(Value::stringValue);
}
}