# Step 5: Add receipt.png

You can use the Java Client Libraries to save non-RDF resources (e.g., **`.png`** , **`.pdf`** ) to your Pod.

For this part of the tutorial, the getting started app uses Inrupt’s Java client library to:

* Store a copy of the expense receipts to your Pod.
* Update the associated expense to link to the URLs of the receipts.

{% hint style="info" %}
You can find the complete code at [Complete Code](/sdk/java-sdk/tutorial/step5/code.md).
{% endhint %}

## Update **`application.properties`**

Add the following properties to the **`src/main/resources/application.properties`** file:

```json
spring.servlet.multipart.max-file-size=128KB
spring.servlet.multipart.max-request-size=128KB
```

## Modify **`Expense`** Class

1. Open **`Expense`** class file:

{% tabs %}
{% tab title="Java" %}
Open `src/main/java/com/example/gettingstarted/Expense.java`
{% endtab %}

{% tab title="Kotlin" %}
Open `src/main/kotlin/com/example/gettingstarted/Expense.kt`
{% endtab %}
{% endtabs %}

2. Add the **`java.util.*`** import statement:

{% tabs %}
{% tab title="Java" %}

```java
import java.util.*;
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
import java.util.*
```

{% endtab %}
{% endtabs %}

3. Add the predicate definition for the receipts in the **`Expense`** class:

{% tabs %}
{% tab title="Java" %}

```java
static IRI SCHEMA_ORG_IMAGE = rdf.createIRI("https://schema.org/image");
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
var SCHEMA_ORG_IMAGE: IRI = rdf.createIRI("https://schema.org/image")
```

{% endtab %}
{% endtabs %}

4. Update the **`Expense`** class constructor to include the receipts:

{% tabs %}
{% tab title="Java" %}

<pre class="language-java"><code class="lang-java">@JsonCreator
public Expense(@JsonProperty(&#x26;quot;identifier&#x26;quot;) final URI identifier,
               @JsonProperty(&#x26;quot;merchantProvider&#x26;quot;) String merchantProvider,
               @JsonProperty(&#x26;quot;expenseDate&#x26;quot;) Date expenseDate,
               @JsonProperty(&#x26;quot;description&#x26;quot;) String description,
               @JsonProperty(&#x26;quot;amount&#x26;quot;) BigDecimal amount,
               @JsonProperty(&#x26;quot;currency&#x26;quot;) String currency,
               @JsonProperty(&#x26;quot;category&#x26;quot;) String category,
<strong>               @JsonProperty(&#x26;quot;receipts&#x26;quot;) String[] receipts) {
</strong>    this(identifier);&#x3C;/strong>
    this.setRDFType(MY_RDF_TYPE_VALUE);
    this.setMerchantProvider(merchantProvider);
    this.setExpenseDate(expenseDate);
    this.setDescription(description);
    this.setAmount(amount);
    this.setCurrency(currency);
    this.setCategory(category);
<strong>    this.setReceipts(receipts);
</strong>}
</code></pre>

{% endtab %}

{% tab title="Kotlin" %}

<pre class="language-kotlin"><code class="lang-kotlin">@JsonCreator
constructor(
    @JsonProperty(&#x26;quot;identifier&#x26;quot;) identifier: URI,
    @JsonProperty(&#x26;quot;merchantProvider&#x26;quot;) merchantProvider: String?,
    @JsonProperty(&#x26;quot;expenseDate&#x26;quot;) expenseDate: Date,
    @JsonProperty(&#x26;quot;description&#x26;quot;) description: String?,
    @JsonProperty(&#x26;quot;amount&#x26;quot;) amount: BigDecimal?,
    @JsonProperty(&#x26;quot;currency&#x26;quot;) currency: String?,
    @JsonProperty(&#x26;quot;category&#x26;quot;) category: String?,
<strong>    @JsonProperty(&#x26;quot;receipts&#x26;quot;) receipts: Set&#x3C;String>?
</strong>) : this(identifier, rdf.createDataset(), Headers.empty()) {
    this.rdfType = MY_RDF_TYPE_VALUE
    this.merchantProvider = merchantProvider
    this.expenseDate = expenseDate
    this.description = description
    this.amount = amount
    this.currency = currency
    this.category = category
<strong>    this.receipts = receipts
</strong>}
</code></pre>

{% endtab %}
{% endtabs %}

5. Add the getter and setters for the receipts in the **`Expense`** class:

{% tabs %}
{% tab title="Java" %}

```java
public Set<String> getReceipts() {
    return subject.getReceipts();
}
// Note:: The setters first uses the getter, which returns a Set, and adds the receipt to the set.
public void addReceipt(String receipt) {
    subject.getReceipts().add(receipt);
}
public void setReceipts(String[] receipts) {
    subject.getReceipts().addAll(List.of(receipts));
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
// Note:: The setters first uses the getter, which returns a Set, and adds the receipt to the set.

var receipts: Set<String>?
    get() = subject.receipts
    set(receipts) {
        if (receipts != null) {
            subject.receipts.addAll(receipts)
        }
    }
fun addReceipt(receipt: String) {
    subject.receipts.add(receipt)
}

```

{% endtab %}
{% endtabs %}

6. Add getter for the receipts in the inner **`Node`** class:

{% tabs %}
{% tab title="Java" %}

```java
public Set<String> getReceipts() {
    return objects(SCHEMA_ORG_IMAGE, TermMappings::asIri, ValueMappings::iriAsString);
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
val receipts: MutableSet<String>
    get() = objects(SCHEMA_ORG_IMAGE, TermMappings::asIri, ValueMappings::iriAsString)
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
Note\
For more detail with regards to handling lists, see [https://github.com/inrupt/docs-gitbook/blob/main/sdk/java-sdk/tutorial/step5/broken-reference/README.md](https://github.com/inrupt/docs-gitbook/blob/main/sdk/java-sdk/tutorial/step5/broken-reference/README.md "mention").
{% endhint %}

## Modify **`ExpenseController`** Class

1. Open **`ExpenseController`** class file.

{% tabs %}
{% tab title="Java" %}
Open `src/main/java/com/example/gettingstarted/ExpenseController.java`
{% endtab %}

{% tab title="Kotlin" %}
Open `src/main/kotlin/com/example/gettingstarted/ExpenseController.kt`
{% endtab %}
{% endtabs %}

2. Add the following import statements:

{% tabs %}
{% tab title="Java" %}

```java
import com.inrupt.client.solid.SolidNonRDFSource;
import org.springframework.web.multipart.MultipartFile;
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
import com.inrupt.client.solid.SolidNonRDFSource
import org.springframework.web.multipart.MultipartFile
```

{% endtab %}
{% endtabs %}

3. Add the following method to handle the upload of non-RDF files:

{% tabs %}
{% tab title="Java" %}

```java

/**
 * Note 9: Stores a non-RDF resource to a Pod
 *
 * Using SolidNonRDFSource and the SolidSyncClient .create() method,
 * - Saves a non-RDF resource at the destinationURL.
 */
@PutMapping("**/resource/nonRDF/add**")
public String addNonRDFFile(@RequestParam(value = "destinationURL") String destinationURL,
                            @RequestParam(value = "file") MultipartFile file) {
    printWriter.println("In addNonRDFFile:: Save Non-RDF File to Pod.");
    try (final var fileStream = file.getInputStream()) {
        SolidNonRDFSource myNonRDFFile = new SolidNonRDFSource(URI.create(destinationURL), file.getContentType(), fileStream);
        return client.create(myNonRDFFile).getIdentifier().toString();
    } catch(PreconditionFailedException e1) {
        // Errors if the resource already exists
        printWriter.println(String.format("[%s] com.inrupt.client.solid.PreconditionFailedException in addNonRDFFile:: %s", e1.getStatusCode(), e1.getMessage()));
    } catch(ForbiddenException e2) {
        // Errors if user does not have access to create
        printWriter.println(String.format("[%s] com.inrupt.client.solid.ForbiddenException in addNonRDFFile:: %s", e2.getStatusCode(), e2.getMessage()));
    } catch(Exception e) {
        e.printStackTrace();
    }
    return null;
}

```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
/**
 * Note 9: Stores a non-RDF resource to a Pod
 *
 * Using SolidNonRDFSource and the SolidSyncClient's .create() method,
 * - Saves a non-RDF resource at the destinationURL.
 */
@PutMapping("**/resource/nonRDF/add**")
fun addNonRDFFile(
    @RequestParam(value = "destinationURL") destinationURL: String,
    @RequestParam(value = "file") file: MultipartFile
): String? {
    printWriter.println("In addNonRDFFile: Save Non-RDF File to Pod.")
    try {
        file.inputStream.use { fileStream ->
           val myNonRDFFile: SolidNonRDFSource = SolidNonRDFSource(URI.create(destinationURL), file.contentType, fileStream)
           return client.create(myNonRDFFile).identifier.toString()
        }
    } catch(e1: PreconditionFailedException) {
        // Errors if the resource already exists
        printWriter.println(String.format("[%s] com.inrupt.client.solid.PreconditionFailedException in addNonRDFFile:: %s", e1.statusCode, e1.localizedMessage))
    } catch(e2: ForbiddenException) {
        // Errors if user does not have access to create
        printWriter.println(String.format("[%s] com.inrupt.client.solid.ForbiddenException in addNonRDFFile:: %s", e2.statusCode, e2.localizedMessage))
    } catch (e: Exception) {
        e.printStackTrace()
    }
    return ""
}

```

{% endtab %}
{% endtabs %}

4. Add the following method that uploads a receipt file and links it to the associated **`Expense`**.

{% tabs %}
{% tab title="Java" %}

```java
/**
 * Note 10: Stores a non-RDF resource (image of the receipt) to a Pod and Attach to an Expense
 * Using methods defined as part of getting started, addReceiptToExpense:
 * - Calls addNonRDFFile() to store the receipt to a Pod
 * - Calls getExpense() to fetch the associated Expense RDF resource.
 * - Calls the Expense's setter `addReceipt` to add the link to the saved receipt.
 * - Calls updateExpense() to save the updated Expense.
 */
@PutMapping("**/expenses/receipts/add**")
public Expense addReceiptToExpense(@RequestParam(value = "destinationURL") String destinationURL,
                                   @RequestParam(value = "file") MultipartFile file,
                                   @RequestParam(value = "expenseURL") String expenseURL) {
    printWriter.println("In addReceiptToExpense: Save Receipt File to Pod and Update Associated Expense.");
    try {
        String receiptLocation = addNonRDFFile(destinationURL, file);
        if (receiptLocation != null) {
            Expense expense = getExpense(expenseURL);
            expense.addReceipt(receiptLocation);
            return updateExpense(expense);
        } else {
            printWriter.println("Error adding receipt");
            return null;
        }
    } catch(ForbiddenException e2) {
        // Errors if user does not have access to read or update the Expense resource
        printWriter.println(String.format("[%s] com.inrupt.client.solid.ForbiddenException in addReceiptToExpense:: %s", e2.getStatusCode(), e2.getMessage()));
    } catch(Exception e) {
        e.printStackTrace();
    }
    return null;
}
```

{% endtab %}

{% tab title="Kotlin" %}

```kotlin
/**
 * Note 10: Stores a non-RDF resource (image of the receipt) to a Pod and Attach to an Expense
 * Using methods defined as part of getting started, addReceiptToExpense:
 * - Calls addNonRDFFile() to store the receipt to a Pod
 * - Calls getExpense() to fetch the associated Expense RDF resource.
 * - Calls the Expense's setter `addReceipt` to add the link to the saved receipt.
 * - Calls updateExpense() to save the updated Expense.
 */
@PutMapping("**/expenses/receipts/add**")
fun addReceiptToExpense(
    @RequestParam(value = "destinationURL") destinationURL: String,
    @RequestParam(value = "file") file: MultipartFile,
    @RequestParam(value = "expenseURL") expenseURL: String
): Expense? {
    printWriter.println("In AddReceiptToExpense: Save Receipt File to Pod and Update Associated Expense.")
    val receiptLocation = addNonRDFFile(destinationURL, file)
    return if (!receiptLocation.isNullOrEmpty()) {
        val expense = getExpense(expenseURL)
        if (expense != null) {
            expense.addReceipt(receiptLocation)
            updateExpense(expense)
        } else {
            null
        }
    } else {
        printWriter.println("Error adding receipt")
        null
    }
}
```

{% endtab %}
{% endtabs %}


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.inrupt.com/sdk/java-sdk/tutorial/step5.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
