# Java

> Build the ReportingCloud Java SDK from source with javac, merge sample_invoice.tx to PDF with mergeDocument and send a downloaded template with the request.

- **Product:** ReportingCloud
- **Section:** Documentation
- **Kind:** Topic
- **Path:** ReportingCloud > Getting Started > Java
- **3896 words**
- **Web URL:** https://docs.textcontrol.com/reporting-cloud/getting-started/java/
- **LLMs URL:** https://docs.textcontrol.com/reporting-cloud/getting-started/java/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/reporting-cloud/getting-started/java/llms-full.txt

---

Java applications use ReportingCloud through the Java SDK, whose class `ReportingCloud` wraps the Web API. The SDK is source code on GitHub, not a package in Maven Central, so you build it first. With a current JDK and one program, you then merge the demo template `sample_invoice.tx` of your template storage with invoice data and save the result as a PDF file from the command line. A second variant of the program sends the template with the request instead of naming a stored one.

### Prerequisites

- **A ReportingCloud account.** A trial account is enough, and it has `sample_invoice.tx` in its template storage. [Creating an Account](https://docs.textcontrol.com/reporting-cloud/installation/account/llms-full.txt) shows how to sign up. The Java SDK authenticates with the user name and password of the account, the email address you signed up with and its password; it has no constructor for an API key.
- **A JDK.** The steps were written with JDK 21. The SDK's own Gradle build needs JDK 8, as [Installing an SDK](https://docs.textcontrol.com/reporting-cloud/installation/sdks/#java) describes; this tutorial compiles the sources with `javac` instead, which works with a current JDK.
- **Git and curl**, to clone the SDK and download its two libraries. On Windows, run the commands in Git Bash, which Git for Windows installs.

### Check Your Java Version

Open a terminal and print the version of the Java compiler:

```bash
javac -version
```

The output names the version, such as `javac 21.0.12.1`. A message that `javac` isn't found means that only a Java runtime is installed; install a JDK.

### Build the SDK

1. Create a directory for the project and change to it:

    ```bash
    mkdir my-application && cd my-application
    ```
2. Clone the SDK:

    ```bash
    git clone https://github.com/TextControl/txtextcontrol-reportingcloud-java.git
    ```
3. Download the two libraries the SDK uses into a *lib* directory: Gson 1.7.2, which writes and reads the JSON of the requests, and `javafx-base`, whose class `javafx.util.Pair` the find and replace method uses:

    ```bash
    mkdir lib
    curl -L -o lib/gson-1.7.2.jar https://repo1.maven.org/maven2/com/google/code/gson/gson/1.7.2/gson-1.7.2.jar
    curl -L -o lib/javafx-base-21.jar https://repo1.maven.org/maven2/org/openjfx/javafx-base/21/javafx-base-21-linux.jar
    ```

    On Windows, download `javafx-base-21-win.jar`, and on macOS `javafx-base-21-mac.jar`, instead of the Linux file.
4. Compile the sources of the SDK and pack them into a JAR file in *lib*:

    ```bash
    javac -d sdk-classes -cp "lib/*" $(find txtextcontrol-reportingcloud-java/src/main/java -name "*.java")
    jar cf lib/reportingcloud-1.3.jar -C sdk-classes .
    ```

`javac` notes that *ReportingCloud.java* uses a deprecated API; the note doesn't stop the build. *lib* now holds the SDK, version 1.3, and the two libraries, the class path of your program.

### Set the Credentials

The program reads the user name and password from the environment variables `REPORTINGCLOUD_USERNAME` and `REPORTINGCLOUD_PASSWORD`, which keeps them out of your source code. Set them in the terminal in which you run the program:

```bash
export REPORTINGCLOUD_USERNAME="<email address>"
export REPORTINGCLOUD_PASSWORD="<password>"
```

In PowerShell, the commands are `$env:REPORTINGCLOUD_USERNAME = "<email address>"` and `$env:REPORTINGCLOUD_PASSWORD = "<password>"`. Put a password that contains `$` in single quotes in bash, so that the shell doesn't expand it.

### Write the Program

Create a file *MergeInvoice.java* in the project directory with the following code:

```java
import com.textcontrol.reportingcloud.MergeBody;
import com.textcontrol.reportingcloud.ReportingCloud;
import com.textcontrol.reportingcloud.ReturnFormat;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

public class MergeInvoice {

    // One line item of the merge block item
    static class Item {
        String qty;
        String item_no;
        String item_description;
        String item_unitprice;
        String item_discount;
        String item_total;

        Item(String qty, String itemNo, String description, String unitPrice, String discount, String total) {
            this.qty = qty;
            this.item_no = itemNo;
            this.item_description = description;
            this.item_unitprice = unitPrice;
            this.item_discount = discount;
            this.item_total = total;
        }
    }

    // One record: the field names are the merge field names of sample_invoice.tx
    static class Invoice {
        String yourcompany_companyname = "Text Control, LLC";
        String invoice_no = "778723";
        String billto_name = "Joey Montana";
        String billto_companyname = "Montana, LLC";
        String billto_street = "1 Washington Dr";
        String billto_zip = "27878";
        String billto_city = "Charlotte";
        String payment_due = "20/1/2016";
        List<Item> item = new ArrayList<>();
        String total_sub = "7673.4";
        String total_tax = "537.138";
        String total = "8210.538";
    }

    public static void main(String[] args) throws IOException {
        // Read the credentials from the environment, never from source code
        String username = System.getenv("REPORTINGCLOUD_USERNAME");
        String password = System.getenv("REPORTINGCLOUD_PASSWORD");

        if (username == null || password == null) {
            System.out.println("Set REPORTINGCLOUD_USERNAME and REPORTINGCLOUD_PASSWORD.");
            return;
        }

        ReportingCloud reportingCloud = new ReportingCloud(username, password);

        Invoice invoice = new Invoice();
        invoice.item.add(new Item("1", "1", "Item description 1", "2663", "20", "2130.40"));
        invoice.item.add(new Item("1", "2", "Item description 2", "5543", "0", "5543"));

        MergeBody mergeBody = new MergeBody(new Object[] { invoice });

        try {
            // Merge the template of the template storage and receive one PDF per record
            List<byte[]> documents = reportingCloud.mergeDocument(
                    mergeBody, "sample_invoice.tx", ReturnFormat.PDF);

            Files.write(Paths.get("invoice.pdf"), documents.get(0));
            System.out.println("Written to invoice.pdf");
        } catch (IllegalArgumentException exception) {
            // The message is the response body of the Web API
            System.out.println(exception.getMessage());
        }
    }
}
```

#### What the Code Does

- **`ReportingCloud`**, in the package `com.textcontrol.reportingcloud`, has a method for most endpoints of the Web API. The constructor takes the user name and password, which the SDK sends with every request in a Basic `Authorization` header, and sends the requests to `https://api.reporting.cloud`.
- **`Invoice` and `Item`** hold the data of one *record*, the data of one document. Gson writes their fields with the names they have in Java, so the field names are the names of the *merge fields* in the template, placeholders such as `invoice_no`, and the list `item` fills the *merge block* `item`, which repeats a table row for each line item. Merge fields without data are removed from the document.
- **`MergeBody`** carries the records, as an `Object[]` or an `Iterable<Object>`, and optionally merge settings and a template.
- **`mergeDocument`** sends the request `POST /v1/document/merge` and returns a `List<byte[]>` with one entry per created document. Its parameters are the body, the name of a stored template and the return format; overloads add whether to append all records into one document and test mode, both `false` by default.
- **`ReturnFormat.PDF`** is the return format `PDF`. The enumeration also has `PDFA`, `DOC`, `DOCX`, `RTF`, `TX` and `TXT`; a trial account creates no PDF/A.
- **`Files.write`** writes the first and only document to *invoice.pdf*.

### Run the Program

Compile the program with the SDK on the class path and run it in the terminal in which you set the credentials:

```bash
javac -cp "lib/*" MergeInvoice.java
java -cp ".:lib/*" MergeInvoice
```

On Windows, separate the entries of the class path with a semicolon: `java -cp ".;lib/*" MergeInvoice`. The program prints `Written to invoice.pdf`. Open the file in a PDF viewer to see the merged invoice; a trial account adds the ReportingCloud logo to every document. Each run is a merge request and counts 1 against the monthly document quota of your plan.

### Send the Template With the Request

A template doesn't have to be in your storage. `MergeBody` also takes the template itself as a byte array, which the SDK sends Base64-encoded in the `template` property of the request; the Web API merges it without storing it. The template can be any file of a supported format that your program reads with `Files.readAllBytes`. Here, the program downloads `sample_invoice.tx` from the storage with `downloadTemplate`, which counts against no quota, and sends it back. In *MergeInvoice.java*, replace everything from the line `MergeBody mergeBody = ...` to the line before `} catch` with:

```java
        try {
            // Download the template from the storage, as a template file of your own would be read
            byte[] template = reportingCloud.downloadTemplate("sample_invoice.tx");

            // Send the template with the data instead of naming a stored template
            MergeBody mergeBody = new MergeBody(new Object[] { invoice }, null, template);

            List<byte[]> documents = reportingCloud.mergeDocument(
                    mergeBody, null, ReturnFormat.PDF);

            Files.write(Paths.get("invoice_template_sent.pdf"), documents.get(0));
            System.out.println("Written to invoice_template_sent.pdf");
```

The `null` in place of the merge settings keeps the defaults, and the `null` in place of the template name says that no stored template is used: passing both a template name and a template, or neither, throws an `InvalidParameterException`. Compile and run the program again and open *invoice\_template\_sent.pdf*.

### Troubleshooting

When the Web API answers with an error, the SDK throws an `IllegalArgumentException` whose message is the response body; the program prints it. The status code isn't part of the message.

- **`Invalid username/password combination or invalid APIKey`.** The user name or password is wrong, or the environment variables are set in another terminal. The user name is the email address of the account.
- **`Template doesn't exist in storage`.** The template name is not in your template storage. Check it on the **My Templates** page of the portal.
- **`package com.textcontrol.reportingcloud does not exist`.** `javac` doesn't find the SDK. Run the commands in the project directory, in which *lib* holds *reportingcloud-1.3.jar*, and keep the quotes around `"lib/*"`.
- **`package javafx.util does not exist`** while the SDK is built. *lib* doesn't hold the `javafx-base` file of step 3.
- **`Could not determine java version`** from `gradlew` in the SDK's directory. Its Gradle 2.9 doesn't run on a current JDK; build the SDK with `javac` as above.

### Next Steps

- [Web API](https://docs.textcontrol.com/reporting-cloud/getting-started/web-api/llms-full.txt): the request of this program with curl.
- [Merge Data](https://docs.textcontrol.com/reporting-cloud/mail-merge/data-sources/llms-full.txt): how objects and lists map to merge fields and merge blocks.
- [Merge Settings](https://docs.textcontrol.com/reporting-cloud/mail-merge/settings/llms-full.txt): the settings that `MergeSettings` passes, and the defaults of the Java SDK.
- [Java](https://docs.textcontrol.com/reporting-cloud/sdks/java/llms-full.txt): every method of the Java SDK, its constructors and its errors.

---

## Nearby

- **Previous:** [PHP](https://docs.textcontrol.com/reporting-cloud/getting-started/php/llms-full.txt)
- **Next:** [Ruby](https://docs.textcontrol.com/reporting-cloud/getting-started/ruby/llms-full.txt)
