> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ancestraldev.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Java License Validation with Avalex

> Validate Avalex software licenses in Java applications using java.net.http.HttpClient and MessageDigest for HWID generation.

This guide demonstrates how to integrate Avalex license validation into a Java application (Java 11+). It uses the built-in `java.net.http.HttpClient` and `java.security.MessageDigest` libraries, requiring zero third-party dependencies.

## Prerequisites

* **Java 11** or newer (utilizes native `java.net.http.HttpClient`).

## Full Working Example

The following class is self-contained and ready to run in any Java 11+ application. Adjust `AVALEX_API_URL` and `PRODUCT_ID` to match your deployment environment.

```java theme={null}
package com.avalex.licensing;

import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.time.Duration;

public class AvalexLicenseValidator {

    private static final String AVALEX_API_URL = "http://localhost:8080/licenses/validate";
    private static final String PRODUCT_ID = "prod_enterprise_2026";

    private static final HttpClient HTTP_CLIENT = HttpClient.newBuilder()
            .connectTimeout(Duration.ofSeconds(5))
            .build();

    /**
     * Generates a stable hardware identifier (HWID) based on machine properties.
     */
    public static String getHwid() {
        try {
            String osName = System.getProperty("os.name").toLowerCase();
            String userName = System.getProperty("user.name");
            String arch = System.getProperty("os.arch");
            String rawId = osName + ":" + userName + ":" + arch;

            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(rawId.getBytes(StandardCharsets.UTF_8));
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) hexString.append('0');
                hexString.append(hex);
            }
            return hexString.toString().toUpperCase().substring(0, 32);
        } catch (Exception e) {
            return "HWID-FALLBACK-JAVA-CLIENT";
        }
    }

    /**
     * Validates a license against the Avalex Core API.
     *
     * @param licenseId The license key provided by the user
     * @return true if valid; false if invalid, expired, rate-limited, or error
     */
    public static boolean validateLicense(String licenseId) {
        String hwid = getHwid();
        String jsonPayload = String.format(
            "{\"licenseId\":\"%s\",\"productId\":\"%s\",\"hwid\":\"%s\"}",
            escapeJson(licenseId),
            escapeJson(PRODUCT_ID),
            escapeJson(hwid)
        );

        try {
            HttpRequest request = HttpRequest.newBuilder()
                    .uri(URI.create(AVALEX_API_URL))
                    .timeout(Duration.ofSeconds(5))
                    .header("Content-Type", "application/json")
                    .POST(HttpRequest.BodyPublishers.ofString(jsonPayload))
                    .build();

            HttpResponse<String> response = HTTP_CLIENT.send(request, HttpResponse.BodyHandlers.ofString());

            if (response.statusCode() == 200) {
                return response.body().contains("\"valid\":true") || response.body().contains("\"valid\": true");
            } else if (response.statusCode() == 429) {
                System.err.println("[Avalex] Rate limit reached. Backing off.");
                return false;
            } else {
                System.err.println("[Avalex] Validation error status: " + response.statusCode());
                return false;
            }
        } catch (Exception e) {
            System.err.println("[Avalex] Network or connection failure: " + e.getMessage());
            return false;
        }
    }

    private static String escapeJson(String raw) {
        return raw.replace("\\", "\\\\").replace("\"", "\\\"");
    }

    public static void main(String[] args) {
        String testLicense = "LIC-8829-X19";
        boolean isValid = validateLicense(testLicense);

        if (isValid) {
            System.out.println("License active! Launching application...");
        } else {
            System.err.println("License invalid or expired. Shutting down.");
        }
    }
}
```

## Code Walkthrough

**1. HWID Generation (`getHwid`)** Uses Java's `System.getProperty` to gather machine details (`os.name`, `user.name`, `os.arch`), hashes them with `MessageDigest.getInstance("SHA-256")`, and formats the output into a uppercase 32-character hex string.

**2. Asynchronous HTTP POST Request** Uses `java.net.http.HttpClient` with a 5-second connection timeout to call `POST /licenses/validate`.

**3. Status Handling & Rate Limiting** Inspects HTTP status code:

* `200 OK`: Parses JSON response for `"valid": true`.
* `429 Too Many Requests`: Logs a rate-limit warning and returns `false`.
* `Other / Transport Error`: Catches exceptions and returns `false`.

<Tip>
  Cache successful validation results locally using an encrypted storage mechanism to support a **24–72 hour offline grace period**.
</Tip>
