diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..a4e8d76
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,72 @@
+name: CI
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ java-version: ['8', '11', '17', '21']
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up JDK ${{ matrix.java-version }}
+ uses: actions/setup-java@v4
+ with:
+ java-version: ${{ matrix.java-version }}
+ distribution: 'temurin'
+
+ - name: Cache Maven dependencies
+ uses: actions/cache@v4
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-maven-
+
+ - name: Run tests
+ run: mvn clean test
+
+ - name: Build project
+ run: mvn clean package
+
+ - name: Generate JavaDoc
+ run: mvn javadoc:javadoc
+
+ - name: Upload test results
+ if: always()
+ uses: actions/upload-artifact@v4
+ with:
+ name: test-results-java-${{ matrix.java-version }}
+ path: target/surefire-reports/
+
+ code-quality:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up JDK 8
+ uses: actions/setup-java@v4
+ with:
+ java-version: '8'
+ distribution: 'temurin'
+
+ - name: Cache Maven dependencies
+ uses: actions/cache@v4
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-maven-
+
+ - name: Check for dependency updates
+ run: mvn versions:display-dependency-updates
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
new file mode 100644
index 0000000..64f777c
--- /dev/null
+++ b/.github/workflows/release.yml
@@ -0,0 +1,144 @@
+name: Release to Maven Central
+
+on:
+ push:
+ tags:
+ - 'v*'
+ workflow_dispatch:
+ inputs:
+ version:
+ description: 'Version to release (e.g., 1.5.4)'
+ required: true
+ type: string
+
+jobs:
+ release:
+ runs-on: ubuntu-latest
+ outputs:
+ version: ${{ steps.set-version.outputs.version }}
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Set up JDK 8
+ uses: actions/setup-java@v4
+ with:
+ java-version: '8'
+ distribution: 'temurin'
+
+ - name: Cache Maven dependencies
+ uses: actions/cache@v4
+ with:
+ path: ~/.m2/repository
+ key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }}
+ restore-keys: |
+ ${{ runner.os }}-maven-
+
+ - name: Import GPG key
+ env:
+ GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
+ GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
+ run: |
+ echo "$GPG_PRIVATE_KEY" | base64 --decode | gpg --batch --import
+ echo "allow-preset-passphrase" >> ~/.gnupg/gpg-agent.conf
+ echo "pinentry-mode loopback" >> ~/.gnupg/gpg.conf
+ gpg --list-secret-keys --keyid-format LONG
+
+ - name: Configure Maven settings
+ env:
+ MAVEN_CENTRAL_USERNAME: ${{ secrets.MAVEN_CENTRAL_USERNAME }}
+ MAVEN_CENTRAL_TOKEN: ${{ secrets.MAVEN_CENTRAL_TOKEN }}
+ run: |
+ mkdir -p ~/.m2
+ cat > ~/.m2/settings.xml << EOF
+
+
+
+ central
+ ${MAVEN_CENTRAL_USERNAME}
+ ${MAVEN_CENTRAL_TOKEN}
+
+
+
+ EOF
+
+ - name: Set version from tag
+ id: set-version
+ if: startsWith(github.ref, 'refs/tags/')
+ run: |
+ VERSION=${GITHUB_REF#refs/tags/v}
+ echo "VERSION=$VERSION" >> $GITHUB_ENV
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
+ mvn versions:set -DnewVersion=$VERSION -DgenerateBackupPoms=false
+
+ - name: Set version from input
+ id: set-version-input
+ if: github.event_name == 'workflow_dispatch'
+ run: |
+ VERSION=${{ github.event.inputs.version }}
+ echo "VERSION=$VERSION" >> $GITHUB_ENV
+ echo "version=$VERSION" >> $GITHUB_OUTPUT
+ mvn versions:set -DnewVersion=$VERSION -DgenerateBackupPoms=false
+
+ - name: Run tests
+ run: mvn clean test
+
+ - name: Deploy to Maven Central
+ env:
+ GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}
+ run: |
+ mvn clean deploy -Dgpg.passphrase=$GPG_PASSPHRASE
+
+ - name: Create GitHub Release
+ if: startsWith(github.ref, 'refs/tags/')
+ uses: actions/github-script@v7
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const releaseBody = `## Mixpanel Java SDK v${process.env.VERSION}
+
+ ### Maven
+ \`\`\`xml
+
+ com.mixpanel
+ mixpanel-java
+ ${process.env.VERSION}
+
+ \`\`\`
+
+ ### Changes
+ See [CHANGELOG](https://github.com/mixpanel/mixpanel-java/blob/master/CHANGELOG.md) for details.
+
+ ### Links
+ - [Maven Central](https://central.sonatype.com/artifact/com.mixpanel/mixpanel-java/${process.env.VERSION})
+ - [JavaDoc](http://mixpanel.github.io/mixpanel-java/)`;
+
+ await github.rest.repos.createRelease({
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ tag_name: context.ref.replace('refs/tags/', ''),
+ name: `Release ${process.env.VERSION}`,
+ body: releaseBody,
+ draft: false,
+ prerelease: false
+ });
+
+ verify:
+ needs: release
+ runs-on: ubuntu-latest
+ if: success()
+
+ steps:
+ - name: Wait for Maven Central sync
+ run: sleep 300 # Wait 5 minutes for synchronization
+
+ - name: Verify artifact on Maven Central
+ run: |
+ VERSION=${{ needs.release.outputs.version }}
+ RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" https://repo1.maven.org/maven2/com/mixpanel/mixpanel-java/${VERSION}/mixpanel-java-${VERSION}.jar)
+ if [ $RESPONSE -eq 200 ]; then
+ echo "✅ Artifact successfully published to Maven Central"
+ else
+ echo "⚠️ Artifact not yet available on Maven Central (HTTP $RESPONSE). This is normal - it may take up to 30 minutes to appear."
+ fi
\ No newline at end of file
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..fb0b356
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1,158 @@
+# CLAUDE.md
+
+This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
+
+## Project Overview
+
+This is the official Mixpanel tracking library for Java - a production-ready library for sending analytics events and user profile updates to Mixpanel from Java server-side applications.
+
+## Release Process
+
+### Quick Commands for Releases
+
+```bash
+# 1. Update version (remove -SNAPSHOT from pom.xml)
+mvn versions:set -DnewVersion=1.5.4
+
+# 2. Run tests
+mvn clean test
+
+# 3. Deploy to Maven Central Portal
+mvn clean deploy
+
+# 4. After release, prepare next version
+mvn versions:set -DnewVersion=1.5.5-SNAPSHOT
+```
+
+### Key Files
+- **RELEASE.md**: Complete release documentation with step-by-step instructions
+- **.github/workflows/release.yml**: Automated release workflow triggered by version tags
+- **.github/workflows/ci.yml**: Continuous integration for all PRs and master commits
+
+### Maven Central Portal
+- The project uses the new Maven Central Portal (not the deprecated OSSRH)
+- Deployments are visible at: https://central.sonatype.com/publishing/deployments
+- Published artifacts: https://central.sonatype.com/artifact/com.mixpanel/mixpanel-java
+
+### Required GitHub Secrets for CI/CD
+- `GPG_PRIVATE_KEY`: Base64-encoded GPG private key
+- `GPG_PASSPHRASE`: GPG key passphrase
+- `MAVEN_CENTRAL_USERNAME`: Maven Central Portal username
+- `MAVEN_CENTRAL_TOKEN`: Maven Central Portal token
+
+## Build and Development Commands
+
+```bash
+# Build the project and create JAR
+mvn clean package
+
+# Run all tests
+mvn test
+
+# Run a specific test class
+mvn test -Dtest=MixpanelAPITest
+
+# Run a specific test method
+mvn test -Dtest=MixpanelAPITest#testBuildEventMessage
+
+# Install to local Maven repository
+mvn install
+
+# Generate JavaDoc
+mvn javadoc:javadoc
+
+# Clean build artifacts
+mvn clean
+
+# Run the demo application (after building)
+java -cp target/mixpanel-java-1.5.3.jar:target/classes:lib/json-20231013.jar com.mixpanel.mixpanelapi.demo.MixpanelAPIDemo
+```
+
+## High-Level Architecture
+
+### Core Design Pattern
+The library implements a **Producer-Consumer** pattern with intentional thread separation:
+
+1. **Message Production** (`MessageBuilder`): Creates properly formatted JSON messages on application threads
+2. **Message Batching** (`ClientDelivery`): Collects messages into efficient batches (max 50 per request)
+3. **Message Transmission** (`MixpanelAPI`): Sends batched messages to Mixpanel servers
+
+This separation allows for flexible threading models - the library doesn't impose any specific concurrency pattern, letting applications control their own threading strategy.
+
+### Key Architectural Decisions
+
+**No Built-in Threading**: Unlike some analytics libraries, this one doesn't start background threads. Applications must manage their own async patterns, as demonstrated in `MixpanelAPIDemo` which uses a `ConcurrentLinkedQueue` with producer/consumer threads.
+
+**Message Format Validation**: `MessageBuilder` performs validation during message construction, throwing `MixpanelMessageException` for malformed data before network transmission.
+
+**Batch Encoding**: Messages are JSON-encoded, then Base64-encoded, then URL-encoded for HTTP POST transmission. This triple encoding ensures compatibility with Mixpanel's API requirements.
+
+**Network Communication**: Uses Java's built-in `java.net.URL` and `URLConnection` classes - no external HTTP client dependencies. Connection timeout is 2 seconds, read timeout is 10 seconds.
+
+### Message Types and Endpoints
+
+The library supports three message categories, each sent to different endpoints:
+
+- **Events** (`/track`): User actions and behaviors
+- **People** (`/engage`): User profile updates (set, increment, append, etc.)
+- **Groups** (`/groups`): Group profile updates
+
+Each message type has specific JSON structure requirements validated by `MessageBuilder`.
+
+## Package Structure
+
+All production code is in the `com.mixpanel.mixpanelapi` package:
+
+- `MixpanelAPI`: HTTP communication with Mixpanel servers
+- `MessageBuilder`: Constructs and validates JSON messages
+- `ClientDelivery`: Batches multiple messages for efficient transmission
+- `Config`: Contains API endpoints and configuration constants
+- `Base64Coder`: Base64 encoding utility (modified third-party code)
+- `MixpanelMessageException`: Runtime exception for message format errors
+- `MixpanelServerException`: IOException for server rejection responses
+
+## Testing Approach
+
+Tests extend JUnit 4's `TestCase` and are located in `MixpanelAPITest`. The test suite covers:
+
+- Message format validation for all message types
+- Property operations (set, setOnce, increment, append, union, remove, unset)
+- Large batch delivery behavior
+- Encoding verification
+- Error conditions and exception handling
+
+When adding new functionality, follow the existing test patterns - each message type operation has corresponding test methods that verify both the JSON structure and the encoded format.
+
+## Common Development Tasks
+
+### Adding a New Message Type
+1. Add the message construction method to `MessageBuilder`
+2. Validate required fields and structure
+3. Add corresponding tests in `MixpanelAPITest`
+4. Update `ClientDelivery` if special handling is needed
+
+### Modifying Network Behavior
+Network configuration is centralized in `MixpanelAPI.sendData()`. Connection and read timeouts are hardcoded but could be made configurable by modifying the `Config` class.
+
+### Debugging Failed Deliveries
+The library throws `MixpanelServerException` with the HTTP response code and server message. Check:
+1. Token validity in `MessageBuilder` constructor
+2. Message size (batches limited to 50 messages)
+3. JSON structure using the test suite patterns
+
+## Dependencies
+
+The library has minimal dependencies:
+- **Production**: `org.json:json:20231013` for JSON manipulation
+- **Test**: `junit:junit:4.13.2` for unit testing
+- **Java Version**: Requires Java 8 or higher
+
+## API Patterns to Follow
+
+When working with this codebase:
+
+1. **Immutable Messages**: Once created by `MessageBuilder`, JSON messages should not be modified
+2. **Fail Fast**: Validate message structure early in `MessageBuilder` rather than during transmission
+3. **Preserve Thread Safety**: `MessageBuilder` instances are NOT thread-safe; create one per thread
+4. **Batch Appropriately**: `ClientDelivery` handles batching; don't exceed 50 messages per delivery
+5. **Exception Handling**: Distinguish between `MixpanelMessageException` (client error) and `MixpanelServerException` (server error)
\ No newline at end of file
diff --git a/README.md b/README.md
index a4d254d..f96e562 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ Latest Version
```
-You can alternatively download the library jar directly from Maven [here](http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22com.mixpanel%22%20AND%20a%3A%22mixpanel-java%22).
+You can alternatively download the library jar directly from Maven Central [here](https://central.sonatype.com/artifact/com.mixpanel/mixpanel-java).
How To Use
----------
diff --git a/pom.xml b/pom.xml
index 280b269..be3d320 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
com.mixpanel
mixpanel-java
- 1.5.3
+ 1.5.4-SNAPSHOT
jar
mixpanel-java
@@ -44,22 +44,24 @@
- ossrh
- https://oss.sonatype.org/content/repositories/snapshots
+ central
+ https://central.sonatype.com/repository/maven-snapshots/
+
- org.sonatype.plugins
- nexus-staging-maven-plugin
- 1.6.3
+ org.sonatype.central
+ central-publishing-maven-plugin
+ 0.9.0
true
- ossrh
- https://oss.sonatype.org/
- false
+ central
+ mixpanel-java-${project.version}
+
+ false
@@ -93,7 +95,7 @@
org.apache.maven.plugins
maven-gpg-plugin
- 1.4
+ 3.2.4
sign-artifacts
@@ -101,22 +103,15 @@
sign
+
+
+ --pinentry-mode
+ loopback
+
+
-
-
- org.sonatype.plugins
- nexus-staging-maven-plugin
- 1.6.3
- true
-
- ossrh
- https://oss.sonatype.org/
- true
-
-
-