RDF4J RIO UnsupportedRDFormatException при добавлении данных в HTTPRepository с использованием автономного приложения

У меня есть HTTPRepository инициализируется URL-адресом хранилища. Я использую RepositoryConnection извлекать и добавлять данные о погоде в хранилище. Данные извлекаются из веб-службы, затем преобразуются в операторы RDF и добавляются в хранилище. Это делается периодически с помощью отдельного приложения.

Когда я запускаю это приложение в IntelliJ, все работает нормально.

Чтобы запустить это приложение на сервере, я создал файл JAR (содержащий все зависимости). Приложение запускается, как и ожидалось, и может извлекать данные из хранилища.

Однако, когда приложение пытается записать данные в хранилище, я получаю UnsupportedRDFormatException:

org.eclipse.rdf4j.rio.UnsupportedRDFormatException: Did not recognise RDF format object BinaryRDF (mimeTypes=application/x-binary-rdf; ext=brf)
    at org.eclipse.rdf4j.rio.Rio.lambda$unsupportedFormat$0(Rio.java:568) ~[weatherData-1.0-SNAPSHOT-jar-with-dependencies.jar:na]
    at java.util.Optional.orElseThrow(Optional.java:290) ~[na:1.8.0_111]
    at org.eclipse.rdf4j.rio.Rio.createWriter(Rio.java:134) ~[weatherData-1.0-SNAPSHOT-jar-with-dependencies.jar:na]
    at org.eclipse.rdf4j.rio.Rio.write(Rio.java:371) ~[weatherData-1.0-SNAPSHOT-jar-with-dependencies.jar:na]
    at org.eclipse.rdf4j.rio.Rio.write(Rio.java:324) ~[weatherData-1.0-SNAPSHOT-jar-with-dependencies.jar:na]
    at org.eclipse.rdf4j.repository.http.HTTPRepositoryConnection.addModel(HTTPRepositoryConnection.java:588) ~[weatherData-1.0-SNAPSHOT-jar-with-dependencies.jar:na]
    at org.eclipse.rdf4j.repository.http.HTTPRepositoryConnection.flushTransactionState(HTTPRepositoryConnection.java:662) ~[weatherData-1.0-SNAPSHOT-jar-with-dependencies.jar:na]
    at org.eclipse.rdf4j.repository.http.HTTPRepositoryConnection.commit(HTTPRepositoryConnection.java:326) ~[weatherData-1.0-SNAPSHOT-jar-with-dependencies.jar:na]
    at org.eclipse.rdf4j.repository.base.AbstractRepositoryConnection.conditionalCommit(AbstractRepositoryConnection.java:366) ~[weatherData-1.0-SNAPSHOT-jar-with-dependencies.jar:na]
    at org.eclipse.rdf4j.repository.base.AbstractRepositoryConnection.add(AbstractRepositoryConnection.java:431) ~[weatherData-1.0-SNAPSHOT-jar-with-dependencies.jar:na]
    at nl.wur.fbr.data.weather.WeatherApp.retrieveData(WeatherApp.java:122) ~[weatherData-1.0-SNAPSHOT-jar-with-dependencies.jar:na]
    at nl.wur.fbr.data.weather.WeatherData$WeatherTask.run(WeatherData.java:105) [weatherData-1.0-SNAPSHOT-jar-with-dependencies.jar:na]
    at java.util.TimerThread.mainLoop(Timer.java:555) [na:1.8.0_111]
    at java.util.TimerThread.run(Timer.java:505) [na:1.8.0_111]

Исходный код, в котором возникает ошибка:

    public void retrieveData(){
        logger.info("Retrieving data for weather for app: "+ID+" ");
        RepositoryConnection connection = null;
        ValueFactory vf = SimpleValueFactory.getInstance();
        try {
            connection = repository.getConnection();

            // Retrieving the locations from the repository (no problem here).
            List<Location> locations = this.retrieveLocations(connection);
            List<Statement> statements = new ArrayList<>();

            // Retrieving weather data from each location and transforming it to statements.
            for(Location location : locations){
                List<Weather> retrievedWeather = weatherService.retrieveWeatherData(location.name,location.latitude,location.longitude);
                for(Weather weather : retrievedWeather){
                    BNode phenomenon = vf.createBNode();
                    statements.add(vf.createStatement(location.ID,WEATHER.HAS_WEATHER,phenomenon,rdfStoreGraph));
                    statements.addAll(weather.getStatements(phenomenon,vf,rdfStoreGraph));
                    statements = this.correctOMIRIs(statements,vf);
                }
            }

            // Adding data retrieved from the weather API
            // This is where the exception happens.
            connection.add(statements,rdfStoreGraph);

        } catch (Exception e) {
            logger.error("Could not retrievedata for weather app: '"+ID+"' because no monitor locations could be found.",e);
        } finally {
            if(connection != null){
                connection.close();
            }
        }
    }

HTTPRespository инициализируется так:

        repository = new HTTPRepository(rdfStore.toString());
        ((HTTPRepository)repository).setPreferredRDFFormat(RDFFormat.BINARY);
        ((HTTPRepository)repository).setPreferredTupleQueryResultFormat(TupleQueryResultFormat.BINARY);

Я пытался изменить форматы на TURTLE, Но это не имеет значения.

Можете ли вы сказать мне, как решить эту проблему?

NB. Сервер и библиотека RDF4J имеют версию 2.0.1 (rdf4j).

2 ответа

Решение

Чтобы запустить это приложение на сервере, я создал файл JAR (содержащий все зависимости).

Проблема в том, что вы создали "толстую банку" и, возможно, неправильно слили файлы реестра SPI.

Парсеры Rio RDF4J (а также несколько других модулей) используют механизм интерфейса поставщика услуг (SPI) Java для регистрации себя. Этот механизм опирается на текстовый файл в META-INF\services в файле jar, содержащем полное имя каждой реализации парсера.

Проблема возникает при объединении jar-файлов: каждый jar-анализатор Rio имеет файл реестра с тем же именем, но разным содержимым. Если вы используете что-то вроде подключаемого модуля maven для создания толстого фляги, каждый файл реестра перезаписывается следующим. В результате RDF4J может найти только один парсер - тот, чей файл реестра был добавлен последним в толстый файл.

Решение состоит в том, чтобы либо вообще не создавать толстую банку, либо, если необходимо, использовать другую технику для ее создания, которая объединяет файлы реестра, а не перезаписывает их. Плагин Maven Shade имеет хороший вариант конфигурации для этого: ServicesResourceTransformer.

Я откладываю этот пост, потому что я застрял в нем на несколько часов. Наконец, я мог сгенерировать исполняемый jar-файл с помощью плагина maven shade со следующей конфигурацией:

      <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.2.4</version>
    <configuration>
        <filters>
            <filter>
                <artifact>*:*</artifact>
                <excludes>
                    <exclude>META-INF/*.SF</exclude>
                    <exclude>META-INF/*.DSA</exclude>
                    <exclude>META-INF/*.RSA</exclude>
                </excludes>
            </filter>
        </filters>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
            <configuration>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer"/>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>${fully.qualified.main.class}</mainClass>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

Я использовал затененный плагин с ManifestResourceTransformer для создания исполняемого jar-файла, указывающего основной класс моего проекта, и с ServicesResourceTransformer для работы с именами пакетов RDF4J, чтобы избежать того, что один синтаксический анализатор переопределяет предыдущий. Кроме того, мне пришлось включить раздел фильтров, чтобы избежать ошибок JNI, связанных с сигнатурами пакетов.

Надеюсь, это кому-то пригодится.

Привет.

Другие вопросы по тегам