1

Trying to create a Java Object and store it in a NOsql Object (NO2 a.k.a Nitrite) database.

Upon trying to store the object in an object repository the com.fasterxml.jackson.databind.exc.InvalidDefinitionException exception is thrown.

I have tried to export the bank.java class in the module-info.java file; I have tried to add a default constructor in the bank.java class. Neither have solved the problem.

Thank you for the help in solving this error.

The full error is displayed as follows:

Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Invalid type definition for type `com.app.bankapplet.bank.Bank`: Failed to construct BeanSerializer for [simple type, class com.app.bankapplet.bank.Bank]: (java.lang.IllegalArgumentException) Failed to call `setAccess()` on Field 'id' (of class `com.app.bankapplet.bank.Bank`) due to `java.lang.reflect.InaccessibleObjectException`, problem: Unable to make field private int com.app.bankapplet.bank.Bank.id accessible: module com.app.bankapplet does not "opens com.app.bankapplet.bank" to unnamed module @6293abcc
    at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:72)
    at com.fasterxml.jackson.databind.SerializerProvider.reportBadTypeDefinition(SerializerProvider.java:1280)
    at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.constructBeanOrAddOnSerializer(BeanSerializerFactory.java:475)
    at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.findBeanOrAddOnSerializer(BeanSerializerFactory.java:295)
    at com.fasterxml.jackson.databind.ser.BeanSerializerFactory._createSerializer2(BeanSerializerFactory.java:240)
    at com.fasterxml.jackson.databind.ser.BeanSerializerFactory.createSerializer(BeanSerializerFactory.java:174)
    at com.fasterxml.jackson.databind.SerializerProvider._createUntypedSerializer(SerializerProvider.java:1503)
    at com.fasterxml.jackson.databind.SerializerProvider._createAndCacheUntypedSerializer(SerializerProvider.java:1451)
    at com.fasterxml.jackson.databind.SerializerProvider.findValueSerializer(SerializerProvider.java:556)
    at com.fasterxml.jackson.databind.SerializerProvider.findTypedValueSerializer(SerializerProvider.java:834)
    at com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:307)
    at com.fasterxml.jackson.databind.ObjectMapper._convert(ObjectMapper.java:4522)
    ... 18 more
Exception running application com.app.bankapplet.Main

What I would expect to have happen is the object is stored in the object repository.

Please find below my code:

Main.java

package com.app.bankapplet;

import com.app.bankapplet.bank.Bank;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import javafx.util.Callback;
import org.dizitart.no2.Nitrite;
import org.dizitart.no2.NitriteCollection;
import org.dizitart.no2.objects.ObjectRepository;

import java.io.File;
import java.io.IOException;
import java.util.Optional;

public class Main extends Application {
    private static Nitrite db;
    private static Bank bank;
    @Override
    public void start(Stage stage) throws IOException {
        // Generates a bank
        bank = getBank();
        ObjectRepository<Bank> bankObjectStore = db.getRepository(Bank.class);
        bankObjectStore.insert(bank);

        FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("main.fxml"));
        Scene scene = new Scene(fxmlLoader.load(), 800, 600);
        stage.setTitle("Bank Manager");
        stage.setScene(scene);
        stage.show();
    }
    public static void main(String[] args) {
        db = Nitrite.builder()
                .filePath(new File("target/appdb.db"))
                .compressed()
                .openOrCreate();
        launch();
    }
    protected static Bank getBank() {
        Dialog<Bank> dialog = new Dialog<>();
        dialog.setTitle("New Bank");
        dialog.setHeaderText("Enter the name of the bank:");

        Label label = new Label("Bank Name: ");
        TextField text = new TextField();

        GridPane gridPane = new GridPane();
        gridPane.add(label, 1, 1);
        gridPane.add(text, 2, 1);
        dialog.getDialogPane().setContent(gridPane);

        ButtonType buttonTypeOK = new ButtonType("OK", ButtonBar.ButtonData.OK_DONE);
        dialog.getDialogPane().getButtonTypes().add(buttonTypeOK);

        dialog.setResultConverter(new Callback<ButtonType, Bank>() {
            @Override
            public Bank call(ButtonType b) {
                if(b == buttonTypeOK) {
                    return new Bank(text.getText());
                }
                return null;
            }
        });

        Optional<Bank> result = dialog.showAndWait();

        if(result.isPresent()) {
            return result.get();
        }

        return null;
    }
}

Bank.java

package com.app.bankapplet.bank;

import org.dizitart.no2.objects.Id;

public class Bank {
    @Id
    private int id;
    private String name;

    public Bank() {

    }
    public Bank(String name) {
        this.id = 0;
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    private static String toTitleCase(String item) {
        if (item == null || item.isEmpty()) {
            return item;
        }

        StringBuilder convertedString = new StringBuilder();
        boolean convertNext = true;

        for (char ch : item.toCharArray()) {
            if (Character.isSpaceChar(ch)) {
                convertNext = true;
            } else if (convertNext) {
                ch = Character.toTitleCase(ch);
                convertNext = false;
            } else {
                ch = Character.toLowerCase(ch);
            }
            convertedString.append(ch);
        }

        return convertedString.toString();
    }
}

module-info.java

module com.app.bankapplet {
    requires javafx.controls;
    requires javafx.fxml;
    requires nitrite;

    opens com.app.bankapplet to javafx.fxml;
    exports com.app.bankapplet.bank;
    exports com.app.bankapplet;
}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.app</groupId>
    <artifactId>BankApplet</artifactId>
    <version>1.0-SNAPSHOT</version>
    <name>BankApplet</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <junit.version>5.9.2</junit.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>20</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>20</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-api</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.15.1</version>
        </dependency>
        <dependency>
            <groupId>org.dizitart</groupId>
            <artifactId>nitrite</artifactId>
            <version>3.4.4</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.11.0</version>
                <configuration>
                    <source>20</source>
                    <target>20</target>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.8</version>
                <executions>
                    <execution>
                        <!-- Default configuration for running with: mvn clean javafx:run -->
                        <id>default-cli</id>
                        <configuration>
                            <mainClass>com.app.bankapplet/com.app.bankapplet.Main</mainClass>
                            <launcher>app</launcher>
                            <jlinkZipName>app</jlinkZipName>
                            <jlinkImageName>app</jlinkImageName>
                            <noManPages>true</noManPages>
                            <stripDebug>true</stripDebug>
                            <noHeaderFiles>true</noHeaderFiles>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>
TroyPilewski
  • 359
  • 8
  • 27

2 Answers2

1

Solution: Update your Maven project and Java module-info

To get your example to run, I needed to make the following changes.

  1. Replace the FXML scene with an empty scene, as there is no FXML in your question.

    // FXMLLoader fxmlLoader = new FXMLLoader(Main.class.getResource("main.fxml"));
    // Scene scene = new Scene(fxmlLoader.load(), 800, 600);
    stage.setScene(new Scene(new Group()));
    
  2. Add a dependency management section to the pom.xml as recommended by the Jackson maven guide and replace the remove the version for the jackson-databind so that it could be derived from the bill of materials (bom) in the dependency management section. (I am not sure why this was required, but, without it, my IDE complained that the dependent jackson-core jar file was empty).

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.fasterxml.jackson</groupId>
                <artifactId>jackson-bom</artifactId>
                <version>2.15.2</version>
                <scope>import</scope>
                <type>pom</type>
            </dependency>
        </dependencies>
    </dependencyManagement>
    . . .
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</>
    </dependency>
    
  3. Update the module-info.java to require the jackson databind module.

    requires com.fasterxml.jackson.databind;
    
  4. Update the module-info.java to open the bank package to the jackson databind module.

    opens com.app.bankapplet.bank to com.fasterxml.jackson.databind;
    

I did not need to apply the modification to add an id setter proposed by AhmedNabil's answer. This is because Jackson works on introspection by reflection, and was able to introspect your Bank class once the package opened it to the module, even without a public setter on the id field. You might want to consider using an ID setter regardless.

Additional Comments

You need to:

  1. Require the jackson modules in your module-info, (look for the names in their module-info or META-INF).
  2. In your module-info open the required package (you can see it in the error message) to the appropriate jackson module (it will tell you the name of the jackson module you need to open it to in the error it will generate after you fix step 1).

If you don't understand, study Understanding Java 9 modules.

Although my answer solves the issue marked in your question, future work may also be more complicated because of the use of the non-modular nitrate jar which you are using as an automatic module (so stuff like jlink like you have in your Maven project) won't work with it.

You might want to consider getting rid of the module info altogether and creating a non-modular project, then either having JavaFX in the runtime (e.g. Azul Zulu JDK FX distribution) or providing the JavaFX modules via command line arguments (see openjfx.io getting started for more info about that configuration).

FAQ

Why would I be required to load an empty Scene?

You don’t need to do that.

It is what I needed to do, to be able to run the app in my environment. Your application loads an fxml file which I don’t have. The required fxml file is not included your question, so unless I changed your code, it would not run.

What is jlink?

jlink is a Java linker.

It is a part of the JDK. It builds custom native runtimes for Java and for modular Java applications.

The Maven project file you provided in your question refers to it using an execution of the javafx-maven-plugin, so I thought you might be trying to use it (which won't work for an app with automatic modules such as you have).

jewelsea
  • 150,031
  • 14
  • 366
  • 406
  • 1
    Thank you for the more informative answer. I was able to get the code to work with 3. and 4. of your answer. I'm not sure what 1. was doing. Why would I be required to load an empty Scene? What is ```jlink```? – TroyPilewski Jul 06 '23 at 02:15
  • Added FAQ section to answer to address additional questions. – jewelsea Jul 06 '23 at 22:21
  • Thank you so much. I would love to learn more about project modularization in this way. – TroyPilewski Jul 14 '23 at 04:40
0

java.lang.reflect.InaccessibleObjectException, problem: Unable to make field private int com.app.bankapplet.bank.Bank.id accessible

add setter for id property so that jackson can access it

public void setId(int id) {
   this.id= id;
}
Ahmed Nabil
  • 457
  • 1
  • 9