This is an automated email from the ASF dual-hosted git repository. hasan pushed a commit to branch reunited in repository https://gitbox.apache.org/repos/asf/clerezza.git
commit 50211e4260e2b30e4b36ea95be29f3bbbb1fc811 Author: Hasan <[email protected]> AuthorDate: Sun Feb 3 18:36:35 2019 +0100 CLEREZZA-1031: Move rdf.core access to a new module called dataset --- dataset/pom.xml | 114 ++++ .../dataset/EntityAlreadyExistsException.java | 50 ++ .../dataset/EntityUndeletableException.java | 50 ++ .../dataset/ImmutableGraphServiceFactory.java | 58 ++ .../clerezza/dataset/MGraphServiceFactory.java | 54 ++ .../clerezza/dataset/NoSuchEntityException.java | 51 ++ .../clerezza/dataset/QueryableTcProvider.java | 41 ++ .../org/apache/clerezza/dataset/SecuredGraph.java | 191 +++++++ .../org/apache/clerezza/dataset/TcManager.java | 613 +++++++++++++++++++++ .../org/apache/clerezza/dataset/TcProvider.java | 151 +++++ .../clerezza/dataset/TcProviderMultiplexer.java | 446 +++++++++++++++ .../dataset/WeightedProviderComparator.java | 41 ++ .../clerezza/dataset/WeightedTcProvider.java | 36 ++ .../java/org/apache/clerezza/dataset/package.html | 56 ++ .../dataset/security/TcAccessController.java | 342 ++++++++++++ .../clerezza/dataset/security/TcPermission.java | 149 +++++ .../org/apache/clerezza/dataset/SecurityTest.java | 175 ++++++ .../org/apache/clerezza/dataset/TcManagerTest.java | 299 ++++++++++ .../clerezza/dataset/providers/WeightedA.java | 103 ++++ .../clerezza/dataset/providers/WeightedA1.java | 103 ++++ .../clerezza/dataset/providers/WeightedAHeavy.java | 101 ++++ .../clerezza/dataset/providers/WeightedBlight.java | 106 ++++ .../clerezza/dataset/providers/WeightedDummy.java | 155 ++++++ 23 files changed, 3485 insertions(+) diff --git a/dataset/pom.xml b/dataset/pom.xml new file mode 100644 index 0000000..3ce095f --- /dev/null +++ b/dataset/pom.xml @@ -0,0 +1,114 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- + + 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. + +--> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <parent> + <groupId>org.apache.clerezza</groupId> + <artifactId>clerezza</artifactId> + <version>8-SNAPSHOT</version> + <relativePath/> + </parent> + + <artifactId>dataset</artifactId> + <packaging>bundle</packaging> + <version>8-SNAPSHOT</version> + <name>Clerezza - DataSet</name> + <description>Interfaces and utilities to access RDF Graphs</description> + + <dependencies> + <dependency> + <groupId>org.apache.clerezza</groupId> + <artifactId>api</artifactId> + <version>8-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.apache.clerezza</groupId> + <artifactId>api.impl</artifactId> + <version>8-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.apache.clerezza</groupId> + <artifactId>sparql</artifactId> + <version>8-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.scr.annotations</artifactId> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.scr.ds-annotations</artifactId> + <version>1.2.8</version> + </dependency> + <dependency> + <groupId>org.apache.clerezza</groupId> + <artifactId>utils</artifactId> + <version>0.2</version> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>1.9.5</version> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <plugins> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Export-Package>org.apache.clerezza.dataset.*</Export-Package> + </instructions> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-scr-plugin</artifactId> + <executions> + <execution> + <id>generate-scr-scrdescriptor</id> + <goals> + <goal>scr</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/dataset/src/main/java/org/apache/clerezza/dataset/EntityAlreadyExistsException.java b/dataset/src/main/java/org/apache/clerezza/dataset/EntityAlreadyExistsException.java new file mode 100644 index 0000000..15dcb56 --- /dev/null +++ b/dataset/src/main/java/org/apache/clerezza/dataset/EntityAlreadyExistsException.java @@ -0,0 +1,50 @@ +/* + * 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.clerezza.dataset; + +import org.apache.clerezza.api.IRI; + +/** + * is thrown on an attempt to create an entity with a name which already exists + * + * @author hasan + */ +public class EntityAlreadyExistsException extends RuntimeException { + + private IRI entityName; + + /** + * creates an exception indicating that an entity with the specified name + * already exists. + * + * @param entityName the name of the entity which already exists + */ + public EntityAlreadyExistsException(IRI entityName) { + super("An entity with this name already exists: "+entityName); + this.entityName = entityName; + } + + /** + * + * @return the name of the entity which already exists + */ + public IRI getEntityName() { + return entityName; + } +} diff --git a/dataset/src/main/java/org/apache/clerezza/dataset/EntityUndeletableException.java b/dataset/src/main/java/org/apache/clerezza/dataset/EntityUndeletableException.java new file mode 100644 index 0000000..32c5088 --- /dev/null +++ b/dataset/src/main/java/org/apache/clerezza/dataset/EntityUndeletableException.java @@ -0,0 +1,50 @@ +/* + * 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.clerezza.dataset; + +import org.apache.clerezza.api.IRI; + +/** + * is thrown on an attempt to delete an entity with a provider that + * supports the delete operation when the specified entity cannot be deleted + * + * @author reto + */ +public class EntityUndeletableException extends RuntimeException { + private IRI entityName; + + /** + * creates an exception indicating that the entity with the specified name + * cannot be deleted + * + * @param entityName the name of the entity which is undeletable + */ + public EntityUndeletableException(IRI entityName) { + super("This entity is undeletable: "+entityName); + this.entityName = entityName; + } + + /** + * + * @return the name of the entity which is undeletable + */ + public IRI getEntityName() { + return entityName; + } +} diff --git a/dataset/src/main/java/org/apache/clerezza/dataset/ImmutableGraphServiceFactory.java b/dataset/src/main/java/org/apache/clerezza/dataset/ImmutableGraphServiceFactory.java new file mode 100644 index 0000000..b775d82 --- /dev/null +++ b/dataset/src/main/java/org/apache/clerezza/dataset/ImmutableGraphServiceFactory.java @@ -0,0 +1,58 @@ +/* + * 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.clerezza.dataset; + +import org.apache.clerezza.api.Graph; +import org.apache.clerezza.api.IRI; +import org.apache.clerezza.dataset.security.TcAccessController; +import org.osgi.framework.Bundle; +import org.osgi.framework.ServiceFactory; +import org.osgi.framework.ServiceRegistration; + +/** + * @see <a href="http://www.osgi.org/javadoc/r4v41/org/osgi/framework/ServiceFactory.html"> + * Interface ServiceFactory</a> + * + * @author mir + */ +public class ImmutableGraphServiceFactory implements ServiceFactory { + + private final TcManager tcManager; + private final IRI name; + private final TcAccessController tcAccessController; + + ImmutableGraphServiceFactory(TcManager tcManager, IRI name, + TcAccessController tcAccessController) { + this.tcManager = tcManager; + this.name = name; + this.tcAccessController = tcAccessController; + } + + @Override + public Object getService(Bundle arg0, ServiceRegistration arg1) { + Graph tc = + new SecuredGraph(tcManager.getImmutableGraph(name), name, + tcAccessController); + return tc.getImmutableGraph(); + } + + @Override + public void ungetService(Bundle arg0, ServiceRegistration arg1, Object arg2) { + } +} diff --git a/dataset/src/main/java/org/apache/clerezza/dataset/MGraphServiceFactory.java b/dataset/src/main/java/org/apache/clerezza/dataset/MGraphServiceFactory.java new file mode 100644 index 0000000..cf3435c --- /dev/null +++ b/dataset/src/main/java/org/apache/clerezza/dataset/MGraphServiceFactory.java @@ -0,0 +1,54 @@ +/* + * 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.clerezza.dataset; + +import org.apache.clerezza.api.IRI; +import org.apache.clerezza.dataset.security.TcAccessController; +import org.osgi.framework.Bundle; +import org.osgi.framework.ServiceFactory; +import org.osgi.framework.ServiceRegistration; + +/** + * @see <a href="http://www.osgi.org/javadoc/r4v41/org/osgi/framework/ServiceFactory.html"> + * Interface ServiceFactory</a> + * + * @author mir + */ +public class MGraphServiceFactory implements ServiceFactory { + + private TcManager tcManager; + private IRI name; + private final TcAccessController tcAccessController; + + MGraphServiceFactory(TcManager tcManager, IRI name, + TcAccessController tcAccessController) { + this.tcManager = tcManager; + this.name = name; + this.tcAccessController = tcAccessController; + } + + @Override + public Object getService(Bundle arg0, ServiceRegistration arg1) { + return new SecuredGraph(tcManager.getMGraph(name), name, tcAccessController); + } + + @Override + public void ungetService(Bundle arg0, ServiceRegistration arg1, Object arg2) { + } +} diff --git a/dataset/src/main/java/org/apache/clerezza/dataset/NoSuchEntityException.java b/dataset/src/main/java/org/apache/clerezza/dataset/NoSuchEntityException.java new file mode 100644 index 0000000..789dc6b --- /dev/null +++ b/dataset/src/main/java/org/apache/clerezza/dataset/NoSuchEntityException.java @@ -0,0 +1,51 @@ +/* + * 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.clerezza.dataset; + +import org.apache.clerezza.api.IRI; + +/** + * is thrown on an attempt to perform an operation on an entity (i.e. a + * <code>ImmutableGraph</code> or <code>Graph</code> that does not exist. + * + * @author reto + */ +public class NoSuchEntityException extends RuntimeException { + private IRI entityName; + + /** + * creates an exception indicating that the entity with the specified name + * does not exist. + * + * @param entityName the name for which no entity exists + */ + public NoSuchEntityException(IRI entityName) { + super("No such entity: "+entityName); + this.entityName = entityName; + } + + /** + * the name for which no entity exists. + * + * @return the name of the entity that doesn't exist + */ + public IRI getEntityName() { + return entityName; + } +} diff --git a/dataset/src/main/java/org/apache/clerezza/dataset/QueryableTcProvider.java b/dataset/src/main/java/org/apache/clerezza/dataset/QueryableTcProvider.java new file mode 100644 index 0000000..0490a4c --- /dev/null +++ b/dataset/src/main/java/org/apache/clerezza/dataset/QueryableTcProvider.java @@ -0,0 +1,41 @@ +/* + * 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.clerezza.dataset; + +import org.apache.clerezza.api.IRI; + +/** + * Extends the TcProvider interface for providers that support sparql queries. + */ +public interface QueryableTcProvider extends TcProvider { + + /** + * Executes any sparql query. The type of the result object will vary + * depending on the type of the query. + * + * @param query + * the sparql query to execute + * @param defaultGraph + * the default ImmutableGraph against which to execute the query if not + * FROM clause is present + * @return the resulting ResultSet, ImmutableGraph or Boolean value + */ + public Object executeSparqlQuery(String query, IRI defaultGraphUri); + +} diff --git a/dataset/src/main/java/org/apache/clerezza/dataset/SecuredGraph.java b/dataset/src/main/java/org/apache/clerezza/dataset/SecuredGraph.java new file mode 100644 index 0000000..afc2102 --- /dev/null +++ b/dataset/src/main/java/org/apache/clerezza/dataset/SecuredGraph.java @@ -0,0 +1,191 @@ +/* + * 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.clerezza.dataset; + +import org.apache.clerezza.api.*; +import org.apache.clerezza.api.impl.graph.SimpleImmutableGraph; +import org.apache.clerezza.api.impl.graph.WriteBlockedGraph; +import org.apache.clerezza.dataset.security.TcAccessController; + +import java.security.AccessControlException; +import java.util.Collection; +import java.util.Iterator; +import java.util.concurrent.locks.ReadWriteLock; + +/** + * A Secured triple collection wraps a triple collection checking each access + * for the rights on a the ImmutableGraph for which the uri is passed to the + * constructor. + * + * @author mir, hasan + */ +public class SecuredGraph implements Graph { + + private final Graph wrapped; + private final IRI name; + private final TcAccessController tcAccessController; + + public SecuredGraph(Graph wrapped, IRI name, + TcAccessController tcAccessController) { + this.wrapped = wrapped; + this.name = name; + this.tcAccessController = tcAccessController; + } + + @Override + public Iterator<Triple> filter(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) { + final Iterator<Triple> baseIter = wrapped.filter(subject, predicate, object); + return new Iterator<Triple>() { + + @Override + public boolean hasNext() { + checkRead(); + return baseIter.hasNext(); + } + + @Override + public Triple next() { + checkRead(); + return baseIter.next(); + } + + @Override + public void remove() { + checkWrite(); + baseIter.remove(); + } + }; + } + + @Override + public int size() { + checkRead(); + return wrapped.size(); + } + + @Override + public boolean isEmpty() { + checkRead(); + return wrapped.isEmpty(); + } + + @Override + public Object[] toArray() { + checkRead(); + return wrapped.toArray(); + } + + @Override + public <T> T[] toArray(T[] a) { + checkRead(); + return wrapped.toArray(a); + } + + @Override + public boolean add(Triple e) { + checkWrite(); + return wrapped.add(e); + } + + @Override + public boolean remove(Object o) { + checkWrite(); + return wrapped.remove(o); + } + + @Override + public boolean addAll(Collection<? extends Triple> c) { + checkWrite(); + return wrapped.addAll(c); + } + + @Override + public boolean removeAll(Collection<?> c) { + checkWrite(); + return wrapped.removeAll(c); + } + + @Override + public boolean retainAll(Collection<?> c) { + checkWrite(); + return wrapped.retainAll(c); + } + + @Override + public void clear() { + checkWrite(); + wrapped.clear(); + } + + void checkRead() { + tcAccessController.checkReadPermission(name); + } + + void checkWrite() { + tcAccessController.checkReadWritePermission(name); + } + + @Override + public boolean contains(Object o) { + checkRead(); + return wrapped.contains((Triple) o); + } + + + @Override + public Iterator<Triple> iterator() { + return filter(null, null, null); + } + + @Override + public boolean containsAll(Collection<?> c) { + checkRead(); + return wrapped.containsAll(c); + } + + @Override + public ImmutableGraph getImmutableGraph() { + return new SimpleImmutableGraph(this); + } + + @Override + public ReadWriteLock getLock() { + return wrapped.getLock(); + } + + /** + * Returns the wrapped Graph if the caller has all access rights. + * If the caller has only the read access right, then a write-blocked + * Graph is returned. If the caller has neither the read nor the write + * access right then an AccessControlException is thrown. + * + * @return the wrapped Graph or a write-block Graph depending + * on the access rights of the caller. + */ + public Graph getUnsecuredGraph() { + try { + checkWrite(); + return wrapped; + } catch (AccessControlException ex) { + checkRead(); + return new WriteBlockedGraph(wrapped); + } + + } +} diff --git a/dataset/src/main/java/org/apache/clerezza/dataset/TcManager.java b/dataset/src/main/java/org/apache/clerezza/dataset/TcManager.java new file mode 100644 index 0000000..8981ad3 --- /dev/null +++ b/dataset/src/main/java/org/apache/clerezza/dataset/TcManager.java @@ -0,0 +1,613 @@ +/* + * 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.clerezza.dataset; + +import org.apache.clerezza.api.Graph; +import org.apache.clerezza.api.IRI; +import org.apache.clerezza.api.ImmutableGraph; +import org.apache.clerezza.api.impl.graph.SimpleGraph; +import org.apache.clerezza.api.impl.graph.WriteBlockedGraph; +import org.apache.clerezza.dataset.security.TcAccessController; +import org.apache.clerezza.sparql.*; +import org.apache.clerezza.sparql.query.*; +import org.osgi.framework.Bundle; +import org.osgi.framework.ServiceRegistration; +import org.osgi.service.component.ComponentContext; +import org.osgi.service.component.annotations.Component; +import org.osgi.service.component.annotations.Reference; +import org.osgi.service.component.annotations.ReferenceCardinality; +import org.osgi.service.component.annotations.ReferencePolicy; + +import java.security.AccessControlException; +import java.util.*; + +/** + * This class implements + * <code>TcManager</code>, delegating the actual provision and creation of + * Graphs or MGraphs to registered + * <code>TcProvider</code>s. The class attempts to satisfy the request using the + * register + * <code>WeightedTcProvider</code> in decreasing order of weight. If multiple + * providers have the same weight the lexicographical order of the fully + * qualified class name determines which one is used, namely the one that occurs + * earlier. If a call to a registered provider causes an + * <code>IllegalArgumentException</code>, + * <code>NoSuchEntityException</code> or + * <code>UnsupportedOperationException</code> then the call is delegated to the + * next provider. + * + * Only one instance of this class should exist in a system, the public no + * argument constructor is meant for initialization by dependency injection + * systems such as OSGi-DS. Applications should use the static + * <code>getInstance()</code> method if they aren't using a framework that + * injects them the instance. + * + * This class returns + * <code>Graph</code>s a subtype of + * <code>Graph</code> that allows read/write locks. + * + * This class also registers all Graphs as services with the property + * 'name' indicating there name. + * + * Security checks are done when a Graph is retrieved. The returned + * Graph will do no further security checks. Because of this it + * should not be passed to a context where different access control applies. If + * an Graph is retrieved without having write permission the returned graph + * will be read-only. + * + * If a Graphs needs to passed around across different security + * contexts the one retrieved from the OSGi service whiteboard should be used as + * this performs access control on every access. + * + * @author reto, mir, hasan + * + */ +//immedia is set to true as this should register the ImmutableGraph services (even if manager service is not required) +@Component(service = TcManager.class, immediate = true, + property={ + "graph.cache.enabled=true", + "Graph.services.enabled=true"}) +public class TcManager extends TcProviderMultiplexer implements GraphStore { + + public final static String GENERAL_PURPOSE_TC = "general.purpose.tc"; + public final static String Graph_SERVICES_ENABLED = "Graph.services.enabled"; + public final static String MGRAPH_CACHE_ENABLED = "graph.cache.enabled"; + + private static volatile TcManager instance; + private TcAccessController tcAccessController = new TcAccessController() { + + @Override + protected TcManager getTcManager() { + return TcManager.this; + } + + }; + private Map<IRI, ServiceRegistration> serviceRegistrations = Collections + .synchronizedMap(new HashMap<IRI, ServiceRegistration>()); + + protected QueryEngine queryEngine; + private boolean isActivated = false; + private boolean isTcServicesEnabled = true; + + private ComponentContext componentContext; + protected SortedSet<WeightedTcProvider> tempProviderList = new TreeSet<WeightedTcProvider>( + new WeightedProviderComparator()); + + /** + * the constructor sets the singleton instance to allow instantiation by + * OSGi-DS. This constructor should not be called except by OSGi-DS, + * otherwise the static + * <code>getInstance</code> method should be used. + */ + public TcManager() { + TcManager.instance = this; + } + + /** + * This returns the singleton instance. If an instance has been previously + * created (e.g. by OSGi declarative services) this instance is returned, + * otherwise a new instance is created and providers are injected using the + * service provider interface (META-INF/services/) + * + * @return the singleton instance + */ + public static TcManager getInstance() { + if (instance == null) { + synchronized (TcManager.class) { + if (instance == null) { + instance = new TcManager(); + instance.isActivated = true; + Iterator<WeightedTcProvider> weightedProviders = ServiceLoader + .load(WeightedTcProvider.class).iterator(); + while (weightedProviders.hasNext()) { + WeightedTcProvider weightedProvider = weightedProviders + .next(); + instance + .bindWeightedTcProvider(weightedProvider); + } + Iterator<QueryEngine> queryEngines = ServiceLoader.load( + QueryEngine.class).iterator(); + System.out.println("looking for QE"); + if (queryEngines.hasNext()) { + instance.queryEngine = queryEngines.next(); + System.out.println("QE: " + + instance.queryEngine.getClass()); + } + } + } + } + return instance; + } + + protected void activate(final ComponentContext componentContext) { + this.componentContext = componentContext; + + // Read configuration + isTcServicesEnabled = true; + Object configTcServicesEnabled = componentContext.getProperties().get(Graph_SERVICES_ENABLED); + if ( configTcServicesEnabled != null && configTcServicesEnabled instanceof String ) { + isTcServicesEnabled = Boolean.valueOf((String)configTcServicesEnabled); + } + Object configCacheEnabled = componentContext.getProperties().get(MGRAPH_CACHE_ENABLED); + if ( configCacheEnabled != null && configCacheEnabled instanceof String ) { + setCachingEnabled(Boolean.valueOf((String)configCacheEnabled)); + } + isActivated = true; + + for (WeightedTcProvider provider : tempProviderList) { + addWeightedTcProvider(provider); + } + tempProviderList.clear(); + } + + protected void deactivate(final ComponentContext componentContext) { + for (ServiceRegistration registration : serviceRegistrations.values()) { + registration.unregister(); + } + serviceRegistrations.clear(); + this.componentContext = null; + isActivated = false; + } + + @Override + public ImmutableGraph getImmutableGraph(IRI name) throws NoSuchEntityException { + tcAccessController.checkReadPermission(name); + return super.getImmutableGraph(name); + } + + @Override + public Graph getMGraph(IRI name) { + try { + tcAccessController.checkReadWritePermission(name); + } catch (AccessControlException e) { + tcAccessController.checkReadPermission(name); + return new WriteBlockedGraph(super.getMGraph(name)); + } + return super.getMGraph(name); + } + + @Override + public Graph getGraph(IRI name) { + try { + tcAccessController.checkReadWritePermission(name); + } catch (AccessControlException e) { + tcAccessController.checkReadPermission(name); + return new WriteBlockedGraph( + super.getGraph(name)); + } + return super.getGraph(name); + } + + @Override + public Graph createGraph(IRI name) + throws UnsupportedOperationException { + tcAccessController.checkReadWritePermission(name); + return super.createGraph(name); + } + + @Override + public ImmutableGraph createImmutableGraph(IRI name, Graph triples) { + tcAccessController.checkReadWritePermission(name); + return super.createImmutableGraph(name, triples); + } + + @Override + public void deleteGraph(IRI name) { + tcAccessController.checkReadWritePermission(name); + super.deleteGraph(name); + } + + @Override + public Set<IRI> getNames(ImmutableGraph ImmutableGraph) { + return super.getNames(ImmutableGraph); + } + + @Override + public Set<IRI> listNamedGraphs() { + return this.listGraphs(); + } + + @Override + public Set<IRI> listGraphs() { + Set<IRI> result = super.listGraphs(); + return excludeNonReadable(result); + } + + @Override + public Set<IRI> listMGraphs() { + Set<IRI> result = super.listMGraphs(); + return excludeNonReadable(result); + } + + @Override + public Set<IRI> listImmutableGraphs() { + Set<IRI> result = super.listImmutableGraphs(); + return excludeNonReadable(result); + } + + private Set<IRI> excludeNonReadable(Set<IRI> tcNames) { + SecurityManager security = System.getSecurityManager(); + if (security == null) { + return tcNames; + } + Set<IRI> result = new HashSet<IRI>(); + for (IRI name : tcNames) { + try { + tcAccessController.checkReadPermission(name); + } catch (AccessControlException e) { + continue; + } + result.add(name); + } + return result; + } + + /** + * Executes any sparql query. The type of the result object will vary + * depending on the type of the query. If the defaultGraph is available + * in this TcManages executeSparqlQuery(String, UriRef) should be used instead. + * + * @param query the sparql query to execute + * @param defaultGraph the default ImmutableGraph against which to execute the query + * if no FROM clause is present + * @return the resulting ResultSet, ImmutableGraph or Boolean value + */ + public Object executeSparqlQuery(String query, Graph defaultGraph) throws ParseException { + TcProvider singleTargetTcProvider = null; + + final IRI defaultGraphName = new IRI("urn:x-temp:/kjsfadfhfasdffds"); + final SparqlPreParser sparqlPreParser = new SparqlPreParser(this); + final Set<IRI> referencedGraphs = sparqlPreParser.getReferredGraphs(query, defaultGraphName); + if ((referencedGraphs != null) && (!referencedGraphs.contains(defaultGraphName))) { + singleTargetTcProvider = getSingleTargetTcProvider(referencedGraphs); + } + if ((singleTargetTcProvider != null) && (singleTargetTcProvider instanceof QueryableTcProvider)) { + return ((QueryableTcProvider) singleTargetTcProvider).executeSparqlQuery(query, null); + } + final QueryEngine queryEngine = this.queryEngine; + if (queryEngine != null) { + return queryEngine.execute(this, defaultGraph, query); + } else { + throw new NoQueryEngineException(); + } + } + + /** + * Executes any sparql query. The type of the result object will vary + * depending on the type of the query. Note that this method only works for + * queries that do not need a default ImmutableGraph. + * + * @param query the sparql query to execute + * @param forceFastlane indicate whether to force fastlane usage. + * @return the resulting ResultSet, ImmutableGraph or Boolean value + */ + public Object executeSparqlQuery(String query, boolean forceFastlane) throws ParseException { + TcProvider singleTargetTcProvider = null; + if (forceFastlane) { + singleTargetTcProvider = getSingleTargetTcProvider(Collections.EMPTY_SET); + } else { + final IRI defaultGraphName = new IRI("urn:x-temp:/kjsfadfhfasdffds"); + SparqlPreParser sparqlPreParser = new SparqlPreParser(this); + final Set<IRI> referencedGraphs = sparqlPreParser.getReferredGraphs(query, defaultGraphName); + if ((referencedGraphs != null) && (!referencedGraphs.contains(defaultGraphName))) { + singleTargetTcProvider = getSingleTargetTcProvider(referencedGraphs); + } + } + + if ((singleTargetTcProvider != null) && (singleTargetTcProvider instanceof QueryableTcProvider)) { + return ((QueryableTcProvider)singleTargetTcProvider).executeSparqlQuery(query, null); + } + final QueryEngine queryEngine = this.queryEngine; + if (queryEngine != null) { + return queryEngine.execute(this, new SimpleGraph(), query); + } else { + throw new NoQueryEngineException(); + } + } + + /** + * Executes any sparql query. The type of the result object will vary + * depending on the type of the query. If the defaultGraph is available + * in this TcManages executeSparqlQuery(String, UriRef) should be used instead. + * + * @param query the sparql query to execute + * @param defaultGraphName the ImmutableGraph to be used as default ImmutableGraph in the Sparql ImmutableGraph Store + * @return the resulting ResultSet, ImmutableGraph or Boolean value + */ + public Object executeSparqlQuery(String query, IRI defaultGraphName) throws ParseException { + return executeSparqlQuery(query, defaultGraphName, false); + } + + /** + * Executes any sparql query. The type of the result object will vary + * depending on the type of the query. If the defaultGraph is available + * in this TcManages executeSparqlQuery(String, UriRef) should be used instead. + * + * @param query the sparql query to execute + * @param defaultGraph the ImmutableGraph to be used as default ImmutableGraph in the Sparql ImmutableGraph Store + * @param forceFastlane indicate whether to force fastlane usage. + * @return the resulting ResultSet, ImmutableGraph or Boolean value + */ + public Object executeSparqlQuery(String query, IRI defaultGraphName, boolean forceFastlane) throws ParseException { + TcProvider singleTargetTcProvider = null; + if (forceFastlane) { + singleTargetTcProvider = getSingleTargetTcProvider(Collections.singleton(defaultGraphName)); + } else { + SparqlPreParser sparqlPreParser = new SparqlPreParser(this); + final Set<IRI> referencedGraphs = sparqlPreParser.getReferredGraphs(query, defaultGraphName); + if ((referencedGraphs != null)) { + singleTargetTcProvider = getSingleTargetTcProvider(referencedGraphs); + } + } + if ((singleTargetTcProvider != null) && (singleTargetTcProvider instanceof QueryableTcProvider)) { + return ((QueryableTcProvider)singleTargetTcProvider).executeSparqlQuery(query, defaultGraphName); + } + final QueryEngine queryEngine = this.queryEngine; + if (queryEngine != null) { + return queryEngine.execute(this, this.getGraph(defaultGraphName), query); + } else { + throw new NoQueryEngineException(); + } + } + + /** + * Executes any sparql query. The type of the result object will vary + * depending on the type of the query. + * + * @param query the sparql query to execute + * @param defaultGraph the default ImmutableGraph against which to execute the query + * if no FROM clause is present + * @return the resulting ResultSet, ImmutableGraph or Boolean value + * + * @deprecated Query is discontinued + */ + @Deprecated + public Object executeSparqlQuery(Query query, Graph defaultGraph) { + final QueryEngine queryEngine = this.queryEngine; + if (queryEngine != null) { + return queryEngine.execute(this, defaultGraph, query.toString()); + } else { + throw new NoQueryEngineException(); + } + } + + /** + * Executes a sparql SELECT query. + * + * @param query the sparql SELECT query to execute + * @param defaultGraph the default ImmutableGraph against which to execute the query + * if not FROM clause is present + * @return the resulting ResultSet + * @deprecated Query is discontinued + */ + @Deprecated + public ResultSet executeSparqlQuery(SelectQuery query, + Graph defaultGraph) { + return (ResultSet) executeSparqlQuery((Query) query, defaultGraph); + } + + /** + * Executes a sparql ASK query. + * + * @param query the sparql ASK query to execute + * @param defaultGraph the default ImmutableGraph against which to execute the query + * if not FROM clause is present + * @return the boolean value this query evaluates to + * @deprecated Query is discontinued + */ + @Deprecated + public boolean executeSparqlQuery(AskQuery query, + Graph defaultGraph) { + return (Boolean) executeSparqlQuery((Query) query, defaultGraph); + } + + /** + * Executes a sparql DESCRIBE query. + * + * @param query the sparql DESCRIBE query to execute + * @param defaultGraph the default ImmutableGraph against which to execute the query + * if not FROM clause is present + * @return the resulting ImmutableGraph + * @deprecated Query is discontinued + */ + @Deprecated + public ImmutableGraph executeSparqlQuery(DescribeQuery query, + Graph defaultGraph) { + return (ImmutableGraph) executeSparqlQuery((Query) query, defaultGraph); + } + + /** + * Executes a sparql CONSTRUCT query. + * + * @param query the sparql CONSTRUCT query to execute + * @param defaultGraph the default ImmutableGraph against which to execute the query + * if not FROM clause is present + * @return the resulting ImmutableGraph + * @deprecated Query is discontinued + */ + @Deprecated + public ImmutableGraph executeSparqlQuery(ConstructQuery query, + Graph defaultGraph) { + return (ImmutableGraph) executeSparqlQuery((Query) query, defaultGraph); + } + + /** + * @return the TcAccessController that can be used to set the permissions + * needed to access a Triple Collection + */ + public TcAccessController getTcAccessController() { + return tcAccessController; + } + + /** + * Registers a provider + * + * @param provider the provider to be registered + */ + @Reference(policy = ReferencePolicy.DYNAMIC, + cardinality = ReferenceCardinality.MULTIPLE) + protected void bindWeightedTcProvider(WeightedTcProvider provider) { + if (isActivated) { + addWeightedTcProvider(provider); + } else { + tempProviderList.add(provider); + } + } + + /** + * Unregister a provider + * + * @param provider the provider to be deregistered + */ + protected void unbindWeightedTcProvider( + WeightedTcProvider provider) { + removeWeightedTcProvider(provider); + } + + /** + * Registers a provider + * + * @param provider the provider to be registered + */ + @Reference(policy = ReferencePolicy.DYNAMIC, + cardinality = ReferenceCardinality.AT_LEAST_ONE, + target = "("+ TcManager.GENERAL_PURPOSE_TC+"=true)") + protected void bindGpWeightedTcProvider(WeightedTcProvider provider) { + if (isActivated) { + addWeightedTcProvider(provider); + } else { + tempProviderList.add(provider); + } + } + + /** + * Unregister a provider + * + * @param provider the provider to be deregistered + */ + protected void unbindGpWeightedTcProvider( + WeightedTcProvider provider) { + removeWeightedTcProvider(provider); + } + + @Reference(policy = ReferencePolicy.DYNAMIC, + cardinality = ReferenceCardinality.OPTIONAL) + protected void bindQueryEngine(QueryEngine queryEngine) { + this.queryEngine = queryEngine; + } + + protected void unbindQueryEngine(QueryEngine queryEngine) { + this.queryEngine = null; + } + + @Override + protected void mGraphAppears(IRI name) { + if (isTcServicesEnabled()) { + // Only create the service when activated. When not activated + // creating will be delayed till after activation. + if (componentContext != null) { + registerGraphAsService(name, true); + } + } + } + + @Override + protected void graphAppears(IRI name) { + if (isTcServicesEnabled()) { + // Only create the service when activated. When not activated + // creating will be delayed till after activation. + if (componentContext != null) { + registerGraphAsService(name, false); + } + } + } + + private void registerGraphAsService(IRI name, boolean isMGraph) { + Dictionary<String,Object> props = new Hashtable<String, Object>(); + props.put("name", name.getUnicodeString()); + String[] interfaceNames; + Object service; + if (isMGraph) { + interfaceNames = new String[]{ + Graph.class.getName(), + Graph.class.getName() + }; + service = new MGraphServiceFactory(this, name, tcAccessController); + } else { + interfaceNames = new String[]{ImmutableGraph.class.getName()}; + service = new ImmutableGraphServiceFactory(this, name, tcAccessController); + } + final int bundleState = componentContext.getBundleContext().getBundle().getState(); + if ((bundleState == Bundle.ACTIVE) || (bundleState == Bundle.STARTING)) { + ServiceRegistration serviceReg = componentContext.getBundleContext() + .registerService(interfaceNames, service, props); + serviceRegistrations.put(name, serviceReg); + } + } + + @Override + protected void tcDisappears(IRI name) { + ServiceRegistration reg = serviceRegistrations.get(name); + if (reg != null) { + reg.unregister(); + serviceRegistrations.remove(name); + } + } + + private TcProvider getSingleTargetTcProvider(final Set<IRI> referencedGraphs) { + TcProvider singleTargetTcProvider = null; + for (WeightedTcProvider provider : providerList) { + final Set<IRI> providerGraphs = provider.listGraphs(); + if (providerGraphs.containsAll(referencedGraphs)) { + singleTargetTcProvider = provider; + break; //success + } + for (IRI graphName : referencedGraphs) { + if (providerGraphs.contains(graphName)) { + break; //failure + } + } + } + return singleTargetTcProvider; + } + + public boolean isTcServicesEnabled() { + return isTcServicesEnabled; + } +} diff --git a/dataset/src/main/java/org/apache/clerezza/dataset/TcProvider.java b/dataset/src/main/java/org/apache/clerezza/dataset/TcProvider.java new file mode 100644 index 0000000..6135790 --- /dev/null +++ b/dataset/src/main/java/org/apache/clerezza/dataset/TcProvider.java @@ -0,0 +1,151 @@ +/* + * 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.clerezza.dataset; + +import org.apache.clerezza.api.Graph; +import org.apache.clerezza.api.IRI; +import org.apache.clerezza.api.ImmutableGraph; + +import java.util.Set; + +/** + * A TC (Graph) Provider allows access to and optionally + * creation of named {@link ImmutableGraph}s and {@link Graph}s (mutable graphs) + * + * @author reto + */ +public interface TcProvider { + + /** + * Get a <code>ImmutableGraph</code> by its name + * + * @param name the name of the ImmutableGraph + * @return the <code>ImmutableGraph</code> with the specified name + * @throws NoSuchEntityException if there is no <code>ImmutableGraph</code> + * with the specified name + */ + ImmutableGraph getImmutableGraph(IRI name) throws NoSuchEntityException; + + /** + * Get an <code>Graph</code> taht is not <code>ImmutableGrah</code>. The instances + * returned in different invocations are <code>equals</code>. + * + * @param the name of the <code>Graph</code> + * @return name the <code>Graph</code> with the specified name + * @throws NoSuchEntityException if there is no <code>Graph</code> + * with the specified name + */ + Graph getMGraph(IRI name) throws NoSuchEntityException; + + /** + * This method is used to get a <code>Graph</code> indifferently + * whether it's a ImmutableGraph or not. If the <code>name</code> names an + * <code>Graph</code> the result is the same as when invoking + * <code>getMGraph</code> with that argument, analogously for + * <code>ImmutableGraph</code>S the method returns an instance equals to what + * <code>getImmutableGraph</code> would return. + * + * @param name the name of the <Code>ImmutableGraph</code> or <code>Graph</code> + * @return the <Code>ImmutableGraph</code> or <code>Graph</code> + * @throws NoSuchEntityException if there is no <code>ImmutableGraph</code> + * or <code>Graph</code> with the specified name + */ + Graph getGraph(IRI name) throws NoSuchEntityException; + + /** + * Lists the name of the <Code>ImmutableGraph</code>s available through this + * <code>TcProvider</code>, implementations may take into account the + * security context and omit <Code>ImmutableGraph</code>s for which access is not + * allowed. + * + * @return the list of <Code>ImmutableGraph</code>s + */ + Set<IRI> listImmutableGraphs(); + + /** + * Lists the name of the <Code>Graph</code>s available through this + * <code>TcProvider</code> that are not <Code>ImmutableGraph</code>, implementations may take into account the + * security context and omit <Code>Graph</code>s for which access is not + * allowed. + * + * @return the list of <Code>Graph</code>s + */ + Set<IRI> listMGraphs(); + + /** + * Lists the name of the <Code>Graph</code>s available through this + * <code>TcProvider</code> indifferently whether they are mutables or + * immutables, implementations may take into account the security context and + * omit <Code>Graph</code>s for which access is not allowed. + * + * @return the list of <Code>Graph</code>s + */ + Set<IRI> listGraphs(); + + /** + * Creates an initially empty <code>Graph</code> with a specified name + * + * @param name names the new <code>Graph</code> + * @return the newly created <code>Graph</code> + * @throws UnsupportedOperationException if this provider doesn't support + * creating <code>Graph</code>S + * @throws EntityAlreadyExistsException if an Graph with the specified name + * already exists + */ + Graph createGraph(IRI name) throws UnsupportedOperationException, + EntityAlreadyExistsException; + + /** + * Creates a <code>ImmutableGraph</code> with a specified name + * + * @param name the name of the <code>ImmutableGraph</code> to be created + * @param triples the triples of the new <code>ImmutableGraph</code> + * @return the newly created <code>ImmutableGraph</code> + * @throws UnsupportedOperationException if this provider doesn't support + * creating <code>ImmutableGraph</code>S + * @throws EntityAlreadyExistsException if a ImmutableGraph with the specified name + * already exists + */ + ImmutableGraph createImmutableGraph(IRI name, Graph triples) + throws UnsupportedOperationException, EntityAlreadyExistsException; + + /** + * Deletes the <code>ImmutableGraph</code> or <code>Graph</code> of a specified name. + * If <code>name</code> references a ImmutableGraph and the ImmutableGraph has other names, it + * will still be available with those other names. + * + * @param name the entity to be removed + * @throws UnsupportedOperationException if this provider doesn't support + * entities deletion. + * @throws NoSuchEntityException if <code>name</code> doesn't refer to a + * <code>ImmutableGraph</code> or an <code>Graph</code>. + * @throws EntityUndeletableException if the specified ImmutableGraph is undeletable + */ + void deleteGraph(IRI name) throws UnsupportedOperationException, + NoSuchEntityException, EntityUndeletableException; + + /** + * get a set of the names of a <code>ImmutableGraph</code> + * + * @param ImmutableGraph + * @return the set names of <code>ImmutableGraph</code>, the set is empty if + * <code>ImmutableGraph</code> is unknown + */ + Set<IRI> getNames(ImmutableGraph immutableGraph); +} diff --git a/dataset/src/main/java/org/apache/clerezza/dataset/TcProviderMultiplexer.java b/dataset/src/main/java/org/apache/clerezza/dataset/TcProviderMultiplexer.java new file mode 100644 index 0000000..ceaddc3 --- /dev/null +++ b/dataset/src/main/java/org/apache/clerezza/dataset/TcProviderMultiplexer.java @@ -0,0 +1,446 @@ +/* + * 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.clerezza.dataset; + +import org.apache.clerezza.api.Graph; +import org.apache.clerezza.api.IRI; +import org.apache.clerezza.api.ImmutableGraph; + +import java.lang.ref.WeakReference; +import java.util.*; + +/** + * This makes a set of WeightedTcProvider appear as one TcProvider. It delegates + * requests to the WeightedTcProvider with the highest Weight + * + * @author reto + */ +public class TcProviderMultiplexer implements TcProvider { + + protected SortedSet<WeightedTcProvider> providerList = new TreeSet<>( + new WeightedProviderComparator()); + /** + * Mapping to Graph's and ServiceRegistration using their URI's as key. + * Makes sure that per URI only one instance of the Graph is used, + * otherwise the locks in the <code>Graph</code>s would have no effect + * between different instances and concurrency issues could occur. + */ + private Map<IRI, MGraphHolder> mGraphCache = Collections.synchronizedMap(new HashMap<IRI, MGraphHolder>()); + + /** + * Flag to indicate whether mgraphs should be cached for faster access. By + * default caching is enabled for backward compatibility. + */ + private boolean isCachingEnabled = true; + + /** + * Registers a provider + * + * @param provider + * the provider to be registered + */ + public void addWeightedTcProvider(WeightedTcProvider provider) { + providerList.add(provider); + updateGraphCache(provider, true); + } + + /** + * Unregister a provider + * + * @param provider + * the provider to be deregistered + */ + public void removeWeightedTcProvider( + WeightedTcProvider provider) { + providerList.remove(provider); + updateGraphCache(provider, false); + } + + /** + * subclasses overwrite this method to be notified when a new + * ImmutableGraph is available (either because it has been created or being + * provided by a newly added WeightedTcProvider). The default implementation + * does nothing. + * + * @param name + */ + protected void graphAppears(IRI name) { + } + + /** + * subclasses overwrite this method to be notified when a new + * Graph is available (either because it has been created or being + * provided by a newly added WeightedTcProvider). The default implementation + * does nothing. + * + * @param name + */ + protected void mGraphAppears(IRI name) { + } + + /** + * subclasses overwrite this method to be notified whenGraph is + * no longer available (either because it has been deleted or bacause its + * WeightedTcProvider was removed). The default implementation does nothing. + * + * for implementational reasons even for name of Graph not + * previously registered. + * + * @param name + */ + protected void tcDisappears(IRI name) { + } + + /** + * Updates the lockableMGraphCache AFTER a new <code>provider</code> was + * bound or unbound. + * This method also takes care of registering and unregistering + * provided triple collections as services based on the weight of + * all affected providers. + * + * @param provider + * the provider that was added or removed + * @param providerAdded + * <code>boolean</code> that should be set as <code>true</code> + * if <code>provider</code> was added to + * <code>org.apache.clerezza.rdf.core.TcManager.providerList</code> + * otherwise <code>false</code> + */ + private void updateGraphCache(WeightedTcProvider provider, + boolean providerAdded) { + Set<IRI> uriSet = provider.listGraphs(); + if (!(uriSet == null || uriSet.isEmpty())) { + if (providerAdded) { + weightedProviderAdded(provider, uriSet); + } else { + weightedProviderRemoved(provider, uriSet); + } + } + } + + private void weightedProviderAdded(WeightedTcProvider newProvider, + Set<IRI> newProvidedUris) { + Set<WeightedTcProvider> lowerWeightedProviderList = getLowerWeightedProvider(newProvider); + if (isCachingEnabled()) { + for (IRI name : newProvidedUris) { + final MGraphHolder holder = mGraphCache.get(name); + if ((holder != null) && (holder.getWeightedTcProvider() != null)) { + if (lowerWeightedProviderList.contains(holder.getWeightedTcProvider())) { + tcDisappears(name); + mGraphCache.remove(name); + } else { + continue; + } + } + Graph triples = newProvider.getGraph(name); + if (triples instanceof Graph) { + mGraphCache.put(name, new MGraphHolder(newProvider, ensureLockable((Graph)triples))); + mGraphAppears(name); + } else { + graphAppears(name); + } + } + } + } + + + + private Set<WeightedTcProvider> getLowerWeightedProvider( + WeightedTcProvider newProvider) { + boolean referenceProviderPassed = false; + Set<WeightedTcProvider> lowerWeightedProviderList = new HashSet<WeightedTcProvider>(); + for (WeightedTcProvider weightedProvider : providerList) { + if (referenceProviderPassed) { + lowerWeightedProviderList.add(weightedProvider); + } else if (newProvider.equals(weightedProvider)) { + referenceProviderPassed = true; + } + } + return lowerWeightedProviderList; + } + + private void weightedProviderRemoved(WeightedTcProvider oldProvider, + Set<IRI> oldProvidedUris) { + for (IRI name : oldProvidedUris) { + final MGraphHolder holder = mGraphCache.get(name); + if ((holder != null) && (holder.getWeightedTcProvider() != null) + && holder.getWeightedTcProvider().equals(oldProvider)) { + tcDisappears(name); + mGraphCache.remove(name); + + if (isCachingEnabled()) { + // check if another WeightedTcProvider has the Graph. + // And if so register as service. + for (WeightedTcProvider provider : providerList) { + try { + Graph triples = provider.getGraph(name); + if (triples instanceof Graph) { + mGraphCache.put(name, new MGraphHolder(provider, ensureLockable((Graph)triples))); + mGraphAppears(name); + } else { + graphAppears(name); + } + break; + } catch (NoSuchEntityException e) { + // continue; + } + } + } + } + } + } + + @Override + public ImmutableGraph getImmutableGraph(IRI name) throws NoSuchEntityException { + for (TcProvider provider : providerList) { + try { + return provider.getImmutableGraph(name); + } catch (NoSuchEntityException e) { + //we do nothing and try our luck with the next provider + } catch (IllegalArgumentException e) { + //we do nothing and try our luck with the next provider + } + } + throw new NoSuchEntityException(name); + } + + @Override + public Graph getMGraph(IRI name) + throws NoSuchEntityException { + Graph result = getMGraphFromCache(name); + if (result == null) { + synchronized (this) { + result = getMGraphFromCache(name); + if (result == null) { + result = getUnsecuredMGraphAndAddToCache(name); + } + } + } + return result; + } + + private Graph getMGraphFromCache(IRI name) { + MGraphHolder holder = mGraphCache.get(name); + if (holder == null) { + return null; + } + return holder.getMGraph(); + } + + private Graph getUnsecuredMGraphAndAddToCache(IRI name) + throws NoSuchEntityException { + for (WeightedTcProvider provider : providerList) { + try { + Graph providedMGraph = provider.getMGraph(name); + Graph result = ensureLockable(providedMGraph); + + if (isCachingEnabled()) { + mGraphCache.put(name, new MGraphHolder( + provider, result)); + } + return result; + } catch (NoSuchEntityException e) { + //we do nothing and try our luck with the next provider + } catch (IllegalArgumentException e) { + //we do nothing and try our luck with the next provider + } + } + throw new NoSuchEntityException(name); + } + + @Override + public Graph getGraph(IRI name) + throws NoSuchEntityException { + Graph result; + for (WeightedTcProvider provider : providerList) { + try { + result = provider.getGraph(name); + if (result instanceof ImmutableGraph) { + return result; + } else { + // This is to ensure the Graph gets added to the cache + return getMGraph(name); + } + } catch (NoSuchEntityException e) { + //we do nothing and try our luck with the next provider + } catch (IllegalArgumentException e) { + //we do nothing and try our luck with the next provider + } + } + throw new NoSuchEntityException(name); + } + + @Override + public Graph createGraph(IRI name) + throws UnsupportedOperationException { + + for (WeightedTcProvider provider : providerList) { + try { + Graph result = provider.createGraph(name); + // unregisters a possible ImmutableGraph or Graph service under this name + // provided by a WeightedTcProvider with a lower weight. + tcDisappears(name); + if (isCachingEnabled()) { + mGraphCache.put(name, new MGraphHolder(provider, null)); + } + mGraphAppears(name); + return result; + } catch (UnsupportedOperationException e) { + //we do nothing and try our luck with the next provider + } catch (IllegalArgumentException e) { + //we do nothing and try our luck with the next provider + } + } + throw new UnsupportedOperationException( + "No provider could create Graph."); + } + + @Override + public ImmutableGraph createImmutableGraph(IRI name, Graph triples) { + for (WeightedTcProvider provider : providerList) { + try { + ImmutableGraph result = provider.createImmutableGraph(name, triples); + + // unregisters a possible ImmutableGraph or Graph service under this name + // provided by a WeightedTcProvider with a lower weight. + tcDisappears(name); + if (isCachingEnabled()) { + mGraphCache.put(name, new MGraphHolder(provider, null)); + } + graphAppears(name); + return result; + } catch (UnsupportedOperationException e) { + //we do nothing and try our luck with the next provider + } catch (IllegalArgumentException e) { + //we do nothing and try our luck with the next provider + } + } + throw new UnsupportedOperationException( + "No provider could create ImmutableGraph."); + } + + @Override + public void deleteGraph(IRI name) { + for (TcProvider provider : providerList) { + try { + provider.deleteGraph(name); + final MGraphHolder holder = mGraphCache.get(name); + if ((holder != null) + && (holder.getWeightedTcProvider() != null) + && holder.getWeightedTcProvider().equals(provider)) { + tcDisappears(name); + mGraphCache.remove(name); + } + return; + } catch (UnsupportedOperationException e) { + // we do nothing and try our luck with the next provider + } catch (NoSuchEntityException e) { + //we do nothing and try our luck with the next provider + } catch (IllegalArgumentException e) { + //we do nothing and try our luck with the next provider + } + } + // this throws a NoSuchEntityException if the ImmutableGraph doesn't exist + getGraph(name); + // the entity exists but cannot be deleted + throw new UnsupportedOperationException( + "No provider could delete the entity."); + } + + @Override + public Set<IRI> getNames(ImmutableGraph ImmutableGraph) { + Set<IRI> result = new HashSet<IRI>(); + for (TcProvider provider : providerList) { + result.addAll(provider.getNames(ImmutableGraph)); + } + return result; + } + + @Override + public Set<IRI> listGraphs() { + Set<IRI> result = new HashSet<IRI>(); + for (TcProvider provider : providerList) { + result.addAll(provider.listGraphs()); + } + return result; + } + + @Override + public Set<IRI> listMGraphs() { + Set<IRI> result = new HashSet<IRI>(); + for (TcProvider provider : providerList) { + result.addAll(provider.listMGraphs()); + } + return result; + } + + @Override + public Set<IRI> listImmutableGraphs() { + Set<IRI> result = new HashSet<IRI>(); + for (TcProvider provider : providerList) { + result.addAll(provider.listImmutableGraphs()); + } + return result; + } + + private Graph ensureLockable(Graph providedMGraph) { + //Graphs are alway locable now + return providedMGraph; + } + + /** + * Contains an unsecured Graph, a ServiceRegistration and + * the WeightedTcProvider that generated the ImmutableGraph + */ + private static class MGraphHolder { + + private WeightedTcProvider tcProvider; + private WeakReference<Graph> mGraphReference; + + MGraphHolder(WeightedTcProvider tcProvider, Graph graph) { + this.tcProvider = tcProvider; + this.mGraphReference = new WeakReference<Graph>(graph); + } + + Graph getMGraph() { + return this.mGraphReference.get(); + } + + WeightedTcProvider getWeightedTcProvider() { + return this.tcProvider; + } + } + + //methods for debuging / monitoring + public SortedSet<WeightedTcProvider> getProviderList() { + return providerList; + } + + public boolean isCachingEnabled() { + return isCachingEnabled; + } + + public void setCachingEnabled(boolean isCachingEnabled) { + this.isCachingEnabled = isCachingEnabled; + + if (!isCachingEnabled()) { + mGraphCache.clear(); + } + } +} diff --git a/dataset/src/main/java/org/apache/clerezza/dataset/WeightedProviderComparator.java b/dataset/src/main/java/org/apache/clerezza/dataset/WeightedProviderComparator.java new file mode 100644 index 0000000..66752f3 --- /dev/null +++ b/dataset/src/main/java/org/apache/clerezza/dataset/WeightedProviderComparator.java @@ -0,0 +1,41 @@ +package org.apache.clerezza.dataset; +/* + * + * 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. + * +*/ + + +import java.util.Comparator; + +/** + * Compares the WeightedTcManagementProviders, descending for weight and + * ascending by name + */ +public class WeightedProviderComparator implements Comparator<WeightedTcProvider> { + + @Override + public int compare(WeightedTcProvider o1, WeightedTcProvider o2) { + int o1Weight = o1.getWeight(); + int o2Weight = o2.getWeight(); + if (o1Weight != o2Weight) { + return o2Weight - o1Weight; + } + return o1.getClass().toString().compareTo(o2.getClass().toString()); + } +} diff --git a/dataset/src/main/java/org/apache/clerezza/dataset/WeightedTcProvider.java b/dataset/src/main/java/org/apache/clerezza/dataset/WeightedTcProvider.java new file mode 100644 index 0000000..e50514d --- /dev/null +++ b/dataset/src/main/java/org/apache/clerezza/dataset/WeightedTcProvider.java @@ -0,0 +1,36 @@ +/* + * 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.clerezza.dataset; + +/** + * This interface is implemented by providers to which {@link TcManagerImpl} + * delegates. + * + * @author reto + */ +public interface WeightedTcProvider extends TcProvider { + + /** + * Get the weight of this provider. {@link TcManager} will prioritize + * <code>TcProvider</code>s with greater weight. + * + * @return a positive number indicating the weight of the provider + */ + int getWeight(); +} diff --git a/dataset/src/main/java/org/apache/clerezza/dataset/package.html b/dataset/src/main/java/org/apache/clerezza/dataset/package.html new file mode 100644 index 0000000..8d136ed --- /dev/null +++ b/dataset/src/main/java/org/apache/clerezza/dataset/package.html @@ -0,0 +1,56 @@ + + <!-- +/* + * 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. + */ +--> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> +<html> + <head> + <title>org.apache.clerezza.dataset</title> + <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> + </head> + <body> + <p>This package provides access to named {@link + org.apache.clerezza.api.ImmutableGraph}S and {@link + org.apache.clerezza.api.Graph}S that may originate from various + implementations.</p> + <h3>Accessing</h3> + <p> + If running in a dependency injection system you should get an instance + of + <code>TcManager</code> + by the framework, otherwise use the static method + <code>TcManager.getInstance</code> + . + </p> + <h3>Adding a provider</h3> + <p>A provider implements {@link WeightedTcProvider}.</p> + <p> + To work outside a component injection framework it contains a file + <code>META-INF/services/org.apache.clerezza.dataset.WeightedTcProvider</code> + containing the name of the implementing class. + </p> + <p> + To work in a OSGi-DS context the provider register a component + exposing + <code>org.apache.clerezza.dataset.WeightedTcProvider</code> + as service interface. + </p> + </body> +</html> \ No newline at end of file diff --git a/dataset/src/main/java/org/apache/clerezza/dataset/security/TcAccessController.java b/dataset/src/main/java/org/apache/clerezza/dataset/security/TcAccessController.java new file mode 100644 index 0000000..93b54ff --- /dev/null +++ b/dataset/src/main/java/org/apache/clerezza/dataset/security/TcAccessController.java @@ -0,0 +1,342 @@ +/* + * 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.clerezza.dataset.security; + +import org.apache.clerezza.api.*; +import org.apache.clerezza.api.impl.TripleImpl; +import org.apache.clerezza.api.impl.literal.LiteralFactory; +import org.apache.clerezza.dataset.NoSuchEntityException; +import org.apache.clerezza.dataset.TcManager; +import org.apache.clerezza.utils.security.PermissionParser; + +import java.security.AccessControlException; +import java.security.AccessController; +import java.security.AllPermission; +import java.security.Permission; +import java.util.*; +import java.util.concurrent.locks.Lock; + +/** + * Controls the permissions needed to access a triple collection provided by + * <code>TcManager</code>. + * + * Clients with a ConfigureTcAcessPermission can set the permissions required to + * access a Graph. These permissions are stored persistently in an + * Graph named urn:x-localinstance:/graph-access.graph + * + * Clients should get an instance from TcManager.getTcAccessController() + * + * @author reto + */ +public abstract class TcAccessController { + + private final TcManager tcManager; + private final IRI permissionGraphName = new IRI("urn:x-localinstance:/graph-access.graph"); + //we can't rely on ontology plugin in rdf core + private String ontologyNamespace = "http://clerezza.apache.org/2010/07/10/graphpermssions#"; + private final IRI readPermissionListProperty = new IRI(ontologyNamespace + "readPermissionList"); + private final IRI readWritePermissionListProperty = new IRI(ontologyNamespace + "readWritePermissionList"); + /** + * The first item in the subject RDF list. + */ + public static final IRI first = new IRI("http://www.w3.org/1999/02/22-rdf-syntax-ns#first"); + /** + * The rest of the subject RDF list after the first item. + */ + public static final IRI rest = new IRI("http://www.w3.org/1999/02/22-rdf-syntax-ns#rest"); + public static final IRI rdfNil = new IRI("http://www.w3.org/1999/02/22-rdf-syntax-ns#nil"); + private final Map<IRI, Collection<Permission>> readPermissionCache = + Collections.synchronizedMap(new HashMap<IRI, Collection<Permission>>()); + private final Map<IRI, Collection<Permission>> readWritePermissionCache = + Collections.synchronizedMap(new HashMap<IRI, Collection<Permission>>()); + + /** + * + * @param tcManager the tcManager used to locate urn:x-localinstance:/graph-access.graph + */ + public TcAccessController() { + this.tcManager = getTcManager(); + } + + public void checkReadPermission(IRI GraphUri) { + if (GraphUri.equals(permissionGraphName)) { + //This is world readable, as this prevents as from doingf things as + //priviledged during verfification + return; + } + SecurityManager security = System.getSecurityManager(); + if (security != null) { + //will AllPermissions the rest is obsolete + try { + AccessController.checkPermission(new AllPermission()); + } catch (AccessControlException e) { + Collection<Permission> perms = getRequiredReadPermissions(GraphUri); + if (perms.size() > 0) { + for (Permission permission : perms) { + AccessController.checkPermission(permission); + } + } else { + AccessController.checkPermission(new TcPermission( + GraphUri.getUnicodeString(), TcPermission.READ)); + } + } + } + } + + public void checkReadWritePermission(IRI GraphUri) { + SecurityManager security = System.getSecurityManager(); + if (security != null) { + //will AllPermissions the rest is obsolete + try { + AccessController.checkPermission(new AllPermission()); + } catch (AccessControlException e) { + if (GraphUri.equals(permissionGraphName)) { + AccessController.checkPermission(new TcPermission( + GraphUri.getUnicodeString(), TcPermission.READWRITE)); + } else { + Collection<Permission> perms = getRequiredReadWritePermissions(GraphUri); + if (perms.size() > 0) { + for (Permission permission : perms) { + AccessController.checkPermission(permission); + } + } else { + AccessController.checkPermission(new TcPermission( + GraphUri.getUnicodeString(), TcPermission.READWRITE)); + } + } + } + } + } + + /** + * Set the set of permissions required for read access to a triple-collection, if + * the set is non-empty the default TCPermisson is no longer required. + * + * @param GraphUri + * @param permissionDescriptions + */ + public void setRequiredReadPermissionStrings(IRI GraphUri, + Collection<String> permissionDescriptions) { + readPermissionCache.remove(GraphUri); + final Graph permissionMGraph = getOrCreatePermisionGraph(); + Lock l = permissionMGraph.getLock().writeLock(); + l.lock(); + try { + removeExistingRequiredReadPermissions(GraphUri, permissionMGraph); + final BlankNodeOrIRI permissionList = createList(permissionDescriptions.iterator(), permissionMGraph); + permissionMGraph.add(new TripleImpl(GraphUri, + readPermissionListProperty, permissionList)); + } finally { + l.unlock(); + } + } + + /** + * Set the set of permissions required for read access to a triple-collection, if + * the set is non-empty the default TCPermisson is no longer required. + * + * @param GraphUri + * @param permissionDescriptions + */ + public void setRequiredReadPermissions(IRI GraphUri, + Collection<Permission> permissions) { + Collection<String> permissionStrings = new ArrayList<String>(); + for (Permission permission : permissions) { + permissionStrings.add(permission.toString()); + } + setRequiredReadPermissionStrings(GraphUri, permissionStrings); + } + + /** + * Set the set of permissions required for read-write access to a + * triple-collection, if + * the set is non-empty the default TCPermisson is no longer required. + * + * @param GraphUri + * @param permissionDescriptions + */ + public void setRequiredReadWritePermissionStrings(IRI GraphUri, + Collection<String> permissionDescriptions) { + readWritePermissionCache.remove(GraphUri); + final Graph permissionMGraph = getOrCreatePermisionGraph(); + Lock l = permissionMGraph.getLock().writeLock(); + l.lock(); + try { + removeExistingRequiredReadPermissions(GraphUri, permissionMGraph); + final BlankNodeOrIRI permissionList = createList(permissionDescriptions.iterator(), permissionMGraph); + permissionMGraph.add(new TripleImpl(GraphUri, + readWritePermissionListProperty, permissionList)); + } finally { + l.unlock(); + } + } + + /** + * Set the set of permissions required for read-write access to a + * triple-collection, if + * the set is non-empty the default TCPermisson is no longer required. + * + * @param GraphUri + * @param permissionDescriptions + */ + public void setRequiredReadWritePermissions(IRI GraphUri, + Collection<Permission> permissions) { + Collection<String> permissionStrings = new ArrayList<String>(); + for (Permission permission : permissions) { + permissionStrings.add(permission.toString()); + } + setRequiredReadWritePermissionStrings(GraphUri, permissionStrings); + } + + /** + * Get the set of permissions required for read access to the + * triple-collection, the set may be empty meaning that the default + * TCPermission is required. + * + * @param GraphUri + * @return the collection of permissions + */ + public Collection<Permission> getRequiredReadPermissions(IRI GraphUri) { + Collection<Permission> result = readPermissionCache.get(GraphUri); + if (result == null) { + result = new ArrayList<Permission>(); + Collection<String> permissionStrings = getRequiredReadPermissionStrings(GraphUri); + for (String string : permissionStrings) { + result.add(PermissionParser.getPermission(string, getClass().getClassLoader())); + } + readPermissionCache.put(GraphUri, result); + } + return result; + } + + /** + * Get the set of permissions required for read-write access to the + * triple-collection, the set may be empty meaning that the default + * TCPermission is required. + * + * @param GraphUri + * @return the collection of permissions + */ + public Collection<Permission> getRequiredReadWritePermissions(IRI GraphUri) { + Collection<Permission> result = readWritePermissionCache.get(GraphUri); + if (result == null) { + result = new ArrayList<Permission>(); + Collection<String> permissionStrings = getRequiredReadWritePermissionStrings(GraphUri); + for (String string : permissionStrings) { + result.add(PermissionParser.getPermission(string, getClass().getClassLoader())); + } + readWritePermissionCache.put(GraphUri, result); + } + return result; + } + + private BlankNodeOrIRI createList(Iterator<String> iterator, Graph permissionMGraph) { + if (!iterator.hasNext()) { + return rdfNil; + } + final BlankNode result = new BlankNode(); + permissionMGraph.add(new TripleImpl(result, first, + LiteralFactory.getInstance().createTypedLiteral(iterator.next()))); + permissionMGraph.add(new TripleImpl(result, rest, + createList(iterator, permissionMGraph))); + return result; + + } + + //called withiong write-lock + private void removeExistingRequiredReadPermissions(IRI GraphUri, + Graph permissionMGraph) { + try { + Triple t = permissionMGraph.filter(GraphUri, readPermissionListProperty, null).next(); + RDFTerm list = t.getObject(); + removeList((BlankNodeOrIRI) list, permissionMGraph); + permissionMGraph.remove(t); + } catch (NoSuchElementException e) { + //There was no existing to remove + } + } + + private void removeList(BlankNodeOrIRI list, Graph permissionMGraph) { + try { + Triple t = permissionMGraph.filter(list, rest, null).next(); + RDFTerm restList = t.getObject(); + removeList((BlankNodeOrIRI) restList, permissionMGraph); + permissionMGraph.remove(t); + Iterator<Triple> iter = permissionMGraph.filter(list, first, null); + iter.next(); + iter.remove(); + } catch (NoSuchElementException e) { + //if it has no rest its rdf:NIL and has no first + } + } + + private Collection<String> getRequiredReadWritePermissionStrings(final IRI GraphUri) { + return getRequiredPermissionStrings(GraphUri, readWritePermissionListProperty); + } + private Collection<String> getRequiredReadPermissionStrings(final IRI GraphUri) { + return getRequiredPermissionStrings(GraphUri, readPermissionListProperty); + } + private Collection<String> getRequiredPermissionStrings(final IRI GraphUri, IRI property) { + try { + final Graph permissionMGraph = tcManager.getMGraph(permissionGraphName); + Lock l = permissionMGraph.getLock().readLock(); + l.lock(); + try { + Triple t = permissionMGraph.filter(GraphUri, property, null).next(); + BlankNodeOrIRI list = (BlankNodeOrIRI) t.getObject(); + LinkedList<String> result = new LinkedList<String>(); + readList(list, permissionMGraph, result); + return result; + } catch (NoSuchElementException e) { + return new ArrayList<String>(0); + } finally { + l.unlock(); + } + } catch (NoSuchEntityException e) { + return new ArrayList<String>(0); + } + } + + private void readList(BlankNodeOrIRI list, Graph permissionMGraph, LinkedList<String> target) { + if (list.equals(rdfNil)) { + return; + } + Triple restTriple = permissionMGraph.filter(list, rest, null).next(); + BlankNodeOrIRI restList = (BlankNodeOrIRI) restTriple.getObject(); + readList(restList, permissionMGraph, target); + Triple firstTriple = permissionMGraph.filter(list, first, null).next(); + Literal firstValue = (Literal) firstTriple.getObject(); + String value = LiteralFactory.getInstance().createObject(String.class, firstValue); + target.addFirst(value); + } + + private Graph getOrCreatePermisionGraph() { + try { + return tcManager.getMGraph(permissionGraphName); + } catch (NoSuchEntityException e) { + return tcManager.createGraph(permissionGraphName); + } + } + + /** + * Note that this will only be invoked once + * @return + */ + protected abstract TcManager getTcManager(); +} diff --git a/dataset/src/main/java/org/apache/clerezza/dataset/security/TcPermission.java b/dataset/src/main/java/org/apache/clerezza/dataset/security/TcPermission.java new file mode 100644 index 0000000..c218dd9 --- /dev/null +++ b/dataset/src/main/java/org/apache/clerezza/dataset/security/TcPermission.java @@ -0,0 +1,149 @@ +/* + * 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.clerezza.dataset.security; + +import java.security.Permission; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.regex.Pattern; + +/** + * A permission to access <code>Graph<code>s matching a specified + * name pattern. A pattern is matched if and only if the pattern is equals + * to name of the <code>Graph<code> or the pattern ends with "/*" and + * the name of the <code>Graph<code> starts with the characters + * preceding the '*' in the pattern. + * + * @author reto, tsuy + */ +public class TcPermission extends Permission { + public final static String READWRITE = "readwrite"; + public final static String READ = "read"; + + private String tcNamePattern; + /** + * true if readwrite granted false if only read + */ + private boolean allowReadWrite = false; + + final static Pattern actionPattern = Pattern.compile(",( *)"); + /** + * Conststructs a TcPermission for a specified name pattern and a list of + * actions. + * + * @param tcNamePattern see class description + * @param actions a comma separated list of the strings "read" and "readwrite", + * the canonical form is just "read" or "readwrite" as "readwrite" + * implies "read". + */ + public TcPermission(String tcNamePattern, String actions) { + super(tcNamePattern); + this.tcNamePattern = tcNamePattern; + //check and set actions + final Set actionSet = new HashSet(Arrays.asList(actionPattern.split(actions))); + if (actionSet.remove(READWRITE)) { + allowReadWrite = true; + } else { + if (!actionSet.contains(READ)) { + throw new IllegalArgumentException("actions must be either \"read\" or \"readwrite\""); + } + } + actionSet.remove(READ); + if (actionSet.size() > 0) { + throw new IllegalArgumentException("actions must only contain \"read\" and \"readwrite\""); + } + } + + @Override + public boolean implies(Permission permission) { + if (permission instanceof TcPermission) { + TcPermission other = (TcPermission) permission; + if (!patternImplies(other.tcNamePattern)) { + return false; + } + if (!actionsImplies(other.allowReadWrite)) { + return false; + } + return true; + + + } + return false; + } + + private boolean actionsImplies(boolean readwriteOther) { + if (!readwriteOther) { + return true; + } else { + return allowReadWrite; + } + + } + + private boolean patternImplies(String tcNamePatternOther) { + if (tcNamePattern.equals(tcNamePatternOther)) { + return true; + } + if (tcNamePattern.endsWith("/*")) { + return tcNamePatternOther.startsWith( + tcNamePattern.substring(0, tcNamePattern.length()-1)); + } + return false; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final TcPermission other = (TcPermission) obj; + if (this.tcNamePattern != other.tcNamePattern + && (this.tcNamePattern == null + || !this.tcNamePattern.equals(other.tcNamePattern))) { + return false; + } + if (this.allowReadWrite != other.allowReadWrite) { + return false; + } + return true; + } + + @Override + public int hashCode() { + int hash = 3; + hash = 97 * hash + (this.tcNamePattern != null ? + this.tcNamePattern.hashCode() : 0); + if (allowReadWrite) { + hash++; + } + return hash; + } + + + + @Override + public String getActions() { + return allowReadWrite ? READWRITE : READ; + } + +} diff --git a/dataset/src/test/java/org/apache/clerezza/dataset/SecurityTest.java b/dataset/src/test/java/org/apache/clerezza/dataset/SecurityTest.java new file mode 100644 index 0000000..ffea930 --- /dev/null +++ b/dataset/src/test/java/org/apache/clerezza/dataset/SecurityTest.java @@ -0,0 +1,175 @@ +/* + * 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.clerezza.dataset; + +import org.apache.clerezza.api.Graph; +import org.apache.clerezza.api.IRI; +import org.apache.clerezza.api.Triple; +import org.apache.clerezza.api.impl.graph.ReadOnlyException; +import org.apache.clerezza.api.impl.literal.PlainLiteralImpl; +import org.apache.clerezza.api.impl.TripleImpl; +import org.apache.clerezza.dataset.providers.WeightedA; +import org.apache.clerezza.dataset.providers.WeightedDummy; +import org.apache.clerezza.dataset.security.TcPermission; +import org.junit.*; + +import java.io.FilePermission; +import java.lang.reflect.ReflectPermission; +import java.security.*; +import java.util.Collections; +import java.util.PropertyPermission; + +/** + * + * @author reto + */ +public class SecurityTest { + + public SecurityTest() { + } + + @BeforeClass + public static void setUpClass() throws Exception { + ////needed to unbind because this is injected with META-INF/services - file + TcManager.getInstance().unbindWeightedTcProvider(new WeightedA()); + TcManager.getInstance().bindWeightedTcProvider(new WeightedDummy()); + TcManager.getInstance().createGraph(new IRI("http://example.org/ImmutableGraph/alreadyexists")); + TcManager.getInstance().createGraph(new IRI("http://example.org/read/ImmutableGraph")); + } + + @AfterClass + public static void tearDownClass() throws Exception { + } + + @Before + public void setUp() { + + Policy.setPolicy(new Policy() { + + @Override + public PermissionCollection getPermissions(CodeSource codeSource) { + PermissionCollection result = new Permissions(); + result.add(new TcPermission("http://example.org/permitted", "read")); + result.add(new TcPermission("http://example.org/ImmutableGraph/alreadyexists", "readwrite")); + result.add(new TcPermission("http://example.org/read/ImmutableGraph", "read")); + result.add(new TcPermission("http://example.org/area/allowed/*", "readwrite")); + result.add(new TcPermission("urn:x-localinstance:/graph-access.graph", "readwrite")); + //result.add(new AllPermission()); + result.add(new RuntimePermission("*")); + result.add(new ReflectPermission("suppressAccessChecks")); + result.add(new PropertyPermission("*", "read")); + //(java.util.PropertyPermission line.separator read) + result.add(new FilePermission("/-", "read,write")); + return result; + } + }); + System.setSecurityManager(new SecurityManager() { + + @Override + public void checkPermission(Permission perm) { + //System.out.println("Checking "+perm); + super.checkPermission(perm); + } + + @Override + public void checkPermission(Permission perm, Object context) { + //System.out.println("Checking "+perm); + super.checkPermission(perm, context); + } + + }); + } + + @After + public void tearDown() { + System.setSecurityManager(null); + } + + + @Test(expected=NoSuchEntityException.class) + public void testAcessGraph() { + TcManager.getInstance().getImmutableGraph(new IRI("http://example.org/permitted")); + } + + @Test(expected=AccessControlException.class) + public void testNoWildCard() { + TcManager.getInstance().getImmutableGraph(new IRI("http://example.org/permitted/subthing")); + } + + @Test(expected=NoSuchEntityException.class) + public void testAllowedArea() { + TcManager.getInstance().getImmutableGraph(new IRI("http://example.org/area/allowed/something")); + } + + @Test(expected=AccessControlException.class) + public void testAcessForbiddenGraph() { + TcManager.getInstance().getImmutableGraph(new IRI("http://example.org/forbidden")); + } + + @Test(expected=NoSuchEntityException.class) + public void testCustomPermissions() { + IRI graphUri = new IRI("http://example.org/custom"); + TcManager.getInstance().getTcAccessController().setRequiredReadPermissionStrings(graphUri, + Collections.singletonList("(java.io.FilePermission \"/etc\" \"write\")")); + //new FilePermission("/etc", "write").toString())); + Graph ag = TcManager.getInstance().getGraph(new IRI("urn:x-localinstance:/graph-access.graph")); + System.out.print(ag.toString()); + TcManager.getInstance().getMGraph(graphUri); + } + + @Test(expected=AccessControlException.class) + public void testCustomPermissionsIncorrect() { + IRI graphUri = new IRI("http://example.org/custom"); + TcManager.getInstance().getTcAccessController().setRequiredReadPermissionStrings(graphUri, + Collections.singletonList("(java.io.FilePermission \"/etc\" \"write\")")); + //new FilePermission("/etc", "write").toString())); + Graph ag = TcManager.getInstance().getGraph(new IRI("urn:x-localinstance:/graph-access.graph")); + System.out.print(ag.toString()); + TcManager.getInstance().createGraph(graphUri); + } + + @Test + public void testCustomReadWritePermissions() { + IRI graphUri = new IRI("http://example.org/read-write-custom"); + TcManager.getInstance().getTcAccessController().setRequiredReadWritePermissionStrings(graphUri, + Collections.singletonList("(java.io.FilePermission \"/etc\" \"write\")")); + //new FilePermission("/etc", "write").toString())); + Graph ag = TcManager.getInstance().getGraph(new IRI("urn:x-localinstance:/graph-access.graph")); + System.out.print(ag.toString()); + TcManager.getInstance().createGraph(graphUri); + } + + @Test(expected=EntityAlreadyExistsException.class) + public void testCreateMGraph() { + TcManager.getInstance().createGraph(new IRI("http://example.org/ImmutableGraph/alreadyexists")); + } + @Test(expected=AccessControlException.class) + public void testCreateMGraphWithoutWritePermission() { + TcManager.getInstance().createGraph(new IRI("http://example.org/read/ImmutableGraph")); + } + @Test(expected=ReadOnlyException.class) + public void testAddTripleToMGraph() { + Graph graph = TcManager.getInstance().getMGraph(new IRI("http://example.org/read/ImmutableGraph")); + Triple triple = new TripleImpl( + new IRI("http://example.org/definition/isNonLiteral"), + new IRI("http://example.org/definition/isTest"), + new PlainLiteralImpl("test")); + graph.add(triple); + } +} \ No newline at end of file diff --git a/dataset/src/test/java/org/apache/clerezza/dataset/TcManagerTest.java b/dataset/src/test/java/org/apache/clerezza/dataset/TcManagerTest.java new file mode 100644 index 0000000..619e86a --- /dev/null +++ b/dataset/src/test/java/org/apache/clerezza/dataset/TcManagerTest.java @@ -0,0 +1,299 @@ +/* + * 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.clerezza.dataset; + +import org.apache.clerezza.api.Graph; +import org.apache.clerezza.api.IRI; +import org.apache.clerezza.api.ImmutableGraph; +import org.apache.clerezza.api.Triple; +import org.apache.clerezza.api.impl.TripleImpl; +import org.apache.clerezza.api.impl.graph.SimpleMGraph; +import org.apache.clerezza.dataset.providers.WeightedA; +import org.apache.clerezza.dataset.providers.WeightedA1; +import org.apache.clerezza.dataset.providers.WeightedAHeavy; +import org.apache.clerezza.dataset.providers.WeightedBlight; +import org.apache.clerezza.sparql.NoQueryEngineException; +import org.apache.clerezza.sparql.QueryEngine; +import org.apache.clerezza.sparql.query.*; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import java.lang.reflect.Field; +import java.util.Iterator; + +import static org.junit.Assert.*; + +/** + * + * @author reto + */ +public class TcManagerTest { + + public static IRI uriRefAHeavy = new IRI("http://example.org/aHeavy"); + public static IRI uriRefB = new IRI("http://example.org/b");; + public static final IRI uriRefA = new IRI("http://example.org/a"); + public static final IRI uriRefA1 = new IRI("http://example.org/a1"); + private TcManager graphAccess; + private QueryEngine queryEngine; + private final WeightedA weightedA = new WeightedA(); + private final WeightedA1 weightedA1 = new WeightedA1(); + private WeightedTcProvider weightedBlight = new WeightedBlight(); + + @Before + public void setUp() { + graphAccess = TcManager.getInstance(); + graphAccess.bindWeightedTcProvider(weightedA); + graphAccess.bindWeightedTcProvider(weightedA1); + graphAccess.bindWeightedTcProvider(weightedBlight); + + queryEngine = Mockito.mock(QueryEngine.class); + } + + @After + public void tearDown() { + graphAccess = TcManager.getInstance(); + graphAccess.unbindWeightedTcProvider(weightedA); + graphAccess.unbindWeightedTcProvider(weightedA1); + graphAccess.unbindWeightedTcProvider(weightedBlight); + + queryEngine = null; + } + + @Test + public void getGraphFromA() { + ImmutableGraph graphA = graphAccess.getImmutableGraph(uriRefA); + Iterator<Triple> iterator = graphA.iterator(); + assertEquals(new TripleImpl(uriRefA, uriRefA, uriRefA), iterator.next()); + assertFalse(iterator.hasNext()); + Graph triplesA = graphAccess.getGraph(uriRefA); + iterator = triplesA.iterator(); + assertEquals(new TripleImpl(uriRefA, uriRefA, uriRefA), iterator.next()); + assertFalse(iterator.hasNext()); + } + + @Test + public void getGraphFromB() { + ImmutableGraph graphA = graphAccess.getImmutableGraph(uriRefB); + Iterator<Triple> iterator = graphA.iterator(); + assertEquals(new TripleImpl(uriRefB, uriRefB, uriRefB), iterator.next()); + assertFalse(iterator.hasNext()); + Graph triplesA = graphAccess.getGraph(uriRefB); + iterator = triplesA.iterator(); + assertEquals(new TripleImpl(uriRefB, uriRefB, uriRefB), iterator.next()); + assertFalse(iterator.hasNext()); + } + + @Test + public void getGraphFromAAfterUnbinding() { + graphAccess.unbindWeightedTcProvider(weightedA); + ImmutableGraph graphA = graphAccess.getImmutableGraph(uriRefA); + Iterator<Triple> iterator = graphA.iterator(); + assertEquals(new TripleImpl(uriRefA1, uriRefA1, uriRefA1), + iterator.next()); + assertFalse(iterator.hasNext()); + Graph triplesA = graphAccess.getGraph(uriRefA); + iterator = triplesA.iterator(); + assertEquals(new TripleImpl(uriRefA1, uriRefA1, uriRefA1), + iterator.next()); + assertFalse(iterator.hasNext()); + } + + @Test + public void getGraphFromAWithHeavy() { + final WeightedAHeavy weightedAHeavy = new WeightedAHeavy(); + graphAccess.bindWeightedTcProvider(weightedAHeavy); + ImmutableGraph graphA = graphAccess.getImmutableGraph(uriRefA); + Iterator<Triple> iterator = graphA.iterator(); + assertEquals(new TripleImpl(uriRefAHeavy, uriRefAHeavy, uriRefAHeavy), + iterator.next()); + assertFalse(iterator.hasNext()); + Graph triplesA = graphAccess.getGraph(uriRefA); + iterator = triplesA.iterator(); + assertEquals(new TripleImpl(uriRefAHeavy, uriRefAHeavy, uriRefAHeavy), + iterator.next()); + assertFalse(iterator.hasNext()); + graphAccess.unbindWeightedTcProvider(weightedAHeavy); + } + + @Test(expected = NoQueryEngineException.class) + public void executeSparqlQueryNoEngineWithString() throws Exception { + // Prepare + injectQueryEngine(null); + + // Execute + graphAccess.executeSparqlQuery("", new SimpleMGraph()); + } + + @Test(expected = NoQueryEngineException.class) + public void executeSparqlQueryNoEngineWithQuery() throws Exception { + // Prepare + injectQueryEngine(null); + + // Execute + graphAccess.executeSparqlQuery((Query) null, new SimpleMGraph()); + } + + @Test(expected = NoQueryEngineException.class) + public void executeSparqlQueryNoEngineWithSelectQuery() throws Exception { + // Prepare + injectQueryEngine(null); + + // Execute + graphAccess.executeSparqlQuery((SelectQuery) null, new SimpleMGraph()); + } + + @Test(expected = NoQueryEngineException.class) + public void executeSparqlQueryNoEngineWithAskQuery() throws Exception { + // Prepare + injectQueryEngine(null); + + // Execute + graphAccess.executeSparqlQuery((AskQuery) null, new SimpleMGraph()); + } + + @Test(expected = NoQueryEngineException.class) + public void executeSparqlQueryNoEngineWithDescribeQuery() throws Exception { + // Prepare + injectQueryEngine(null); + + // Execute + graphAccess + .executeSparqlQuery((DescribeQuery) null, new SimpleMGraph()); + } + + @Test(expected = NoQueryEngineException.class) + public void executeSparqlQueryNoEngineWithConstructQuery() throws Exception { + // Prepare + injectQueryEngine(null); + + // Execute + graphAccess.executeSparqlQuery((ConstructQuery) null, + new SimpleMGraph()); + } + + @Test + public void executeSparqlQueryWithEngineWithString() throws Exception { + // Prepare + injectQueryEngine(queryEngine); + Graph Graph = new SimpleMGraph(); + + // Execute + graphAccess.executeSparqlQuery("", Graph); + + // Verify + Mockito.verify(queryEngine).execute(graphAccess, Graph, ""); +// Mockito.verify(queryEngine, Mockito.never()).execute( +// (TcManager) Mockito.anyObject(), +// (Graph) Mockito.anyObject(), +// Mockito.anyString()); + } + + @Test + public void executeSparqlQueryWithEngineWithSelectQuery() throws Exception { + // Prepare + injectQueryEngine(queryEngine); + Graph Graph = new SimpleMGraph(); + SelectQuery query = Mockito.mock(SelectQuery.class); + + // Execute + graphAccess.executeSparqlQuery(query, Graph); + + // Verify + Mockito.verify(queryEngine).execute(graphAccess, Graph, + query.toString()); +// Mockito.verify(queryEngine, Mockito.never()).execute( +// (TcManager) Mockito.anyObject(), +// (Graph) Mockito.anyObject(), Mockito.anyString()); + } + + @Test + public void executeSparqlQueryWithEngineWithAskQuery() throws Exception { + // Prepare + injectQueryEngine(queryEngine); + Graph Graph = new SimpleMGraph(); + AskQuery query = Mockito.mock(AskQuery.class); + + Mockito.when( + queryEngine.execute((TcManager) Mockito.anyObject(), + (Graph) Mockito.anyObject(), + Mockito.anyString())).thenReturn(Boolean.TRUE); + + // Execute + graphAccess.executeSparqlQuery(query, Graph); + + // Verify + Mockito.verify(queryEngine).execute(graphAccess, Graph, + query.toString()); +// Mockito.verify(queryEngine, Mockito.never()).execute( +// (TcManager) Mockito.anyObject(), +// (Graph) Mockito.anyObject(), Mockito.anyString()); + } + + @Test + public void executeSparqlQueryWithEngineWithDescribeQuery() + throws Exception { + // Prepare + injectQueryEngine(queryEngine); + Graph Graph = new SimpleMGraph(); + DescribeQuery query = Mockito.mock(DescribeQuery.class); + + // Execute + graphAccess.executeSparqlQuery(query, Graph); + + // Verify + Mockito.verify(queryEngine).execute(graphAccess, Graph, + query.toString()); +// Mockito.verify(queryEngine, Mockito.never()).execute( +// (TcManager) Mockito.anyObject(), +// (Graph) Mockito.anyObject(), Mockito.anyString()); + } + + @Test + public void executeSparqlQueryWithEngineWithConstructQuery() + throws Exception { + // Prepare + injectQueryEngine(queryEngine); + Graph Graph = new SimpleMGraph(); + ConstructQuery query = Mockito.mock(ConstructQuery.class); + + // Execute + graphAccess.executeSparqlQuery(query, Graph); + + // Verify + Mockito.verify(queryEngine).execute(graphAccess, Graph, + query.toString()); +// Mockito.verify(queryEngine, Mockito.never()).execute( +// (TcManager) Mockito.anyObject(), +// (Graph) Mockito.anyObject(), Mockito.anyString()); + } + + // ------------------------------------------------------------------------ + // Implementing QueryableTcProvider + // ------------------------------------------------------------------------ + + private void injectQueryEngine(QueryEngine engine) + throws NoSuchFieldException, IllegalAccessException { + Field queryEngineField = TcManager.class + .getDeclaredField("queryEngine"); + queryEngineField.setAccessible(true); + queryEngineField.set(graphAccess, engine); + } +} \ No newline at end of file diff --git a/dataset/src/test/java/org/apache/clerezza/dataset/providers/WeightedA.java b/dataset/src/test/java/org/apache/clerezza/dataset/providers/WeightedA.java new file mode 100644 index 0000000..76b6f36 --- /dev/null +++ b/dataset/src/test/java/org/apache/clerezza/dataset/providers/WeightedA.java @@ -0,0 +1,103 @@ +/* + * 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.clerezza.dataset.providers; + +import org.apache.clerezza.api.Graph; +import org.apache.clerezza.api.IRI; +import org.apache.clerezza.api.ImmutableGraph; +import org.apache.clerezza.api.impl.TripleImpl; +import org.apache.clerezza.api.impl.graph.SimpleMGraph; +import org.apache.clerezza.dataset.EntityUndeletableException; +import org.apache.clerezza.dataset.NoSuchEntityException; +import org.apache.clerezza.dataset.TcManagerTest; +import org.apache.clerezza.dataset.WeightedTcProvider; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * + * @author reto + */ +public class WeightedA implements WeightedTcProvider { + private Set<IRI> mGraphList = new HashSet<IRI>(); + @Override + public int getWeight() { + return 5; + } + + @Override + public ImmutableGraph getImmutableGraph(IRI name) throws NoSuchEntityException { + if (name.equals(TcManagerTest.uriRefA)) { + Graph mResult = new SimpleMGraph(); + mResult.add(new TripleImpl(TcManagerTest.uriRefA, + TcManagerTest.uriRefA, TcManagerTest.uriRefA)); + mGraphList.add(name); + return mResult.getImmutableGraph(); + } + throw new NoSuchEntityException(name); + } + + @Override + public Graph getMGraph(IRI name) throws NoSuchEntityException { + throw new NoSuchEntityException(name); + } + + @Override + public Graph getGraph(IRI name) throws NoSuchEntityException { + return getImmutableGraph(name); + } + + @Override + public Graph createGraph(IRI name) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public ImmutableGraph createImmutableGraph(IRI name, Graph triples) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void deleteGraph(IRI name) throws NoSuchEntityException, + EntityUndeletableException { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Set<IRI> getNames(ImmutableGraph ImmutableGraph) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Set<IRI> listGraphs() { + return Collections.singleton(TcManagerTest.uriRefA); + } + + @Override + public Set<IRI> listMGraphs() { + return mGraphList; + } + + @Override + public Set<IRI> listImmutableGraphs() { + return listGraphs(); + } +} diff --git a/dataset/src/test/java/org/apache/clerezza/dataset/providers/WeightedA1.java b/dataset/src/test/java/org/apache/clerezza/dataset/providers/WeightedA1.java new file mode 100644 index 0000000..effd599 --- /dev/null +++ b/dataset/src/test/java/org/apache/clerezza/dataset/providers/WeightedA1.java @@ -0,0 +1,103 @@ +/* + * 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.clerezza.dataset.providers; + +import org.apache.clerezza.api.Graph; +import org.apache.clerezza.api.IRI; +import org.apache.clerezza.api.ImmutableGraph; +import org.apache.clerezza.api.impl.TripleImpl; +import org.apache.clerezza.api.impl.graph.SimpleMGraph; +import org.apache.clerezza.dataset.EntityUndeletableException; +import org.apache.clerezza.dataset.NoSuchEntityException; +import org.apache.clerezza.dataset.TcManagerTest; +import org.apache.clerezza.dataset.WeightedTcProvider; + +import java.util.HashSet; +import java.util.Set; + +/** + * Same weight as WeightedA, but later in string-ordering + * + * @author reto + */ +public class WeightedA1 implements WeightedTcProvider { + private Set<IRI> mGraphList = new HashSet<IRI>(); + @Override + public int getWeight() { + return 5; + } + + @Override + public ImmutableGraph getImmutableGraph(IRI name) throws NoSuchEntityException { + if (name.equals(TcManagerTest.uriRefA)) { + Graph mResult = new SimpleMGraph(); + mResult.add(new TripleImpl(TcManagerTest.uriRefA1, + TcManagerTest.uriRefA1, TcManagerTest.uriRefA1)); + mGraphList.add(name); + return mResult.getImmutableGraph(); + } + throw new NoSuchEntityException(name); + } + + @Override + public Graph getMGraph(IRI name) throws NoSuchEntityException { + throw new NoSuchEntityException(name); + } + + @Override + public Graph getGraph(IRI name) throws NoSuchEntityException { + return getImmutableGraph(name); + } + + @Override + public Graph createGraph(IRI name) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public ImmutableGraph createImmutableGraph(IRI name, Graph triples) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void deleteGraph(IRI name) throws NoSuchEntityException, + EntityUndeletableException { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Set<IRI> getNames(ImmutableGraph ImmutableGraph) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Set<IRI> listImmutableGraphs() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Set<IRI> listMGraphs() { + return mGraphList; + } + + @Override + public Set<IRI> listGraphs() { + return listMGraphs(); + } +} diff --git a/dataset/src/test/java/org/apache/clerezza/dataset/providers/WeightedAHeavy.java b/dataset/src/test/java/org/apache/clerezza/dataset/providers/WeightedAHeavy.java new file mode 100644 index 0000000..3cc1421 --- /dev/null +++ b/dataset/src/test/java/org/apache/clerezza/dataset/providers/WeightedAHeavy.java @@ -0,0 +1,101 @@ +/* + * 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.clerezza.dataset.providers; + +import org.apache.clerezza.api.Graph; +import org.apache.clerezza.api.IRI; +import org.apache.clerezza.api.ImmutableGraph; +import org.apache.clerezza.api.impl.TripleImpl; +import org.apache.clerezza.api.impl.graph.SimpleGraph; +import org.apache.clerezza.dataset.EntityUndeletableException; +import org.apache.clerezza.dataset.NoSuchEntityException; +import org.apache.clerezza.dataset.TcManagerTest; +import org.apache.clerezza.dataset.WeightedTcProvider; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +/** + * + * @author reto + */ +public class WeightedAHeavy implements WeightedTcProvider { + + @Override + public int getWeight() { + return 20; + } + + @Override + public ImmutableGraph getImmutableGraph(IRI name) throws NoSuchEntityException { + if (name.equals(TcManagerTest.uriRefA)) { + Graph mResult = new SimpleGraph(); + mResult.add(new TripleImpl(TcManagerTest.uriRefAHeavy, + TcManagerTest.uriRefAHeavy, TcManagerTest.uriRefAHeavy)); + return mResult.getImmutableGraph(); + } + throw new NoSuchEntityException(name); + } + + @Override + public Graph getMGraph(IRI name) throws NoSuchEntityException { + throw new NoSuchEntityException(name); + } + + @Override + public Graph getGraph(IRI name) throws NoSuchEntityException { + return getImmutableGraph(name); + } + + @Override + public Graph createGraph(IRI name) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public ImmutableGraph createImmutableGraph(IRI name, Graph triples) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void deleteGraph(IRI name) throws NoSuchEntityException, + EntityUndeletableException { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Set<IRI> getNames(ImmutableGraph ImmutableGraph) { + throw new UnsupportedOperationException("Not supported yet."); + } + @Override + public Set<IRI> listGraphs() { + return Collections.singleton(TcManagerTest.uriRefA); + } + + @Override + public Set<IRI> listMGraphs() { + return new HashSet<IRI>(); + } + + @Override + public Set<IRI> listImmutableGraphs() { + return listGraphs(); + } +} diff --git a/dataset/src/test/java/org/apache/clerezza/dataset/providers/WeightedBlight.java b/dataset/src/test/java/org/apache/clerezza/dataset/providers/WeightedBlight.java new file mode 100644 index 0000000..161599b --- /dev/null +++ b/dataset/src/test/java/org/apache/clerezza/dataset/providers/WeightedBlight.java @@ -0,0 +1,106 @@ +/* + * 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.clerezza.dataset.providers; + +import org.apache.clerezza.api.Graph; +import org.apache.clerezza.api.IRI; +import org.apache.clerezza.api.ImmutableGraph; +import org.apache.clerezza.api.impl.TripleImpl; +import org.apache.clerezza.api.impl.graph.SimpleMGraph; +import org.apache.clerezza.dataset.EntityUndeletableException; +import org.apache.clerezza.dataset.NoSuchEntityException; +import org.apache.clerezza.dataset.TcManagerTest; +import org.apache.clerezza.dataset.WeightedTcProvider; + +import java.util.HashSet; +import java.util.Set; + +/** + * + * @author reto + */ +public class WeightedBlight implements WeightedTcProvider { + + @Override + public int getWeight() { + return 2; + } + + @Override + public ImmutableGraph getImmutableGraph(IRI name) throws NoSuchEntityException { + if (name.equals(TcManagerTest.uriRefB)) { + Graph mResult = new SimpleMGraph(); + mResult.add(new TripleImpl(TcManagerTest.uriRefB, TcManagerTest.uriRefB, TcManagerTest.uriRefB)); + return mResult.getImmutableGraph(); + } + if (name.equals(TcManagerTest.uriRefA)) { + Graph mResult = new SimpleMGraph(); + //empty ImmutableGraph + return mResult.getImmutableGraph(); + } + throw new NoSuchEntityException(name); + } + + @Override + public Graph getMGraph(IRI name) throws NoSuchEntityException { + throw new NoSuchEntityException(name); + } + + @Override + public Graph getGraph(IRI name) throws NoSuchEntityException { + return getImmutableGraph(name); + } + + @Override + public Graph createGraph(IRI name) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public ImmutableGraph createImmutableGraph(IRI name, Graph triples) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public void deleteGraph(IRI name) throws NoSuchEntityException, + EntityUndeletableException { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Set<IRI> getNames(ImmutableGraph ImmutableGraph) { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Set<IRI> listImmutableGraphs() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Set<IRI> listMGraphs() { + return new HashSet<IRI>(); + } + + @Override + public Set<IRI> listGraphs() { + return listMGraphs(); + } + +} diff --git a/dataset/src/test/java/org/apache/clerezza/dataset/providers/WeightedDummy.java b/dataset/src/test/java/org/apache/clerezza/dataset/providers/WeightedDummy.java new file mode 100644 index 0000000..44ace0e --- /dev/null +++ b/dataset/src/test/java/org/apache/clerezza/dataset/providers/WeightedDummy.java @@ -0,0 +1,155 @@ +/* + * 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.clerezza.dataset.providers; + +import org.apache.clerezza.api.Graph; +import org.apache.clerezza.api.IRI; +import org.apache.clerezza.api.ImmutableGraph; +import org.apache.clerezza.api.impl.graph.SimpleImmutableGraph; +import org.apache.clerezza.api.impl.graph.SimpleMGraph; +import org.apache.clerezza.dataset.EntityAlreadyExistsException; +import org.apache.clerezza.dataset.EntityUndeletableException; +import org.apache.clerezza.dataset.NoSuchEntityException; +import org.apache.clerezza.dataset.WeightedTcProvider; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +/** + * + * @author mir + */ +public class WeightedDummy implements WeightedTcProvider { + + private Map<IRI, Graph> tripleMap = new HashMap<IRI, Graph>(); + + @Override + public ImmutableGraph createImmutableGraph(IRI name, Graph triples) + throws EntityAlreadyExistsException { + if ((name == null) || (name.getUnicodeString() == null) + || (name.getUnicodeString().trim().length() == 0)) { + throw new IllegalArgumentException("Name must not be null"); + } + + try { + // throws NoSuchEntityException if a Graph with that name + // already exists + this.getGraph(name); + } catch (NoSuchEntityException e) { + ImmutableGraph result; + if (ImmutableGraph.class.isAssignableFrom(triples.getClass())) { + result = (ImmutableGraph) triples; + } else { + result = new SimpleImmutableGraph(triples); + } + tripleMap.put(name, result); + + return result; + } + throw new EntityAlreadyExistsException(name); + } + + @Override + public Graph createGraph(IRI name) throws EntityAlreadyExistsException { + if ((name == null) || (name.getUnicodeString() == null) + || (name.getUnicodeString().trim().length() == 0)) { + throw new IllegalArgumentException("Name must not be null"); + } + + try { + // throws NoSuchEntityException if a Graph with that name + // already exists + this.getGraph(name); + } catch (NoSuchEntityException e) { + Graph result = new SimpleMGraph(); + tripleMap.put(name, result); + return result; + } + throw new EntityAlreadyExistsException(name); + } + + @Override + public void deleteGraph(IRI name) + throws NoSuchEntityException, EntityUndeletableException { + if (tripleMap.remove(name) == null) { + throw new NoSuchEntityException(name); + } + } + + @Override + public ImmutableGraph getImmutableGraph(IRI name) throws NoSuchEntityException { + Graph Graph = tripleMap.get(name); + if (Graph == null) { + throw new NoSuchEntityException(name); + } else if (ImmutableGraph.class.isAssignableFrom(Graph.getClass())) { + return (ImmutableGraph) Graph; + } + throw new NoSuchEntityException(name); + } + + @Override + public Graph getMGraph(IRI name) throws NoSuchEntityException { + Graph Graph = tripleMap.get(name); + if (Graph == null) { + throw new NoSuchEntityException(name); + } else if (Graph.class.isAssignableFrom(Graph.getClass())) { + return (Graph) Graph; + } + throw new NoSuchEntityException(name); + } + + @Override + public Set<IRI> getNames(ImmutableGraph ImmutableGraph) { + throw new UnsupportedOperationException( + "Not supported yet. equals() has to be implemented first"); + } + + @Override + public Graph getGraph(IRI name) + throws NoSuchEntityException { + Graph Graph = tripleMap.get(name); + if (Graph == null) { + throw new NoSuchEntityException(name); + } else { + return Graph; + } + } + + @Override + public int getWeight() { + return 1; + } + + @Override + public Set<IRI> listImmutableGraphs() { + throw new UnsupportedOperationException("Not supported yet."); + } + + @Override + public Set<IRI> listMGraphs() { + return new HashSet<IRI>(); + } + + @Override + public Set<IRI> listGraphs() { + return listMGraphs(); + } +}
