{"id":50911536,"url":"https://github.com/dfki/atic","last_synced_at":"2026-06-16T11:00:30.085Z","repository":{"id":361205260,"uuid":"1253447075","full_name":"DFKI/ATIC","owner":"DFKI","description":"A Specialized Quadstore Tailored for Corporate Memories","archived":false,"fork":false,"pushed_at":"2026-06-11T12:48:51.000Z","size":3667,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-11T14:22:53.912Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DFKI.png","metadata":{"files":{"readme":"README.MD","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-05-29T13:22:33.000Z","updated_at":"2026-06-11T12:48:57.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/DFKI/ATIC","commit_stats":null,"previous_names":["dfki/atic"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/DFKI/ATIC","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DFKI%2FATIC","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DFKI%2FATIC/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DFKI%2FATIC/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DFKI%2FATIC/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DFKI","download_url":"https://codeload.github.com/DFKI/ATIC/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DFKI%2FATIC/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34402663,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-16T02:00:06.860Z","response_time":126,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-06-16T11:00:16.675Z","updated_at":"2026-06-16T11:00:30.055Z","avatar_url":"https://github.com/DFKI.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Towards ATIC: A Specialized Quadstore Tailored for Corporate Memories\n\n\u003cdiv align=\"center\" style=\"font-size: 20px\"\u003e\n\n 🚀 [Overview](#overview) • 📦 [Installation](#installation) • ⚙️ [Usage](#usage) • 🛠️ [Developer Guide](#developer-guide) • 📊 [Evaluation](#evaluation)\n\n\u003c/div\u003e\n\n**Table of Contents**\n\n\u003c!-- TOC depthFrom:2 depthTo:5 withLinks:1 updateOnSave:1 orderedList:0 --\u003e\n\n- [Overview](#overview)\n- [Installation](#installation)\n- [Usage](#usage)\n\t- [Developer API (Java / SDK)](#developer-api-java-sdk)\n\t\t- [Factory](#factory)\n\t\t- [Using the Dataset](#using-the-dataset)\n\t\t- [ATIC-Specific Methods](#atic-specific-methods)\n\t\t\t- [Invocation Context](#invocation-context)\n\t\t\t- [User \\\u0026 Group Management](#user-group-management)\n\t\t\t- [Discretionary Access Control (Sharing)](#discretionary-access-control-sharing)\n\t\t\t- [Virtual Graph](#virtual-graph)\n\t\t\t- [RDF Patch Apply and Listener](#rdf-patch-apply-and-listener)\n\t\t\t- [Data Generation](#data-generation)\n\t\t\t- [Query Logger](#query-logger)\n\t- [REST / Server Usage](#rest-server-usage)\n\t\t- [Configuration](#configuration)\n\t\t- [Authorization](#authorization)\n\t\t- [POST `/auth/register`](#post-authregister)\n\t\t- [POST `/auth/token`](#post-authtoken)\n\t\t- [GET `/auth/me`](#get-authme)\n\t\t- [POST `/auth/logout`](#post-authlogout)\n\t\t- [GET `/config`](#get-config)\n\t\t- [GET `/config/{name}`](#get-configname)\n\t\t- [GET `/sparql`](#get-sparql)\n\t\t- [POST `/sparql`](#post-sparql)\n\t\t- [POST `/update`](#post-update)\n\t\t- [GET `/graph`](#get-graph)\n\t\t- [POST `/graph`](#post-graph)\n\t\t- [DELETE `/graph/{uri}`](#delete-graphuri)\n\t\t- [POST `/graph/share`](#post-graphshare)\n\t\t- [DELETE `/graph/share`](#delete-graphshare)\n\t\t- [GET `/graph/access`](#get-graphaccess)\n\t\t- [POST `/resource/share`](#post-resourceshare)\n\t\t- [DELETE `/resource/share`](#delete-resourceshare)\n\t\t- [GET `/resource/access`](#get-resourceaccess)\n\t\t- [POST `/upload`](#post-upload)\n\t\t- [GET `/principal`](#get-principal)\n- [Developer Guide](#developer-guide)\n\t- [atic-api](#atic-api)\n\t\t- [jenatic](#jenatic)\n\t\t- [ac](#ac)\n\t\t- [api](#api)\n\t\t- [conf](#conf)\n\t- [atic-sqlite](#atic-sqlite)\n\t\t- [SQL Database Schema](#sql-database-schema)\n\t\t\t- [Resource Description Framework (RDF)](#resource-description-framework-rdf)\n\t\t\t- [User \u0026 Group Management](#user-group-management)\n\t\t\t- [Access Control](#access-control)\n\t\t\t- [RDF-star](#rdf-star)\n\t\t- [Java Classes](#java-classes)\n\t- [atic-server](#atic-server)\n\t\t- [Java Classes](#java-classes)\n- [Evaluation](#evaluation)\n\t- [Correctness](#correctness)\n\t- [Performance](#performance)\n\t\t- [AticTdbComparisonBsbmLoading](#atictdbcomparisonbsbmloading)\n\t\t- [AticTdbComparisonBsbmQueryMix](#atictdbcomparisonbsbmquerymix)\n\n\u003c!-- /TOC --\u003e\n\n## Overview\n\nCorporate Memories are computer systems in organizations that continuously collect, update and structure knowledge for various tasks.\nThe Resource Description Framework (RDF) is commonly used to represent such knowledge, which is stored in quadstores as subject–predicate–object–graph quadruples.\nSince typical RDF stores lack crucial features for enterprise scenarios, we propose an initial version of a tailored quadstore as part of a larger system called ATIC.\nOur implementation in this demo paper comprises RDF management with an SQLite backend, Discretionary Access Control on graphs and resources, confidence-attributed triples, change tracking with RDF patches and a virtual graph for accessing a file hosting service via RDF.\nThis is accompanied with a server component providing endpoints and corresponding frontends.\nResults of a preliminary evaluation verify the correct operation of our prototype and indicate moderate performance.\n\nOur [demo page](https://www.dfki.uni-kl.de/~mschroeder/demo/atic/) contains a link to a deployed instance for testing and also presents a video showing the main aspects in action.\n\n## Installation\n\nTo build and install all projects, run:\n```\nmvn install\n```\n\nIf you would like to skip the JUnit test cases, run:\n```\nmvn install -DskipTests\n```\n\nIf you would like to get the executable `atic.jar` which contains the server component, run:\n```\nmvn install -DskipTests -Pdeploy -pl atic-server -am\n```\nYou will find the file under `atic-server/target/atic.jar`.\n\n\n## Usage\n\n### Developer API (Java / SDK)\n\nInclude this project in your pom.xml as a dependency:\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003ede.dfki.sds\u003c/groupId\u003e\n    \u003cartifactId\u003eatic-sqlite\u003c/artifactId\u003e\n    \u003cversion\u003e1.0.0\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n#### Factory\n\nUse its factory, to create an instance (similar to TDB2):\n```java\nDataset dataset = AticFactory.connectDataset(\"path/to/folder\");\n```\n\nFor testing purposes, you can create a `DatasetGraph` backed by a temporary storage location.\n```java\nDatasetGraph datasetGraph = AticFactory.createTxn();\n```\n\nUse this for accessing the `DatasetGraph` as `admin` user.\n```java\nDatasetGraph datasetGraph = AticFactory.createTxnAdminDataset();\n```\n\n#### Using the Dataset\n\nUse it as a [Jena Dataset](https://jena.apache.org/documentation/javadoc/arq/org.apache.jena.arq/org/apache/jena/query/Dataset.html).\n\nTo switch the user who invokes the calls:\n\n```java\nUser user = server.getDatasetGraph().calculateRead(() -\u003e {\n    return server.getDatasetGraph().getUser(username, InvocationContext.EMPTY);\n});\nInvocationContext ictx = new InvocationContext.Builder().fromUser(user).build();\nictx.transferContext(dataset.getContext());\n```\n\n#### ATIC-Specific Methods\n\nCast to our specific implementation to access the methods.\n\n```java\nDatasetGraph datasetGraph;\nSqliteAticDatasetGraph aticDatasetGraph = (SqliteAticDatasetGraph) datasetGraph;\n```\n\n##### Invocation Context\n\nEach method is extended with an `InvocationContext` which mainly states what user is requesting the operation.\nYou can build an `InvocationContext` from a `User` in the following way:\n```java\nUser user = server.getDatasetGraph().calculateRead(() -\u003e {\n    return server.getDatasetGraph().getUser(username, InvocationContext.EMPTY);\n});\nInvocationContext ictx = new InvocationContext.Builder().fromUser(user).build();\n```\n\nUsers with different permissions may receive different results for the same operations and arguments.\n```java\nInvocationContext user1ctx;\nInvocationContext user2ctx;\n\ndatasetGraph.find(Node.ANY, Node.ANY, Node.ANY, Node.ANY, user1ctx);\ndatasetGraph.find(Node.ANY, Node.ANY, Node.ANY, Node.ANY, user2ctx);\n```\n\nTo pass no `InvocationContext` use `InvocationContext.EMPTY` constant.  \n\n##### User \\\u0026 Group Management\n\n* `String addUser(String firstname, String lastname, String email, String username, InvocationContext ctx)`\n* `void addGroup(String groupname, InvocationContext ctx)`\n* `User getUser(String username, InvocationContext ctx)`\n* `User getUser(int userId, InvocationContext ctx)`\n* `Group getGroup(String groupname, InvocationContext ctx)`\n* `void assignUserToGroup(String username, String groupname, InvocationContext ctx)`\n* `void unassignUserFromGroup(String username, String groupname, InvocationContext ctx)`\n* `List\u003cUser\u003e searchUsers(String query, InvocationContext ctx)`\n* `List\u003cGroup\u003e searchGroups(String query, InvocationContext ctx)`\n* `List\u003cPrincipal\u003e searchPrincipals(String query, InvocationContext ctx)`\n\n##### Discretionary Access Control (Sharing)\n\n* `void shareGraphs(Set\u003cString\u003e graphUris, Set\u003cString\u003e groupUris, Permission permission, InvocationContext ctx)`\n* `void unshareGraphs(Set\u003cString\u003e graphUris, Set\u003cString\u003e groupUris, InvocationContext ctx)`\n* `void shareResources(Set\u003cString\u003e resourceUris, Set\u003cString\u003e groupUris, Permission permission, InvocationContext ctx)`\n* `void unshareResources(Set\u003cString\u003e resourceUris, Set\u003cString\u003e groupUris, InvocationContext ctx)`\n* `Map\u003cString, Permission\u003e listResourcePermissions(Set\u003cString\u003e resourceUris, InvocationContext ctx)`\n* `Map\u003cString, Permission\u003e listGraphPermissions(Set\u003cString\u003e graphUris, InvocationContext ctx)`\n* `Map\u003cString, List\u003cPrincipalPermission\u003e\u003e listPrincipalPermissions( Set\u003cString\u003e uris, boolean forGraphs, InvocationContext ctx)`\n\n##### Virtual Graph\n\n* `void addVirtualGraph(Node graphName, String factoryMethodPath, JSONObject config, InvocationContext ctx)`\n\n##### RDF Patch Apply and Listener\n\n* `void apply(RDFPatch rdfPatch, InvocationContext ctx)`\n* `void addListener(RDFPatchListener listener)`\n* `void removeListener(RDFPatchListener listener)`\n\n##### Data Generation\n\n* `void generateLUBMftGraph(Node graphName, int univNum, int startIndex, int seed, boolean names, boolean docs, int bufferSize, int batchSize, InvocationContext ctx)`\n\n##### Query Logger\n\n* `void enableQueryLogger(String dbFilePath)`\n* `void disableQueryLogger()`\n\n### REST / Server Usage\n\nUse the `atic.jar` built by the `atic-server` project (see [Installation](#installation)).\n\n```bash\nexport JWT_SECRET=\"my-secret\"\njava -jar atic.jar\n```\n\nBy default, the server runs at `http://127.0.0.1:6583`.\n\n#### Configuration\n\nUse `--help` for possible CLI arguments.\n\n```\njava -jar atic.jar --help\n```\n\nYou can also specify the options in a `atic.toml` file located in the working directory.\n\nIt is also possible to specify the options as environment variables.\nIn this case, an option, for example, `instance.name` has to be defined as `ATIC_INSTANCE_NAME`.\n\n#### Authorization\n\nAll endpoints except `/auth/register` and `/auth/token` require a bearer token:\n\n```bash\n-H \"Authorization: Bearer \u003cTOKEN\u003e\"\n```\n\n#### POST `/auth/register`\n\nCreates a new user account.\n\n```bash\ncurl -X POST http://127.0.0.1:6583/auth/register \\\n  -d \"firstname=John\" \\\n  -d \"lastname=Doe\" \\\n  -d \"email=john@example.org\" \\\n  -d \"username=jdoe\"\n```\n\nA generated password is returned.\nPasswords are stored for lookup in a `passwords.json.generated` file.\n\n#### POST `/auth/token`\n\nAuthenticates a user and returns a JWT access token.\n\n```bash\ncurl -X POST http://127.0.0.1:6583/auth/token \\\n  -d \"username=jdoe\" \\\n  -d \"password=secret\"\n```\n\nOn success, the server returns a JSON response containing the access token (and a corresponding cookie):\n\n```json\n{\n  \"access_token\": \"\u003cTOKEN\u003e\",\n  \"successful\": true,\n  \"redirect\": \"/\"\n}\n```\n\n#### GET `/auth/me`\n\nReturns information about the currently authenticated user.\n\n```bash\ncurl \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  http://127.0.0.1:6583/auth/me\n```\n\n\n\n#### POST `/auth/logout`\n\nLogs out the current user.\n\n```bash\ncurl -X POST \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  http://127.0.0.1:6583/auth/logout\n```\n\n\n\n#### GET `/config`\n\nReturns all visible configuration entries.\n\n```bash\ncurl \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  http://127.0.0.1:6583/config\n```\n\n\n\n#### GET `/config/{name}`\n\nReturns a single configuration value.\n\n```bash\ncurl \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  http://127.0.0.1:6583/config/port\n```\n\n\n\n#### GET `/sparql`\n\nExecutes a SPARQL query.\n\n```bash\ncurl --get \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  http://127.0.0.1:6583/sparql \\\n  --data-urlencode \"query=SELECT * WHERE { ?s ?p ?o } LIMIT 10\"\n```\n\n\n\n#### POST `/sparql`\n\nExecutes a SPARQL query.\n\n```bash\ncurl -X POST \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  http://127.0.0.1:6583/sparql \\\n  --data 'SELECT * WHERE { ?s ?p ?o } LIMIT 10'\n```\n\n\n\n#### POST `/update`\n\nExecutes a SPARQL Update request.\n\n```bash\ncurl -X POST \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  http://127.0.0.1:6583/update \\\n  --data 'INSERT DATA { GRAPH \u003chttps://example.org/g\u003e { \u003ca\u003e \u003cb\u003e \u003cc\u003e } }'\n```\n\n\n\n#### GET `/graph`\n\nReturns graphs accessible to the current user.\n\n```bash\ncurl \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  \"http://127.0.0.1:6583/graph\"\n```\n\n```bash\ncurl \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  \"http://127.0.0.1:6583/graph?triples\"\n```\n\n```bash\ncurl \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  \"http://127.0.0.1:6583/graph?permissions\"\n```\n\n\n\n#### POST `/graph`\n\nCreates a graph.\n\n```bash\ncurl -X POST \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  http://127.0.0.1:6583/graph\n```\n\n```bash\ncurl -X POST \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  -H \"Content-Type: application/json\" \\\n  http://127.0.0.1:6583/graph \\\n  -d '{\"graph\":\"https://example.org/graph/my-graph\"}'\n```\n\n\n\n#### DELETE `/graph/{uri}`\n\nDeletes a graph.\n\n```bash\ncurl -X DELETE \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  \"http://127.0.0.1:6583/graph/https%3A%2F%2Fexample.org%2Fgraph%2Fmy-graph\"\n```\n\n\n#### POST `/graph/share`\n\nShares graphs with groups.\n\n```bash\ncurl -X POST \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  -H \"Content-Type: application/json\" \\\n  http://127.0.0.1:6583/graph/share \\\n  -d '{\n    \"graphs\": [\"https://example.org/graph/a\"],\n    \"groups\": [\"https://example.org/group/admins\"],\n    \"permission\": \"READ\"\n  }'\n```\n\n\n\n#### DELETE `/graph/share`\n\nRemoves a previously granted permission from groups.\n\n```bash\ncurl -X DELETE \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  -H \"Content-Type: application/json\" \\\n  http://127.0.0.1:6583/graph/share \\\n  -d '{\n    \"graphs\": [\"https://example.org/graph/a\"],\n    \"groups\": [\"https://example.org/group/admins\"],\n    \"permission\": \"READ\"\n  }'\n```\n\n\n\n#### GET `/graph/access`\n\nReturns the permissions assigned to one or more graphs.\n\nThe endpoint accepts one or more graph URIs via the `uri` query parameter.\n\n```bash\ncurl --get \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  http://127.0.0.1:6583/graph/access \\\n  --data-urlencode \"uri=https://example.org/graph/a\"\n```\n\nResponse:\n\n```json\n{\n  \"https://example.org/graph/a\": [\n    {\n      \"principalName\": \"Administrators\",\n      \"principalUri\": \"https://example.org/group/admins\",\n      \"principalShareUri\": \"https://example.org/share/admins\",\n      \"permission\": \"ADMIN\",\n      \"type\": \"group\"\n    },\n    {\n      \"principalName\": \"John Doe\",\n      \"principalUri\": \"https://example.org/user/jdoe\",\n      \"principalShareUri\": \"https://example.org/share/jdoe\",\n      \"permission\": \"READ\",\n      \"type\": \"user\"\n    }\n  ]\n}\n```\n\n#### POST `/resource/share`\n\nShares resources with groups.\n\n```bash\ncurl -X POST \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  -H \"Content-Type: application/json\" \\\n  http://127.0.0.1:6583/resource/share \\\n  -d '{\n    \"resources\": [\"https://example.org/resource/a\"],\n    \"groups\": [\"https://example.org/group/admins\"],\n    \"permission\": \"READ\"\n  }'\n```\n\n#### DELETE `/resource/share`\n\nRemoves a previously granted permission from groups.\n\n```bash\ncurl -X DELETE \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  -H \"Content-Type: application/json\" \\\n  http://127.0.0.1:6583/resource/share \\\n  -d '{\n    \"resources\": [\"https://example.org/resource/a\"],\n    \"groups\": [\"https://example.org/group/admins\"],\n    \"permission\": \"READ\"\n  }'\n```\n\n\n\n#### GET `/resource/access`\n\nReturns the permissions assigned to one or more resources.\n\nThe endpoint accepts one or more resource URIs via the `uri` query parameter.\n\n```bash\ncurl --get \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  http://127.0.0.1:6583/resource/access \\\n  --data-urlencode \"uri=https://example.org/resource/a\"\n```\n\nResponse:\n\n```json\n{\n  \"https://example.org/resource/a\": [\n    {\n      \"principalName\": \"Researchers\",\n      \"principalUri\": \"https://example.org/group/researchers\",\n      \"principalShareUri\": \"https://example.org/share/researchers\",\n      \"permission\": \"READ\",\n      \"type\": \"group\"\n    },\n    {\n      \"principalName\": \"John Doe\",\n      \"principalUri\": \"https://example.org/user/jdoe\",\n      \"principalShareUri\": \"https://example.org/share/jdoe\",\n      \"permission\": \"EDIT\",\n      \"type\": \"user\"\n    }\n  ]\n}\n```\n\n\n#### POST `/upload`\n\nUploads RDF data or files.\n\nOptionally, the imported data can be assigned to a graph and permissions can be granted to groups for resources contained in the imported dataset.\n\n\n```bash\ncurl -X POST \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  -F \"file=@data.ttl\" \\\n  http://127.0.0.1:6583/upload\n```\n\n\nUpload into a specific graph:\n\n```bash\ncurl -X POST \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  -F \"file=@data.ttl\" \\\n  -F \"graph=https://example.org/graph/my-graph\" \\\n  http://127.0.0.1:6583/upload\n```\n\nUpload data and grant read access to a group for imported resources:\n\n```bash\ncurl -X POST \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  -F \"file=@data.ttl\" \\\n  -F \"group=https://example.org/group/researchers\" \\\n  -F \"permission=READ\" \\\n  http://127.0.0.1:6583/upload\n```\n\nUpload data with custom import settings:\n\n```bash\ncurl -X POST \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  -F \"file=@data.ttl\" \\\n  -F \"buffer-size=1000\" \\\n  -F \"batch-size=1000\" \\\n  http://127.0.0.1:6583/upload\n```\n\n\n#### GET `/principal`\n\nSearches principals (users and groups) matching the provided search query.\n\n```bash\ncurl --get \\\n  -H \"Authorization: Bearer \u003cTOKEN\u003e\" \\\n  http://127.0.0.1:6583/principal \\\n  --data-urlencode \"query=admin\"\n```\n\nThe response contains a list of matching principals:\n\n```json\n{\n  \"principals\": [\n    {\n      \"uri\": \"https://example.org/group/admins\",\n      \"name\": \"Administrators\"\n    }\n  ]\n}\n```\n\n## Developer Guide\n\nThe following projects exist:\n* atic-api - contains interfaces and data classes\n* atic-sqlite - quadstore implementation for SQLite\n* atic-server - server component and frontends\n\nAdditonal projects for [evaluation](#evaluation):\n* atic-sqlite-tdb2tests - JUnit test cases from TDB2\n* atic-sqlite-jmh - uses Java Microbenchmark Harness (JMH) for benchmarking\n\n### atic-api\n\nThe API project is separated into six packages: `jenatic`, `ac` (Access Control), `api`, `conf`, `helper`,  and `vocab`.\n\n#### jenatic\n\n*jenatic* is a blend word between Jena and ATIC to emphasise that Jena interfaces are extended for our prototype.\n\n[InvocationContext](atic-api/src/main/java/de/dfki/sds/atic/jenatic/InvocationContext.java) is a class to express the invocation of Jena methods from a certain context.\nIt is mainly used to state which user invokes the method by providing `userId`, `primaryGroupId` and `groupIds`.\n\n[AticDatasetGraph](atic-api/src/main/java/de/dfki/sds/atic/jenatic/AticDatasetGraph.java) extends Jena's [DatasetGraph](https://jena.apache.org/documentation/javadoc/arq/org.apache.jena.arq/org/apache/jena/sparql/core/DatasetGraph.html) class by adding for certain methods context-awareness in form of an additional `InvocationContext` method parameter.\nIn case a legacy Jena method is called, `InvocationContext.EMPTY` is passed.\nAnalogously, [AticGraph](atic-api/src/main/java/de/dfki/sds/atic/jenatic/AticGraph.java) extends Jena's [Graph](https://jena.apache.org/documentation/javadoc/jena/org.apache.jena.core/org/apache/jena/graph/Graph.html) interface.\n\nTo pass more information in a triple, [AticTriple](atic-api/src/main/java/de/dfki/sds/atic/jenatic/AticTriple.java) extends Jena's [Triple](https://jena.apache.org/documentation/javadoc/jena/org.apache.jena.core/org/apache/jena/graph/Triple.html) class.\nCurrently, an additional `confidence` value can be stored.\n\nFor virtual graphs, a separate [AticVirtualGraph](atic-api/src/main/java/de/dfki/sds/atic/jenatic/AticVirtualGraph.java) exists which extends the `AticGraph` with an additonal method:\nSince virtual graphs are allowed to response to REST requests, a `handleRequest` method can be implemented.\nThe method returns [AticVirtualGraphResponse](atic-api/src/main/java/de/dfki/sds/atic/jenatic/AticVirtualGraphResponse.java).\n\n#### ac\n\nThe [UserGroupManagement](atic-api/src/main/java/de/dfki/sds/atic/ac/UserGroupManagement.java) interface defines methods for adding or removing users and groups as well as user group assignments.\nThe [SharingManagement](atic-api/src/main/java/de/dfki/sds/atic/ac/SharingManagement.java) interface defines methods for giving permissions to graphs and resources to principals.\nA [Principal](atic-api/src/main/java/de/dfki/sds/atic/ac/Principal.java) (like [User](atic-api/src/main/java/de/dfki/sds/atic/ac/User.java) or [Group](atic-api/src/main/java/de/dfki/sds/atic/ac/Group.java)) can receive a permission from the [Permission](atic-api/src/main/java/de/dfki/sds/atic/ac/Permission.java) enum.\nIf a permission is denied a [PermissionDeniedException](atic-api/src/main/java/de/dfki/sds/atic/ac/PermissionDeniedException.java) is thrown.\n\n#### api\n\nThe general api package contains data classes for two aspects:\nassociating numeric id values with textual URI values ([IdAndUri](atic-api/src/main/java/de/dfki/sds/atic/api/IdAndUri.java)) and mapping between objects and URIs used in virtual graphs ([UriMapper](atic-api/src/main/java/de/dfki/sds/atic/api/UriMapper.java)).\n\n#### conf\n\nThe package is used for configuration management.\n\n[Config](atic-api/src/main/java/de/dfki/sds/atic/conf/Config.java) is a field annotation used in [ConfigLoader](atic-api/src/main/java/de/dfki/sds/atic/conf/ConfigLoader.java).\nThe loader class is able to read configurations passed with `Config` annotations from TOML file, CLI arguments or environment variables.\n\n### atic-sqlite\n\nThis project contains the Quadstore implementation.\n\n#### SQL Database Schema\n\n![](docu/sql-diagram.png)\n\nSQL files defining the database schema are located [here](atic-sqlite/src/main/resources/de/dfki/sds/aticsqlite/sql).\n\nThe schema is separated in four areas: Resource Description Framework (RDF), User \u0026 Group Management, Access Control and RDF-star.\n\n##### Resource Description Framework (RDF)\n\nQuadruples with literal objects are stored in `splg` table, while quadruples with resource objects are stored in `spog` table. Both store provenance information for `creator` (`user`) and `created_at` time as well as a `confidence` value.\n\nA separate `graph` table manages graphs (fourth component of quadruple).\nAagin, provenance information for `creator` (`user`) and `created_at` time is saved.\nAdditionally, for virtual graphs corresponding meta-data is recorded.\n\nResources are stored in `resource` table and properties are stored in `property` table.\nAagin, provenance information for `creator` (`user`) and `created_at` time is saved.\n\nSince a resource can be either a URI (including blank node) or triple term (RDF-star), there are additional tables `resource_uri` as well as `resource_spo` and `resource_spl` (see RDF-star).\n\nFor blank nodes, `is_blank` is set to 1 and `uri` is used for a skolemized blank node label.\n\nPrefix mapping is recorded in `prefixmap` table.\n\n##### User \u0026 Group Management\n\nUsers are stored in a `user` table with `username`, `firstname`, `lastname`, `email` and hashed `password` metadata as well as uniquely generated `uri`.\n\nGroups are stored in `group` table with `groupname` and uniquely generated `uri`.\nSince each user has a primary group (same `groupname` as `username`), a primary group refers to its user with `user_id`.\n\nAssignments of users to groups is recorded in `user_group_assignment`.  \n\n\n##### Access Control\n\nAccess control is enforced on resources (`resource_acl`) and graphs (`graph_acl`).\nIn both cases, the `group_id` that receives the `permission` which was granted by a group (`granted_by_group_id`) is recorded.\nThe permission code is defined as follows: READ=1, REFER=2, EDIT=3, ADMIN=4.\nIf the target is a user, its primary group is used, otherwise a regular group is used.\n\n##### RDF-star\n\nTriple terms are stored in `resource_spo` table for triples with URI objects and `resource_spl` table for triples with literal objects.\nThey refer to `resource` and `property` table.\n\n#### Java Classes\n\nThe [Database](atic-sqlite/src/main/java/de/dfki/sds/aticsqlite/Database.java) interface class extends Jena's [Transactional](https://jena.apache.org/documentation/javadoc/arq/org.apache.jena.arq/org/apache/jena/sparql/core/Transactional.html) interface and provides methods for reading from and writing into a database.\n[DatabaseLongLivedConnection](atic-sqlite/src/main/java/de/dfki/sds/aticsqlite/DatabaseLongLivedConnection.java) is an implementation that features long-lived connections as well as prepared and cached SQL statements for performance ([PooledPreparedStatement](atic-sqlite/src/main/java/de/dfki/sds/aticsqlite/PooledPreparedStatement.java)).\nSince it implements the Jena's `Transactional` interface, it also features transactions.\nIt can be configured through [DatabaseOptions](atic-sqlite/src/main/java/de/dfki/sds/aticsqlite/DatabaseOptions.java).\n\nRDF management is implemented in [SqliteAticDatasetGraph](atic-sqlite/src/main/java/de/dfki/sds/aticsqlite/SqliteAticDatasetGraph.java) and [SqliteAticGraph](atic-sqlite/src/main/java/de/dfki/sds/aticsqlite/SqliteAticGraph.java).\n`SqliteAticDatasetGraph` uses a passed `Database` implementation to read and write database records.\nIt implements methods for database bootstrapping, user \u0026 group management, access control (sharing) and (virtual) graph management.\nReading and writing RDF is passed down to the `SqliteAticGraph` implementation.\n\nThe [SqliteAticGraph](atic-sqlite/src/main/java/de/dfki/sds/aticsqlite/SqliteAticGraph.java) class implements the main methods `add`, `remove`, `find`, `contains` and `size`.\nFor performance reasons, added triples are stored in a in-memory queue until a configured threshold is reached (`SqliteAticGraph.setDefaultBufferSize`).\nThis reduces communication with the database and allows to update the database in batches.\nThe `find` method requires additional helper classes: [TransactionalResultSet](atic-sqlite/src/main/java/de/dfki/sds/aticsqlite/TransactionalResultSet.java) is a database result set which is aware of the transaction is was created.\nIt is used by [PagedTripleIterator](atic-sqlite/src/main/java/de/dfki/sds/aticsqlite/PagedTripleIterator.java) that is aware of a limited (paged) triple iteration.\nIt uses a passed [ResultSetTripleMapper](atic-sqlite/src/main/java/de/dfki/sds/aticsqlite/ResultSetTripleMapper.java) implementation to map the database resultset to RDF triples.\n\n[SqlitePrefixMap](atic-sqlite/src/main/java/de/dfki/sds/aticsqlite/SqlitePrefixMap.java) implements the storage and retrieval of prefix mappings using the database.\n\n[RDFPatchEmitterTransactional](atic-sqlite/src/main/java/de/dfki/sds/aticsqlite/RDFPatchEmitterTransactional.java) is used by `SqliteAticDatasetGraph` to emit RDF patches for each transaction.\nIt informs a list of given [RDFPatchListener](atic-sqlite/src/main/java/de/dfki/sds/aticsqlite/RDFPatchListener.java)s.\n\nThe [AticFactory](atic-sqlite/src/main/java/de/dfki/sds/aticsqlite/AticFactory.java) provides methods to create a `SqliteAticDatasetGraph` instance.\n\nTwo virtual graph implementations are available: [LocalFilesystemVirtualGraph](atic-sqlite/src/main/java/de/dfki/sds/aticsqlite/vkg/LocalFilesystemVirtualGraph.java) and [WebdavVirtualGraph](atic-sqlite/src/main/java/de/dfki/sds/aticsqlite/vkg/WebdavVirtualGraph.java).\nThe latter is used to access a [Nextcloud](https://nextcloud.com) instance via its WebDAV endpoint during the demo.\n\nFor performance reasons, we modified [JDBC3Statement](atic-sqlite/src/main/java/org/sqlite/jdbc3/JDBC3Statement.java): The method `getResultSet` is extended with an argument `boolean skipColsMeta` to be able to skip the retrieval of column names.\n\n### atic-server\n\nThis project contains the server implementation.\n\n#### Java Classes\n\nThe [Main](atic-server/src/main/java/de/dfki/sds/aticserver/Main.java) class contains the `main` method which loads configuration and starts the server.\n[AticConfig](atic-server/src/main/java/de/dfki/sds/aticserver/AticConfig.java) contains all config options.\n\n[AticServer](atic-server/src/main/java/de/dfki/sds/aticserver/AticServer.java) contains the [Javalin](https://javalin.io/)-based server.\nHere, all endpoints are defined.\nIt uses [ConfigDrivenCrudEndpoints](atic-server/src/main/java/de/dfki/sds/aticserver/ConfigDrivenCrudEndpoints.java) implementation.\nAn example YAML configuration file is [person.yml](atic-server/src/test/resources/de/dfki/sds/aticserver/cdce/person.yml).\n`AticServer` uses also [MoleculeEndpoint](atic-server/src/main/java/de/dfki/sds/aticserver/MoleculeEndpoint.java).\n\n[RDFPatchWriter](atic-server/src/main/java/de/dfki/sds/aticserver/RDFPatchWriter.java) is a `RDFPatchListener` and writes for each transaction into an SQLite database using `rdfpatch-sqlite` project.\n\n\n## Evaluation\n\nIn a preliminary evaluation, our quadstore is assessed with respect to two aspects: correctness and performance.\n\n### Correctness\n\nWe added [JUnit test cases in atic-sqlite](atic-sqlite/src/test/java/de/dfki/sds/aticsqlite).\n\n```\nRunning de.dfki.sds.aticsqlite.LimitOffsetUnitTest\nTests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.742 s -- in de.dfki.sds.aticsqlite.LimitOffsetUnitTest\nRunning de.dfki.sds.aticsqlite.SparqlBasedUnitTest\nTests run: 10, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.642 s -- in de.dfki.sds.aticsqlite.SparqlBasedUnitTest\nRunning de.dfki.sds.aticsqlite.QueryLoggerUnitTest\nTests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.067 s -- in de.dfki.sds.aticsqlite.QueryLoggerUnitTest\nRunning de.dfki.sds.aticsqlite.BSBMUnitTest\nTests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.105 s -- in de.dfki.sds.aticsqlite.BSBMUnitTest\nRunning de.dfki.sds.aticsqlite.BlankNodeUnitTest\nTests run: 16, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.934 s -- in de.dfki.sds.aticsqlite.BlankNodeUnitTest\nRunning de.dfki.sds.aticsqlite.GraphUnitTest\nTests run: 8, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.554 s -- in de.dfki.sds.aticsqlite.GraphUnitTest\nRunning de.dfki.sds.aticsqlite.ResourceSharingUnitTest\nTests run: 11, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.509 s -- in de.dfki.sds.aticsqlite.ResourceSharingUnitTest\nRunning de.dfki.sds.aticsqlite.VirtualGraphUnitTest\nTests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.280 s -- in de.dfki.sds.aticsqlite.VirtualGraphUnitTest\nRunning de.dfki.sds.aticsqlite.ComparisonUnitTest\nTests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.290 s -- in de.dfki.sds.aticsqlite.ComparisonUnitTest\nRunning de.dfki.sds.aticsqlite.ConsistencyUnitTest\nTests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.309 s -- in de.dfki.sds.aticsqlite.ConsistencyUnitTest\nRunning de.dfki.sds.aticsqlite.FactoryUnitTest\nTests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.108 s -- in de.dfki.sds.aticsqlite.FactoryUnitTest\nRunning de.dfki.sds.aticsqlite.DatasetGraphUnitTest\nTests run: 8, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.463 s -- in de.dfki.sds.aticsqlite.DatasetGraphUnitTest\nRunning de.dfki.sds.aticsqlite.RdfStarUnitTest\nTests run: 11, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.535 s -- in de.dfki.sds.aticsqlite.RdfStarUnitTest\nRunning de.dfki.sds.aticsqlite.GraphSharingUnitTest\nTests run: 20, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.468 s -- in de.dfki.sds.aticsqlite.GraphSharingUnitTest\nRunning de.dfki.sds.aticsqlite.DatabaseLongLivedConnectionUnitTest\nTests run: 16, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.533 s -- in de.dfki.sds.aticsqlite.DatabaseLongLivedConnectionUnitTest\nRunning de.dfki.sds.aticsqlite.RDFPatchEmitterTransactionalUnitTest\nTests run: 6, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.423 s -- in de.dfki.sds.aticsqlite.RDFPatchEmitterTransactionalUnitTest\nRunning de.dfki.sds.aticsqlite.DatabaseConnectionPerTransactionUnitTest\nTests run: 13, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.877 s -- in de.dfki.sds.aticsqlite.DatabaseConnectionPerTransactionUnitTest\nRunning de.dfki.sds.aticsqlite.BootstrapUnitTest\nTests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.275 s -- in de.dfki.sds.aticsqlite.BootstrapUnitTest\n\nResults:\n\nTests run: 145, Failures: 0, Errors: 0, Skipped: 0\n```\n\nIn [atic-sqlite-tdb2tests](atic-sqlite-tdb2tests), we collected TDB2 JUnit tests.\nThe only modification made to the code was replacing TDB2 with our quadstore instance, mainly in the [TL](atic-sqlite-tdb2tests/src/test/java/org/apache/jena/tdb2/junit/TL.java) class and other places (look for `AticFactory`).\n\n```\nRunning org.apache.jena.tdb2.solver.TestSolverTDB\nTests run: 7, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.407 s -- in org.apache.jena.tdb2.solver.TestSolverTDB\nRunning org.apache.jena.tdb2.graph.TestDatasetGraphTDB2\nTests run: 32, Failures: 0, Errors: 0, Skipped: 2, Time elapsed: 1.843 s -- in org.apache.jena.tdb2.graph.TestDatasetGraphTDB2\nRunning org.apache.jena.tdb2.graph.TestPrefixMappingTDB2\nTests run: 8, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.441 s -- in org.apache.jena.tdb2.graph.TestPrefixMappingTDB2\nRunning org.apache.jena.tdb2.graph.TestGraphViewSwitchable\nTests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.059 s -- in org.apache.jena.tdb2.graph.TestGraphViewSwitchable\nRunning org.apache.jena.tdb2.graph.TestGraphsTDB2_B\nTests run: 20, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.230 s -- in org.apache.jena.tdb2.graph.TestGraphsTDB2_B\nRunning org.apache.jena.tdb2.graph.TestGraphOverDatasetTDB2\nTests run: 11, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.611 s -- in org.apache.jena.tdb2.graph.TestGraphOverDatasetTDB2\nRunning org.apache.jena.tdb2.graph.TestPrefixMappingTDBExtra\nTests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.891 s -- in org.apache.jena.tdb2.graph.TestPrefixMappingTDBExtra\nRunning org.apache.jena.tdb2.graph.TestGraphsTDB2_A\nTests run: 20, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.110 s -- in org.apache.jena.tdb2.graph.TestGraphsTDB2_A\nRunning org.apache.jena.tdb2.store.TestTransPromoteTDB\nTests run: 24, Failures: 0, Errors: 0, Skipped: 24, Time elapsed: 0 s -- in org.apache.jena.tdb2.store.TestTransPromoteTDB\nRunning org.apache.jena.tdb2.store.TestVisibilityOfChanges\nTests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.246 s -- in org.apache.jena.tdb2.store.TestVisibilityOfChanges\nRunning org.apache.jena.tdb2.store.TestGraphTDB\nTests run: 16, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.890 s -- in org.apache.jena.tdb2.store.TestGraphTDB\nRunning org.apache.jena.tdb2.store.TestQueryExecTDB\nTests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.120 s -- in org.apache.jena.tdb2.store.TestQueryExecTDB\nRunning org.apache.jena.tdb2.store.TestGraphNamedTDB\nTests run: 16, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.881 s -- in org.apache.jena.tdb2.store.TestGraphNamedTDB\nRunning org.apache.jena.tdb2.store.TestTransactionLifecycleTDB\nTests run: 51, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 4.798 s -- in org.apache.jena.tdb2.store.TestTransactionLifecycleTDB\nRunning org.apache.jena.tdb2.store.TestDynamicDatasetTDB\nTests run: 27, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.505 s -- in org.apache.jena.tdb2.store.TestDynamicDatasetTDB\nRunning org.apache.jena.tdb2.store.TestTransactions\nTests run: 7, Failures: 0, Errors: 0, Skipped: 1, Time elapsed: 0.336 s -- in org.apache.jena.tdb2.store.TestTransactions\nRunning org.apache.jena.tdb2.store.Test_SPARQL_TDB\nTests run: 9, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.408 s -- in org.apache.jena.tdb2.store.Test_SPARQL_TDB\nRunning org.apache.jena.tdb2.store.TestDatasetTDB\nTests run: 12, Failures: 0, Errors: 0, Skipped: 2, Time elapsed: 0.558 s -- in org.apache.jena.tdb2.store.TestDatasetTDB\n\nResults:\n\nTests run: 269, Failures: 0, Errors: 0, Skipped: 29\n```\n\n\n### Performance\n\nThe [Java Microbenchmark Harness](https://github.com/openjdk/jmh) (JMH) is used to get benchmark results.\n\n#### AticTdbComparisonBsbmLoading\n\n* Java Class: [AticTdbComparisonBsbmLoading.java](atic-sqlite-jmh/src/main/java/de/dfki/sds/aticsqlitejmh/AticTdbComparisonBsbmLoading.java)\n* Benchmark Result in JSON: [AticTdbComparisonBsbmLoading.json](evaluation/AticTdbComparisonBsbmLoading.json)\n\n![](evaluation/AticTdbComparisonBsbmLoading.png)\n\n\n#### AticTdbComparisonBsbmQueryMix\n\n* Java Class: [AticTdbComparisonBsbmQueryMix.java](atic-sqlite-jmh/src/main/java/de/dfki/sds/aticsqlitejmh/AticTdbComparisonBsbmQueryMix.java)\n* Benchmark Result in JSON: [AticTdbComparisonBsbmQueryMix.json](evaluation/AticTdbComparisonBsbmQueryMix.json)\n\n![](evaluation/AticTdbComparisonBsbmQueryMix.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdfki%2Fatic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdfki%2Fatic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdfki%2Fatic/lists"}