---
title: "Apache Jena Fusekiとは？Jena 6の変更点とインストール・Docker運用を実装目線で解説"
url: "https://www.issoh.co.jp/tech/details/10916/"
published: 2026-02-10
updated: 2026-10-14
categories: ["データベース"]
publisher: "株式会社一創"
---

# Apache Jena Fusekiとは？Jena 6の変更点とインストール・Docker運用を実装目線で解説

Apache Jena Fusekiとは、RDFデータをHTTP経由のSPARQLエンドポイントとして公開するサーバーです。Javaで書かれたRDF処理フレームワークであるApache Jenaの一部として配布され、ライセンスはApache License 2.0です。展開して1コマンド叩けば、ブラウザから使えるクエリ画面付きのトリプルストアが立ち上がります。

ただし2026年2月のJena 6.0.0で、配布と運用の前提が大きく変わりました。Java 21が必須になり、Tomcatに載せるWARファイル版は廃止され、TDB1は非推奨になっています。この記事は6.2.0（2026年7月31日リリース）を実際に起動して、導入から管理画面での操作、データ投入、Javaへの組み込み、永続化、認証、サービス化までを検証した記録です（最終確認は2026年10月14日）。公式ドキュメントに残っている記述と現物が食い違う箇所も、実測したエラーメッセージ付きで示します。

## まとめ：Apache Jena Fusekiの要点

- FusekiはSPARQLサーバーです。1つのデータセットに対してSPARQL Query、SPARQL Update、Graph Store Protocol（読み書き）、RDF Patchの各エンドポイントをHTTPで提供します。SPARQL 1.1に対応し、6.1.0以降はRDF 1.2とSPARQL 1.2の書式にも追随しています。
- 現行版は6.2.0（2026年7月31日リリース）です。Jena 6系は**Java 21以上が必須**で、Java 17では動きません。
- Jena 6.0.0でWARファイル版（jena-fuseki-webapp／jena-fuseki-war）が廃止されました。最後にWARが作られたのは5.6.0です。公式ドキュメントのFuseki Serverのページには今もWAR運用の説明が残っているので、そこを読んで進めると入手先で行き詰まります。
- TDB1はJena 6.0.0で非推奨になりました（削除はされていません）。新規構築は既定のTDB2を使います。
- Docker Hubに公式イメージはありません。Apacheが配っているのはイメージを作るためのキットで、自分でビルドする前提です。
- 管理画面で作れるデータセットはメモリ型とTDB2の2種類です。メモリ型は再起動すると名前だけ残って中身が0件に戻ります。データセットの作成は管理機能なので、別のマシンのブラウザから開くと403で失敗します。
- 既定のセキュリティ設定は「管理機能だけlocalhost限定、データの読み書きは誰でも可」です。外部に出す前に必ず変更してください。
- Javaアプリケーションからは、jena-fuseki-mainを依存に加えればサーバーをプロセス内で起動でき、RDFConnectionでリモートのFusekiと同じAPIで読み書きできます。

## Apache Jena Fusekiとは：Jenaプロジェクトの中でのSPARQLサーバー

Apache Jenaは、RDF（Resource Description Framework）で表現されたデータを読み書きし、SPARQLで問い合わせるためのJava製フレームワークです。Fusekiはその中で「HTTPの口を開ける」役割を担当します。Jena本体がライブラリとしてJavaアプリケーションに組み込まれるのに対し、Fusekiは独立したプロセスとして動き、言語を問わずHTTPクライアントから利用できるようにします。

| コンポーネント    | 役割                 | 配布形態                  |
| ---------- | ------------------ | --------------------- |
| Fuseki     | SPARQLサーバー（HTTP公開） | apache-jena-fuseki    |
| ARQ        | SPARQLクエリエンジン      | apache-jena           |
| TDB2       | ネイティブRDFストア（永続化）   | apache-jena           |
| RIOT       | RDFの入出力・書式変換       | apache-jena           |
| SHACL／ShEx | RDFグラフの形状検証        | apache-jena           |
| GeoSPARQL  | 地理空間データの拡張         | Maven（jena-geosparql） |

クエリ言語そのものの書き方は[SPARQLとは？RDFを検索するクエリ言語の書き方とSQLとの違いを実行例で解説【2026年版】](/tech/details/3428/)で扱っています。データにどう意味を与えるかという上位の設計は[オントロジーとは？RDFとOWLで意味を定義する実装手順【2026年版】](/tech/details/17449/)を参照してください。

### 1データセットに立つ5系統のエンドポイント

Fusekiが何を公開しているかは、起動後に管理用エンドポイントへ問い合わせれば確認できます。以下は6.2.0を `--update --mem /ds` で起動した直後の応答を抜粋したものです。

```
$ curl -s 'http://localhost:3030/$/server'
{
  "version" : "6.2.0" ,
  "startDateTime" : "2026-09-07T22:45:56.475+00:00" ,
  "uptime" : 9 ,
  "datasets" : [
      {
        "ds.name" : "/ds" ,
        "ds.state" : true ,
        "ds.services" : [
            { "srv.type" : "gsp-r" ,  "srv.endpoints" : [ "get" ] } ,
            { "srv.type" : "query" ,  "srv.endpoints" : [ "sparql", "query", "" ] } ,
            { "srv.type" : "gsp-rw" , "srv.endpoints" : [ "data", "" ] } ,
            { "srv.type" : "update" , "srv.endpoints" : [ "", "update" ] } ,
            { "srv.type" : "patch" ,  "srv.endpoints" : [ "patch", "" ] }
          ]
      }
    ]
}
```

`srv.endpoints` に空文字列が含まれている点に注意してください。これはデータセットのURL自体（この例では `/ds`）でもその操作を受け付けるという意味です。つまり `/ds/sparql` でも `/ds` でも検索でき、Content-Typeによって更新かどうかが判別されます。

## Jena 6で変わった点：Java 21必須、WAR廃止、TDB1非推奨

4系や5系の解説を読んで作業すると詰まる箇所があるので、先に版差を押さえます。以下はApache Jenaのリポジトリにある[CHANGES.txt](https://github.com/apache/jena/blob/main/CHANGES.txt)の記載に基づきます。

| 版     | リリース日      | 主な変更                    |
| ----- | ---------- | ----------------------- |
| 6.0.0 | 2026-02-04 | Java 21必須、WAR廃止、TDB1非推奨 |
| 6.1.0 | 2026-05-11 | RDF 1.2構文の入出力に対応        |
| 6.2.0 | 2026-07-31 | Shiro利用時のメモリリーク修正       |

6.0.0の節には`Minimum Java version: Java 21`、`Retired - the Fuseki WAR file, jena-fuseki-webapp, jena-fuseki-war`、`Deprecation of TDB1 (but not removed)`、`Reloading data into TDB2 is recommended` の4行が並んでいます。WARの廃止についてはApache Jenaの[issue #3537](https://github.com/apache/jena/issues/3537)に `The last release producing the WAR file was Jena 5.6.0.` と明記されており、Maven Centralでも jena-fuseki-war と jena-fuseki-webapp は5.6.0で更新が止まっています。同じ時期のリリースでも jena-fuseki-server は6.2.0まで出ているので、成果物ごとに追随状況が違う点は確認してから移行計画を立ててください。

ここで注意が要るのが、公式ドキュメントが追随していないことです。[Fuseki Serverのページ](https://jena.apache.org/documentation/fuseki2/fuseki-server.html)には2026年10月14日時点でも「Fuseki as a Web Application」という節があり、WARをダウンロードしてTomcatに配置する手順が書かれています。しかし6系のダウンロードページにWARはありません。5.6.0以前のWARはApacheのアーカイブから取得できますが、Java 21移行と同時に進めるなら、スタンドアロンサーバーへ切り替えるほうが素直です。

Java 21必須というのは推奨ではなく物理的な制約です。6.2.0の `fuseki-server.jar` に入っているクラスファイルはバージョン65（Java 21）でコンパイルされているため、Java 17で起動すると `UnsupportedClassVersionError` で読み込みに失敗します。既存サーバーのJavaを上げずに6系へ移ることはできません。

TDB2については、6.0.0でxsd:decimalの扱いに修正が入ったため、既存データベースの再ロードが推奨されています。小数値を格納しているデータセットを6系に載せ替えるときは、そのままディレクトリを持ち込まず、一度エクスポートしてから読み直してください。

## インストールと起動：tar.gz展開から1コマンド起動まで

Fusekiの導入はアーカイブを展開するだけです。事前にJava 21以上を用意してください。

```
$ curl -LO https://dlcdn.apache.org/jena/binaries/apache-jena-fuseki-6.2.0.tar.gz
$ tar zxf apache-jena-fuseki-6.2.0.tar.gz
$ cd apache-jena-fuseki-6.2.0
$ ./fuseki-server --update --mem /ds
```

Windowsでは同梱の `fuseki-server.bat` を使います。起動すると次のようなログが出ます。

```
07:45:56 INFO  Fuseki          :: Apache Jena Fuseki 6.2.0
07:45:56 INFO  Config          :: Fuseki Base = /tmp/jenachk/work/run
07:45:56 INFO  Config          :: Database: /ds
07:45:57 INFO  Config          :: UI Base: fuseki-server.jar
07:45:57 INFO  Shiro           :: Shiro configuration: file:/tmp/jenachk/work/run/shiro.ini
07:45:58 INFO  Server          :: Path = /ds
07:45:58 INFO  Server          :: System
07:45:58 INFO  Server          ::   Memory: 4.0 GiB
07:45:58 INFO  Server          ::   PID:    61041
07:45:58 INFO  Server          ::   Port:   http=3030
07:45:58 INFO  Server          :: Start Fuseki
```

ブラウザで `http://localhost:3030/` を開くと管理画面が出ます。「UI Base: fuseki-server.jar」とあるとおり、6系では画面のファイル一式もjarの中に入っており、外部ディレクトリを配置する必要はありません。

### 配布物の中身：12エントリだけの構成

6.2.0のtar.gzを展開して得られるのは、LICENSE・NOTICE・READMEを含めて12エントリだけです。4系のようにwebappディレクトリやshiro.iniが最初から置かれているわけではありません。

| ファイル                           | 用途                  |
| ------------------------------ | ------------------- |
| fuseki-server                  | UI付きで起動する起動スクリプト    |
| fuseki-plain                   | UIなしで起動する起動スクリプト    |
| fuseki-server.bat              | Windows用の起動スクリプト    |
| fuseki-server.jar              | サーバー本体（UIを内包）       |
| fuseki-backup                  | バックアップを起動するシェルスクリプト |
| log4j2.properties              | ログ設定の既定値            |
| service/service/fuseki.service | systemdのユニットファイル    |
| service/service/fuseki.initd   | init.d用の起動スクリプト     |

### fuseki-serverとfuseki-plainの違い

2つの起動スクリプトは同じjarを呼び、環境変数 `MAIN` でエントリポイントを切り替えているだけです。UIと管理機能が要るかどうかで選びます。

| MAINの値       | UI | 管理作業領域 | 想定用途           |
| ------------ | -- | ------ | -------------- |
| serverUI（既定） | あり | あり     | 開発・運用の通常構成     |
| plain        | なし | なし     | コンテナ・組み込み      |
| main         | なし | なし     | モジュールを使わない最小構成 |
| basic        | なし | なし     | 追加機能なしの検証      |

ここで1つ落とし穴があります。`fuseki-plain` の中身は `exec fuseki-server` という1行で、パスを付けずに呼び出しています。そのため配布ディレクトリがPATHに入っていない状態で絶対パス指定して実行すると、次のエラーで落ちます。

```
/tmp/jenachk/apache-jena-fuseki-6.2.0/fuseki-plain: line 21: exec: fuseki-server: not found
```

配布ディレクトリをPATHへ追加してから実行するか、配布ディレクトリで `MAIN=plain ./fuseki-server` に必要な起動引数を付けて実行してください。この挙動は6.2.0の配布物で確認したものです。

### Fusekiの環境変数：配置先・作業領域・ヒープ・起動方式

| 変数           | 既定値              | 意味       |
| ------------ | ---------------- | -------- |
| FUSEKI\_HOME | スクリプトのあるディレクトリ   | 展開先      |
| FUSEKI\_BASE | カレントディレクトリ配下のrun | 作業領域     |
| JVM\_ARGS    | \-Xmx4G          | JVMへ渡す引数 |
| MAIN         | serverUI         | エントリポイント |

公式ドキュメントは作業領域について「カレントディレクトリ」と書いていますが、6.2.0の `fuseki-server` スクリプトが実際に設定しているのはカレントディレクトリ直下の `run` です。この `run` の中に、設定ファイル、データベース、バックアップ、ログ、そして後述する `shiro.ini` が初回起動時に自動生成されます。コンテナで動かすときにどこを永続化ボリュームにするかは、この位置を基準に決めてください。

ヒープの既定値が4GBである点にも注意してください。`-Xmx` は上限なので起動時に4GBを確保するわけではありませんが、メモリ上限の小さいコンテナで動かすと、ヒープが伸びた時点でJVMが上限に達する前にコンテナごと停止させられます。コンテナのメモリ上限からヒープ外領域とOS分を差し引いた値を `-Xmx` に設定し、起動ログのMemory欄が意図した値になっているかを確認してください。

## Docker運用：公式イメージの不在とビルドキット

Dockerで導入する際は、Apacheのビルドキットと第三者が配布するイメージを区別し、それぞれのJenaバージョンを確認してください。2026年10月14日時点で、Docker Hubに `apache/jena-fuseki` も `apache/jena` も存在しません（APIはいずれも object not found を返します）。Apacheが配布しているのはイメージそのものではなく、イメージを作るためのキット jena-fuseki-docker で、使い方は公式の[Fuseki Docker Toolsのページ](https://jena.apache.org/documentation/fuseki2/fuseki-docker.html)にまとまっています。これはMaven Centralに置かれており、6.2.0も公開されています。

```
$ curl -LO https://repo1.maven.org/maven2/org/apache/jena/jena-fuseki-docker/6.2.0/jena-fuseki-docker-6.2.0.zip
$ unzip jena-fuseki-docker-6.2.0.zip
$ cd jena-fuseki-docker-6.2.0
$ docker compose build --build-arg JENA_VERSION=6.2.0
$ docker compose run --rm --service-ports fuseki --mem /ds
```

ビルド時にはMaven Centralからサーバーのバイナリを取得し、SHA1チェックサムと突き合わせます。バージョン番号は必ず引数で渡してください。キットのREADMEに載っている例は6.2.0版でも `JENA_VERSION=5.3.0` のままなので、そのままコピーすると意図しない版が入ります。同梱のdocker-compose.yamlはカレントディレクトリの `databases` と `logs` をコンテナ内へマウントする設定なので、TDB2データベースをここに置けばコンテナを消してもデータは残ります。なおREADMEのコマンドは `docker-compose` 表記のままですが、Compose v2の `docker compose` でも同じように扱えます。

このイメージはFuseki Plainをベースにしているため**管理画面は付きません**。データセットの作成もデータ投入も、設定ファイルとHTTPだけで行うことになります。ブラウザから操作したい用途には向きません。

### 第三者イメージを使うときの確認点

自前ビルドを避けたい場合は有志のイメージを使うことになりますが、更新状況に大きな差があります。

| イメージ                | 最新のJenaタグ | リポジトリ最終更新  |
| ------------------- | --------- | ---------- |
| stain/jena-fuseki   | 5.1.0     | 2024-07-31 |
| secoresearch/fuseki | 6.2.0     | 2026-08-14 |

解説記事でよく名前が挙がるのは `stain/jena-fuseki` ですが、latestタグの実体は2024年7月で止まった5.1.0です。そのまま `docker pull` すると、Java 21必須化もWAR廃止も入っていない5.1.0が手元に来ます。検証目的なら問題ありませんが、6系向けの手順どおりに動かして挙動が違うと悩む原因になります。使う前にタグの更新日を必ず確認してください。

## データセットの作り方：起動オプション、設定ファイル、管理画面

データセットは、起動オプションで指定する方法、設定ファイルに書く方法、起動後に管理画面から追加する方法の3つで作れます。まずコマンドラインからの起動です。

```
$ ./fuseki-server --mem /ds                    # メモリ上、常に更新可、終了で消える
$ ./fuseki-server --loc=DB2 --update /ds        # ディレクトリ上のTDB2、更新可
$ ./fuseki-server --file=data.ttl /ds           # ファイルを読み込んで読み取り専用で公開
```

`--loc` は既定でTDB2のデータベースとして扱われます。TDB1にしたい場合だけ `--tdb1` を付けますが、前述のとおり非推奨なので新規では選びません。

### `--loc`のディレクトリ事前作成という前提

ここは公式の説明と実際の挙動が食い違う箇所です。`--help` の説明文は `Use an existing TDB database (or create if does not exist)` となっており、無ければ作ってくれるように読めます。しかし6.2.0で存在しないディレクトリを指定すると、データベースを作らずに終了します。

```
$ ./fuseki-server --loc=DBnew --port 3031 --update /ds2
07:46:52 INFO  Fuseki          :: Apache Jena Fuseki 6.2.0
07:46:52 ERROR Fuseki          :: Directory does not exist: DBnew
```

空のディレクトリを先に作ってから指定すれば、その中にTDB2のデータベースファイルが生成されます。コンテナやsystemdから `--loc` を使うと、マウント前にディレクトリが用意されているかどうかで起動可否が変わるため、初回デプロイでつまずきやすい箇所です。

### 設定ファイルによるエンドポイント定義

エンドポイント名や読み取り専用の制御まで決めたいときは、Turtle形式の設定ファイルを `--config` で渡します。以下は6.2.0で起動を確認した最小構成です。

```
PREFIX fuseki: <http://jena.apache.org/fuseki#>
PREFIX rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX tdb2:   <http://jena.apache.org/2016/tdb#>
PREFIX :       <#>

:service rdf:type fuseki:Service ;
    fuseki:name "books" ;
    fuseki:endpoint [ fuseki:operation fuseki:query  ; fuseki:name "sparql" ] ;
    fuseki:endpoint [ fuseki:operation fuseki:update ; fuseki:name "update" ] ;
    fuseki:endpoint [ fuseki:operation fuseki:gsp_rw ; fuseki:name "data" ] ;
    fuseki:dataset :dataset ;
    .

:dataset rdf:type tdb2:DatasetTDB2 ;
    tdb2:location "/var/lib/fuseki/DB2" ;
    .
```

```
$ ./fuseki-server --config=config.ttl
```

読み取り専用で公開したいときは `fuseki:update` と `fuseki:gsp_rw` のエンドポイントを外し、検索用の `fuseki:query` を残します。Graph Store Protocolによる読み取りも必要なら、`fuseki:gsp_r` のエンドポイントを追加します。`fuseki:operation` を使うこの記法が現行のもので、公式ドキュメントによれば古い `fuseki:serviceQuery` 形式も引き続きサポートされています。ただしjarに同梱されている雛形（config-tdb2など）は旧記法のままなので、雛形をコピーして書き足すと記法が混在します。新規に書くなら `fuseki:endpoint` で統一してください。

### 管理画面からのデータセット追加と再起動後の中身

Qiitaなどの入門記事が使っているのはこの方法です。データセットを指定せずに `./fuseki-server` だけで起動し、ブラウザで「manage」から「new dataset」を開くと、名前と種類を入力する画面になります（この節と、後述するアップロード・LOADの実行例は、`--port 3330` を付けて起動した別のサーバーで取ったものです）。6.2.0の画面で選べる種類は次の2つだけで、TDB1は選択肢にありません。

| 画面の選択肢            | dbType | 再起動後       |
| ----------------- | ------ | ---------- |
| In-memory         | mem    | 名前は残り中身は0件 |
| Persistent (TDB2) | tdb2   | 中身も残る      |

画面の裏では、管理用エンドポイントへフォームを1本POSTしているだけです。同じことはcurlでもできます。

```
$ curl -X POST --data 'dbName=lib&dbType=tdb2' 'http://localhost:3330/$/datasets'
$ ls run/configuration run/databases
run/configuration:
lib.ttl

run/databases:
lib
```

作成すると、作業領域の `run/configuration` に設定ファイルが、`run/databases` にTDB2のディレクトリが書き出されます。次回の起動時にはこの設定ファイルが読み込まれるので、起動オプションを付けなくても同じデータセットが復元されます。実際にTDB2型とメモリ型を1つずつ作ってデータを入れ、再起動して件数を数えると、TDB2型は133トリプルが残り、メモリ型は0件でした。画面の説明文どおり、メモリ型は「入れ物だけ作り直される」動きです。

管理画面から作ったデータセットには、起動時に `--update` を付けていなくても更新とGraph Store Protocolの書き込みエンドポイントが付きます。起動オプションの `--loc` で作ったデータセットが既定で読み取り専用になるのとは扱いが違います。

もう1つ詰まりやすいのが、別のマシンのブラウザから開いたときです。データセットの作成や一覧は `/$/` で始まる管理機能なので、既定の設定ではlocalhost以外から叩くと拒否されます。同じサーバーにLAN側のIPアドレスで接続して試した結果は次のとおりです。

| リクエスト（LAN側IPから）               | 結果  |
| ----------------------------- | --- |
| GET /$/datasets（一覧）           | 403 |
| POST /$/datasets（作成）          | 403 |
| GET /$/server（状態）             | 200 |
| POST /lib/update（INSERT DATA） | 204 |

サーバー機で起動して手元のPCから画面を開くと、データセットの作成だけが失敗するのはこのためです。作成はサーバー機上でcurlを使うか、SSHのポート転送でlocalhostとして接続して行ってください。一方で最後の行のとおり、データの更新はLAN側から通ってしまいます。この非対称が後述する認証設定を急ぐ理由です。

## RDFデータの投入とSPARQLの実行

ここからは `--update --mem /ds` で起動したサーバーに対して、実際に叩いた結果を示します。投入する書籍データは次のTurtleファイルです。

```
@prefix ex: <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

ex:book1 rdf:type ex:Book ;
    ex:title "Effective Java" ;
    ex:author "Joshua Bloch" .

ex:book2 rdf:type ex:Book ;
    ex:title "Java Concurrency in Practice" ;
    ex:author "Brian Goetz" .
```

### Graph Store Protocolによるデータ投入

デフォルトグラフへの投入は `data` エンドポイントにPOSTします。応答として取り込んだトリプル数が返るので、投入できたかどうかがその場で分かります。

```
$ curl -X POST -H 'Content-Type: text/turtle' --data-binary @books.ttl \
    'http://localhost:3030/ds/data?default'
{
  "count" : 6 ,
  "tripleCount" : 6 ,
  "quadCount" : 0
}
```

管理画面の「add data」からファイルを選んでアップロードする操作も、このエンドポイントへのPOSTです。ブラウザのファイル送信と同じmultipart形式でも受け付けます。

```
$ curl -X POST -F 'file=@books.ttl' 'http://localhost:3330/lib/data?default'
{
  "count" : 6 ,
  "tripleCount" : 6 ,
  "quadCount" : 0
}
```

ここで紛らわしいのが、管理画面が書き出す設定ファイルに `fuseki:serviceUpload "upload"` という行が入っていることです。名前どおり `/lib/upload` へ同じファイルを送ると、6.2.0では405が返ります。CHANGES.txtの4.4.0の節に、アップロード専用のサービスは既定の構成に追加されなくなり、Graph Store ProtocolのPOSTとPUTがHTMLのファイルアップロードを受け付けると書かれています。スクリプトから投入するときの宛先は `/data` です。

### SPARQLでの検索

```
$ curl -H 'Accept: application/sparql-results+json' \
    --data-urlencode 'query=PREFIX ex: <http://example.org/> SELECT ?title ?author WHERE { ?b a ex:Book ; ex:title ?title ; ex:author ?author } ORDER BY ?title' \
    http://localhost:3030/ds/sparql
{ "head": {
    "vars": [ "title" , "author" ]
  } ,
  "results": {
    "bindings": [
      {
        "title": { "type": "literal" , "value": "Effective Java" } ,
        "author": { "type": "literal" , "value": "Joshua Bloch" }
      } ,
      {
        "title": { "type": "literal" , "value": "Java Concurrency in Practice" } ,
        "author": { "type": "literal" , "value": "Brian Goetz" }
      }
    ]
  }
}
```

Acceptヘッダを `text/csv` にすればCSVで返ります。件数だけ確認したいときはこちらのほうが扱いやすくなります。

### SPARQL Updateでの書き換え

更新は `update` エンドポイントへ、Content-Typeを `application/sparql-update` にしてPOSTします。成功時のステータスは204で、本文は返りません。

```
$ curl -X POST -H 'Content-Type: application/sparql-update' \
    --data 'PREFIX ex: <http://example.org/> INSERT DATA { ex:book3 a ex:Book ; ex:title "Java Puzzlers" ; ex:author "Joshua Bloch" }' \
    http://localhost:3030/ds/update
```

`--update` の効き方はデータセットの種類で変わります。`--mem` のメモリ上データセットは常に更新可能で、`--update` を付けなくても更新は204で通ります。一方 `--loc` のTDB2データセットで `--update` を付けずに起動すると、更新もGraph Store Protocolの書き込みも405を返し、検索だけが通ります。読み取り専用で公開したつもりがメモリ上のデータセットだったという取り違えは起こり得るので、公開前に更新リクエストを1本投げて405が返ることを確かめてください。

### LOADによるWeb上のRDFの取り込み

手元にファイルが無くても、公開されているRDFをサーバー側に取りに行かせることができます。[SPARQL 1.1 Update](https://www.w3.org/TR/sparql11-update/)の `LOAD` 操作です。以下はW3Cが公開しているRDF語彙をグラフ名を付けて取り込んだ例です。

```
$ curl -X POST -H 'Content-Type: application/sparql-update' \
    --data 'LOAD <https://www.w3.org/1999/02/22-rdf-syntax-ns> INTO GRAPH <http://example.org/g/rdf>' \
    http://localhost:3330/lib/update

$ curl -H 'Accept: text/csv' \
    --data-urlencode 'query=SELECT ?g (COUNT(*) AS ?n) WHERE { GRAPH ?g { ?s ?p ?o } } GROUP BY ?g' \
    http://localhost:3330/lib/sparql
g,n
http://example.org/g/rdf,127
```

成功すると204が返り、名前付きグラフに127トリプルが入りました。取得先のURLが存在しない場合、Fusekiは400を返し、応答本文に取得先のステータス（`404 - Not Found`）をそのまま載せます。失敗の原因がクエリの書き方なのか取得先なのかは、この本文で切り分けられます。

LOADは、更新エンドポイントに届いたリクエストの内容どおりに、サーバーが任意のURLへ取りに行く機能でもあります。127.0.0.1だけで待ち受けるHTTPサーバーを別に立て、そのURLを指定してLOADしても204で取り込めました（そのサーバーのアクセスログにFusekiからのGETが残ります）。更新エンドポイントを認証なしで外部に開けると、データを書き換えられるだけでなく、サーバーの内側からしか見えないURLを叩かせる踏み台にもなります。

### 大量データのtdb2.tdbloaderによる投入

まとまった量を投入するときは、HTTPではなくバルクローダーを使います。ローダーはFusekiではなく apache-jena 側のアーカイブに入っています。

```
$ mkdir -p DB2
$ apache-jena-6.2.0/bin/tdb2.tdbloader --loc DB2 books.ttl
07:47:37 INFO  loader          :: Loader = LoaderPhased
07:47:37 INFO  loader          :: Start: books.ttl
07:47:37 INFO  loader          :: Finished: books.ttl: 6 tuples in 0.09s (Avg: 69)
07:47:37 INFO  loader          :: Finish - index SPO
07:47:37 INFO  loader          :: Index set:  SPO => SPO->POS, SPO->OSP
```

ローダーは `--loader` で選べます。既定は上のログにも出ている phased で、性能とハードウェア要求のバランス型です。既存データベースへ安全に追加したいときは、単一トランザクションで走る basic を選びます。公式の[TDB2コマンドのページ](https://jena.apache.org/documentation/tdb2/tdb2%5Fcmds.html)は「fully transactional なのは basic と sequential だけで、それ以外は速い代わりに低レベルのデータ構造を直接操作するため、途中で失敗するとデータベースが中途半端な状態になり得る」と明記しています。Wikidata級の巨大データには `tdb2.xloader` が用意されています。いずれのローダーもデータベースを排他ロックするので、Fusekiを起動したまま実行することはできません。大きなファイルは先に `riot --validate` で構文を確認しておくと、途中で失敗してインデックスが壊れる事故を避けられます。

### Pythonからの呼び出し：requestsによるJSON結果の取得

Fusekiは普通のHTTPサーバーなので、Jenaのクライアントライブラリを使わずとも呼び出せます。「apache jena python」で探している場合、Pythonから使うのにSPARQL専用のドライバは要りません。ただしHTTPクライアントは別途入れます。以下の出力例は、books.ttlの2冊だけを投入した状態に対応します。前節のINSERT DATAも実行済みなら、Java Puzzlersを含む3件が出力されます。

```
$ python3 -m pip install requests
```

```
import requests

ENDPOINT = "http://localhost:3030/ds/sparql"
QUERY = """
PREFIX ex: <http://example.org/>
SELECT ?title ?author WHERE {
  ?b a ex:Book ; ex:title ?title ; ex:author ?author .
} ORDER BY ?title
"""

res = requests.post(
    ENDPOINT,
    data={"query": QUERY},
    headers={"Accept": "application/sparql-results+json"},
    timeout=10,
)
res.raise_for_status()
for row in res.json()["results"]["bindings"]:
    print(row["title"]["value"], "/", row["author"]["value"])
```

```
$ python3 query.py
Effective Java / Joshua Bloch
Java Concurrency in Practice / Brian Goetz
```

すでにrequestsを使っているアプリケーションなら、SPARQL専用ライブラリを足さずにJSON結果を取得できます。標準ライブラリだけに絞りたい場合は urllib.request でも同じことができ、結果の整形まで任せたい場合は SPARQLWrapper を使います。

### Javaへの組み込み：FusekiServerとRDFConnection

Javaのアプリケーションでは、別プロセスのFusekiを立てずに、同じJVMの中でSPARQLサーバーを起動することもできます。テストで使い捨てのエンドポイントが欲しいときや、アプリケーションに読み取り用のSPARQL口を付けたいときの方法で、公式ドキュメントでは[Embedded Fuseki server](https://jena.apache.org/documentation/fuseki2/fuseki-embedded.html)として説明されています。依存に加えるのは jena-fuseki-main の1つです。

```
<dependency>
  <groupId>org.apache.jena</groupId>
  <artifactId>jena-fuseki-main</artifactId>
  <version>6.2.0</version>
</dependency>
```

次のコードは、メモリ上のデータセットをポート3335で公開し、[RDFConnection](https://jena.apache.org/documentation/rdfconnection/)でHTTP越しに前述の books.ttl を投入して検索するものです。

```
import org.apache.jena.fuseki.main.FusekiServer;
import org.apache.jena.rdfconnection.RDFConnection;
import org.apache.jena.sparql.core.DatasetGraph;
import org.apache.jena.sparql.core.DatasetGraphFactory;

public class Main {
    public static void main(String[] args) {
        DatasetGraph dsg = DatasetGraphFactory.createTxnMem();
        FusekiServer server = FusekiServer.create()
                .port(3335)
                .add("/ds", dsg)
                .build();
        server.start();

        try (RDFConnection conn = RDFConnection.connect("http://localhost:3335/ds")) {
            conn.load("books.ttl");
            conn.querySelect(
                "PREFIX ex: <http://example.org/> SELECT ?title WHERE { ?b ex:title ?title } ORDER BY ?title",
                row -> System.out.println(row.getLiteral("title").getString()));
        } finally {
            server.stop();
        }
    }
}
```

```
Effective Java
Java Concurrency in Practice
```

Maven 3.9.11でコンパイルレベルを21に指定し、Temurin 25で実行して上の2行を確認しました。RDFConnectionによる投入・検索処理は、`RDFConnection.connect` に渡す接続先URLを変えるだけでスタンドアロンのFusekiにも再利用できます。本番で独立したFusekiへ接続する場合は、この例の組み込みサーバーの生成・起動・停止処理を外します。

導入前に知っておきたい点が2つあります。1つは依存の大きさで、jena-fuseki-main 1つを入れるだけで推移的な依存を含めて80個、合計34MBのjarが入りました。もう1つはログで、SLF4Jの実装を入れていないと起動時に `No SLF4J providers were found` と警告が出て、Fusekiのログは何も出力されません。サーバーの起動やリクエストを記録したい場合は、Log4j2などのSLF4J実装を依存に加えてください。

## TDB2での永続化・バックアップ・ディスク消費

永続化にはTDB2を使います。`--loc` で指定したディレクトリの中に `Data-0001` のような世代ディレクトリが作られ、その中にインデックスファイルが並びます。

### HTTPから起動する無停止バックアップ

サーバーを止めずにバックアップを取れます。管理エンドポイントにPOSTするとタスクIDが返り、実際の処理は非同期で走ります。

```
$ curl -X POST 'http://localhost:3030/$/backup/ds'
{
  "taskId" : "1" ,
  "requestId" : 7
}

$ curl -s 'http://localhost:3030/$/tasks'
[ {
    "task" : "Backup" ,
    "taskId" : "1" ,
    "started" : "2026-09-08T07:46:23.418+09:00" ,
    "finished" : "2026-09-08T07:46:23.475+09:00" ,
    "success" : true
  }
]

$ ls run/backups/
ds_2026-09-08_07-46-23.nq.gz
```

出力はN-Quadsをgzip圧縮したもので、作業領域の `backups` ディレクトリに置かれます。POSTしただけでは完了を待たないため、バッチから呼ぶときは `/$/tasks` をポーリングして `finished` と `success` を確認してください。配布物の `fuseki-backup` スクリプトはバックアップ開始のPOSTだけを実行します。タスク確認用のcurlはコメントアウトされているため、完了確認は別途実装してください。

### 6トリプルで192MiBというディスク消費

TDB2はインデックスファイルをあらかじめまとまった単位で確保します。6トリプルしか入っていないデータベースでも、世代ディレクトリの実サイズは192MiBありました（8MiBのファイルが24個）。さらに `tdb2.tdbcompact` で圧縮すると新しい世代が作られますが、`--deleteOld` を付けない限り古い世代は残るため、実行直後は合計384MiBになります。

```
$ apache-jena-6.2.0/bin/tdb2.tdbcompact --loc DB2
Compacted in 2.162s

$ ls DB2
Data-0001
Data-0002
tdb.lock
```

192MiBと384MiBは、この6トリプルの検証条件での測定値です。ボリュームサイズは、対象環境でのディスク使用量と、圧縮中に新旧世代を保持するための追加容量を測って見積もってください。小さなデータセットを多数作る設計にすると、データ量に対してディスクだけが増えていきます。

## 認証とアクセス制御：既定は管理機能だけlocalhost

ここが最も事故につながる箇所です。Fusekiを初めて起動すると、作業領域に `shiro.ini` が自動生成されます。6.2.0が生成する内容は次のとおりです。

```
[main]
ssl.enabled = false

plainMatcher=org.apache.shiro.authc.credential.SimpleCredentialsMatcher
localhostFilter=org.apache.jena.fuseki.authz.LocalhostFilter

[users]
user1=passwd1

[roles]

[urls]
## Control functions open to anyone
/$/server  = anon
/$/ping    = anon
/$/metrics = anon

## and the rest are restricted to localhost.
/$/** = localhostFilter

# Everything else
/**=anon
```

最後の1行が要点です。`/$/server`・`/$/ping`・`/$/metrics` を除く管理機能はlocalhostに限定されますが、**それ以外のすべて、つまりデータセットのクエリと更新のエンドポイントは誰でもアクセスできます**。`--update` を付けて起動したサーバーをそのままインターネットに露出させると、第三者がSPARQL Updateでデータを消せる状態になります。ローカルホスト限定だから安全、という理解は管理機能についてのみ正しく、データについては当てはまりません（管理画面の節で示したLAN側からの実測結果のとおりです）。

### 方法1：shiro.iniによるBasic認証

生成されたファイルのコメントに手順が書かれています。`[users]` にまともなパスワードを設定し、`[urls]` の記述を書き換えます。

```
[users]
admin=（十分な長さのパスワード）

[urls]
/$/server = anon
/$/ping   = anon
/$/** = authcBasic,user[admin]
/** = authcBasic,user[admin]
```

平文で書きたくない場合はApache Shiroのハッシュに対応しており、argon2id形式の文字列を `[users]` に置いてPasswordMatcherを有効にする書き方が公式の[Fuseki Securityのページ](https://jena.apache.org/documentation/fuseki2/fuseki-security.html)に載っています。

### 方法2：`--passwd`によるパスワードファイル指定

設定ファイルを書かずに済ませたいなら、起動オプションでパスワードファイルを指定するだけでもBasic認証がかかります。UIが不要な構成と組み合わせるとコンテナ向きです。

```
$ cat passwd.txt
admin: pw123

$ MAIN=plain ./fuseki-server --loc=DB2 --passwd=passwd.txt --port 3032 /ds3
```

```
$ curl -s -o /dev/null -w '%{http_code}\n' \
    --data-urlencode 'query=SELECT (COUNT(*) AS ?n) WHERE{?s ?p ?o}' \
    http://localhost:3032/ds3/sparql
401

$ curl -s -u admin:pw123 -H 'Accept: text/csv' \
    --data-urlencode 'query=SELECT (COUNT(*) AS ?n) WHERE{?s ?p ?o}' \
    http://localhost:3032/ds3/sparql
n
6
```

認証なしでは401、正しい資格情報を付けると結果が返ります。ただしBasic認証自体には通信の暗号化機能がないため、外部公開時はFusekiのHTTPS設定またはTLS終端を行うリバースプロキシを併用してください。これに加えて、データセットやエンドポイント単位のアクセス制御、または読み取り専用データセット内のグラフの閲覧制御には[Data Access Control](https://jena.apache.org/documentation/fuseki2/fuseki-data-access-control.html)という別の仕組みが用意されています。HTTPSは `--https` に証明書の場所とパスワードを書いたJSONファイルを渡して有効にします。HTTPS用ポートの既定値は3043です。逆に、外部に出す予定がないなら `--localhost` を付けてループバックだけで待ち受けるのが最も確実です。

## systemdでのサービス常駐

Linuxで常駐させる場合、ユニットファイルを自分で書く必要はありません。配布物の `service/service/fuseki.service` をそのまま使えます。

```
[Unit]
Description=Fuseki

[Service]
Environment=FUSEKI_HOME=/opt/fuseki
Environment=FUSEKI_BASE=/etc/fuseki
Environment=JVM_ARGS=-Xmx4G
ExecStart=/opt/fuseki/fuseki-server
User=fuseki
Restart=on-abort
SuccessExitStatus=143

[Install]
WantedBy=multi-user.target
```

このファイルを `/etc/systemd/system/` に置き、fusekiという名前のシステムユーザーを作って設定とデータベースへのアクセス権を与え、パスを環境に合わせてから有効化します。`SuccessExitStatus=143` はJavaプロセスがSIGTERMで終了するときの終了コードが143になるためで、これを書かないと正常な停止が失敗として記録されます。ログは既定でjournalctlに出ます。

## 他のRDFストア・グラフDBとの使い分け

Fusekiを選ぶかどうかは、扱うデータモデルと運用形態で決まります。RDFとSPARQLの標準に沿って自前運用したいならFusekiが素直な選択です。

| 製品                 | データモデル       | クエリ言語                     | 提供形態       |
| ------------------ | ------------ | ------------------------- | ---------- |
| Apache Jena Fuseki | RDF          | SPARQL                    | 自前運用（OSS）  |
| Neo4j              | プロパティグラフ     | Cypher                    | 自前運用／マネージド |
| Amazon Neptune     | RDF／プロパティグラフ | SPARQL／Gremlin／openCypher | マネージド      |

語彙を他組織と共有する、公開データを取り込む、W3C標準の形式で配布するといった要件があるならRDF側、アプリケーション内部の関係をノードとリレーションシップの属性として持ち、Cypher中心でたどる要件ならプロパティグラフ側が候補になります。性能は同じデータと代表クエリで測って判断してください。既存のRDBにある業務データをRDFへ写すのか、グラフDBへ移すのかといった全体の設計から見直す場合は、[データベース設計・移行支援](/service/system/database/)で相談を受け付けています。両者の構造的な違いは[Neo4jとは？グラフデータベースの構造とCypher・エディション選定を実装視点で解説【2026年版】](/tech/details/15996/)と[Amazon Neptuneとは｜3つのクエリ言語・Analyticsとの違い・料金モデルを実装目線で解説【2026年版】](/tech/details/3337/)で扱っています。RAGの文脈でベクトル検索と併用する設計を検討している場合は[ベクトルデータベースとグラフデータベースの違い｜比較表と使い分け・GraphRAGでの併用【2026年版】](/tech/details/4682/)が参考になります。

## よくある質問

### Apache Jena Fusekiとは何をするソフトウェアですか？

RDFデータをHTTP経由のSPARQLエンドポイントとして公開するサーバーです。SPARQL Query、SPARQL Update、Graph Store Protocol、RDF Patchのエンドポイントを提供し、管理画面も内蔵しています。ライセンスはApache License 2.0です。

### Fusekiの最新バージョンとJavaの要件は？

2026年10月時点の最新は6.2.0（2026年7月31日リリース）です。Jena 6系はJava 21以上を必須としており、Java 17以下では起動しません。

### Docker公式イメージはありますか？

ありません。Apacheが配っているのはMaven Central上のビルドキット jena-fuseki-docker です。有志のイメージを使う場合、stain/jena-fuseki は2024年7月の5.1.0で止まっているので、6系が必要なら別のイメージを選ぶか自分でビルドしてください。

### TDB1とTDB2はどちらを使うべきですか？

新規はTDB2です。`--loc` で指定した場所は既定でTDB2として扱われるため、意識しなければTDB2になります。既存のTDB2データベースを6系へ移す場合は、小数値の扱いに修正が入っているため再ロードが推奨されています。

### WARファイルでTomcatに載せられますか？

6系ではできません。WARが作られた最後のリリースは5.6.0です。公式ドキュメントにはWAR運用の説明が残っていますが、6系のダウンロードページにWARは置かれていないため、スタンドアロンサーバーかコンテナへ移行してください。

### そのままインターネットに公開して大丈夫ですか？

危険です。既定の `shiro.ini` は管理機能をlocalhostに限定する一方、データセットのエンドポイントは `/**=anon` で誰にでも開いています。`--update` 付きで公開すると第三者がデータを更新・削除できます。公開する前に `shiro.ini` でBasic認証をかけるか、`--passwd` でパスワードファイルを指定するか、`--localhost` で待ち受けを制限してください。

## 関連記事

- [SPARQLとは？RDFを検索するクエリ言語の書き方とSQLとの違いを実行例で解説【2026年版】](/tech/details/3428/)
- [オントロジーとは？RDFとOWLで意味を定義する実装手順【2026年版】](/tech/details/17449/)
- [Neo4jとは？グラフデータベースの構造とCypher・エディション選定を実装視点で解説【2026年版】](/tech/details/15996/)
- [Amazon Neptuneとは｜3つのクエリ言語・Analyticsとの違い・料金モデルを実装目線で解説【2026年版】](/tech/details/3337/)
- [ベクトルデータベースとグラフデータベースの違い｜比較表と使い分け・GraphRAGでの併用【2026年版】](/tech/details/4682/)

---

出典: [Apache Jena Fusekiとは？Jena 6の変更点とインストール・Docker運用を実装目線で解説](<https://www.issoh.co.jp/tech/details/10916/>)（株式会社一創）
